总览
许多开发人员进入软件开发是因为他们想开发游戏。 并非每个人都可以成为专业的游戏开发人员,但是每个人都可以出于娱乐目的甚至可能自己的利益而制作自己的游戏。 在这个由五部分组成的系列文章中,我将向您展示如何使用Python 3和出色的Pygame框架创建2D单人游戏。
我们将构建经典突破游戏的一个版本。 一切都说完之后,您将清楚地了解创建自己的游戏所需的时间,熟悉Pygame的功能,并拥有一个示例游戏。
以下是我们将实现的功能:
- 简单的通用GameObject和TextObject
- 简单的通用游戏对象
- 简单的通用按钮
- 配置文件
- 处理键盘和鼠标事件
- 砖,桨和球
- 管理桨运动
- 处理球与所有物体的碰撞
- 背景图像
- 声音特效
- 可扩展的特效系统
您不应该期望的是视觉上令人愉悦的游戏。 我是程序员,而不是艺术家。 我更担心代码的美感。 我的视觉设计结果可能会令人震惊。 从好的方面来说,如果您想改进此版本Breakout的外观,则有大量的改进空间。 有了这个可怕的警告,下面是一个屏幕截图:
完整的源代码在这里 。
游戏编程快速入门
游戏是关于在屏幕上移动像素并发出噪音。 几乎所有视频/计算机游戏都具有以下大多数元素。 本文不涉及客户端服务器游戏和多人游戏,它们也涉及很多网络编程。
主循环
游戏的主循环以固定的时间间隔运行并刷新屏幕。 这是您的帧频,它指示事物的平滑程度。 通常,游戏每秒刷新屏幕30到60次。 如果速度变慢,屏幕上的对象将显得生涩。
在主循环内,有三个主要活动:处理事件,更新游戏状态以及绘制屏幕的当前状态。
处理事件
游戏中的事件包括发生在游戏代码控制之外但与游戏操作相关的所有事件。 例如,如果在“突破”中玩家按下左箭头键,则游戏需要将球拍向左移动。 典型事件包括按键(和释放),鼠标移动,鼠标单击(特别是在菜单中)和计时器事件(例如,特殊效果在10秒后失效)。
更新状态
每个游戏的核心是其状态:它会跟踪并在屏幕上绘制的内容。 在“突破”中,状态包括所有积木的位置,球的位置和速度,球拍的位置以及生命和得分。
还有一种辅助状态可以帮助管理游戏:
- 我们现在显示菜单吗?
- 游戏结束了吗?
- 玩家赢了吗?
画画
游戏需要在屏幕上显示其状态。 这包括绘制几何形状,图像和文本。
游戏物理
大多数游戏模拟物理环境。 在“突破”中,球反弹出物体,并具有非常粗糙的刚体物理系统(如果可以这样称呼)。
更高级的游戏可能具有更复杂,更逼真的物理系统(尤其是3D游戏)。 请注意,某些游戏(如纸牌游戏)根本没有太多的物理原理,这完全可以。
AI(人工智能)
在许多游戏中,您都是与一个或多个人造计算机的对手对抗,或者有一些敌人试图杀死您或更糟。 游戏想象力的这些虚构人物在游戏世界中通常以看似明智的方式表现。
例如,敌人会追赶您并知道您的位置。 突破不代表AI。 您在寒冷,坚硬的砖块上玩耍。 但是,游戏中的AI通常非常简单,并且只需遵循简单(或复杂)的规则即可获得伪智能结果。
播放音讯
播放音频是游戏的另一个重要方面。 通常有两种类型的音频:背景音乐和声音效果。 在“突破”中,我专注于在发生各种事件时短暂播放的声音效果。
背景音乐就是在背景中不断播放的音乐。 有些游戏不使用背景音乐,有些则在每个级别进行切换。
生命,得分和等级
大多数游戏为您提供一定的生命,而当您用尽生命时,游戏就结束了。 您通常还会得到一个分数,使您对自己的表现有所了解,并有动机去改善下一次比赛的机会,或者只是向朋友吹嘘自己的突围疯狂技巧。 许多游戏的级别完全不同或提高了难度。
认识Pygame
在深入并开始实施之前,让我们学习一些有关Pygame的知识,它将为我们带来很多繁重的工作。
什么是Pygame?
Pygame是用于游戏编程的Python框架。 它建立在SDL之上,具有所有优点:
-
成熟
-
伟大的社区
-
开源的
-
跨平台
-
好的文档
-
大量的示例游戏
简单易学
安装Pygame
键入pip install pygame
进行安装。 如果您还需要其他内容,请按照Wiki 入门部分中的说明进行操作。 如果像我一样运行macOS Sierra,则可能会遇到麻烦。 我能够毫不费力地安装Pygame,代码似乎运行得很好,但是游戏窗口从未显示出来。
当您运行游戏时,这真是太无聊了。 我最终不得不求助于在VirtualBox VM上的Windows上运行。 希望在您阅读本文时,该问题已解决。
游戏架构
游戏需要管理大量信息,并对许多对象执行类似的操作。 Breakout是一款迷你游戏,但是尝试在一个文件中管理所有内容将是压倒性的。 相反,我选择创建适合大型游戏的文件结构和体系结构。
目录和文件结构
├── Pipfile
├── Pipfile.lock
├── README.md
├── ball.py
├── breakout.py
├── brick.py
├── button.py
├── colors.py
├── config.py
├── game.py
├── game_object.py
├── images
│ └── background.jpg
├── paddle.py
├── sound_effects
│ ├── brick_hit.wav
│ ├── effect_done.wav
│ ├── level_complete.wav
│ └── paddle_hit.wav
└── text_object.py
Pipfile和Pipfile.lock是在Python中管理依赖项的现代方法。 images目录包含游戏使用的图像(仅此版本中的背景图像),而sound_effects目录包含用作音频效果(您猜对了)的简短音频片段。
ball.py,paddle.py和brick.py文件包含特定于这些Breakout对象中的每个对象的代码。 我将在本系列的后面部分深入介绍它们。 text_object.py文件包含用于在屏幕上显示文本的代码,而background.py文件包含特定于Breakout的游戏逻辑。
但是,有几个模块构成了一个松散的通用框架。 在那里定义的类可以重用于其他基于Pygame的游戏。
GameObject类
GameObject代表一个视觉对象,该对象知道如何渲染自身,保持其边界以及四处移动。 Pygame实际上有一个Sprite类,它具有相似的作用,但是在本系列中,我想说明事物是如何在较低的层次上工作的,而不是依赖过多的预包装魔术。 这是GameObject类:
from pygame.rect import Rect
class GameObject:
def __init__(self, x, y, w, h, speed=(0,0)):
self.bounds = Rect(x, y, w, h)
self.speed = speed
@property
def left(self):
return self.bounds.left
@property
def right(self):
return self.bounds.right
@property
def top(self):
return self.bounds.top
@property
def bottom(self):
return self.bounds.bottom
@property
def width(self):
return self.bounds.width
@property
def height(self):
return self.bounds.height
@property
def center(self):
return self.bounds.center
@property
def centerx(self):
return self.bounds.centerx
@property
def centery(self):
return self.bounds.centery
def draw(self, surface):
pass
def move(self, dx, dy):
self.bounds = self.bounds.move(dx, dy)
def update(self):
if self.speed == [0, 0]:
return
self.move(*self.speed)
GameObject旨在用作其他对象的基类。 它直接公开了其self.bounds矩形的许多属性,并在其update()
方法中根据其当前速度移动对象。 它在其draw()
方法中不执行任何操作,该方法应由子类覆盖。
游戏类
游戏类是游戏的核心。 它运行主循环。 它具有许多有用的功能。 让我们逐个方法。
__init__()
方法初始化Pygame本身,字体系统和音频混合器。 您需要进行三个不同的调用的原因是因为并非所有的Pygame游戏都使用所有组件,所以您可以控制要使用的子系统并仅使用其特定参数初始化那些子系统。 它以正确的帧频创建背景图像,主表面(绘制了所有内容的主表面)和游戏时钟。
self.objects成员将保留所有需要渲染和更新的游戏对象。 各种处理程序管理在某些事件发生时应调用的处理程序功能的列表。
import pygame
import sys
from collections import defaultdict
class Game:
def __init__(self,
caption,
width,
height,
back_image_filename,
frame_rate):
self.background_image = \
pygame.image.load(back_image_filename)
self.frame_rate = frame_rate
self.game_over = False
self.objects = []
pygame.mixer.pre_init(44100, 16, 2, 4096)
pygame.init()
pygame.font.init()
self.surface = pygame.display.set_mode((width, height))
pygame.display.set_caption(caption)
self.clock = pygame.time.Clock()
self.keydown_handlers = defaultdict(list)
self.keyup_handlers = defaultdict(list)
self.mouse_handlers = []
update()
和draw()
方法非常简单。 它们只是遍历所有托管游戏对象并调用其相应方法。 如果两个游戏对象重叠,则对象列表中的顺序将确定首先渲染哪个对象,另一个将部分或全部覆盖它。
def update(self):
for o in self.objects:
o.update()
def draw(self):
for o in self.objects:
o.draw(self.surface)
handle_events()
方法侦听Pygame生成的事件,例如按键和鼠标事件。 对于每个事件,它都会调用所有已注册的处理程序函数来处理此类事件。
def handle_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
for handler in self.keydown_handlers[event.key]:
handler(event.key)
elif event.type == pygame.KEYUP:
for handler in self.keydown_handlers[event.key]:
handler(event.key)
elif event.type in (pygame.MOUSEBUTTONDOWN,
pygame.MOUSEBUTTONUP,
pygame.MOUSEMOTION):
for handler in self.mouse_handlers:
handler(event.type, event.pos)
最后, run()
方法运行主循环。 它一直运行到game_over
成员变为True为止。 在每次迭代中,它都会渲染背景图像,并依次调用handle_events()
, update()
和draw()
方法。
然后,它更新显示,这实际上将使用此迭代期间渲染的所有内容来更新物理显示。 最后但并非最不重要的一点是,它调用clock.tick()
方法来控制何时调用下一个迭代。
def run(self):
while not self.game_over:
self.surface.blit(self.background_image, (0, 0))
self.handle_events()
self.update()
self.draw()
pygame.display.update()
self.clock.tick(self.frame_rate)
结论
在这一部分中,您已经学习了游戏编程的基础知识以及制作游戏所涉及的所有组件。 然后,我们研究了Pygame本身以及如何安装。 最后,我们深入研究了游戏架构,并检查了目录结构, GameObject
类和Game类。
在第二部分中,我们将研究用于在屏幕上呈现文本的TextObject
类。 我们将创建包含背景图像的主窗口,然后学习如何绘制诸如球和球拍之类的对象。
此外,请查看我们在Envato市场上可以出售和研究的产品 ,不要犹豫,使用下面的feed提出任何问题并提供宝贵的反馈。
翻译自: https://code.tutsplus.com/tutorials/building-games-with-python-3-and-pygame-part-1--cms-30081