• [翻译]Python游戏编程入门



    自己学习翻译,原文链接

    此文为Tutorial Team成员 Julian Meyer, 一个13岁的Python开发者所写. 你可以去Google+或者Twitter找到他.

    你有没有想过,一个计算机游戏如何编写的?它并不像你想的那么复杂!

    这篇教程将教你编写一个小游戏,叫做兔子与獾(Bunnies and Badgers), 游戏内容是英雄兔子必须修筑城堡,防御獾群的攻击。:O

    这个游戏用Python编写. 这里的Python并不是指蟒蛇哦! :]

    Python是一门计算机编程语言. 我们选择Python来写该教程,因为它简单,有趣并且容易学习。

    如果你是一名Python新手,先阅读Think Python: How to Think Like a Computer Scientist这本书,将加快你的学习速度。

    然后,再回到这里——兔子与獾之间的战斗即将开始,让我们准备好随时进入战斗吧!

    开始之前:安装Python    


    如果你使用的是Windows操作系统,必须先安装Python。注意,安装版本是2.7.3,而不是3.3.0!安装好后,在开始菜单找到IDLE(Python GUI), 并启动。

    如果你的系统是Mac,系统已经预装Python!只需要打开终端(/Applications/Utilities/Terminal.app),输入python,并按下Enter键。

    笔记:如果你自己从Python官网下载并安装了程序(为了保证PyGame正常运行),确定Python启动的文件夹为"/Applications/Python 2.7"。

    正确安装后,启动Python会看到如下信息:

    Python 2.7.3 (v2.7.3:70274d53c1dd, Apr  9 2012, 20:52:43) 
    [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>>

    笔记:输入exit(),回车, 或者按Control+D, 可以退出Python提示符环境(尖括号提示符,>>>)。

    在Python提示符界面,输入 print 1+1,并按回车,屏幕输出2,程序运行正常!你也刚刚写下你的第一个Python程序!

    Python正常工作后,你必须安装PyGame,才能开发游戏。

    PyGame是一个方便游戏编程的模块!它提供了图片操作和声音回放函数,可以非常方便的整合到游戏中。

    这里下载PyGame程序安装包,注意下载的是python 2.7版本。

    笔记:上面链接下载的PyGame与Mac默认安装的Python并不兼容,你必须从Python官网重新下载并安装Python,或者通过MacPorts安装python和PyGame。

    验证一下PyGame是否安装正确,在Python运行提示符(>>>)后面输入 import pygame并回车,没有返回任何输出,说明安装正确。

    相反,如果输出下面的提示,说明PyGame没有正确安装。

    Python 2.7.2 (default, Jun 20 2012, 16:23:33) 
    [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import pygame
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ImportError: No module named pygame
    >>>

    如果安装失败,在论坛上面截图提问,我会回到你的问题。

    运行文件中的Python代码

    通过提示符环境,可以运行比较剪短的Python代码,如果想要运行大型一点的程序(例如游戏),需要将代码保存到一个文件中,否则你会一遍又一遍的重复输入代码。

    这里有几种运行Python文件的方法。一,通过文本编辑器,例如Notepad(Windows),或者TextEdit(Mac).打开一个新的文本文件,输入Python代码(例如,print 1+1),然后保存为XXX.py

    在Windows系统下,双击改文件可以运行它。在Mac系统,打开终端,输入python ,将你保存的文件拖到终端窗口,并按Enter。

    另一种方法,是在IDLE编辑器中输入,这篇教程中,我们将用此方法。运行IDLE,选择菜单File>New Window,会打开一个新的文本编辑器,在文本编辑器中输入代码,选择菜单File>Save,来保存文件,或者选择Run>Run Module (F5),来运行代码。

    请记住,Run菜单仅出现在文本编辑器上面。

    添加游戏资源

    你已经可以创建自己的游戏了,但在此之前,游戏里面怎么可以没有好看的图片和声音效果呢?我讲所有的图片和声音打包成一个ZIP文件,你可以在这里下载

     下载文件后,在电脑新建一个文件夹,并将下载的压缩包解压到该文件夹下,解压后会多出一个resources的子文件夹,内容如下图展示:

    现在正式开始编写兔子与獾。 :]

     第一步:你好,兔子


    运行Python IDLE,在新编辑窗口输入下面这段代码:

    # 1 - Import library
    import pygame
    from pygame.locals import *
     
    # 2 - Initialize the game
    pygame.init()
    width, height = 640, 480
    screen=pygame.display.set_mode((width, height))
     
    # 3 - Load images
    player = pygame.image.load("resources/images/dude.png")
     
    # 4 - keep looping through
    while 1:
        # 5 - clear the screen before drawing it again
        screen.fill(0)
        # 6 - draw the screen elements
        screen.blit(player, (100,100))
        # 7 - update the screen
        pygame.display.flip()
        # 8 - loop through the events
        for event in pygame.event.get():
            # check if the event is the X button 
            if event.type==pygame.QUIT:
                # if it is quit the game
                pygame.quit() 
                exit(0)

    将文件保存到你的文件夹,并命名为 game.py

    下面分析一下代码:

    1. 导入PyGame库,以在程序中使用PyGame提供的函数。
    2. 初始化游戏,并设置窗口。
    3. 加载兔子图片。
    4. while循环

    笔记:当其他编程语言,例如Objective-C, Java或者PHP用大括号将while循环和if语句执行的代码括起来,Python使用缩进表示代码块。所以,对齐的缩进对Python来说非常重要 - 请牢记这点。 :]

    1. 将背景填充成黑色
    2. 添加一直兔子在屏幕上,坐标为(100,100)
    3. 更新屏幕
    4. 监听是否有新的事件产生,如果新事件为关闭屏幕,则退出程序。

    笔记:根据PyGame官方文档,你不必调用 pygame.quit(),当编译器关闭的时候会自动调用,但是,在Mac系统上游戏会一直运行,直到 pygame.quit()被调用。

    运行代码,看到下面的窗口:

    好啦,兔子已经在屏幕上准备开始战斗了!

    但是一只兔子站在黑漆漆的背景上面,画面看起来既弱又单调,现在我们想办法让它更好看一些。:]

     第二步:丰富画面


    增加多一些背景元素,可以通过调用screen.blit()完成。

    Let’s start by adding a background to the game scene. This can be done with a couple more screen.blit()calls.

    在#3,加载兔子之后,增加下面的代码

    grass = pygame.image.load("resources/images/grass.png")
    castle = pygame.image.load("resources/images/castle.png")

    加载图片,并将其放入变量。接下来可以将它们绘制在屏幕上,但是,如果你检查草地的图片,会发现,它并不能覆盖整个屏幕,因此,需要循环覆盖整个屏幕。

    将下面的代码添加到#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循环覆盖屏幕的宽度,第二个for循环覆盖屏幕的高度。其余的代码,绘制4个城堡在屏幕上。

    运行代码后,看到下面的窗口:

    不错 - 开始好看了!:]

    第三步:让兔子移动


    接下来添加一些真正的游戏元素,例如让兔子响应按键。

    为了这样做,首先得想一个办法跟踪特定时候按动了哪个按键。你可以简单的创建一个按键状态列表——它包含游戏中用到的每一个按键状态。

    添加下面的代码到#3结束的地方(设置屏幕高度和宽度的后面)。

    keys = [False, False, False, False]
    playerpos=[100,100]

    它的作用一看就明白了。keys按顺序跟踪【WASD】是否被按下。列表每个元素代表一个按钮,第一个代表W,第二个代表A...

    playerpos变量代表兔子的坐标。游戏中,兔子移动到其他不同的地方,只需要讲兔子的坐标保存在变量里面,然后再新的坐标重新绘制即可。

    现在修改一下之前的代码,用新的坐标变量绘制兔子,在#6里面,将

    screen.blit(player, (100,100))

    改成

     screen.blit(player, playerpos)

    下面,更新keys代表那个按钮。PyGame提供event.key函数,可以很方便的监测按钮是否按下。

    在#8最后,在event.type==pygame.QUIT后面,输入下面的代码(与pygame.QUIT对齐):

            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分开看,并不复杂。

    首先检查某个按钮是否按下。然后,哪个按钮被按下,是不是游戏中用到的按钮,并根据其状态更新keys变量。

    最后,更新plasyerpos变量以响应按键。是不是很简单。

    添加下面的代码在game.py最后(缩进一层):

        # 9 - Move player
        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三角函数,知道了夹角,很容易可以知道兔子应该往哪个方向移动。:]

    如果对这部分不是很明白,没关系——不会影响你的下一步。但这是你需要使用Math类的原因。在游戏编程当中,你经常需要使用到它。

    现在将上面的内容应用到游戏当中。你可以使用PyGame提供的Surface.rotate(degrees)函数实现它。请记住,这里的Z值为弧度。

     atan2函数在python math 库中。所以,添加下面的代码到#1的开始:

    import math

    然后,用下面的代码替换#6的最后一行(兔子绘制代码):

       # 6.1 - Set player position and rotation
        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函数,得到角度。然后将角度转换为弧度(乘以弧度近似值57.29或者360/2pi)。

    随着兔子的旋转,它的坐标会改变。你需要计算出它新的坐标,并重新绘制在屏幕上。

    再次运行程序。如果你只按下【WASD】,程序还是像之前一样,但是移动鼠标,会发现兔子转动了,Cool!

    第五步:射箭,兔子,快射!


    兔子可以转向四周了,现在可以增加更多动作了。向敌人射箭如何?这可不是一直温柔的兔子!

    这步稍微有点复杂,因为你要跟踪所有的箭,更新、旋转、出屏幕时删除。首先,在初始化那一节#2,的最后增加变量。

    acc=[0,0]
    arrows=[]

    第一个变量保存兔子的准确度,第二个变量保存被跟踪的箭的数量。准确度包括了两个数字,射箭的数量和獾被击中的数量。后面我们会根据这两个数字,计算出准确度的百分比。

    先加载箭的图片到#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])

    这段代码检查鼠标是否被按下,获取鼠标位置,并根据兔子的位置、角度和鼠标的位置,计算出箭的角度。这个角度保存在arraws变量中。

        # 6.2 - Draw arrows
        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]))
    vely 和 velx 根据三角函数计算得到,10是箭的速度。如果只检查箭是否被射出,箭会消失。第二个for循环按箭的角度逐个绘制出它们。

    尝试运行程序。当你点击鼠标的时候,兔子会射箭了!

    第六步:发动攻击吧!獾!


    现在你又了城堡,有了会移动并且射击的战士,但是不是少了什么?对,向城堡发动攻击并向它们射箭的敌人。

    这一步中,我们会随机产生一些跑向城堡的獾。獾的数量会越来越多。所以,我们必须做下面几件事情:

    1. 将獾添加到一个数组中。
    2. 更新獾的数组,并检查它们是否被击中。
    3. 重新绘制獾。

    是不是很简单?

    首先,天剑下面的代码到#2的最后:

    badtimer=100
    badtimer1=0
    badguys=[[640,100]]
    healthvalue=194

    上面设置了一个计时器(和一些其它值),这样随着游戏的进行,会增加一只新的獾。

    The above sets up a timer (as well as a few other values) so that the game adds a new badger after some time has elapsed. You decrease the badtimer every frame until it is zero and then you spawn a new badger.

    添加下面的代码到#3最后:

    badguyimg1 = pygame.image.load("resources/images/badguy.png")
    badguyimg=badguyimg1

    第一行和之前加载图片的代码是类似的。第二行做多了一个备份,这样你可以更容易的让它移动起来。

    下一步,更新并绘制獾,添加下面的代码到#6.2后面:

        # 6.3 - Draw badgers
        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]<-64:
                badguys.pop(index)
            badguy[0]-=7
            index+=1
        for badguy in badguys:
            screen.blit(badguyimg, badguy)

    这是很大一段代码,代码的第一行检查计时器是否为0,如果是新建一直獾并根据计时器运行的时间重新设置计时器。第一个循环更新獾的x坐标,检查獾是否离开屏幕,如果离开屏幕从列表里面删除。第二个循环绘制獾群。

    为了运用随机函数,同样需要加载random模块。所以,把下面的代码加到#1的最后:

    import random

     最后把下面这行代码放到while后面(#4), 这样每一帧计时器都会减少:

    badtimer-=1

    试一下运行所有的代码。现在它看起来更像一个游戏了——你可以射箭,左右移动,獾群会不停的向你移动。

    但是,獾群怎么直接穿过城堡了?我们再加一些代码...

    把下面的代码放在 index += 1前面,#6.3的第一个for循环:

            # 6.3.1 - Attack castle
            badrect=pygame.Rect(badguyimg.get_rect())
            badrect.top=badguy[1]
            badrect.left=badguy[0]
            if badrect.left<64:
                healthvalue -= random.randint(5,20)
                badguys.pop(index)
            # 6.3.3 - Next bad guy

    这段代码非常简单。如果獾距右边的距离小于64,就删掉这只并减少游戏的生命值,随机减少5~20之间的一个数字。(一会儿会显示目前的生命值到屏幕上。)

    如果这个时候运行代码,会看到袭击到城堡的獾消失了,虽然看不见,但獾减少了生命值。

    第七步:让箭击中獾


    獾袭击城堡,但射箭对它们没有效果!这样兔子如何保卫它的家园呢?

    现在让箭射中獾,这样才可以保卫城堡,并且赢得游戏!可以说,你必须循环所有的獾,并且在嵌套循环每只箭,检查箭是否击中獾。如果击中,删除獾和箭,并且增加兔子的准确率。

    在#6.3.1后面加上下面的代码:

            #6.3.2 - Check for collisions
            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语句是一个PyGame的内建函数,它检查两块面积是否有重合。其它的代码和我上面说的一样。

    如果再运行代码,会发现可以射杀獾了。

    第八步:添加一个HUD,显示生命值和时间


    游戏编写的差不多了,有了进攻和防御。现在需要添加记分方法和兔子成绩了。

    最简单的方法是添加一个HUD (Heads Up Display) ,显示城堡的生命值。也可以加多一个时间,显示游戏进行了多长时间。

    先增加时间。将下面的代码加到#7的前面:

     # 6.4 - Draw clock
        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)

    上面的代码建了一个PyGame自带的数据格式,范围为24. 它用来记住时间文本,然后再将文本绘制到屏幕上。

    The above code simply creates a new font using the default PyGame font set to size 24. Then that font is used to render the text of the time onto a surface. After that, the text is positioned and drawn onscreen.

    接下来增加生命进度条。绘制之前,需要先加载进度条图片。将下面的代码加到#3后面:

    healthbar = pygame.image.load("resources/images/healthbar.png")
    health = pygame.image.load("resources/images/health.png")

    第一个是红色的图片,表示满血状态。第二个是绿色的图片,用来表示目前失去的生命值。

    增加下面的代码在#6.4后面,画出生命进度条:

    # 6.5 - Draw health bar
        screen.blit(healthbar, (5,5))
        for health1 in range(healthvalue):
            screen.blit(health, (health1+8,8))

    先画出了整条红色的生命条。再根据剩余的生命值,用一定数量的绿色将其覆盖。

    再运行代码,会看到计时器和生命条。

    第九步:胜利或者失败


    但这是怎么回事?不管你玩多久游戏,即使生命值为0,游戏还在进行!不仅如此,你还可以继续击杀还。生命条还没有起作用,需要制定一套规则判断游戏胜负。

    所以,让我们添加胜利、失败条件和相应界面。这样做需要修改现在的循环,根据条件判断玩家输赢,并在屏幕上展示。

    这里有一个输/赢的基本规则:

    如果时间耗尽(90000毫秒 或者 90秒):

    • 游戏结束
    • 设置结果为 1 或者 胜利

    如果城堡被摧毁:

    • 游戏结束
    • 设置结果为 1 或者胜利

    If time is up (90000 ms or 90 seconds) then:

    • Stop running the game
    • Set outcome of game to 1 or win

    If the castle is destroyed then:

    • Stop running game
    • Set outcome of game to 1 or win

    两种方法计算射箭准确度:

    笔记:acc[0]*1.0将acc[0]转化为浮点型。如果不这样做,除法将返回一个整数,而不是小数。

    添加下面的代码到game.py最后:

    #10 - Win/Lose check
        if pygame.time.get_ticks()>=90000:
            running=0
            exitcode=1
        if healthvalue<=0:
            running=0
            exitcode=0
        if acc[1]!=0:
            accuracy=acc[0]*1.0/acc[1]*100
        else:
            accuracy=0
    # 11 - Win/lose display        
    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()

    这这最长的一段代码,但它并不复杂。

    第一个if 语句检查时间是偶用完。第二个if语句检查城堡是否被摧毁。第三个计算精确度。接下来的if语句检查你是否赢得游戏,并正确展示画面。

    当然,想要展示胜利、失败的图片到屏幕上,需要先将其加载。所以,添加下面的代码到#3的最后:

    gameover = pygame.image.load("resources/images/gameover.png")
    youwin = pygame.image.load("resources/images/youwin.png")

    同时,修改将#4的代码

    # 4 - keep looping through
    while 1:
        badtimer-=1

    为:

    # 4 - keep looping through
    running = 1
    exitcode = 0
    while running:
        badtimer-=1

    running变量标识游戏是否继续执行,exitcode标识游戏胜利或者失败。再次运行程序,尝试一下完成游戏和失败!

    第十步:音乐和音效!


    游戏看起来已经很棒了,如何让它有声音呢?它太安静了!添加一些声音可以让游戏的感觉完全不一样。

    PyGame添加声音也非常的简单。首先你需要把下面的代码添加到#2最后,初始化mixer。

    pygame.mixer.init()

    然后加载声音文件,并设置音量大小,在#3添加下面的代码:

    # 3.1 - Load audio
    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 一行,它加载了背景音乐,并且下一行设置其为无限循环。

    上面做了一些声音的配置,下面在需要的地方加上一些音效。按下面注释的位置加上代码:

    # section 6.3.1 after if badrect.left<64:
    hit.play()
    # section 6.3.2 after if badrect.colliderect(bullrect):
    enemy.play()
    # section 8, after if event.type==pygame.MOUSEBUTTONDOWN:
    shoot.play()

    运行代码,现在游戏有了背景音乐和音效,当射击和击中时,游戏更加有趣了!

  • 相关阅读:
    读书笔记之:高级Linux编程(ch14)
    读书笔记之:C++编程惯用法——高级程序员常用的方法和技巧
    读书笔记之:sed与awk
    读书笔记之:Linux——命令、编辑器与shell编程
    读书笔记之:C++必知必会
    读书笔记之:Linux程序设计(第4版)(ch17)
    读书笔记之:Linux管理员指南与Linux系统一本通
    读书笔记之:C++语言的设计与演化(2002)
    读书笔记之:Linux一站式学习
    读书笔记之:GNU/Linux编程指南
  • 原文地址:https://www.cnblogs.com/zhaoxy/p/4953871.html
Copyright © 2020-2023  润新知