贪吃蛇的PyGame实现 Snake game with PyGame

一个期末作业的贴,模仿诺基亚3310上的贪吃蛇游戏,有基于指令行的菜单,和基于图形界面的的双人和单人模式。这个程序使用第三方PyGame库,采用GUI界面,并且有像素化的风格,是个看似简单,但却满是学问的小游戏,希望对大家初入PyGame有所帮助。
A final project, in imitation of the snake game on Nokia 3310 with help of pygame. Simple but not easy-to-make game.

组分 Subjects

  1. 菜单 menu
  2. 单人模式 single player mode
  3. 双人模式 multi player mode

要求 Requirements

  1. 主菜单 menu:

    • 有基本的用户界面 basic user interface in terminal
    • 允许用户在界面下进行模式选择 allow player to choose mode
    • 程序会根据用户选择执行相应的源代码 run code according to player input
  2. 单人模式
    • 有用户界面 basic user interface in window
    • 蛇会动(看似简单)the snake moves(harder than you think)
    • 蛇会根据用户输入改变方向 snake changes firection with player input
    • 蛇吃树莓后增加长度 snake grow longer after eating raspberry
    • 树莓的位置会随机生成 raspberry regenerate after eaten
  3. 多人模式
    • 有2.的这些功能 all functions as single mode
    • 两条蛇,一红一绿。一个玩家用WASD控制,另外一个用上下左右控制 one player control with WASD while the other with arrow keys
    • 蛇死了之后,不结束游戏,重新生成蛇 snake regenerate after death, instead of game over
    • 有多个树莓,避免双龙戏一珠 multiple raspberries

游戏主菜单 PySnakeMenu

1.导入必要的库和文件 import modules and files

这里,导入sys是为确保程序中的退出等功能可以正常运行。
Imports sys to ensure program functions like quit run normally
也导入了另外两个文件,是游戏的两个模式的源代码。至于为甚要在这里导入,笔者稍后解释是为什么。
Other two files imported are source code of the two modes.
Will explain later why import here.

import sys, pygame, PySnakeSingle, PySnakeMulti

2.显示用户界面 Print out welcome interface

这里是主程序的开始。打印出一个欢迎界面和一个选项列表。
Main function starts here.
这丑丑的蛇和树莓就不过多解释了,唯一值得一提的是用到了罕见的上划线符号“ ̄”。
There are nothing special about the ugly snake and raspberry, but the use of “ ̄”.
选项列表列出了单人模式,双人模式和退出。用户输入的数据会在下一步用到。
The single, multi mode of the game and the quit are listed as options.

def main():print('================================================================================================')print('Welcome to PySnake, a Python-based snake-eating-raspberry game with help of the Pygame module')print('================================================================================================')print('            _= ̄=_                                      ')print('          //    \\\                                _**_  ')print('        //        \\\               / ̄O-_        //  \\')print('      //            \\\          ///     _=--<    ||  ||')print('    //                \\\      //   --= ̄         \\  //')print('   //                  \\\   //                      ̄    ')print('-==/                     =__=                          ')print('=====================================================   ')print('Please select game mode:\n   [1]Single player mode\n   [2]Multi player mode\n   [3]Quit\n')

3.提取用户输入并判断

这里是菜单程序的精华。先提取用户输入,再判断输入并进行相应的操作(即运行相应的程序(函数))
The essense of PySnake Menu. Here the user input is taken and program will be ran accordingly.
一、先用input()函数提取输入,外嵌套的int()转换成整数
First, get input with input() and convert to int with int().

二、下面的判断语法根据用户输入执行程序,the boolean expression judges user input and ran functions.
如果输入:
1,就是单人模式 Input 1 will start single mode
2,就是双人 Input 2 will start multi mode
3,就会退出,刚才导入的sys就是在用在这里:sys.exit(),这句作用是退出当前程序 Input 3 and the program will quit, with the help of sys.exit()

从一个python文件运行另外一个python文件也是一种学问 Running one python file from another

方法一,import

将该文件视作头文件导入,例如导入了file.py:
import as module
import file
运行file中的main是就用:
and run functions in that file
file.main()
这种方法最为高效,稳定,安全
efficient, stable and safe

方法二,exec

