一个简单的游戏
导入并初始化
每个模块都需要导入,pygame 也不例外。虽然我们需要为 pygame 中的所有导入模块调用函数 pygame.init()
来正确初始化。如果我们忘记这一点,一些模块将无法工作。该函数还返回所有成功和失败的初始化的元组(如果模块初始化失败,则不会引发错误)。
import pygame
successes, failures = pygame.init()
print("{0} successes and {1} failures".format(successes, failures))
创造必需品
我们还需要创建一个显示器。Pygame 已经创建了一个(隐藏)显示,所以我们需要做的就是设置显示模式(在这个例子中我们只设置分辨率)。创建一个时钟以确保我们的程序以固定的速度更新也是一个好主意(否则它会以不同的速度运行,具体取决于计算机的速度)。
screen = pygame.display.set_mode((720, 480)) # Notice the tuple! It's not 2 arguments.
clock = pygame.time.Clock()
FPS = 60 # This variable will define how many frames we update per second.
为了稍后在我们的代码中可读性,我们将创建两个颜色常量,它们代表红色,绿色和蓝色(RGB)的元组。值从 0(无光)到 255(全光)。
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
在 pygame 中,我们通常使用 Surface 来表示对象的外观,使用 Rect (矩形)来表示对象的位置。甲表面是像的一张白纸含有颜色或图像。如果你正在创建一个类,你应该将属性命名为 image 和 *rect,*因为许多函数将查找并使用这些属性。这些类可以通过继承 pygame.sprite.Sprite
类而受益,原因可以在这里阅读。
rect = pygame.Rect((0, 0), (32, 32)) # First tuple is position, second is size.
image = pygame.Surface((32, 32)) # The tuple represent size.
image.fill(WHITE) # We fill our surface with a nice white color (by default black).
游戏循环
现在我们为游戏循环设置了所有内容。这是一个为整个游戏运行的循环,我们处理事件并更新屏幕和对象的位置。
首先,我们将确保我们的循环在给定的 FPS 下执行。我们定义了 FPS 并在程序开始时创建了我们的时钟。以下代码将确保我们的程序有足够的时间来使循环重复我们定义 FPS 的量。在这个例子中,每秒 60 次。
clock.tick(FPS)
然后我们将处理事件。事件基本上是用户操作,例如移动鼠标或按键。Pygame 会将所有这些事件注册到我们通过调用 pygame.event.get()
得到的队列中。我们可以迭代这个并检查是否有我们想要处理的事件。事件有一个 type 属性,我们可以检查 pygame 模块中的常量来确定它是什么类型的事件。
for event in pygame.event.get():
if event.type == pygame.QUIT: # The user pressed the close button in the top corner of the window.
quit()
# Close the program. Other methods like 'raise SystemExit' or 'sys.exit()'.
# Calling 'pygame.quit()' won't close the program! It will just uninitialize the modules.
我们还可以检查 if event.type == pygame.KEYDOWN
以查看用户是否按下了按键。在这种情况下,事件有一个属性键,我们可以检查它以查看它代表的键。
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
rect.move_ip(0, -2) # Changes the rect's position.
elif event.key == pygame.K_s:
rect.move_ip(0, 2)
elif event.key == pygame.K_a:
rect.move_ip(-2, 0)
elif event.key == pygame.K_d:
rect.move_ip(2, 0)
现在我们需要显示我们的图像。首先,我们可能希望从之前的渲染中清除屏幕。我们通过用黑色填充整个屏幕来做到这一点(删除代码以了解我们为什么要清除它)。然后,我们需要的 blit 我们的图像在屏幕上。Blitting 实际上意味着将图像复制到另一个表面(在我们的例子中是屏幕)。最后我们翻转或更新屏幕。
当我们进行 blitting 时,我们实际上并没有向用户显示任何内容。想象一下它是一边的计算机而另一边是用户。计算机在屏幕的一侧绘制( blits ),将其翻转向用户,然后重复。
screen.fill(BLACK)
screen.blit(image, rect)
pygame.display.update() # Or 'pygame.display.flip()'.
现在我们有一个基本的游戏! 非常无聊,是的,但必需品在那里! 结合你当前的 Python 知识,你应该能够创建出色的东西。
完整的代码
import pygame
successes, failures = pygame.init()
print("{0} successes and {1} failures".format(successes, failures))
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 60 # Frames per second.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# RED = (255, 0, 0), GREEN = (0, 255, 0), BLUE = (0, 0, 255).
rect = pygame.Rect((0, 0), (32, 32))
image = pygame.Surface((32, 32))
image .fill(WHITE)
while True:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
rect.move_ip(0, -2)
elif event.key == pygame.K_s:
rect.move_ip(0, 2)
elif event.key == pygame.K_a:
rect.move_ip(-2, 0)
elif event.key == pygame.K_d:
rect.move_ip(2, 0)
screen.fill(BLACK)
screen.blit(image, rect)
pygame.display.update() # Or pygame.display.flip()
稍微改进了游戏机制
请注意,程序会检查我们何时按下按键,而不是当我们按住按键时。为了解决这个问题,我们可以引入速度变量。我们可以创建一个播放器类来保持它更有条理。为了避免与帧相关的移动(如果我们将 FPS 更改为 30,对象将以一半的速度移动),我们通过将时间间隔传递给可移动对象来引入时间相关的移动。
import pygame
successes, failures = pygame.init()
print("Initializing pygame: {0} successes and {1} failures.".format(successes, failures))
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 60
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(WHITE)
self.rect = self.image.get_rect() # Get rect of some size as 'image'.
self.velocity = [0, 0]
def update(self):
self.rect.move_ip(*self.velocity)
player = Player()
running = True
while running:
dt = clock.tick(FPS) / 1000 # Returns milliseconds between each call to 'tick'. The convert time to seconds.
screen.fill(BLACK) # Fill the screen with background color.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player.velocity[1] = -200 * dt # 200 pixels per second
elif event.key == pygame.K_s:
player.velocity[1] = 200 * dt
elif event.key == pygame.K_a:
player.velocity[0] = -200 * dt
elif event.key == pygame.K_d:
player.velocity[0] = 200 * dt
elif event.type == pygame.KEYUP:
if event.key == pygame.K_w or event.key == pygame.K_s:
player.velocity[1] = 0
elif event.key == pygame.K_a or event.key == pygame.K_d:
player.velocity[0] = 0
player.update()
screen.blit(player.image, player.rect)
pygame.display.update() # Or pygame.display.flip()
print("Exited the game loop. Game will quit...")
quit() # Not actually necessary since the script will exit anyway.