从如今开始,就是具体游戏的制做了。做者是每章一个游戏,有些游戏我不是很感兴趣,只对其中有兴趣,因此就只讲这一些。python
第一个游戏就是贪吃蛇游戏,提及这个游戏,这多是我玩的最先的游戏之一了,记得那时彩屏手机没有出来时,全部单色手机上面几乎都有这个游戏,简直风靡一时啊。之前在单片机的液晶屏上实现过贪吃蛇,不过太简陋了。编程
看完讲贪吃蛇游戏这章,愈来愈感受到python有意思了,字典这个数据结构的应用让整个程序一会儿简单了不少。并且做者写的很仔细,整个程序设计的思路经过代码就能一目了然,关于旋转图像的坏处也有说明。并且在这章最后关于变量是否要复用这点也给了见解,很好很强大。每段代码为何这样写,也解释的很清楚。并且它的代码风格值得我学习。数据结构
下面是贪吃蛇程序的代码,把代码敲一遍加深理解。app
import pygame, sys, random from pygame.locals import* FPS = 15 WINDOWWIDTH =640 WINDOWHEIGHT = 480 CELLSIZE = 20 assert WINDOWWIDTH % CELLSIZE == 0, "Window width must be a multiple of cell size" assert WINDOWHEIGHT % CELLSIZE == 0, "Window height muset be multiple of cell size" CELLWIDTH = WINDOWWIDTH / CELLSIZE CELLHEIGHT = WINDOWHEIGHT / CELLSIZE #RGB WHITE = (255, 255, 255) BLACK = (0, 0 , 0) RED = (255, 0, 0) GREEN = (0, 255, 0) DARKGREEN = (0, 155, 0) DARKGRAY = (40, 40, 40) BGCOLOR = BLACK UP = 'up' DOWN = 'down' LEFT = 'left' RIGHT = 'right' HEAD = 0 #很巧妙的运用 #main function def main(): global FPSCLOCK, DISPLAYSURF, BASICFONT pygame.init() FPSCLOCK = pygame.time.Clock() DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) BASICFONT = pygame.font.Font('freesansbold.ttf', 18) pygame.display.set_caption('Wormy') showStartScreen() #显示起始画面 while True: runGame() #运行游戏主体 showGameOverScreen() #显示游戏结束画面 def runGame(): #设置蛇身开始在随机位置 startx = random.randint(5, CELLWIDTH-6) starty = random.randint(5, CELLHEIGHT-6) wormCoods = [{'x': startx, 'y': starty}, {'x': startx-1, 'y': starty}, {'x': startx-2, 'y': starty}] direction = RIGHT #蛇初始方向向右 #获得一个随机苹果的位置 apple = getRandomLocation() while True: for event in pygame.event.get(): if event.type == QUIT: terminate() elif event.type == KEYDOWN: #处理蛇的移动方向 if (event.key == K_LEFT or event.key == K_a) and direction != RIGHT: direction = LEFT elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT: direction = RIGHT elif (event.key == K_UP or event.key == K_w) and direction != DOWN: direction = UP elif (event.key == K_DOWN or event.key == K_s) and direction != UP: direction = DOWN elif event.key == K_ESCAPE: terminate() #看蛇身是否撞击到本身或四周墙壁 if wormCoods[HEAD]['x'] == -1 or wormCoods[HEAD]['x'] == CELLWIDTH or wormCoods[HEAD]['y'] == -1 or wormCoods[HEAD]['y'] == CELLHEIGHT: return #game over for wormBody in wormCoods[1:]: if wormBody['x'] == wormCoods[HEAD]['x'] and wormBody['y'] == wormCoods[HEAD]['y']: return #game over #蛇是否迟到苹果 if wormCoods[HEAD]['x'] == apple['x'] and wormCoods[HEAD]['y'] == apple['y']: #不删除蛇身尾段 apple = getRandomLocation() #设置一个新的苹果 else: del wormCoods[-1] #删除蛇身尾段 #添加蛇身头段 if direction == UP: newHead = {'x': wormCoods[HEAD]['x'], 'y': wormCoods[HEAD]['y']-1} elif direction == DOWN: newHead = {'x': wormCoods[HEAD]['x'], 'y': wormCoods[HEAD]['y']+1} elif direction == LEFT: newHead = {'x': wormCoods[HEAD]['x']-1, 'y': wormCoods[HEAD]['y']} elif direction == RIGHT: newHead = {'x': wormCoods[HEAD]['x']+1, 'y': wormCoods[HEAD]['y']} wormCoods.insert(0,newHead) DISPLAYSURF.fill(BGCOLOR) drawGrid() #画格子 drawWorm(wormCoods) #画蛇身 drawApple(apple) #画苹果 drawScore(len(wormCoods) - 3)#显示获得分数 pygame.display.update() FPSCLOCK.tick(FPS) #提示按键消息 def drawPressKeyMsg(): pressKeySurf = BASICFONT.render('Press a key to play.', True, DARKGRAY) pressKeyRect = pressKeySurf.get_rect() pressKeyRect.topleft = (WINDOWWIDTH - 200, WINDOWHEIGHT-30) DISPLAYSURF.blit(pressKeySurf, pressKeyRect) #检测按键 def checkForKeyPress(): if len(pygame.event.get(QUIT)) > 0: terminate() keyUpEvents = pygame.event.get(KEYUP) if len(keyUpEvents) == 0: return None if keyUpEvents[0].key == K_ESCAPE: terminate() return keyUpEvents[0].key #显示开始界面 def showStartScreen(): titleFont = pygame.font.Font('freesansbold.ttf', 100) titleSurf1 = titleFont.render('Wormy!', True, WHITE, DARKGREEN) titleSurf2 = titleFont.render('Wormy!', True, GREEN) degrees1 = 0 degrees2 = 0 while True: DISPLAYSURF.fill(BGCOLOR) rotatedSurf1 = pygame.transform.rotate(titleSurf1, degrees1) rotatedRect1 = rotatedSurf1.get_rect() rotatedRect1.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2) DISPLAYSURF.blit(rotatedSurf1,rotatedRect1) rotatedSurf2 = pygame.transform.rotate(titleSurf2, degrees2) rotatedRect2 = rotatedSurf2.get_rect() rotatedRect2.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2) DISPLAYSURF.blit(rotatedSurf2,rotatedRect2) drawPressKeyMsg() if checkForKeyPress(): pygame.event.get() return pygame.display.update() FPSCLOCK.tick(FPS) degrees1 += 3 degrees2 += 7 #游戏结束 def terminate(): pygame.quit() sys.exit() #获得随机苹果位置 def getRandomLocation(): return {'x': random.randint(0, CELLWIDTH - 1), 'y': random.randint(0, CELLHEIGHT - 1)} #显示游戏结束画面 def showGameOverScreen(): gameOverFont = pygame.font.Font('freesansbold.ttf', 150) gameSurf = gameOverFont.render('GAME', True, WHITE) overSurf = gameOverFont.render('OVER', True, WHITE) gameRect = gameSurf.get_rect() overRect = overSurf.get_rect() gameRect.midtop = (WINDOWWIDTH / 2, 10) overRect.midtop = (WINDOWWIDTH / 2, gameRect.height + 10 + 25) DISPLAYSURF.blit(gameSurf, gameRect) DISPLAYSURF.blit(overSurf, overRect) drawPressKeyMsg() pygame.display.update() pygame.time.wait(500) checkForKeyPress() while True: if checkForKeyPress(): pygame.event.get() return def drawScore(score): scoreSurf = BASICFONT.render('Score: %s' %(score), True, WHITE) scoreRect = scoreSurf.get_rect() scoreRect.topleft = (WINDOWWIDTH - 120, 10) DISPLAYSURF.blit(scoreSurf, scoreRect) def drawWorm(wormCoods): for coord in wormCoods: x = coord['x'] * CELLSIZE y = coord['y'] * CELLSIZE wormSegmentRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE) pygame.draw.rect(DISPLAYSURF, DARKGREEN, wormSegmentRect) wormInnerSegmentRect = pygame.Rect(x+4, y+4, CELLSIZE-8, CELLSIZE-8) pygame.draw.rect(DISPLAYSURF, GREEN, wormInnerSegmentRect) def drawApple(coord): x = coord['x'] * CELLSIZE y = coord['y'] * CELLSIZE appleRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE) pygame.draw.rect(DISPLAYSURF, RED, appleRect) def drawGrid(): for x in range(0, WINDOWWIDTH, CELLSIZE): pygame.draw.line(DISPLAYSURF, DARKGRAY, (x, 0), (x, WINDOWHEIGHT)) for y in range(0, WINDOWHEIGHT, CELLSIZE): pygame.draw.line(DISPLAYSURF, DARKGRAY, (0, y), (WINDOWWIDTH, y)) if __name__ == '__main__': main()
而后就来讲说上面的这段代码。先说明下整个程序流程。dom
一个main()函数里面就是这个程序的大概流程,首先相关pygame的初始化,显示游戏启动画面,接着进入死循环,runGame()就是游戏主体部分了,是程序核心。而若是游戏失败,就会只执行显示结束画面showGameOverScreen(),而后再次进行游戏。编辑器
先看看程序开始初始化的部分有哪些东西。函数
咱们把蛇身当作是一段一段组成的,CELLSIZE这个常量就是蛇身每段的大小。咱们要确保蛇身与整个显示屏幕的大小是成整数倍的关系,否则显示就有问题,因此在开始加了异常处理。assert 语句检测咱们给定的屏幕大小是否与蛇身段大小成整数倍。学习
咱们能够经过蛇身来与屏幕具体像素联系起来,简化编程,因此有了CELLWIDTH 和 CELLHEIGHT两个变量。而后咱们把定义咱们游戏会用到的颜色和把方向也定义成大写的变量,这样加强代码可读性。最后有个HEAD这个变量是在后面颇有用的。下面会介绍。字体
main()函数开始的一部分代码含义动画
首先咱们定义了三个全局变量,由于它们会在其它的一些函数中出现。分别是帧率,游戏显示窗口,基本字体,用global关键字修饰。
而后Pygame进行初始化,设置一些数据,好比帧率,加载基本字体,设置窗口标题,窗口大小。
而后显示游戏启动画面,接着进入游戏循环体,运行游戏。游戏启动画面放在后面说,先说循环体里面的东西。
看看runGame()这个游戏核心部分是怎么实现的。
首先蛇刚开始出来时因显示在屏幕的随机的一个位置,因此咱们须要产生随机数,产生随机数要在程序开始出加载python的random模块。为了防止蛇身一出来就离墙太近,致使游戏失败,因此咱们的蛇身会离墙有一段距离。产生的随机数范围为(5,CELLWIDTH-6)。而后咱们用字典这种数据结构将坐标存放起来(字典真是好用),由于开始蛇身只有三段,因此有3个字典元素。用列表把这三个字典元素包容在一块儿。蛇的初始方向设为像右。
设置完蛇身的初始状态,咱们就要设置初始苹果的状态,getRandomLocation()函数产生一个随机位置用于放置苹果。蛇的头部元素是wormCoords[0],为了代码可读性,由于后面对蛇身头部的操做比较多,因此咱们用wormCoods[HEAD]代替wormCoods[0]。这就是为何前面开始咱们要设置一个HEAD全局变量的缘由了。
接着是游戏循环了,因此跟游戏相关的关键操做都在这里面了,好比显示,按键交互等。循环体中主要处理按键消息,for event in pygame.event.get()获得全部的消息,而后对消息进行判断处理,若是是消息是退出,则终止游戏。接着处理经过按键处理蛇身的移动,看看这里的if里面的条件有个and,因此条件都知足才执行代码。条件是这样来的,好比咱们蛇身开始方向向右,若是咱们按左键的蛇身就向左移动的话,那么蛇身就会本身相撞,游戏立刻失败,这游戏就不合理了。因此咱们要按下左键的时候要保证蛇身移动方向不向右,其它方向按键的处理也是一个道理。代码以下:
for event in pygame.event.get(): if event.type == QUIT: terminate() elif event.type == KEYDOWN: #处理蛇的移动方向 if (event.key == K_LEFT or event.key == K_a) and direction != RIGHT: direction = LEFT elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT: direction = RIGHT elif (event.key == K_UP or event.key == K_w) and direction != DOWN: direction = UP elif (event.key == K_DOWN or event.key == K_s) and direction != UP: direction = DOWN elif event.key == K_ESCAPE: terminate()
蛇身移动一个距离完咱们就要判断它是否撞到墙壁或自身。代码以下:
#看蛇身是否撞击到本身或四周墙壁 if wormCoods[HEAD]['x'] == -1 or wormCoods[HEAD]['x'] == CELLWIDTH or wormCoods[HEAD]['y'] == -1 or wormCoods[HEAD]['y'] == CELLHEIGHT: return #game over for wormBody in wormCoods[1:]: if wormBody['x'] == wormCoods[HEAD]['x'] and wormBody['y'] == wormCoods[HEAD]['y']: return #game over
而后是检测蛇身是否撞到本身,这里用一个循环,依次检查从头部后面的第二段蛇身开始,因此范围是wormCoods[1:] 看 蛇身是否与蛇的头部相撞,只需判断两个坐标是否是相等就是了。
接着就判断蛇是否吃到苹果。代码以下:
#蛇是否迟到苹果 if wormCoods[HEAD]['x'] == apple['x'] and wormCoods[HEAD]['y'] == apple['y']: #不删除蛇身尾段 apple = getRandomLocation() #设置一个新的苹果 else: del wormCoods[-1] #删除蛇身尾段
删除掉蛇身尾段的话,接着就要把丢失的尾段补起来,也就是要添加蛇身的头段。代码以下:
#添加蛇身头段 if direction == UP: newHead = {'x': wormCoods[HEAD]['x'], 'y': wormCoods[HEAD]['y']-1} elif direction == DOWN: newHead = {'x': wormCoods[HEAD]['x'], 'y': wormCoods[HEAD]['y']+1} elif direction == LEFT: newHead = {'x': wormCoods[HEAD]['x']-1, 'y': wormCoods[HEAD]['y']} elif direction == RIGHT: newHead = {'x': wormCoods[HEAD]['x']+1, 'y': wormCoods[HEAD]['y']} wormCoods.insert(0,newHead)
这就是整个游戏的核心部分了。下面就是根据咱们上面对蛇的状态的修改作出的一些显示更新操做了。代码以下:
DISPLAYSURF.fill(BGCOLOR) drawGrid() #画格子 drawWorm(wormCoods) #画蛇身 drawApple(apple) #画苹果 drawScore(len(wormCoods) - 3)#显示获得分数 pygame.display.update() FPSCLOCK.tick(FPS)
这几个函数都比较简单(略)
如今来讲说游戏启动画面函数。showStartScreen().代码以下
def showStartScreen(): titleFont = pygame.font.Font('freesansbold.ttf', 100) titleSurf1 = titleFont.render('Wormy!', True, WHITE, DARKGREEN) titleSurf2 = titleFont.render('Wormy!', True, GREEN) degrees1 = 0 degrees2 = 0 while True: DISPLAYSURF.fill(BGCOLOR) rotatedSurf1 = pygame.transform.rotate(titleSurf1, degrees1) rotatedRect1 = rotatedSurf1.get_rect() rotatedRect1.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2) DISPLAYSURF.blit(rotatedSurf1,rotatedRect1) rotatedSurf2 = pygame.transform.rotate(titleSurf2, degrees2) rotatedRect2 = rotatedSurf2.get_rect() rotatedRect2.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2) DISPLAYSURF.blit(rotatedSurf2,rotatedRect2) drawPressKeyMsg() if checkForKeyPress(): pygame.event.get() return pygame.display.update() FPSCLOCK.tick(FPS) degrees1 += 3 degrees2 += 7
启动画面的显示效果为两个字符串不断的旋转。因此须要建立两个字体字体对象和两个旋转角度的变量。还须要提示显示按下按键开始游戏。因此还有一个显示按键消息的函数,drawPressKeyMsg()。不单独如今这里面的缘由而封装成一个函数的缘由是在游戏结束画面中也会用到这个函数。接着检查按键消息,若是有按键按下则进入游戏,最后在函数末尾更新显示及改变每次增长的角度值。其中旋转图像用到的是pygame.transform.rotate()函数。
文章里面还说到了旋转这个方式并非很完美。由于图像的旋转会给图像带来失真,使图像扭曲。除非你每次旋转的角度是90的整数倍。
最后给出程序运行效果截图:
我愈来愈以为python颇有爱啊。最后还要介绍一款文本编辑器,也是昨天才发现的。名字就是Sublime Text 2。太舒服了这款文本编辑器,并且各类主题很炫。
这是它的下载地址也是官网地址:http://www.sublimetext.com/,最新版是2.0.1。