写 / 在 / 前 / 面
各位,好久没更新了,不知道有没有想我~今晚闲着没事来写点啥,嘿嘿
暑假又来了,很不幸,我又来到了驾校…
然后,更不幸的是……
这次我又挂了
哈哈(无语)
考完出来的时候,教练黑着脸跟我说:一个星期以后再联系我。然后再次预约考试也要等到10天以后,所以我这一个星期突然就感觉没啥事了(?)
于是在今天,
没有愉快的唠嗑,
没有惊喜的分享,
更没有旅游的攻略,
只有硬核的代码教程(狗头)
我怀着非常非常悲痛(bushi)的心情,向大家分享一下我最近完成的一个贪吃蛇游戏。
游戏的主体其实在6月左右就完成了,然后这两天没事对游戏做了一点点修改,今天晚上没啥事,就在这里把游戏教程分享一下。
综 / 述
没错,这就是一款平平无奇的贪吃蛇游戏,要多简单有多简单……界面只有积分、速度、还有蛇(bushi)。
但是吧,写起来也挺费劲,也就一百多行代码()
下面是我在实验报告里对我这款游戏的介绍:
该贪吃蛇游戏实现的主要功能如下:
1)运行程序后进入开始页面,页面显示欢迎提示、游戏规则、开始提示等,并且按下任意键可以开始游戏。
2)贪吃蛇游戏的基本功能实现:蛇的移动、食物的随机产生、蛇吃食物长度增加等合理逻辑。
3)游戏结束的判定:蛇头碰到蛇身、蛇头碰到屏幕边缘时,游戏结束。然后显示游戏结束提示、显示最终得分。
所以,功能就这么简单……上面也说了,今天没有惊喜。嗯,没有。
嗯,再说点你们可能听不懂的。这个游戏是基于Python语言编写的,其中运用了random,sys,pygame模块进行编程。
安 / 装 / 模 / 块
刚刚说过了,这款游戏是基于Python语言编写的,对于Python语言而言,绝大多数电脑上是不自带这个语言以及编译软件的。所以,在开始编程之前也就需要安装。
我安装的版本是Python 3.9.1,但是有大佬跟我讲目前而言Python 3.8.6版本更好。虽然我也不太清楚更好在哪里,但我在使用过程中没有发现3.9.1版本的问题,所以可以放心食用。然后在写这篇文章之前我去看了下,3.9.6版本也出来了,有兴趣的朋友可以去试一下。
然后好像现在网站上只提供了3.8.11和3.9.6,不放心的可以去下载3.8.11,应该没有问题(选择Stable releases)。
https://www.python.org/downloads/windows/
Python Releases for Windows | Python.org
然后在安装完Python以后我们就要安装我们编写这个游戏需要用到的模块了。random模块一般在安装Python的时候会一起安装(自带模块),所以这个我们不需要操作。我们需要安装的是pygame模块。
安装的方法其实很简单:win R打开运行窗口,输入cmd进入命令提示框。然后在成功安装Python语言的情况下输入下列指令:
pip install pygame
pip其实就是Python的安装包管理工具。接下来系统就会自己安装,稍等一会就OK了。
再补充一点,在你Python安装成功的情况下,在cmd里面输入python会出现版本号,如果没有就说明没有安装成功,或者需要检查一下安装位置的问题了。
编 / 写 / 程 / 序
然后我们开始编程:
我们先导入需要进行的模块pygame、random等,同时引用pygame的local模块:
import pygame, sys, random, timefrom pygame.locals import *
然后我们要对游戏做一些初始工作,初始化pygame模块,采用pygame.display.set_mode()函数建立一个大小为1000*700的游戏窗口,并把程序命名为“Snakes”,同时定义之后编程需要用到的帧率控制器、颜色、字体等:
pygame.init()screen = pygame.display.set_mode((1000, 700))pygame.display.set_caption(‘Snakes’)clock = pygame.time.Clock()font = pygame.font.SysFont(“Arial”, 45)rule = pygame.font.SysFont(“Arial”,24)black = pygame.Color(0, 0, 0)red = pygame.Color(255, 0, 0)grey = pygame.Color(150, 150, 150)green = pygame.Color(0,250,154)
这里的几种颜色我们用Color函数结合rgb数组给他定义好。黑色是蛇的颜色,红色是食物的颜色,然后灰色和绿色分别是文字颜色和背景色(这里我采用护眼绿色的纯色背景,保护眼睛,人人有责,游戏虽好,切莫贪杯)。
接下来我们要设定一些游戏的初始值,例如速度,初始位置等。
snake_Head = [220, 200]snake_Body = [[200, 200], [180, 200], [160, 200]]direction = ‘RIGHT’score = 0food_Position = [300, 300]food_flag = 1game_speed = 5
这里我们的蛇的身体我们是用20*20的黑色小方块让他显示在屏幕上的。snake_Body数组保存的是初始情况下,蛇身的几个矩形的坐标。初始的运动方向设为向右,然后初始分数为0,初始食物坐标为(300,300)等等等等,有了这些初始参数,我们接下来的编程也就可以大展拳脚了。
OK,到这里为止我们对于游戏编写前的准备工作也就做的差不多了。接下来我们就要开始编写几个游戏运行所必要的函数了:
其中画蛇和画食物的函数drawSnake()和drawFood()原理基本相同,先确定相应位置,然后利用pygame.draw.rect()函数在对应位置画出相应矩形,蛇的位置由列表保存,食物的位置由随机数生成,在主程序中有所体现。
def drawSnake(snake_Body): for i in snake_Body: pygame.draw.rect(screen, black, Rect(i[0], i[1], 20, 20))def drawFood(food_Position): pygame.draw.rect(screen, red, Rect(food_Position[0], food_Position[1], 20, 20))
然后我们就要编写打印速度和分数的两个函数drawScore()和drawGamespeed(),两个函数原理也是基本相同(其实两个函数的主体都是显示文字,没什么好大区别),均是先用render()函数读取需要打印的文字内容,然后对文字进行处理,最后利用blit()函数在screen上显示。
def drawScore(): score_Surf = font.render(‘Score:%s’ %(score), True, grey) score_Rect = score_Surf.get_rect() score_Rect.midtop = (910, 10) screen.blit(score_Surf, score_Rect)def drawGamespeed(): Gamespeed_Surf = font.render(f’Speed:{game_speed}’, True, grey) Gamespeed_Rect = Gamespeed_Surf.get_rect() Gamespeed_Rect.midtop = (80, 10) screen.blit(Gamespeed_Surf, Gamespeed_Rect)
(可恶写到这里强迫症要犯了,无论如何他第二个函数名就是不给我标红,无语)
然后就是我们游戏结束gg的函数Gameover(),确定在游戏判定结束后需要进行的操作,打印分数和退出游戏。打印分数的原理与上述两个函数的原理一致,退出游戏的代码只需要先退出pygame模块(pygame.quit())然后等待5秒后采用sys.exit()或exit()退出程序即可。
def GameOver(): GameOver_Surf = font.render(f’Game Over!You have got {score} scores.’, True, red) GameOver_Rect = GameOver_Surf.get_rect() GameOver_Rect.midtop = (470, 10) screen.blit(GameOver_Surf, GameOver_Rect) pygame.display.flip() time.sleep(5) pygame.quit() sys.exit()
好了,写到这里,我们的工作其实已经完成了一半了,几个主要的运行函数都完成了。接下来,我们就要开始写主程序了。程序在运行完之前写的那些基本参数代码后,会直接跳到这里继续运行。
首先我们游戏进入是要设计一个简单的开始界面,提示游戏名规则这些东西,我们这里就用全英语编写了一段奇奇怪怪的规则。大概意思就是吃掉食物 1分这种。界面大概长这个样子:
然后我还在网上找了一条可爱的小蛇蛇作为吉祥物(狗头):
不要说我界面长得丑!!我没学过设计更没学过美术,没有一点艺术细胞,设计成这样我已经尽力了……
OK,话说回来。首先我们来说点题外话,怎么把图片插进这个页面呢?学过一点电脑的朋友应该都知道,需要路径,对吧。
路径就是文件的存储位置,也是你还有系统找到文件的途径。基本上路径就是这个样子的:
C:/desktop/…/xxx.txt
上面的路径就表示在C盘中的desktop文件夹里面的有关文件。路径分为绝对路径和相对路径两种,至于二者之间的区别可以自己去问度娘。Python语言一般都是写相对路径,因为绝对路径太长了……但是Python的相对路径其实特别特别恶心,恶心在哪里呢?它在写相对路径的时候一定要把上一级的文件夹(也就是父文件夹)写进去。这是我们用Python编程时一个特别特别要注意的地方,我当时在写程序的时候这个地方卡了好久,受到别的语言的惯性思维影响,一直显示找不到文件,甚至一度放弃了插这张图片的想法,好在最后解决了,其实后面我也是试出来的,百度上一些人说的也不是很清楚。
所以我们定义snake.jpeg的路径时一定要写它所在的父文件夹,我这里因为图片和代码文件放在同一个文件夹里面,所以代码只要写他们俩的父文件夹就可以了。然后多说一句,路径中写两个反斜杠是因为一个反斜杠在Python中会被识别为转义字符,所以用两个反斜杠表示区别。
然后我们用之前定义的green填满整个screen作为背景色,然后显示相应文字,操作与以上函数基本一致,但这里由于需要显示的文字较多,需要借用循环简化操作,所以这里也需要同时定义几个列表,其中text列表用来保存需要显示的相应文字,由于一行难以显示那么多文字,所以把游戏规则分为四行显示,切割成四个字符串储存在列表中,同时midtop列表用来确定文字显示的对应位置坐标。
img = ‘父文件夹\\snake.jpeg’snakeimg = pygame.image.load(img).convert()screen.fill(green)text = [‘Press any key to start the game.’,’Welcome to Snakes!’,’Rules:In this game, you can control the movement direction of the snake through’,’the keyboard, so that the snake can eat food. If the snake head touches the’,’body, or the snake head touches the edge, the game ends. For every 5 pieces’,’of food you eat, your speed will increase by 1 point. Good luck!’]start_text = []start_rect = []for each in range(0,2): start_text.append(font.render(text[each],True,red))for each in range(2,6): start_text.append(rule.render(text[each],True,black))for each in range(0,6): start_rect.append(start_text[each].get_rect())midtop = [(470,380),(470,120),(470,220),(470,250),(470,280),(470,310)]for i in range(0,6): start_rect[i].midtop = midtop[i]for j in range(0,6):screen.blit(start_text[j],start_rect[j])screen.blit(pygame.transform.scale(snakeimg, (75,150)),(700,60))pygame.display.flip()
这里其实是我简化过的,之前我采用的是一步一步来把文字输出到屏幕上。代码就是下面这一堆,非常非常的麻烦,后面想到用循环来解决,稍微简单一点:
Start_surf1 = font.render(‘Press any key to start the game.’,True,red)Start_Rect1 = Start_surf1.get_rect()Start_surf2 = font.render(‘Welcome to Snakes!’,True,red)Start_Rect2 = Start_surf2.get_rect()Start_surf3 = rule.render(‘Rules:In this game, you can control the movement direction of the snake through’,True,black)Start_surf4 = rule.render(‘the keyboard, so that the snake can eat food. If the snake head touches the’,True,black)Start_surf5 = rule.render(‘body, or the snake head touches the edge, the game ends. For every 5 pieces’,True,black)Start_surf6 = rule.render(‘of food you eat, your speed will increase by 1 point. Good luck!’,True,black)Start_Rect3 = Start_surf3.get_rect()Start_Rect4 = Start_surf4.get_rect()Start_Rect5 = Start_surf5.get_rect()Start_Rect6 = Start_surf6.get_rect()Start_Rect1.midtop = (470,380)Start_Rect2.midtop = (470,120)Start_Rect3.midtop = (470,220)Start_Rect4.midtop = (470,250)Start_Rect5.midtop = (470,280)Start_Rect6.midtop = (470,310)screen.blit(Start_surf1,Start_Rect1)screen.blit(Start_surf2,Start_Rect2)screen.blit(Start_surf3,Start_Rect3)screen.blit(Start_surf4,Start_Rect4)screen.blit(Start_surf5,Start_Rect5)screen.blit(Start_surf6,Start_Rect6)screen.blit(pygame.transform.scale(snakeimg, (75,150)),(700,60))pygame.display.flip()
然后我们初始界面就设计的差不多了。我们设定键盘按下任意键进入游戏,这个时候我们就要定义一个flag结合event监听事件来确定是否按下了键,从而确定是否进入游戏。同时这里也给出退出事件的代码:
flag = Falsewhile True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() if event.type == KEYDOWN: flag = True
通过了刚刚的判断,然后我们就正式进入游戏啦。
进入游戏后,先运用以上提前写好的有关函数画出蛇、食物、游戏速度、分数等元素,同时用clock.tick()函数确定游戏速度:
screen.fill(green)drawSnake(snake_Body)drawFood(food_Position)drawScore() drawGamespeed()pygame.display.flip()clock.tick(game_speed)
然后在游戏过程中我们可以随时通过键盘的输入来改变蛇的运动方向。这里我们还是通过event监听键盘输入事件结合if判断语句,从而改变蛇的移动方向。
for event in pygame.event.get(): if event.type == QUIT: sys.exit() elif event.type == KEYDOWN: if (event.key == K_UP or event.key == K_w) and direction != “DOWN”: direction = ‘UP’ if (event.key == K_DOWN or event.key == K_s) and direction != “UP”: direction = ‘DOWN’ if (event.key == K_LEFT or event.key == K_a) and direction != “RIGHT”: direction = ‘LEFT’ if (event.key == K_RIGHT or event.key == K_d) and direction != “LEFT”: direction = ‘RIGHT’
然后我们来考虑蛇的运动。这里我们通过增减对应坐标控制蛇的移动,并且在蛇进行移动后,需要将蛇头的当前位置加入snake_Body列表,从而形成蛇身(蛇身本身坐标也是用一个列表来保存的,之前也有定义过)。
if direction == ‘LEFT’: snake_Head[0] -= 20if direction == ‘RIGHT’: snake_Head[0] = 20if direction == ‘UP’: snake_Head[1] -= 20if direction == ‘DOWN’: snake_Head[1] = 20snake_Body.insert(0, list(snake_Head))
OK,我们再来看看蛇是怎么吃掉食物的。我们要判断蛇是否吃掉了食物,当蛇头位置与食物位置一致时,则认为此时蛇吃掉了食物。此时分数 1,并且蛇每吃掉5个食物(即得分能被5整除时),游戏速度game_speed增加1点。如果没有吃掉食物,则蛇向前移动。至于产生食物,我们利用random模块的random函数产生随机数,从而随机产生食物,注意随机数的范围由游戏窗口大小决定,这个原理其实也比较简单。
if snake_Head[0] == food_Position[0] and snake_Head[1] == food_Position[1]: food_flag = 0 score = 1 if(score % 5 == 0): game_speed = 1else: snake_Body.pop() if food_flag == 0: x = random.randrange(1,50) y = random.randrange(1,35) food_Position = [int(x * 20), int(y * 20)] food_flag = 1
好了,最后我们要完成判定游戏结束的两个条件,蛇身重叠和蛇头触碰窗口边缘均为游戏结束,执行Gameover()函数。这里结束条件的判断同样也是基于坐标确定实现。
# 贪吃蛇触碰到边界if snake_Head[0] < 0 or snake_Head[0] > 1000: GameOver()if snake_Head[1] < 0 or snake_Head[1] > 700: GameOver()# 贪吃蛇碰到自己for i in snake_Body[1:]: if snake_Head[0] == i[0] and snake_Head[1] == i[1]: GameOver()
结 / 尾
写了一晚上也不知道有多少人能看得懂,哈哈。不过以后我可能还会继续写这种类型的文章,毕竟咱是学这个专业的嘛。额,也不排除还会出现其他领域的文章哈,敬请期待咯。对了,最后把整个代码放在最后,有需要的自取。
溜了溜了,打王者的滴滴~
import pygame, sys, random, timefrom pygame.locals import *pygame.init()screen = pygame.display.set_mode((1000, 700))pygame.display.set_caption(‘Snakes’)clock = pygame.time.Clock()font = pygame.font.SysFont(“Arial”, 45)rule = pygame.font.SysFont(“Arial”,24)black = pygame.Color(0, 0, 0)red = pygame.Color(255, 0, 0)grey = pygame.Color(150, 150, 150)green = pygame.Color(0,250,154)snake_Head = [220, 200]snake_Body = [[200, 200], [180, 200], [160, 200]]direction = ‘RIGHT’score = 0food_Position = [300, 300]food_flag = 1# 食物标记:0 代表食物已被吃掉, 1代表未被吃掉game_speed = 5# 绘制贪吃蛇def drawSnake(snake_Body): for i in snake_Body: pygame.draw.rect(screen, black, Rect(i[0], i[1], 20, 20))# 绘制食物的位置def drawFood(food_Position):pygame.draw.rect(screen, red, Rect(food_Position[0], food_Position[1], 20, 20))# 打印当前分数def drawScore(): score_Surf = font.render(‘Score:%s’ %(score), True, grey) score_Rect = score_Surf.get_rect() score_Rect.midtop = (910, 10)screen.blit(score_Surf, score_Rect)# 显示游戏速度def drawGamespeed(): Gamespeed_Surf = font.render(f’Speed:{game_speed}’, True, grey) Gamespeed_Rect = Gamespeed_Surf.get_rect() Gamespeed_Rect.midtop = (80, 10)screen.blit(Gamespeed_Surf, Gamespeed_Rect)# 游戏结束并退出def GameOver(): GameOver_Surf = font.render(f’Game Over!You have got {score} scores.’, True, red) GameOver_Rect = GameOver_Surf.get_rect() GameOver_Rect.midtop = (470, 10) screen.blit(GameOver_Surf, GameOver_Rect) pygame.display.flip() time.sleep(5) pygame.quit()sys.exit()#开始界面img = ‘大作业\\snake.jpeg’snakeimg = pygame.image.load(img).convert()screen.fill(green)text = [‘Press any key to start the game.’,’Welcome to Snakes!’,’Rules:In this game, you can control the movement direction of the snake through’,’the keyboard, so that the snake can eat food. If the snake head touches the’,’body, or the snake head touches the edge, the game ends. For every 5 pieces’,’of food you eat, your speed will increase by 1 point. Good luck!’]start_text = []start_rect = []for each in range(0,2): start_text.append(font.render(text[each],True,red))for each in range(2,6): start_text.append(rule.render(text[each],True,black))for each in range(0,6): start_rect.append(start_text[each].get_rect())midtop = [(470,380),(470,120),(470,220),(470,250),(470,280),(470,310)]for i in range(0,6): start_rect[i].midtop = midtop[i]for j in range(0,6):screen.blit(start_text[j],start_rect[j])screen.blit(pygame.transform.scale(snakeimg, (75,150)),(700,60))pygame.display.flip()flag = Falsewhile True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() if event.type == KEYDOWN: flag = True while flag == True: screen.fill(green) drawSnake(snake_Body) drawFood(food_Position) drawScore() drawGamespeed() pygame.display.flip() clock.tick(game_speed) for event in pygame.event.get(): if event.type == QUIT: sys.exit() elif event.type == KEYDOWN: if (event.key == K_UP or event.key == K_w) and direction != “DOWN”: direction = ‘UP’ if (event.key == K_DOWN or event.key == K_s) and direction != “UP”: direction = ‘DOWN’ if (event.key == K_LEFT or event.key == K_a) and direction != “RIGHT”: direction = ‘LEFT’ if (event.key == K_RIGHT or event.key == K_d) and direction != “LEFT”: direction = ‘RIGHT’ if direction == ‘LEFT’: snake_Head[0] -= 20 if direction == ‘RIGHT’: snake_Head[0] = 20 if direction == ‘UP’: snake_Head[1] -= 20 if direction == ‘DOWN’: snake_Head[1] = 20 # 将蛇的头部当前位置加入到蛇身的列表中 snake_Body.insert(0, list(snake_Head)) # 判断是否吃掉食物 if snake_Head[0] == food_Position[0] and snake_Head[1] == food_Position[1]: food_flag = 0 score = 1 if(score % 5 == 0): game_speed = 1 else: snake_Body.pop() # 生成新的食物 if food_flag == 0: x = random.randrange(1,50) y = random.randrange(1,35) food_Position = [int(x * 20), int(y * 20)] food_flag = 1 # 贪吃蛇触碰到边界 if snake_Head[0] < 0 or snake_Head[0] > 1000: GameOver() if snake_Head[1] < 0 or snake_Head[1] > 700: GameOver() # 贪吃蛇碰到自己 for i in snake_Body[1:]: if snake_Head[0] == i[0] and snake_Head[1] == i[1]: GameOver()