顾名思义,直接运行,
run with python function dedicated to run another file
python2里:execfile('file.py')
python3里:exec(open('file.py').read())
这种方式危险,不稳定,而且慢一丢丢,建议尽可能不用
dangerous and hacky, use with caution

方法三,os.system

这种方法调取系统指令行运行,
run with system command line
os.system('python file.py')
是没办法的办法
desperate way

注:来自StackOverflow,如何从一个python程序运行另外一个 How can I make one python file run another? [duplicate]

这里,我们用方法一,先前导入的py文件就派上用场了,
Here we use way 1
这里如果提示导入失败,在同一文件夹下建立一个叫__init__.py的空文件可以修复,两条下划线哦
if cannot import, put a empty file called __init__.py in the same directory, which tells python interperator it is okay to include
选择1时,就运行单人模式代码中的主程序;
Chosen 1, run single mode.
选择2时,就运行双人模式代码中的主程序;
Chosen 2, run multi mode.
选择3,退出。
Chosen 3, quit.
如果输入123之外的数,会提示再次输入。
Chosen something else, will be prompt to input again.

    while True:select=int(input())if select==1:PySnakeSingle.singlemain()elif select==2:PySnakeMulti.multimain()elif select==3:print('Thank you for using.')sys.exit()else:print('Please select again.')

最后,用这个执行上述的代码
And run the main function above.

if __name__=='__main__':main()

菜单就这样大功告成了!接下来讲一下单人模式。
So much for the Menu! Lets talk about single-player mode next.

单人模式 Single-player Mode

大致思路:
Rough outline:

导入 Import

  1. pygame和pygame.local,保证游戏中的各功能 ensure game functions
  2. sys:有关程序运行过程 ensure program basic functions
  3. time:确定游戏运行速度 control game speed
  4. random:本程序中,树莓的位置是随机生成的,randomly generate raspberry locations
import pygame, sys, time, random
from pygame.locals import *

设置常量 Set consts

这个游戏里有一些常量,最常见的就是颜色,显示区域尺寸等。单人模式这里仅设置一些颜色。
Set some constant values, only colors in this case.

#define color
GRAY=pygame.Color(130,130,130)
RED=pygame.Color(255,0,0)
BLACK=pygame.Color(0,0,0)
WHITE=pygame.Color(255,255,255)

顺便一提,双人模式下会利用到一些数据结构,比如说类。
By the way, there will be Class in teh multi mode
class Snake:
class Raspberry:
但在这里只有一条蛇和一个果子,暂时不用。
, which is not applicable here with 1 snake and 1 raspberry

主函数 Main function

初始化 Initialization and visualization

首先,我们需要初始化pygame,并设置一个运行速度pygame.time.Clock(),稍后给它赋值,来规定游戏的帧率。还要设置游戏的界面playSurface以及命名窗口名称PySnake;
Initialize pygmae, set a framerate. Set game interface(an area) and set windows title PySnake.
其次,定义snake,确定它头的位置snakePosition,以及身体的位置snakeBody
Initialize snake, its head location, and its body location"s".
最后,还要定义raspberry,确定它第一次出现时的“默认”位置,确定它在游戏开始时默认存在。
And initialize raspberry, and its original location, and its existence.
最后,用来储存键盘输入方向的direction被置为“右”,用来储存实际控制方向的changeDirection也储存为“右”。这确定了蛇的初始方向。
Finally, initialize snake’s original direction to be “R”, which is right.

def singlemain():#initialize pygamepygame.init()fpsClock=pygame.time.Clock()#visualizeplaySurface=pygame.display.set_mode((640,480))pygame.display.set_caption('PySnake')#initialize variablesnakePosition=[100,100]#Snake head locationsnakeBody=[[100,100],[80,100],[60,100]]#Snak ebody locationraspberryPosition=[300,300]#Location of raspberryraspberryGenerated=1#Generate Raspberrydirection='R'#Direction at the beganningchangeDirection=direction

玩家控制方向 Player control of direction

1. 提取键盘输入 Get input from keyboard

这里会用到pygame中的event.get来提取键盘输入(用event.type实现)
Use event.get to get event.type
如果输入了ctrl+c,退出程序
If input ctrl+c, which is QUIT, quit the program

而如果输入合法的按键:
If valid input:

  1. K_UP或w
  2. K_DOWN或s
  3. K_LEFT或a
  4. K_RIGHT或d

