分享

手把手教你零基础写Python小游戏——附赠完整代码 资源包

 长沙7喜 2018-01-29

(游戏展示)

爱玩游戏的你是不是也经常想要自己动手制作游戏呢?没问题!今天我们就手把手教你用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坐标velxvely的值都是用基本的三角函数计算出来的。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()


运行游戏,现在我们有背景音乐和各种音效了!


  大功告成!

你该为自己感到骄傲!你刚刚做了一个带有音乐,音效,防御者,攻击者和其他各种有趣元素的游戏!

 

当然,你也可以在这些代码的基础上加入自己的创新!比如把獾换成狐狸,或者升级小兔子的武器等等。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多