0%

利用pygame开发一款游戏:「跳跳兔」(六)

HackPython改名为「懒编程」,纯粹是因为我有另外一个写杂文的号叫「懒写作」,此外,感觉「懒编程」比HackPython好记。

前言

在第5节内容中,实现了积分机制、玩家死亡逻辑以及游戏开始界面逻辑,本节继续完善游戏,来实现游戏结束逻辑与玩家图片化,不再使用方块。

游戏结束逻辑

回忆一下整体逻辑:

1
2
3
4
5
6
7
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()

pg.quit()

可以通过show_go_screen()方法实现游戏结束逻辑,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# main.py/Game

# 每轮游戏结束后,都会调用该方法
def show_go_screen(self):
# game over/continue
if not self.running: # 是否在运行
return
self.screen.fill(BGCOLOR) # 游戏框背景颜色填充
# 绘制文字
self.draw_text("GAME OVER", 48, WHITE, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, WHITE, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press a key to play again", 22, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# 判断分数
if self.score > self.highscore:
self.highscore = self.score
self.draw_text("NEW HIGH SCORE!", 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 记录新的最高分到文件中 - 持久化
with open(os.path.join(self.dir, HS_FILE), 'w') as f:
f.write(str(self.score))
else:
self.draw_text("High Score: " + str(self.highscore), 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 翻转
pg.display.flip()
# 等待敲击任意键,重新开始新的一轮游戏
self.wait_for_key()

show_go_screen()方法逻辑可以阅读详细的注释,不再赘述。

show_go_screen()通过文件的方式来记录最高的分数,所以在游戏一开始,就需要从文件中读取此前的分数,好为这部分逻辑做判断,逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Game:
def __init__(self):
# ...
# 加载最高分数
self.load_data()

def load_data(self):
self.dir = os.path.dirname(__file__)
filepath = os.path.join(self.dir, HS_FILE)
with open(filepath, 'r') as f:
try:
self.highscore = int(f.read())
except:
self.highscore = 0

玩家图片化

一个正常的游戏,肯定要有相应的图片素材的,图片素材是否精美也是影响他人是否要玩你游戏的重要因素。

通常,一个游戏,会有多张大图,多个同类元素都放在这张大的png图片中,而不是每个元素都是一个png元素,通过这种方式,让整个游戏包更小。

如下,一张完整的图

与其对应的就是图中不同元素其坐标位置(x,y)以及元素图片大小

写一段从完整图片中获取对应元素的逻辑,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# sprites.py

# 加载与解析精灵图片
class Spritesheet:
def __init__(self, filename):
# 主要要使用convert()进行优化, convert()方法会 改变图片的像素格式
# 这里加载了整张图片
self.spritesheet = pg.image.load(filename).convert()

# 从比较大的精灵表中,通过相应的xml位置,抓取中出需要的元素
def get_image(self, x, y, width, height):
# 创建Surface对象(画板对象)
image = pg.Surface((width, height))
# blit — 画一个图像到另一个
# 将整张图片中,对应位置(x,y)对应大小(width,height)中的图片画到画板中
image.blit(self.spritesheet, (0, 0), (x, y, width, height))
# pygame.transform.scale 缩放的大小
# 这里将图片缩放为原来的一半
image = pg.transform.scale(image, (width // 2, height // 2))
return image

在__init__()中,通过pygame.image.load()方法加载完整的图片,记得使用convert()方法进行优化,随后定义了get_image()方法,该方法的逻辑也很直接,先实例化Surface类,获得与图片大小相同的面板对象,然后,通过blit()方法将完整图片中对应位置与大小的元素剥离出来。

经过实践,原本图片元素太大,所以通过pygame.transform.scale()方法将图片元素缩小2倍。

编写完Spritesheet类后,在Game类的load_data()方法中实例化一下

1
2
3
4
5
6
7
8
9
10
11
12
13
# main.py/Game

def load_data(self): # 加载数据
self.dir = os.path.dirname(__file__)
filepath = os.path.join(self.dir, HS_FILE)
with open(filepath, 'r') as f:
try:
self.highscore = int(f.read())
except:
self.highscore = 0
img_dir = os.path.join(self.dir, 'img')
# 加载精灵图片
self.spritesheet = Spritesheet(os.path.join(img_dir, SPRITESHEET))

做完这些后,在Player类初始化时调用其中的get_image()方法就大功告成了。

1
2
3
4
5
6
7
8
9
10
11
12
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
# 加载 bunny1_ready状态的兔子图片, xml文件中给出的(x,y)与(width,height)
self.image = self.game.spritesheet.get_image(614, 1063, 120, 191)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0) # 速度
self.acc = vec(0, 0) # 加速度

为了整体美观,修改了一下,游戏框整体的背景颜色,具体通过self.screen.fill(BGCOLOR)方法。

最后,效果如下:

结尾

在本节中,我们实现了游戏结束界面以及使用了玩家元素,后面,会进一步优化玩家元素,让玩家在左右移动时,是不同的图片,从而让整个游戏显得更加灵动。

因为考虑到篇幅,文中没有给出完整的代码,但为了方便大家理解,我将相应的代码上传到了github

https://github.com/ayuLiao/jumprabbit

如果文章对你有帮助或你觉得有点意思,点击「在看」支持作者一波。