按的键就会被储存起来:
The pressed key will be stored:

1->上 Up,即U

changeDirection = ‘U’

2->下 Down,即D

changeDirection = ‘D’

3->左 Left,即L

changeDirection = ‘L’

4->右 Right,即R

changeDirection = ‘R’

    while True:#detect python eventfor event in pygame.event.get():#get eventif event.type == QUIT:pygame.quit()sys.exit()elif event.type == KEYDOWN:#detect keyboard eventif event.key == K_UP or event.key == ord('w') or event.key==ord('W'):changeDirection = 'U'if event.key == K_DOWN or event.key == ord('s') or event.key==ord('S'):changeDirection = 'D'if event.key == K_LEFT or event.key == ord('a') or event.key==ord('A'):changeDirection = 'L'if event.key == K_RIGHT or event.key == ord('d') or event.key==ord('D'):changeDirection = 'R'if event.key == K_ESCAPE:pygame.event.post(pygame.event.Event(QUIT))
2. 判断是否反向 Check if opposite direction

这里好理解,如果蛇在向右走,那就无法转向180°直接向左;如果向下走,就无法转向直接向上。
Snake cannot take 180° turn.
这一步程序就是确认没有输入相反方向。
This step makes sure that does not happen.
原理就是看原始方向direction与输入方向changeDirection是否相反。
By checking if the input direction is opposite to original direction.
如果输入的方向合法,那方向代码就会存入direction,稍后用来改变蛇的方向。
If input is valid and not opposite, its direction will be stored, and later be used to changed snake direction.

#detect if opposite direction inputif changeDirection == 'U' and direction != 'D':direction = changeDirectionif changeDirection == 'D' and direction != 'U':direction = changeDirectionif changeDirection == 'L' and direction != 'R':direction = changeDirectionif changeDirection == 'R' and direction != 'L':direction = changeDirection
3. 相应地改变方向 Change direction accordingly

这里,就会相应改变蛇头snakePosition的坐标,
可以理解为蛇头snakePosition下一步会伸向的方向,蛇身snakeBody会在之后的步骤也沿该方向改变轨迹。

        #change direction accordinglyif direction == 'U':snakePosition[1] -= 20if direction == 'D':snakePosition[1] += 20if direction == 'L':snakePosition[0] -= 20if direction == 'R':snakePosition[0] += 20
4. 蛇的移动(和增长)Snake move and add length

蛇每次都会向前进方向加长一格,
Snake will increase toward its current direction.
然后,如果移动时吃掉了树莓(判断树莓被吃掉的算法稍后讲解),重新生成树莓(也稍后讲解)。
When eaten, regenerate raspberry, which we will talk about later.
如果没有吃掉,那就减少刚才增加的长度。
But if not eaten, undo the length increase just now.

蛇移动和改变方向的算法 Algorithm

1.蛇向某一个方向移动(比如向右)
Snake movign toward a direction (E/X right)
2.pygame.event.get提取用户输入,
Pygame event get user input

  • 如果没有输入,那么原始的方向就不会改变,蛇会向原本方向插入一格蛇头snakePosition,并让蛇身snakeBody向蛇头延长;If not input, snake’s original direction remains. Snake will put its head toward its direction and let body extend to its head
  • 如果有输入,蛇就会向输入的方向插入一格蛇头snakePosition
    If there is input, snake head will progress toward input direction.

比如输入w或者上键,就会往当前蛇头(蛇身的第一格)的上方插入蛇头

并让蛇身snakeBody向蛇头延长;
…and its body will progress toward the head

  • 然后,如果移动这一步的过程中吃掉了树莓,则不删减蛇长,蛇比原有增加一格;如果没有,尾部减掉一格。If eaten rspberry in this process, decrease will not happen after increase and snake will seem to grow. If not, it will increase and decrease length.


如果蛇头(蛇身的第一格)和树莓横纵坐标一致,及重合,则代表树莓被吃,记录树莓是否存在的raspberryGenerated置零;
If snake head is the same coordinate as the raspberry at a moment, raspberry is eaten, its existence will be 0 (eaten)
如果树莓没被吃,如上述,减少长度。
If not, length will be decreased.

        #increment snake lengthsnakeBody.insert(0,list(snakePosition))#check if raspberry goneif snakePosition[0]==raspberryPosition[0] and snakePosition[1]==raspberryPosition[1]:#eatenraspberryGenerated = 0 #raspberry not existelse:#not eatensnakeBody.pop() #increase snake length
