Program Arcade Games
With Python And PygameChapter 8: 动画入门
8.1 弹跳的矩形
在开始我们第一个动画之前,我们先使用第五章的pygame基础模板程序打开一个空白的屏幕。
代码pygame_base_template.py可以在这里找到:
ProgramArcadeGames.com/python_examples/f.php?file=pygame_base_template.py
我们会写一个在黑色背景下弹跳的白色矩形。 你也可以挑选自己喜欢的颜色,只要保证背景色和矩形颜色是不同的!
第一步,从基础模板起手把背景色从白色改成黑色。改动大概在46行左右。
screen.fill(BLACK)
接下来,画一个矩形。一个简单的巨新就足够了。这段代码应该在清理过屏幕之后,以及显示到显示器之前。
pygame.draw.rect(screen, WHITE, [50, 50, 50, 50])
每次循环的时候,矩形都会被画在(50,50)的位置。这是由列表里前两个50控制的。这两个不变更,矩形是不会移动的。
矩形是50像素宽,50像素高。维度是由列表里后两个数字控制的。 我们也可以把这个矩形称为正方形,因为它有着相同的宽和高。 我还是会坚持称它了矩形因为所有的正方形都还是矩形,而且根据不同的显示器和分辨率,像素也不一定是正方形的。 如果对此感兴趣的话查看像素比例表。
我们如何不停地改变位置而不是停在(50, 50)呢? 当然是使用一个变量! 下面的代码是这样做的第一步:
rect_x = 50 pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50])
把矩形向右移, 可以把x每一帧增加1。 下面这段代码很接近,但并没有真正实现。:
rect_x = 50 pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50]) rect_x += 1
问题就在于rect_x每次循环的时候都会重设成50。 要解决这个问题,把rect_x的初始化挪到循环之外。 下面这段代码就可以成功地将矩形向右侧移动。
# 从矩形的x位置开始 # 注意它是如何在主循环之外的 rect_x = 50 # -------- 程序主循环 ----------- while not done: for event in pygame.event.get(): # 用户做了些操作 if event.type == pygame.QUIT: # 如果用户点击了关闭 done = True # 树下旗帜表明我们已经完成了所以要退出循环 # 设置背景色 screen.fill(BLACK) pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50]) rect_x += 1
为了让盒子移动的更快些,增加rect_x的值可以不是1,而是每次增加5:
rect_x += 5
We can expand this code and increase both x and y, causing the square to move both down and right:
# 矩形的起始位置 rect_x = 50 rect_y = 50 # -------- 程序主循环 ----------- while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # 设置屏幕背景 screen.fill(BLACK) # 画出矩形 pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50]) # 移动矩形的起始位置 rect_x += 5 rect_y += 5
矩形移动的方向和速度可以用一个矢量来存储。 这样就使得改变放下和速度更加简单。 下面这段代码就使用了这样的变量来存储。
# 矩形的起始位置 rect_x = 50 rect_y = 50 # 矩形的方向和速度 rect_change_x = 5 rect_change_y = 5 # -------- 循环主程序 ----------- while done == False: for event in pygame.event.get(): # 用户做了些操作 if event.type == pygame.QUIT: # 如果用户点击了关闭 done = True # 树下旗帜表明我们已经完成了所以要退出循环 # 设置屏幕背景 screen.fill(BLACK) # 画出矩形 pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50]) # 移动矩形的起始位置 rect_x += rect_change_x rect_y += rect_change_y
一旦矩形触及到了屏幕边缘仍然会继续前进。 没有什么能让矩形反弹回来。要反转矩形移动的方向继续前行,一旦触到了屏幕底部,rect_change_y需要从5变成-5,也就是当rect_y比屏幕高度大的时候。 虾米的代码就会作出这样的检查并反转方向:
# 如有需要反转移动的方向 if rect_y > 450: rect_change_y = rect_change_y * -1
为什么检查rect_y是不是450? 如果屏幕是500像素高,那么检查500才应该是更加看似合理的猜想。 但请记得矩形是从左上角 开始画的。如果起始位置是500, 那么它会从500画到550, 在反弹之前已经超出屏幕了。 请看图例 8.1.
所以把矩形是50像素高考虑进去的虎,正确的反弹的位置是:
$500-50=450$.
下面的代码展示了一个在700x400的窗口中四面都会反弹的矩形:
# 如有需要反转移动的方向 if rect_y > 450 or rect_y < 0: rect_change_y = rect_change_y * -1 if rect_x > 650 or rect_x < 0: rect_change_x = rect_change_x * -1
感兴趣在尝试一个比矩形复杂一些的形状吗? 许多绘图的命令都是根据rect_x和rect_y的基础上操作的。 下面的代码画了一个嵌套在白色巨新里的红色矩形。红色矩形离白色的矩形的左上角的两边相距各10个像素, 所以总共维度小了20,看起来就想白色矩形包围着红色矩形,间隔10个像素。请看图例8.2.
# 在一个白色巨新内画一个红色矩形矩形ygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50]) pygame.draw.rect(screen, RED, [rect_x + 10, rect_y + 10 ,30, 30])
8.2 下雪动画
只有一个物体在动是不是不够? 一次让成百上千的物体一起动如何? 这里会使用从8.1节介绍的技术来绘制雪花下坠的动画。
8.2.1 代码解释
牙开始写这个程序,首先使用pygame的基础模板来打开一个空白屏幕。
代码pygame_base_template.py还是可以在这里找到:
ProgramArcadeGames.com/python_examples/f.php?file=pygame_base_template.py
我们可以使用随机数来生成诸如星星,雪花和雨滴的坐标位置。 最简单的办法是使用一个for循环在随机的坐标画圆形。 在主循环while循环内试着运行以下一些一下
for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) pygame.draw.circle(screen, WHITE, [x, y], 2)
试一下,会发现这个程序有一个奇怪的问题!每秒20次、每次经过循环的时候,都会在新的随机位置生成雪花。 试着调整雪花的数量来看它如何改变图像的。
很明显地,我们需要随机雪花的定位然后让它们聚集砸一个点上,我们不希望每秒生成20次新的定位。 我们需要一个列表来记录它们在哪儿。我们可以使用Python的列表来完成。这必须完成在主循环之前,否则程序会在每1/20秒给列表添加50个新的雪花。
for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) snow_list.append([x, y])
一旦雪花的位置被添加以后,它么可以像一般的列表一样访问。 下面的代码会同时打印第一个落点的x和y坐标, 存储在位置0:
print(snow_list[0])
我们只想要x坐标或者只想要y坐标怎么做? 我么可以列表嵌套列表。主列表记录所以的坐标。 在主列表里,每个坐标是一个列表(x的位置是0,y的位置是1)。 例如,这是三个坐标:
[[34, 10], [10, 50], [20, 18]]
要打印位置0的y坐标,先选择坐标0,然后选择在位置1的y的值。代码如下:
print(snow_list[0][1])
要打印位置是20的x的值,先选择坐标20,然后选择位置0的x的值: first select coordinate 20, and then the x value at position 0:
print(snow_list[20][0])
在主循环while循环至内, 我们可以使用一个for循环来画雪片列表里的每一个雪片。 请记得len(snow_list)会返回雪片列表的所有元素的数量。
# 处理雪片列表里的每一个雪片 for i in range(len(snow_list)): # 画雪片 pygame.draw.circle(screen, WHITE, snow_list[i], 2)
请记得,有两种类型的for循环。另一种类型的for循环也可以使用,就像这样:
# 处理列表里每一个雪片位置的复制品 for xy_coord in snow_list: # 画雪片 pygame.draw.circle(screen, WHITE, xy_coord, 2)
然而,因为我们是打算更改雪片的位置,所以这种更改其复制品的位置没有实际效用,所以不能采用。
如果一个程序的所有物体都会向下移动,就像下雪,那么我们需要扩展之前的for循环,来增加y坐标的值:
# 处理雪片列表里的每一个雪片 for i in range(len(snow_list)): # 画雪片 pygame.draw.circle(screen, WHITE, snow_list[i], 2) # 把雪片向下移动一个像素 snow_list[i][1] += 1
这会使得雪花下落,但是一旦离开了屏幕就没有新的出现了。添加以下的代码,雪花会重新在顶部的随机位置生成:
# 如果雪片已经落在屏幕底端之外 if snow_list[i][1] > 400: # 把它重设在顶端之上 y = random.randrange(-50, -10) snow_list[i][1] = y # 给它一个新的x坐标值 x = random.randrange(0, 400) snow_list[i][0] = x
我们也可以给列表里的不同元素添加不同尺寸、形状、颜色、速度和方向。 这会变得有些复杂,因为列表里需要记录各种数据类型。 我们现在就还是保持简单,不过一旦我们学习里12章中的“类”,管理多个对象的不同属性会变得很简单。
8.2.2 完整程序的清单
""" Animating multiple objects using a list. Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ Explanation video: http://youtu.be/Gkhz3FuhGoI """ # Import a library of functions called 'pygame' import pygame import random # Initialize the game engine pygame.init() BLACK = [0, 0, 0] WHITE = [255, 255, 255] # Set the height and width of the screen SIZE = [400, 400] screen = pygame.display.set_mode(SIZE) pygame.display.set_caption("Snow Animation") # Create an empty array snow_list = [] # Loop 50 times and add a snow flake in a random x,y position for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) snow_list.append([x, y]) clock = pygame.time.Clock() # Loop until the user clicks the close button. done = False while not done: for event in pygame.event.get(): # User did something if event.type == pygame.QUIT: # If user clicked close done = True # Flag that we are done so we exit this loop # Set the screen background screen.fill(BLACK) # Process each snow flake in the list for i in range(len(snow_list)): # Draw the snow flake pygame.draw.circle(screen, WHITE, snow_list[i], 2) # Move the snow flake down one pixel snow_list[i][1] += 1 # If the snow flake has moved off the bottom of the screen if snow_list[i][1] > 400: # Reset it just above the top y = random.randrange(-50, -10) snow_list[i][1] = y # Give it a new x position x = random.randrange(0, 400) snow_list[i][0] = x # Go ahead and update the screen with what we've drawn. pygame.display.flip() clock.tick(20) # Be IDLE friendly. If you forget this line, the program will 'hang' # on exit. pygame.quit()
这个例子展示了每个雪片在同一方向移动。 如何让每个雪片有自己的动画,有自己的方向? 如果你的游戏需要这些,请前去12章节学习如何使用类。 实验8会一步一步指导你创造成百上千不的元素,运动在不同的方向。
8.3 3D动画
从2D环境拓展到3D环境以及伴随的游戏物理机制的更改并没有想象中那么困难。 尽管这超出了这门课的范畴,但是还是值得来瞧一瞧这是如何湾趁的。
有一个免费的3D程序叫作Blender,它有一个游戏引擎可供程序员创建3D游戏。游戏中的3D物体可以通过附着的Python代码来控制它们的行动。
看一下这个图例8.3。它展示了一个绿色的器皿盛着几个物件。 蓝色的物件是由一个Python脚本控制、围绕着器皿移动、再同其他物体碰撞的。脚本如下所示,有许多同2D程序相同的特征。 有一个主循环,有一个x,y坐标的列表,有一些变量控制矢量。
主程序是由Blender控制的。下面的Python代码会砸游戏的每一帧渲染时被Blender召用 这也是为何Python代码没有主循环。但它是存在的。存砸存在>蓝色物体以x,y,z的形式记录位置。它可以通过blue_object.position这个变量来访问或者更改。位置0存x,位置1存y,位置2存z。
不像这一章先前介绍2D例子时使用的change_x和change_y变量,这个Blender的例子使用了关联数组:
blue_object["x_change"]
blue_object["y_change"]
if语句用拉检查蓝色物体是否抵达了屏幕边缘和反置的方向。 不像2D游戏里的像素,这里物理的坐标肯恩是浮点类型。 定义在5和6之间的一个位置,设置成5.5是被允许的。
# 导入Blender游戏引擎 import bge # 获得一份蓝色物体的参考 cont = bge.logic.getCurrentController() blue_object = cont.owner # 打印蓝色物体的坐标位置x,y print(blue_object.position[0], blue_object.position[1] ) # 根据x_change和y_change来改变位置 # x_change和y_change是蓝色物体这个对象所关联的游戏属性 blue_object.position[0] += blue_object["x_change"] blue_object.position[1] += blue_object["y_change"] # 检查物体是否到达了边缘 # 如果是的,反转方向。4个边缘都要囊括。 if blue_object.position[0] > 6 and blue_object["x_change"] > 0: blue_object["x_change"] *= -1 if blue_object.position[0] < -6 and blue_object["x_change"] < 0: blue_object["x_change"] *= -1 if blue_object.position[1] > 6 and blue_object["y_change"] > 0: blue_object["y_change"] *= -1 if blue_object.position[1] < -6 and blue_object["y_change"] < 0: blue_object["y_change"] *= -1
Blender可以在这里下载:
http://www.blender.org/
一个完整的Blender的例子在这里可以下载:
ProgramArcadeGames.com/chapters/08_intro_to_animation/simple_block_move.blend.
8.4 复习
8.4.1 选择题小测验
Click here for a multiple-choice quiz.
8.4.2 练习表
Click here for the chapter worksheet.
8.4.3 实验
Click here for the chapter lab.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin