(游戏展示) 爱玩游戏的你是不是也经常想要自己动手制作游戏呢?没问题!今天我们就手把手教你用Python写出有趣的小游戏! 不管你是学生还是家长,我们都希望可以一起来学习计算机思维。如果你之前有关注我们公众号的话,相信你已经了解到为什么Python是最适合青少年学习的编程语言(点击蓝色文字自动跳转)。 我们今天要制作的小游戏是Bunnies vs. Badgers (兔獾大作战)。游戏中的兔子通过射箭抵御獾的进攻,从而保卫自己的四个堡垒。 (最终效果) 准备工作:安装Python 在这篇文章中,我们使用的是Python2.7 Windows32位下载地址:https://www./ftp/python/2.7.13/python-2.7.13.msi Windows64位下载地址:https://www./ftp/python/2.7.13/python-2.7.13.amd64.msi Mac下载地址:https://www./ftp/python/2.7.13/python-2.7.13-macosx10.6.pkg 成功安装后,你将在“所有程序”中找到IDLE, 这是Python的集成开发学习环境(Integrated Development and Learning Environment)。运行IDLE就可以开始愉快地写码了! 如果你要想要测试一下Python是否正常运行,可以试着输入print 1+1并按回车键。如果输出的是2,那么恭喜你!你的Python是正常的! 接下来你需要安装PyGame来用Python做游戏。 PyGame是Python的一个库(library)。Python有很多这样的库,库内自带能完成各种功能的许多函数(functions)。例如,我们可以使用PyGame导入游戏的图片,音频等 —— PyGame让制作Python小游戏变得更简单了! PyGame Windows下载地址:http:///ftp/pygame-1.9.1.win32-py2.7.msi PyGame Mac 下载地址:http:///ftp/pygame-1.9.1release--32bit-py2.7-macosx10.3.dmg 为了测试PyGame是否正常运行,你可以打开IDLE并输入import pygame,如果没有出现任何提示,PyGame就安装成功了! 运行文件中的Python代码 在之前的测试中,我们使用了print和import写了两行短代码,但如果我们要写一些比较大的程序(比如游戏),就需要把代码保存在一个文件里。在IDLE中,只要点击File – New Window新建文件就能将你写的代码保存在这个文件中。 添加游戏素材 我们马上就可以开始写游戏了!不过在此之前,我们需要一些图像和音频素材。你可以拉到文章最后,扫码关注码趣学院获取素材压缩包! 解压缩文件后,你将得到一个名为resources 的文件夹,所有的音频(audio)和图像(images)文件已经分类放好了!你需要做的是为你的游戏创建一个文件夹,如上图的PyGame,然后将resources文件夹放在PyGame文件夹内。 我们现在可以开始做小兔子和獾了! 第一步:添加小兔子 首先,打开IDLE并新建窗口,然后在编辑窗口内输入以下代码: # 1 – 导入pygame库 import pygame from pygame.locals import * # 2 – 初始化游戏 pygame.init() width, height = 640, 480 screen=pygame.display.set_mode((width, height)) # 3 – 载入图片 player = pygame.image.load('resources/images/dude.png') # 4 – 不断循环 while 1: # 5 – 每次绘图前清除屏幕 screen.fill(0) # 6 – 绘制屏幕组成元素 screen.blit(player, (100,100)) # 7 – 刷新屏幕 pygame.display.flip() # 8 – 循环事件 for event in pygame.event.get(): # 检验事件是否是X键 if event.type==pygame.QUIT: # 如果是的话就退出游戏 pygame.quit() exit(0) 将写好的代码文件保存在你的PyGame文件夹中(我们刚刚创建的),并将文件命名为game.py。 现在让我们一步步分析上面的代码: 1. 导入pygame库 这一步让你可以直接使用pygame库内的函数来实现各种功能(如导入图像,音频等)。 2. 初始化游戏 初始化游戏并设置一个640 x 480的窗口。 3. 载入图片 载入小兔子的图片。 4. 不断循环 循环执行被缩进(前面有空格)的代码。 小提示:Python使用缩进来区分代码块,所以缩进在Python中十分重要。 5. 每次绘图前清除屏幕 每次绘图前先用黑色填充整个窗口。 6. 绘制屏幕组成元素 在坐标x=100,y=100的地方放上小兔子。 7. 刷新屏幕 8. 循环事件 检查是否有新的事件(event),在这里,这个事件是退出(quit)命令。如果有的话,退出程序。 小提示:其实根据PyGame规定,你不需要使用pygame.quit()就能够退出程序,因为当编译器关闭的时候程序会自动关闭。但是在Mac中,如果不用pygame.quit(),游戏不会自动停止。 如果你执行上面的代码(在IDLE的菜单栏中点Run或Run Module),你将会看到一个类似下图的窗口: 我们可以看到,小兔子已经在屏幕上做好准备了! 但是这一片黑黑的屏幕看上去略吓人,而且屏幕上孤零零的一只小兔子似乎十分孤独…让我们加点背景和其他东西吧~ 第二步:制作游戏场景 现在我们要制作游戏场景,这只需要多用几次screen.blit()就可以做到。 在代码的第三部分 (#3 )载入小兔子图片后,加上下面两行代码: grass = pygame.image.load('resources/images/grass.png') castle = pygame.image.load('resources/images/castle.png') 这两行代码载入了两张不同的图片并把图片分别放入了不同的变量(variable)中。现在这两张图片就能出现在屏幕上了。不过你会发现,由grass.png载入的草坪背景不能覆盖整个640 x 480的窗口。这意味着你必须调整图片来完全覆盖窗口。 在第六部分(#6)的开头添加以下代码(在确定小兔子位置之前):
for x in range(width/grass.get_width()+1): for y in range(height/grass.get_height()+1): screen.blit(grass,(x*100,y*100)) screen.blit(castle,(0,30)) screen.blit(castle,(0,135)) screen.blit(castle,(0,240)) screen.blit(castle,(0,345)) 让我们一起来看看上面的代码~for语句先使用x进行循环。在这一层循环中,代码又通过y进行内层循环,然后把草坪图片放在循环得到的x, y 坐标上。接下来的几行代码在屏幕上放置堡垒(castle)。 如果你现在执行程序,你将看到下图的场景: 是不是比刚才好多了?:) 第三步:让小兔子动起来! 接下来我们要真正开始加入游戏元素啦!比如通过按键盘让小兔子动起来。 首先,我们需要让程序追踪玩家什么时候按下了什么键。将下面两行代码添加到第二部分(#2)的末尾(在设定游戏窗口的高和宽之后):
keys = [False, False, False, False] playerpos=[100,100] 在这两行代码中,我们创建了一个名为keys的数组(array),其中有4个False值,每个值对应W、A、S、D中的一个按键。 变量playerpos对应小兔子位置坐标。因为在游戏进行的过程中,小兔子会不断移动,所以用一个变量储存小兔子的位置更加方便。 现在,我们要修改现有的代码,把小兔子图片放在playerpos对应的坐标上。把第六部分(#6)中的这行代码: screen.blit(player, (100, 100)) 改为: screen.blit(player, playerpos) 接下来,我们要使用PyGame自带的event.key函数来检测玩家按下了什么键,并相应地将keys内的False值改为True。
在第八部分(#8)的最后,紧接着event.type == pygame.QUIT后,加上这些代码(缩进和pygame.QUIT的if代码块相同):
if event.type == pygame.KEYDOWN: if event.key==K_w: keys[0]=True elif event.key==K_a: keys[1]=True elif event.key==K_s: keys[2]=True elif event.key==K_d: keys[3]=True if event.type == pygame.KEYUP: if event.key==pygame.K_w: keys[0]=False elif event.key==pygame.K_a: keys[1]=False elif event.key==pygame.K_s: keys[2]=False elif event.key==pygame.K_d: keys[3]=False 哇哦!这里有好多行代码呢!不过如果我们将这些代码拆分来看,其实并不是太难。
首先,我们要看看是否(if)有键被按下或者松开了,接着检测是哪个键被按下或者松开了。如果检测到的键是W、A、S、D中的某一个,就相应更新keys数列中的值。
接着,你需要根据玩家按下的键更新playerpos变量,这个非常简单,只需要把下面代码加到目前所写的代码最后(使用一个缩进,和for循环同一层)
# 9 – 移动小兔子 if keys[0]: playerpos[1]-=5 elif keys[2]: playerpos[1]+=5 if keys[1]: playerpos[0]-=5 elif keys[3]: playerpos[0]+=5 这些代码检测哪个键被按下了,并对小兔子的x或y坐标做相应的增减。
运行游戏,你会看到和刚才相似的场景。试着按按WASD,你的小兔子动起来了! 第四步:让小兔子转向 现在,你的小兔子可以根据你的按键移动了! 不过现在小兔子一直面向右边。如果能用鼠标让小兔子面对不同的方向,不就更酷了吗!我们只需要用一些简单的三角函数知识就能解决这个问题。
让我们一起看看下面的插图 如上图,假设(5,3)是小兔子的位置,(2,4)是鼠标的位置,旋转角度z就可以由三角函数atan2来求出(diff x和diff y分别为x、y坐标的差)。求出旋转角度z以后,我们便可以相应地让小兔子转向。
如果你没有完全理解这部分也没关系,你还是可以继续我们的教程!不过我们应该能够由此意识到数学在编程中的重要性。
现在我们要把上面的数学式用到我们的游戏里了。我们可以用surface.rotate(degress)函数来让小兔子转向。 小提示:用atan2求出的z值是用弧度制(radians)表示的。 atan2函数来自Python 的math库。所以我们要先把下面这行代码加到第一部分(#1)开头: import math 接着,把第六部分(#6)的最后一行(带有player.blit的这行)改成下面的代码:
# 6.1 – 设定小兔子的位置并让它转向 position = pygame.mouse.get_pos() angle = math.atan2(position[1]-(playerpos[1]+32),position[0]-(playerpos[0]+26)) playerrot = pygame.transform.rotate(player, 360-angle*57.29) playerpos1 = (playerpos[0]-playerrot.get_rect().width/2, playerpos[1]-playerrot.get_rect().height/2) screen.blit(playerrot, playerpos1) 让我们一起看看上面的代码。首先,我们要知道鼠标的位置和小兔子的位置。接着,我们atan2函数得到角度。然后,我们要将atan2函数得到的角度从弧度制(radians)转化为角度制(degrees)(把弧度乘以57.29或者360/2π)。
因为小兔子被转向了,它的位置也会相应改变。所以最后我们要计算小兔子的新位置,然后让小兔子走到屏幕上相应的地方。
再次运行游戏。现在,如果你移动你的鼠标,小兔子也会跟着转动了! 第五步:小兔子,射击! 你的小兔子已经能够转动了,现在让我们给它添加一些动作!让小兔子在敌人来袭的时候向敌人射箭!
这一步可能稍微有些复杂,因为你需要用代码不停追踪弓箭的位置,计算弓箭射出的角度,并在它们超出屏幕范围时把它们删掉。
首先,在第二部分(#2)末尾添加一些要用到的变量:
acc=[0,0] arrows=[] 第一个变量acc检测玩家的准确度,第二个变量arrows用于追踪所有弓箭。变量acc实际上是一个由两个数字组成的列表(list),它记录了射击的次数和被击中的獾的数量,我们要用这两个数字来计算精确度百分比。
接着,在第三部分(#3)末尾载入弓箭的图像: arrow = pygame.image.load('resources/images/bullet.png') 当玩家点击鼠标的时候,需要有弓箭射出。把下面的代码加到第八部分(#8)末尾来实现这一功能:
if event.type==pygame.MOUSEBUTTONDOWN: position=pygame.mouse.get_pos() acc[1]+=1 arrows.append([math.atan2(position[1]-(playerpos1[1]+32),position[0]-(playerpos1[0]+26)),playerpos1[0]+32,playerpos1[1]+32]) 这些代码检测玩家是否点击了鼠标。如果点击了鼠标,就记下鼠标的位置,并根据小兔子的位置和鼠标的位置得出弓箭射出的角度。这些角度值被记录在arrows列表中。
我们现在要让弓箭显示在屏幕上,把下面的代码加在#6.1末尾:
# 6.2 – 在屏幕上显示弓箭 for bullet in arrows: index=0 velx=math.cos(bullet[0])*10 vely=math.sin(bullet[0])*10 bullet[1]+=velx bullet[2]+=vely if bullet[1]<-64 or bullet[1]>640 or bullet[2]<-64 or bullet[2]>480: arrows.pop(index) index+=1 for projectile in arrows: arrow1 = pygame.transform.rotate(arrow, 360-projectile[0]*57.29) screen.blit(arrow1, (projectile[1], projectile[2])) 弓箭的x、y坐标velx和vely的值都是用基本的三角函数计算出来的。10是弓箭的速度。if语句用于检测弓箭是否超出了窗口范围,如果超出的话,就把超出的弓箭从列表中删去。第二个for语句对所有弓箭进行循环,并画出它们正确的位置和角度。
现在运行游戏, 你应该能让小兔子在你点击鼠标的时候射击了! 第六步:獾,拿起你们的武器! 好,现在我们造出了堡垒,也有了保卫城堡的主角兔子,是时候让敌人上场了!
在这一步中,我们要让獾随机地从不同方向出现并冲向堡垒。在游戏进行的过程中,獾也会变得越来越多。我们先列个清单,看看我们需要做些什么: 1. 把敌人獾加到一个列表(list)中 2. 每过一帧就更新一次獾的位置,并检测它们是否超出了屏幕范围 3. 把獾显示在屏幕上 首先,把下面的代码加到第二部分(#2)的末尾:
badtimer=100 badtimer1=0 badguys=[[640,100]] healthvalue=194 上面的代码设置了一个计时器和一些其他的变量,所以每隔一段时间,游戏里就会出现一只新的獾。每过一帧,计时器badtimer就减少1,直到它的值变为0,这时让一只新的獾出现在屏幕上。
把下面两行代码加到第三部分(#3)末尾:
badguyimg1 = pygame.image.load('resources/images/badguy.png') badguyimg=badguyimg1 第一行和前面其他载入图片等代码都差不多。第二行复制了獾的图像。
接着,你需要更新獾的位置,让它们相应地显示在屏幕上。把下面的代码加到#6.2后面:
# 6.3 – 在屏幕上显示獾 if badtimer==0: badguys.append([640, random.randint(50,430)]) badtimer=100-(badtimer1*2) if badtimer1>=35: badtimer1=35 else: badtimer1+=5 index=0 for badguy in badguys: if badguy[0]<> badguys.pop(index) badguy[0]-=7 index+=1 for badguy in badguys: screen.blit(badguyimg, badguy) 第一行代码检查badtimer是否为0,如果是的话,就产生一只新獾,并根据badtimer已经被重设的次数来重新设置badtimer的值。第一个for循环更新了獾的x坐标,检测獾是否超出了屏幕范围,如果超出的话,就把它删除出列表。第二个for循环让所有在屏幕范围内的獾显示在屏幕上。
为了使用上面的random函数,我们需要用到random库。在第一部分末尾加上这行代码: import random 最后,把这行代码加到第四部分(#4)的while语句后面,这样每一帧结束后badtimer的值就减少1: badtimer-=1 再次运行游戏,现在小兔子可以射击,移动,转向,而獾也在试着冲向堡垒。 不过等等…为什么獾碰到堡垒时也没能摧毁它们呢?让我们赶紧补上一些代码 …
把这些代码加到#6.3的index+=1之前:
# 6.3.1 – 攻击堡垒 badrect=pygame.Rect(badguyimg.get_rect()) badrect.top=badguy[1] badrect.left=badguy[0] if badrect.left<> healthvalue -= random.randint(5,20) badguys.pop(index) # 6.3.3 - 下一个敌人 这些代码比较简单。如果某只獾的x坐标小于64,就删掉那只獾,并把堡垒的健康值(health value)随机地减少5到20点(之后,我们将把健康值显示在屏幕上)。
现在运行游戏,你应该能看到一群獾冲向堡垒,并在成功攻击堡垒后消失。虽然你现在不能看见,但是堡垒的健康值也在相应地减少。 第七步:成功击中敌人! 獾在攻击你的堡垒,不过小兔子的弓箭却伤害不了他们,这怎么行?在这一步中我们要让弓箭能够射杀敌人。
为了让弓箭射杀敌人,我们要对所有的獾进行循环,并在这一层循环中,对所有的弓箭进行循环,检测这些弓箭是否射到了獾。如果射到了,就删掉獾和弓箭,并给准确度acc加1点。
在#6.3.1之后加上这些代码:
#6.3.2 - 检测弓箭是否射中獾 index1=0 for bullet in arrows: bullrect=pygame.Rect(arrow.get_rect()) bullrect.left=bullet[1] bullrect.top=bullet[2] if badrect.colliderect(bullrect): acc[0]+=1 badguys.pop(index) arrows.pop(index1) index1+=1 在这些代码中有一点需要注意。If语句中的.colliderect是pygame自带的函数,能够检测两个矩形是否相交。剩下的代码执行的任务就和前面解释的一样。
如果你执行这个程序,会发现小兔子能够射杀敌人了! 第八步:显示健康值和时间 现在游戏进行得很不错了!我们现在需要不断记录分数,看看小兔子的防守做得怎么样。我们可以在屏幕左上角加一个“血槽”来表示现在堡垒的健康值,并在屏幕右上角显示存活的时间。
首先把下面的代码加在第七部分(#7)开头,让屏幕右上角显示时间:
# 6.4 – 显示时间 font = pygame.font.Font(None, 24) survivedtext = font.render(str((90000-pygame.time.get_ticks())/60000)+':'+str((90000-pygame.time.get_ticks())/1000%60).zfill(2), True, (0,0,0)) textRect = survivedtext.get_rect() textRect.topright=[635,5] screen.blit(survivedtext, textRect) 上面的代码设置了一个字号为24 的新字体,font.render把这个新字体用于屏幕上所有的函数。接着,我们把文字放在屏幕右上角(topright)并显示在屏幕上。 现在我们加“血槽”。不过在此之前,我们需要先加载血槽的图像。把下面两行代码加到第三部分(#3)末尾:
healthbar = pygame.image.load('resources/images/healthbar.png') health = pygame.image.load('resources/images/health.png') 第一张图片healthbar.png是用于整个血槽的红色图片。第二张图片health.png是用于显示当下健康值的绿色图片。 现在把下面的代码添加在#6.4之后
# 6.5 – 显示血槽 screen.blit(healthbar, (5,5)) for health1 in range(healthvalue): screen.blit(health, (health1+8,8)) 这几行代码首先显示出整个红色血槽,然后根据当下的健康值改变绿色的部分。
运行程序,你现在能够看到左上角的血槽和右上角的时间。 第九步:揭晓胜负 到目前为止,即使你的血槽空了,游戏还能继续,你还能继续攻击敌人。为了解决这个问题,我们需要加上决定胜负的条件。
为了确定胜负,我们需要退出主循环,进入胜负循环。在胜负循环中,你需要知道玩家是赢了还是输了,然后把胜负显示在屏幕上。
现在我们确定一下大致的胜负规则:
如果存活到了规定时间(90000ms 或 90s): · 停止运行游戏 · 把游戏结果设置为1(即胜利)
如果堡垒都被摧毁了 · 停止运行游戏 · 把游戏结果设置为0(即失败) 计算出射击的精确度acc
把这些代码加到最后:
#10 – 胜负判断 if pygame.time.get_ticks()>=90000: running=0 exitcode=1 if healthvalue<> running=0 exitcode=0 if acc[1]!=0: accuracy=acc[0]*1.0/acc[1]*100 else: accuracy=0 # 11 –在屏幕上显示胜负 if exitcode==0: pygame.font.init() font = pygame.font.Font(None, 24) text = font.render('Accuracy: '+str(accuracy)+'%', True, (255,0,0)) textRect = text.get_rect() textRect.centerx = screen.get_rect().centerx textRect.centery = screen.get_rect().centery+24 screen.blit(gameover, (0,0)) screen.blit(text, textRect) else: pygame.font.init() font = pygame.font.Font(None, 24) text = font.render('Accuracy: '+str(accuracy)+'%', True, (0,255,0)) textRect = text.get_rect() textRect.centerx = screen.get_rect().centerx textRect.centery = screen.get_rect().centery+24 screen.blit(youwin, (0,0)) screen.blit(text, textRect) while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit(0) pygame.display.flip() 小提示:其中,acc[0] * 1.0是把acc[0]转化为浮点数。如果不将其转化为浮点数的话,计算的结果将是整数而不是小数。 第一个if语句检测时间是否用完。第二个if语句检测堡垒是否被摧毁。第三个if语句计算玩家的设计精准度。在第十一部分(#11)中,if语句检测玩家的胜负情况并显示相应的图片。
为了显示与胜负相关的图片,我们需要先加载这些图片。把下面的两行代码加到第三部分(#3)末尾:
gameover = pygame.image.load('resources/images/gameover.png') youwin = pygame.image.load('resources/images/youwin.png') 最后,把第四部分(#4)中的:
# 4 – 不断循环 while 1: badtimer-=1 改为: # 4 – 不断循环 running = 1 exitcode = 0 while running: badtimer-=1 变量running判定游戏是否结束,变量exitcode判定玩家是赢了还是输了。
再次执行游戏,现在我们能知道输赢情况了! ![]() 第十步:音乐和音效 最后,让我们给游戏加点音乐和音效吧!
首先,我们需要把下面这行代码加到第二部分(#2)末尾: pygame.mixer.init() 然后把这些代码加到第三部分(#3)末尾来加载音效并设置音量:
# 3.1 – 载入音频 hit = pygame.mixer.Sound('resources/audio/explode.wav') enemy = pygame.mixer.Sound('resources/audio/enemy.wav') shoot = pygame.mixer.Sound('resources/audio/shoot.wav') hit.set_volume(0.05) enemy.set_volume(0.05) shoot.set_volume(0.05) pygame.mixer.music.load('resources/audio/moonlight.wav') pygame.mixer.music.play(-1, 0.0) pygame.mixer.music.set_volume(0.25) 上面这块代码大部分都是用于加载音效文件以及设置音量的。不过注意pygame.mixer.music.load这一行 —— 这行用于加载背景音乐,它的下一行则是让音乐不断重复播放。
现在,我们需要让音效根据我们的需要进行播放。根据注释(#后)的内容把代码放在相应的位置:
# 放在# 6.3.1 if badrect.left<> hit.play() # 放在#6.3.2 if badrect.colliderect(bullrect):之后 enemy.play() # 放在#8, if event.type==pygame.MOUSEBUTTONDOWN:之后 shoot.play() 运行游戏,现在我们有背景音乐和各种音效了! ![]() 大功告成! ![]() 你该为自己感到骄傲!你刚刚做了一个带有音乐,音效,防御者,攻击者和其他各种有趣元素的游戏!
当然,你也可以在这些代码的基础上加入自己的创新!比如把獾换成狐狸,或者升级小兔子的武器等等。
|
|