前言
今天要做的游戏叫Pong,它是世界上第一款街机游戏。(关键它好做)
制作游戏的引擎有很多,比如Unity
、Construct
.
.
.
.
.
.
......
......而今天我们用的既不是Unity,也不是Construct,而是Python
。
制作游戏的模块是pgzero
,它是在 Python
比较有名的库 Pygame
上又加了一层封装,对初学者制作游戏非常友好。
安装pgzero
pgzero
可以进入cmd,然后用“pip install pgzero”来安装。(前提是你得先安装Python)
完成以后,就可以开始你的游戏制作之旅了 😃
角色
首先,先写好这两行代码:
import pgzrun
pgzrun.go()
运行这两行代码,你就会发现:屏幕中多了一个黑色(指背景)窗口!
这就是pgzero的神奇之处。如果有了解过pygame的朋友肯定知道:要想让游戏窗口正常的运行,必须写上很多的代码才可以,有时候还会记错一些。
言归正传,再写上这几行代码:
import pgzrun
import os
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
pad1 = Rect((20, 20), (10, 100))
def draw():
screen.clear()
screen.draw.filled_rect(pad1, "white")
pgzrun.go()
os.environ["SDL_VIDEO_CENTERED"] = '1'
用来设定pgzero窗口为居中。WIDTH
和HEIGHT
用来定义窗口的长宽。pad1 = Rect((20, 20), (10, 100))
用来定义一个矩形,参数一是矩形的位置,参数二是矩形的长宽。screen.draw.filled_rect(pad1, "white")
用来填充矩形,第一个参数是目标矩形。第二个参数是颜色,与(255, 255, 255)
等价。
如法炮制,再写(粘贴)出第二个球拍的代码:
import pgzrun
import os
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
pad1 = Rect((20, 20), (10, 100))
pad1 = Rect((WIDTH - 40, 20), (10, 100))
def draw():
screen.clear()
screen.draw.filled_rect(pad1, "white")
pgzrun.go()
然后定义一个类,类名是 Ball
,写上这样的代码:
class Ball:
def __init__(self):
self.pos = [WIDTH / 2, HEIGHT / 2]
self.speed = [200, 200]
这段代码定义了一个球,有pos
(位置)和speed
(速度)两个列表。有人可能会问,speed
为什么也是列表,就不能是普通的 int 吗?而且 speed
里的元素这么大,这小球移动的速度不应该是非常快吗?这些代码都有什么用呢?
别着急,这段代码在后面有大用。
先实例化Ball
类:
ball = Ball()
然后最关键的代码来了:在 screen
里面填充我们的小球。
screen.draw.filled_circle(ball.pos, 4, "white")
其中第一个参数是填充的位置,第二个参数是小球的半径,第三个参数是颜色。
这是现在的代码:
import pgzrun
import os
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
class Ball:
def __init__(self):
self.pos = [WIDTH / 2, HEIGHT / 2]
self.speed = [200, 200]
ball = Ball()
pad1 = Rect((20, 20), (10, 100))
pad2 = Rect((WIDTH - 40, 20), (10, 100))
def draw():
screen.clear()
screen.draw.filled_rect(pad1, "white")
screen.draw.filled_rect(pad2, "white")
screen.draw.filled_circle(ball.pos, 4, "white")
pgzrun.go()
截至目前为止,角色已经定义好了,接下来就是让他们动起来。
让他们动起来吧!
首先先定义一个 update
的函数,参数是 dt
,像这样:
def update():
pass # 这是占位用的
随后写上让小球移动的代码:
for i in range(2):
ball.pos[i] += speed[i] * dt
其中ball.pos[i] = speed[i] * dt
是计算出小球在下一帧的位置,这就是为什么 speed 列表中的元素会这么大的原因,但是我这个代码不是唯一的,因为不同的电脑性能不同,帧数(fps)也不同。
你可能会说:循环里的代码到底是什么意思,实现的又是什么?
这个嘛…其实就是路程=速度×时间
运行上面的代码,我们就能得到一个会动的小球,但是多运行一会儿后你就会发现:小球离开屏幕后就不回来了。那么我们就能到update
函数里面添加代码:
if ball.pos[1] >= HEIGHT or ball.pos[1] <= 0:
ball.speed[1] *= -1
这两行代码是为了检测小球是否碰到上下边缘,如果碰到,那就让小球反弹。
接下来就是检测小球是否碰到两边,如果碰到,就直接重置小球。
在这之前,我们要先解决两个问题:
- 小球碰到边缘后怎么重置?
- 如何检测碰到边缘?
首先解决第一个问题:小球碰到边缘后重置。
这个问题很简单,只需修改Ball
类的代码:
class Ball:
def __init__(self):
self.reset()
def reset(self):
self.pos = [WIDTH / 2, HEIGHT / 2]
self.speed = [200, 200]
- 小球碰到边缘后重置
再解决第二个问题,如何检测碰到边缘。
再update
函数里添加代码:
if ball.pos[0] >= WIDTH or ball.pos[0] <= 0:
ball.reset()
这样就实现了小球碰到边缘的检测。
- 小球碰到边缘的检测
小球的问题解决了。接下来应该看球拍了。
在Pong里,小球碰到球拍是能动的,如何让球拍动呢?可以用keyboard
检测按键是否被按下。(直接用就可以了 ,是不是很人性化 )
首先先新建一个常量:PAD_SPEED
,把它赋值为10
然后检测按键:
if keyboard.w:
pad1.y -= PAD_SPEED
elif keyboard.s:
pad1.y += PAD_SPEED
if keyboard.up:
pad2.y -= PAD_SPEED
elif keyboard.down:
pad2.y += PAD_SPEED
球拍能动了,如何检测小球碰到拍子呢?这就涉及到一个函数:collidepoint
。
collidepoint
是用来检测角色碰到坐标点的,可以这样用:
a.collidepoint(b)
根据这个函数我们就能写出如下代码:
if pad1.collidepoint(ball.pos) or pad2.collidepoint(ball.pos):
ball.speed[0] *= -1
把以上所有的代码都按照提示写进你的代码后,你的代码应该是这样的:
import pgzrun
import os
import time
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
PAD_SPEED = 10
pad1 = Rect((20, 20), (10, 100))
pad2 = Rect((WIDTH - 20, 20), (10, 100))
class Ball:
def __init__(self):
self.reset()
def reset(self):
self.pos = [WIDTH / 2, HEIGHT / 2]
self.speed = [200, 200]
ball = Ball()
def draw():
screen.clear()
screen.draw.filled_rect(pad1, "white")
screen.draw.filled_rect(pad2, "white")
screen.draw.filled_circle(ball.pos, 4, "white")
def update(dt):
if keyboard.w:
pad1.y -= PAD_SPEED
elif keyboard.s:
pad1.y += PAD_SPEED
if keyboard.up:
pad2.y -= PAD_SPEED
elif keyboard.down:
pad2.y += PAD_SPEED
for i in range(2):
ball.pos[i] += ball.speed[i] * dt
if ball.pos[1] >= HEIGHT or ball.pos[1] <= 0:
ball.speed[1] *= -1
if ball.pos[0] >= WIDTH or ball.pos[0] <= 0:
ball.reset()
if pad1.collidepoint(ball.pos) or pad2.collidepoint(ball.pos):
ball.speed[0] *= -1
pgzrun.go()
写到这,游戏的基本功能就写完了,接下来就是给他添加分数列表,小球在哪重置,就给对面的玩家加分,修改后代码是这样的:
import pgzrun
import os
import time
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
PAD_SPEED = 10
scores = [0, 0]
pad1 = Rect((20, 20), (10, 100))
pad2 = Rect((WIDTH - 20, 20), (10, 100))
class Ball:
def __init__(self):
self.reset()
def reset(self):
self.pos = [WIDTH / 2, HEIGHT / 2]
self.speed = [200, 200]
ball = Ball()
def draw():
global scores
screen.clear()
screen.draw.filled_rect(pad1, "white")
screen.draw.filled_rect(pad2, "white")
screen.draw.filled_circle(ball.pos, 4, "white")
screen.draw.text(":", midtop=(WIDTH // 2, 20), fontname=None, fontsize=64)
screen.draw.text(f"{scores[0]}", topright=(WIDTH // 2 - 40, 20), fontname=None, fontsize=64)
screen.draw.text(f"{scores[1]}", topright=(WIDTH // 2 + 60, 20), fontname=None, fontsize=64)
def update(dt):
global scores
if keyboard.w:
pad1.y -= PAD_SPEED
elif keyboard.s:
pad1.y += PAD_SPEED
if keyboard.up:
pad2.y -= PAD_SPEED
elif keyboard.down:
pad2.y += PAD_SPEED
for i in range(2):
ball.pos[i] += ball.speed[i] * dt
if ball.pos[1] >= HEIGHT or ball.pos[1] <= 0:
ball.speed[1] *= -1
if ball.pos[0] <= 0:
scores[1] += 1
ball.reset()
if ball.pos[0] >= WIDTH:
scores[0] += 1
ball.reset()
if pad1.collidepoint(ball.pos) or pad2.collidepoint(ball.pos):
ball.speed[0] *= -1
pgzrun.go()
其他优化
如果还嫌做的不够好,还可以用random
模块实现方向随机和位置随机(作者这里做的不太好)
import pgzrun
import os
import random
os.environ["SDL_VIDEO_CENTERED"] = '1'
WIDTH = 1000
HEIGHT = 800
PAD_SPEED = 10
scores = [0, 0]
pad1 = Rect((20, 20), (10, 100))
pad2 = Rect((WIDTH - 20, 20), (10, 100))
class Ball:
def __init__(self):
self.reset()
def reset(self):
self.pos = [WIDTH / 2, random.randint(1, HEIGHT - 1)]
randomx = random.randint(200, 400)
randomy = random.randint(200, 400)
sx = random.choice([randomx, randomx * -1])
sy = random.choice([randomy, randomy * -1])
self.speed = [sx, sy]
ball = Ball()
def draw():
global scores
screen.clear()
screen.draw.filled_rect(pad1, "white")
screen.draw.filled_rect(pad2, "white")
screen.draw.filled_circle(ball.pos, 4, "white")
screen.draw.text(":", midtop=(WIDTH // 2, 20), fontname=None, fontsize=64)
screen.draw.text(f"{scores[0]}", topright=(WIDTH // 2 - 40, 20), fontname=None, fontsize=64)
screen.draw.text(f"{scores[1]}", topright=(WIDTH // 2 + 60, 20), fontname=None, fontsize=64)
def update(dt):
global scores
if keyboard.w:
pad1.y -= PAD_SPEED
elif keyboard.s:
pad1.y += PAD_SPEED
if keyboard.up:
pad2.y -= PAD_SPEED
elif keyboard.down:
pad2.y += PAD_SPEED
for i in range(2):
ball.pos[i] += ball.speed[i] * dt
if ball.pos[1] >= HEIGHT or ball.pos[1] <= 0:
ball.speed[1] *= -1
if ball.pos[0] <= 0:
scores[1] += 1
ball.reset()
if ball.pos[0] >= WIDTH:
scores[0] += 1
ball.reset()
if pad1.collidepoint(ball.pos) or pad2.collidepoint(ball.pos):
ball.speed[0] *= -1
pgzrun.go()
以上就是关于pgzero
制作游戏的教程了,希望你们喜欢。