5. 判断树莓是否被吃,以及重新生成树莓Check if raspberry eaten and shall be regenerate

raspberryGenerated记录树莓是否还在,当它为1时,树莓还在;为0时,树莓被吃。
If the existence is 0, the raspberry is eaten. If it is 1, raspberry is not.
如果被吃,random库中的random.randint()会再次生成一个坐标,并记录在raspberryPosition中,并把树莓的状态(raspberryGenerated)置为1(存在)。
If eaten, a random coordinate will be generated and stored and teh status of the raspberry will be 1 (exist).

        #if eatenif raspberryGenerated==0:x=random.randint(2,30)y=random.randint(2,22)raspberryPosition=[int(x*20),int(y*20)]raspberryGenerated=1
6. 如果蛇死了,Snake dead & GameOver

这里的几个数字是窗口的尺寸。判断蛇的位置超过他们,就是判断蛇是否出界,
The numbers here are the edge of windows. If snake goes beyond these edge, they are dead.

        #if snake is deadif snakePosition[0]>640 or snakePosition[0]<0:gameOver(playSurface)if snakePosition[1]>460 or snakePosition[1]<0:gameOver(playSurface)

如果出界,Gameover函数就会运行
If snake is dead, GameOver function will be ran
规定字体,内容,在界面的位置,显示出来并刷新就可以了。
Initiate font, color, text content, position. Refresh to show
五秒之后,退出程序。
Five seconds later, quit the program

#game over screen
def gameOver(playSurface):gameOverFont=pygame.font.SysFont('arial.ttf', 72)gameOverSurface=gameOverFont.render("Game Over", True, GRAY)gameOverRect=gameOverSurface.get_rect()gameOverRect.midtop=(320,10)playSurface.blit(gameOverSurface, gameOverRect)pygame.display.flip()time.sleep(5)pygame.quit()sys.exit()

绘制显示 draw Display

位于主程序末尾。这一部分也极为重要,它是游戏能显示出来的关键:
它绘制背景色,并利用pygame中的draw.rect按照蛇的snakeBody和树莓的raspberryPosition坐标以方块的形式把他们显示出来。
Pygame draw:
Draw the black background, the white snake body and the red raspberry.

        #generate pygame displayplaySurface.fill(BLACK)for position in snakeBody:pygame.draw.rect(playSurface,WHITE,Rect(position[0],position[1],20,20))pygame.draw.rect(playSurface,RED,Rect(raspberryPosition[0],raspberryPosition[1],20,20))#refresh pygame display            pygame.display.flip()

控制游戏帧率 Control frame rate

        #lastbut not least, control speed of the gamefpsClock.tick(5)

单人模式可以运行了,
接下来是双人模式。
So much for the Single Mode.

双人模式 Multi-player Mode

双人模式,看似只是多了一条蛇和若干树莓,实际则更加复杂。
Looks like 1 more snake and 9 more raspberris, actually waymore complicated.
大体思路是这样的:
Rough outline:

猜到你们的表情了,我会一点一点讲解的。
Scary but I’d show how it is done.

写这段代码时已经是写“单人模式”后的一个月,部分算法已经和以前不同。比如接下来的类。
介于双人模式有十个树莓和两条蛇,所以用来储存数据

类 Classes

双人游戏比较复杂,所以用类
Complicated that single mode so use Class.

Final Project 期末项目: PySnake相关推荐

  1. 【Chips】VLSI Final Project:小型卷积核单元设计 项目总结

    VLSI Final Project:小型卷积核单元设计 项目总结 0 项目背景 ​ VLSI流程课程在期末要求完成一个project,来让我这样的小白简单走一遍VLSI的设计流程有个概念.作业内容是 ...

  2. Alphabet Project Loon项目负责人半年再度易人

    Google最有希望商业化的登月计划之一,利用高纬度氦气球为偏远地区提供互联网服务的Project Loon项目刚刚又更换了项目负责人,任职仅半年时间的Tom Moore被替换成为Alastair W ...

  3. java期末项目实验答辩毕业设计工程项目源码

    java期末项目实验答辩毕业设计工程项目源码 自己收集的各行各业的都有,这一套源码吃遍所有作业项目! 1.BBS论坛系统(jsp+sql) 2.ERP管理系统(jsp+servlet) 3.OA办公自 ...

  4. 分享java web 期末项目实验源码20套,BBS论坛,ERP管理系统,OA自动化等等

    分享java web 期末项目实验源码20套,BBS论坛,ERP管理系统,OA自动化等等 我自己也从里面学习到了很多东西! 1.BBS论坛系统(jsp+sql) 2.ERP管理系统(jsp+servl ...

  5. 软件工程专业期末项目开发全流程模拟日志(第一天)

    引言 本人软件工程专业大三在读,之前几学期的期末项目写得稀碎,虽然勉强能看,但是难登大雅之堂.此后几学期,决定严格按照软件开发过程去实现自己大大小小的开发项目,为我的毕设做准备,为我的工作做准备. 分 ...

  6. WEB 期末项目 小黑帽书屋

    WEB 期末项目 小黑帽书屋 一.前言 二.项目要求 三.Web项目需求分析 (一)网站功能 (二)开发技术介绍 (三)功能结构 (四)数据库及数据表设计 四.页面展示 五.部分代码 链接: 源代码 ...

  7. 基于机器学习的2022卡塔尔世界杯冠军预测-个人期末项目总结

    声明:文中内容基于山东某高校数据挖掘课程的学习成果,本系列文章为课程期末项目的个人总结. #--------------- [2022年6月补充:非常不好意思大家,当时是笔记用,我没想到这么久还有人看 ...

  8. 类似 MS Project 的项目管理工具有哪些

    类似 MS Project 的项目管理工具有哪些?国内外类似 Microsoft Project 的项目管理软件有:1.Worktile:2.PingCode:3.Asana:4.Zoho Proje ...

  9. WEB编程期末项目——我的第一个网站

    node.js,网络爬虫 文章目录 前言 一.网站演示 二.网站构造过程 1.爬取数据 2.网站构建 总结 前言 这是我首次接触和使用javascript语言,也是我制作的第一个网站,虽然在制作的过程 ...

  10. java实验项目代码_java web 期末项目实验源码20套,自用学习非常不错!

    分享java web 期末项目实验源码20套,自用学习非常不错! 我自己也从里面学习到了很多东西! 1.BBS论坛系统(jsp+sql) 2.ERP管理系统(jsp+servlet) 3.OA办公自动 ...

最新文章

  1. 2017-09-29 前端日报
  2. linux下的c库函数大全
  3. java 对变量加锁_Java最全锁剖析:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁...
  4. 静态页面cors跨域问题
  5. 怎样用uml类图生成java类_JAVA:面向对象编程的底层逻辑,深度剖析
  6. Android如何获得手机power_profile.xml文件
  7. 升级计算机方案,关于计算机升级方案
  8. 安装两个mysql3308_在同一台机器上安装两个版本的MySQL
  9. 拿去吧你,软件测试的文件和目录测试都在这
  10. Unity调用动态链接库dll和so
  11. iOS 使用fopen返回null
  12. 织梦程序添加音乐上传功能
  13. 图解机器学习算法(1) | 机器学习基础知识(机器学习通关指南·完结)
  14. 万里汇WorldFirst提现人民币实现当天到账!
  15. DTOJ3704 威士忌(whiskey)
  16. 【软件测试】——软件测试经验总结
  17. 【推荐系统】User-Item CF:GC-MC
  18. 蓝牙技术|蓝牙助力智能手表的未来发展
  19. Android下的特殊文件夹
  20. clonezilla(再生龙)克隆linux系统 操作指南

热门文章

  1. AppStore SDK
  2. iOS App Store Connect 内购详解
  3. python图像降采样,【图像处理】——改变图像的大小(降采样重采样)
  4. VS2013_CodeLens
  5. 数字档案馆子系统划分与功能
  6. 电脑中病毒以后,如何删掉右键残留的菜单
  7. android shell强制删除文件夹_Windows 强制删除文件及文件夹命令
  8. z反变换计算机控制,计算机控制4.Z变换.ppt
  9. 关于stylebook样式调整的部分说明
  10. 9.屏幕宽高比判断(Screen.width,Screen.height)