Python 0基础开发游戏,打地鼠(详细教程)
一、准备工作
1 下载安装 python
2 下载安装VS code
编辑器
安装时,要注意勾选 添加到path
3 安装pygame模块
注意:很多人学Python过程中会遇到各种烦恼问题,没有人帮答疑。为此小编建了个Python全栈免费答疑交流.裙 :一久武其而而流一思(数字的谐音)转换下可以找到了,不懂的问题有老司机解决里面还有最新Python教程项目可拿,,一起相互监督共同进步!
- 在VisualStudioCode的顶部菜单【
Terminal-New Teminal
】打开命令行终端,然后输入命令python -m pip install --upgrade pip
,回车,等待完成。 然后同样输入命令pip install pygame,等待完成安装,可能需要几分钟
二、创建项目
<meta charset="utf-8">
在桌面上创建一个文件夹
mygame
,然后在VSCode中使用菜单【File-Open Folder】,选择mygame
文件夹,VSCode左侧将会出现EXPLORER导航栏。在左侧导航栏中,【右键-New File】创建文件
main.py
。
- 将下面的代码粘贴到右侧`main.py`文件中。
import pygame import sys pygame.display.set_mode([600,400]) while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit()
- 运行代码。
* 仍然【Terminal-New terminal】终端中输入命令`python main.py`,这将运行我们上面的代码,看到弹出一个黑色窗口。
三、可选操作
执行上面的操作的时候,VSCode的右下角会经常弹出一些提示,如果有【Install】字样,可以放心的点击它进行安装更多内容。
也可以从左侧栏点击图标打开【EXTENSIONS】,然后搜索
@id:ms-python.python
,点击找到的结果,右侧再点击【Install】按钮进行安装。安装之后main.py
文件的右上角就会出现三角形运行按钮,点击它同样可以运行代码,相当于终端中输入python main.py
。
pip install ...
安装命令太慢。Windows用户,可以从上面的网盘中下载pip.ini
文件,然后在【C盘-用户-用户名】文件夹下面创建pip
文件夹,再把下载的pip.ini
文件拷贝进去,此后再运行pip install ...
安装速度就会快很多。对于苹果用户就麻烦很多。先在终端执行
cd ~
切换到用户文件夹,然后执行mkdir .pip
创建.pip
文件夹,它是隐身的,我们打开访达,从菜单执行【前往-前往文件夹...】,前往~/.test
目录,把下载的pip.conf
文件粘贴进去,搞定。
pip.ini
或者pip.conf
文件是把原来pip
默认从国外下载安装改成了从国内下载,所以速度会变快很多。
其中
import
是导入我们要使用的外部代码模块,pygame
当然是必须的,sys
是system系统
的简写,因为我们的游戏要运行在系统(windows或者苹果macOS)上面,所以我们会用到系统的一些命令,比如下面的sys.exit()
这个命令。pyagme.display.set_mode([600,400])
,这里的[600,400]
是一对数字组合在一起的,叫二元数组,这里它表示宽600,高400的一个矩形。整句话就是设置要弹出的窗口的大小,display显示
,set设置
,mode模式
。while 1:...当是1的时候,就...
,1在代码里面表示正确的、真的、存在的,相反,0表示错误、假的、不存在的。while 1:do something
那么something就会做,如果while 0: do something
那么就不会做了。for ... sys.exit()
这一段暂时可以不深究,只是固定格式。只要知道它表示游戏程序运行结束的时候系统把窗口也关掉,清理好计算机不要留痕迹,exit退出
。
游戏开发的思路
游戏开发都有固定的套路,无论是打地鼠、愤怒的小鸟,还是西瓜忍者,甚至是王者荣耀这样的大型游戏,他们大致都遵循下面几个思路:
创建一个地图场景,上面可能有些道具。
比如几个地鼠洞,一些可以放小猪的木盒子,甚至非常复杂的山谷地形,上面还有很多野怪。
这些地图上的元素一般都是被动的,就是你不去靠近或招惹野怪的话,它们不会互相打起来自相残杀,同样,小鸟还没发射的时候,木盒子也不会自己倒塌。创建至少一个玩家可以控制的元素,它可以和地图场景发生交互。
这个可以被控制的元素我们称为玩家角色。在打地鼠游戏中这个角色就是一个锤子,愤怒的小鸟中这个角色其实是弹弓,弹出的小鸟其实是个道具,在王者荣耀游戏中玩家的角色就是自己的英雄。
必须要有评判标准,用来衡量输赢胜败。
玩家控制的角色和地图场景进行交互,发生反应,对应的也必须要有一个评判标准,比如计算3分钟内击中地鼠的次数,或者计算砸死的绿猪的数量,或者是打野怪获得的经验,这些规则一定要清晰而且不能互相矛盾。
大多数游戏都有输赢胜败,而胜败往往本质上只是谁的积分首先达到某个临界点。可以是某个关键道具的变化,比如对战游戏中塔被摧毁,也可以是玩家角色的属性变化,比如格斗游戏中被击杀;也可以只是纯粹的某项积分评比,用排行榜代替输赢。
游戏开发的技术点
要能够在窗口内绘制图形。
可以是直接用代码绘制几何图形,也可以是载入图片显示内容。
要能用代码控制每个元素(道具和角色)的动画。
动画就是一组图片不停地轮番变化。要能用代码控制播放和停止每个元素的动画,还能在不同动画之间快速切换。
能够接收用户的控制,并借此影响游戏中的元素。
知道用户什么时候按了键盘,什么时候点了鼠标,按了哪个按键,鼠标左键还是右键?我们经常把这些操作称之为交互事件。
能够对游戏中各种元素产生的有效数据进行计算和管理。
玩家角色一刀砍下去,怪物的血量减少了100点,这个就是数据,而且是很有用的数据,没有这个数据的话怪物可能永远砍不死了。
有时候这些数据要保存好,让用户下一次打开游戏的时候仍然看到自己的等级和装备都还存在。有些时候这些数据要及时清理,比如新的一局又开始了,地图上的道具和角色都要恢复原样。
打地鼠游戏
我们可以把经典的打地鼠游戏简化概括为:
- 地图和道具:随机位置出现地鼠图形
- 交互角色:控制锤子图形,点击地鼠图形使其消失
- 积分输赢:限定时间内击中地鼠图形的次数
核心玩法简化成一句话就是:点击随机出现图形。
绘制地鼠
我们用一个蓝色的圆形代表地鼠。那怎么在窗口中绘制一个圆形呢?
可以百度【pygame 画圆圈】类似的关键字,可以查到要使用pygame.draw.circle
语句,它的具体语法可以从官方说明文档中找到,英文版详细说明点这里。
我们查到它的语法是:
pygame.draw.circle() circle(surface, color, center, radius) -> Rect
这表示draw.circle()
需要四个参数,分别是surface表面,color颜色,center中心点,radius半径
。
我们继续看surface
参数的说明:
surface (Surface) -- surface to draw on
听上去像是画布,——先要有个画布才能在上面画圆。
点击Surface链接,找到更进一步说明:
Surface((width, height), flags=0, depth=0, masks=None) -> Surface
结尾的->Surface
表示Surface((width....)
这句话可以生成一个Surface表面,我们可以用下面的语句捕捉到这个生成的表面:
sur=pygame.Surface((600, 400)
这样,sur
就是我们生成的表面了。
颜色和位置
再返回来看color
参数:
color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a tuple (RGB[A])
很明显它是表示画什么颜色的圆。tuple(int, int, int, [int])
表示这里需要三个整数int
一起表示颜色,RGB
是指red红,green绿,blue蓝,alpha透明度
:
clr=(0,0,255) #蓝色
对于center
中心位置我们也可以用同样的方法得到,这里的Vector2
表示二元向量,及横向x和竖向y的位置:
pos=pygame.vector2(300,200) #窗口中央
绘制圆形
参数都具备了,那么就可以开始画圆了。运行下面的代码:
import pygame import sys pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos = (300,200) rad = 100 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每帧循环执行的代码 pygame.draw.circle(sur, clr, pos, 100# 绘制圆 # 刷新画面 window.blit(sur, (0, 0)) pygame.display.flip()
注意这里最底部刷新画面的两行,其中window.blit(sur, (0, 0))
表示把我们绘制好的表面sur
刷新到window
窗口中;pygame.display.flip()
表示进行窗口刷新。
随机出现
随机出现就是随机位置,我们必须确保每一次花圆的pos
位置都不同,而且应该是固定的几个地鼠洞位置。——别忘了我们要做打地鼠游戏。
假设有6个地鼠位置pos
分别是[200,200],[300,200],[400,200],[200,300],[300,300],[400,300]
,那么如何随机取到6个中一个呢?也就是如何随机取到1~6其中的一个数字即可。
我们可以百度【python 随机数】查到需要使用random
模块,这是python自带的模块,不需要再重新pip install
。
如果搜索【python random document】可以查找到官方的语法说明,如下:
random.randint(a, b) Return a random integer N such that a <= N <= b. Alias for randrange(a, b+1).
这是说可以随机生成a
和b
之间的一个数字。也可以从中文的菜鸟教程网
学习到这个知识。
新建一个test.py
文件,我们进行测试:
import random a = random.randint(0, 5) print(a)
每次运行都能生成不同的数字。
继续测试:
import random a = random.randint(0, 6) pos6=[[200,200],[300,200],[400,200],[200,300],[300,300],[400,300]] print(pos6[a])
这里的pos6[a]
表示pos6
的六个位置中的第a
个。运行这个代码就会每次生成不同的位置。
测试成功之后我们把它拷贝到刚才的画圆代码中,得到:
import pygame import sys import random pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos6 = [[200, 200], [300, 200], [400, 200], [ 200, 300], [300, 300], [400, 300]] # !!六个位置 rad = 100 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每帧循环执行的代码 sur.fill((0, 0, 0)) # !!用黑色覆盖前一帧的画面,实现刷新 a = random.randint(0, 5) # !!随机0到5 pygame.draw.circle(sur, clr, pos6[a], 100) # !!使用随机位置 # 刷新画面 window.blit(sur, (0, 0)) pygame.display.flip()
注意新增了sur.fill...
一行,这是用黑色(0,0,0)
来清理掉上一帧的内容,避免出现多个圆。
隔n帧刷新
上面的代码运行之后会看到蓝色的圆四处乱跳,太快了,我们希望改变位置之后能停一下,等我们锤它。
我们需要画面的圆每隔n帧再随机变换一次,而不是现在的每帧都随机变。思路是这样的:我们设定一个计数器,开始是0,每帧都给它增加1,就是0,1,2,3,4...
直到它增到到超过50,这时候我们就改变圆的位置并同时把计数器重置为0。
代码如下:
import pygame import sys import random pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos6 = [[200, 200], [300, 200], [400, 200], [ 200, 300], [300, 300], [400, 300]] # 六个位置 rad = 100 tick=0 #!!计数器 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每帧循环执行的代码 if tick>50: #每50次刷新变换一次 sur.fill((0, 0, 0)) # 用黑色覆盖前一帧的画面,实现刷新 a = random.randint(0, 5) # 随机0到5 pygame.draw.circle(sur, clr, pos6[a], 100) # 使用随机位置 tick=0 else: #!!不刷新变换的时候 tick=tick+1 #!!增加计数器 # 刷新画面 window.blit(sur, (0, 0)) pygame.display.flip()
增加交互点击
当用户点击画面的时候,我们要知道它点击了哪里,是否点击到了我们画的圆上面。
百度搜索【pygame 点击】可以找到相关资源,也可以直接在官方说明文档中找到。
思路是我们添加对event.type
事件类型的实时监测,一旦发现点击事件就获取位置坐标。代码如下:
import pygame import sys import random from pygame.locals import * # 引入鼠标事件类型 pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos6 = [[200, 200], [300, 200], [400, 200], [ 200, 300], [300, 300], [400, 300]] # 六个位置 rad = 100 tick = 0 # !!计数器 pos = pos6[0] # !!在外面记录圆的位置 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == MOUSEBUTTONDOWN: # !!如果是鼠标按下事件 mpos = pygame.mouse.get_pos() # !!获取鼠标位置 print(mpos) # 每帧循环执行的代码 if tick > 50: # 每50次刷新变换一次 sur.fill((0, 0, 0)) # 用黑色覆盖前一帧的画面,实现刷新 a = random.randint(0, 5) # 随机0到5 pos = pos6[a] # !!更新外部记录的圆的位置 pygame.draw.circle(sur, clr, pos, 100) # !!使用随机位置 tick = 0 # 重置计数器 else: # !!不刷新变换的时候 tick = tick+1 # !!增加计数器 # 刷新画面 window.blit(sur, (0, 0)) pygame.display.flip()
运行这个代码,任意点击屏幕上的时候就会打印出档期鼠标点击的位置。
距离测量
知道当前圆的位置pos
,也知道当前点击的位置mpos
,这样我们就可以计算出两点之间的距离,距离大于圆半径的就是没有点到地鼠,距离小于半径的就是点到地鼠了。
百度搜索【pygame 两点距离】可以搜到一些计算距离的方法,我们这里使用pygame
官方提供的方法,测试下面代码:
import pygame a=pygame.math.Vector2.length(pygame.math.Vector2(3,4)) print(a)
它会输出5(勾三股四玄五)。这里的(3,4)
是pos
和mpos
相减得到的差。
把这个思路带入原来的代码,得到:
import pygame import sys import random from pygame.locals import * # 引入鼠标事件类型 pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos6 = [[200, 200], [300, 200], [400, 200], [ 200, 300], [300, 300], [400, 300]] # 六个位置 rad = 50 tick = 0 # 计数器 pos = pos6[0] # 外面记录圆的位置 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == MOUSEBUTTONDOWN: # 如果是鼠标按下事件 mpos = pygame.mouse.get_pos() # 获取鼠标位置 dis = pygame.math.Vector2( mpos[0]-pos[0], mpos[1]-pos[1]) # !!计算坐标差 len = pygame.math.Vector2.length(dis) # !!计算距离 if len < rad: tick = 51 # !!立即变换位置 # 每帧循环执行的代码 if tick > 50: # 每50次刷新变换一次 sur.fill((0, 0, 0)) # 用黑色覆盖前一帧的画面,实现刷新 a = random.randint(0, 5) # 随机0到5 pos = pos6[a] # 更新外部记录的圆的位置 pygame.draw.circle(sur, clr, pos, 100) # 使用随机位置 tick = 0 # 重置计数器 else: # 不刷新变换的时候 tick = tick+1 # 增加计数器 # 刷新画面 window.blit(sur, (0, 0)) pygame.display.flip()
在这里我们设定如果距离长度len
小于圆半径rad
,那么就立即设置tick=51
使它大于50,立即进行随机位置变换。
截止到这里运行上面的代码,可以实现随机出现地鼠(圆)并能够点击使它消失,这也实现了游戏的最基本逻辑功能。后续我们将进一步编写更多内容,让它更完善一些。
记录分数
计算数字增加很容易,设定一个score=0
,然后击中地鼠的时候增加1就可以了。但是,如何把它显示到屏幕上呢?
可以百度搜索【pygame 显示文字】然后就可以找到大致方法,我们先进行一些测试:
import pygame pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 # 显示文字 pygame.font.init() # !!初始化文字 font = pygame.font.SysFont(‘微软雅黑‘, 30) # !!设定字体和字号 sur = font.render("Hello World!!{}".format(999), False, (255, 0, 0)) # !!生成w文字表面 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() window.blit(sur, (200, 10)) # !!增加分数表面 pygame.display.flip()
这段代码中可以看到pygame绘制文字分三步:
pygame.font.init()
先要初始化pygame.font.SysFont(‘微软雅黑‘, 30)
设定字体和字号大小font.render("Hello World!!{}".format(999), False, (255, 0, 0))
生成一个Surface表面
当然,最后别忘了把表面放到窗口里window.blit(sur, (200, 10))
运行上面的代码得到一个窗口如下:
我们根据这个经验改进的代码:
import pygame import sys import random from pygame.locals import * # 引入鼠标事件类型 pygame.init() # 初始化 window = pygame.display.set_mode([600, 400]) # 设定窗口 sur = pygame.Surface([600, 400]) # 绘制背景容器 clr = (0, 0, 255) pos6 = [[200, 200], [300, 200], [400, 200], [ 200, 300], [300, 300], [400, 300]] # 六个位置 rad = 50 tick = 0 # 计数器 pos = pos6[0] # 外面记录圆的位置 # 分数 score = 0 # !!分数计数 pygame.font.init() # !!初始化文字 score_font = pygame.font.SysFont(‘微软雅黑‘, 30) # !!设定字体和字号 score_sur = score_font.render(str(score), False, (255, 0, 0)) # !!生成计数表面 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == MOUSEBUTTONDOWN: # 如果是鼠标按下事件 mpos = pygame.mouse.get_pos() # 获取鼠标位置 dis = pygame.math.Vector2( mpos[0]-pos[0], mpos[1]-pos[1]) # 计算坐标差 len = pygame.math.Vector2.length(dis) # 计算距离 if len < rad: tick = 1000 # 立即变换位置 score = score+1 # 计分增加 # 每帧循环执行的代码 if tick > 50: # 每50次刷新变换一次 score_sur = score_font.render( str(score), False, (255, 0, 0)) # !!重新生成分数文字表面 sur.fill((0, 0, 0)) # 用黑色覆盖前一帧的画面,实现刷新 a = random.randint(0, 5) # 随机0到5 pos = pos6[a] # 更新外部记录的圆的位置 pygame.draw.circle