【Python项目实战】主流游戏引擎(上)

引言

相信大多数人在小时候都或多或少玩过许多款游戏,当我们第一次接触编程时,心中想到的第一件事情可能也是开发一款非常酷的游戏,可以分享给我们的家人、同学和朋友。

倘若没有现成的框架或足够的学习资料时,常人是难以开发出一款带有高级特效的游戏。现在,学完了Python这门编程语言,结合强大且丰富的Python社区,使得我们制作优秀电脑游戏比以往容易得多。

在本教程中,我们将带你探索几款热门的基于Python的游戏引擎,带领大家学习如何制作自己的第一款Python小游戏!

本文的大纲目录如下:

  • 了解现如今几款流行的Python游戏引擎的优缺点
  • 了解这些游戏引擎的运行情况
  • 了解它们与独立游戏引擎的区别
  • 了解其他可用的Python游戏引擎

Source: Top Python Game Engines
Author: Jon Fincher
Date: May 04, 2022

Python Game Engines Overview

Python游戏引擎通常以Python库的形式呈现给大家,因此可以以多种方式进行安装。大多数都可以在PyPI上使用,并且可以通过pip工具进行安装。然而,有一些只能在GitHubGitLab或其他代码托管仓库上使用,而且它们可能需要其他安装步骤。本文将介绍几个常用游戏引擎的安装方法。

Python是一种通用编程语言,除了编写电脑游戏外,它还被用于各种任务。相比之下,有许多不同的独立游戏引擎是专门为编写游戏而定制的。其中包括:

  • The Unreal Engine
  • Unity
  • Godot

这些独立的游戏引擎与Python游戏引擎的几个关键区别点在于:

语言支持 C++、C#和JavaScript等语言在独立游戏引擎中很受欢迎,因为引擎本身通常是用这些语言编写的。很少有独立引擎支持Python

专有脚本支持 此外,许多独立的游戏引擎维护和支持自己的脚本语言,这可能与Python不同。例如,Unity原生使用C#,而Unreal最适合使用C++

平台支持 许多现代的独立游戏引擎能够为各种平台(包括手机和专用游戏系统)制作游戏,而无需复杂的程序。相比之下,将Python游戏移植到不同平台,特别是移动端平台,可能是一项艰巨的任务。

授权选项 基于所使用的引擎,使用独立游戏引擎编写的游戏可能有不同的授权选项和限制。

那么为什么我们还要用Python来编写游戏呢?其中一个很重要的原因便是使用独立游戏引擎通常需要你学习一种新的编程或脚本语言,即学习成本很高。而Python游戏引擎只需要你捡起现有的Python基础知识便可以完成许多游戏demo的开发,降低学习难度,让你快速入门。

Python环境中有许多好用的游戏引擎,这里的优势主要是指以下几点:

  • 社区活跃
  • 持续维护
  • 文档丰富

下面,我们将按照下述步骤介绍每个游戏引擎:

  • 安装方法
  • 基础概念
  • 主要功能
  • 代码实现

Pygame

所有接触过Python编程的人,第一个能想到的游戏引擎应该就是Pygame。事实上,在Real Python中已经有一个关于Pygame的很好的入门教程,强烈推荐大家关注学习!后续我们也会同步更新相关的教程呈现给大家。

Pygame是作为被搁置的PySDL库的替换而编写的,它封装并扩展了SDL库,SDLSimple DirectMedia Layer的缩写。SDL提供对系统底层多媒体硬件组件(如声音、视频、鼠标、键盘和操纵杆)的跨平台访问。SDLPygame的跨平台特性意味着你可以为支持它们的每个平台编写游戏和丰富的多媒体Python程序!

Pygame Installation

PygamePyPI上可用,因此在创建并激活虚拟环境后,可以使用适当的pip命令安装它:

$ python -m pip install pygame

完成之后,你可以通过运行该库附带的例子来验证安装是否成功:

python -m pygame.examples.aliens

现在你已经安装了Pygame,你可以立即开始使用它。如果在安装过程中遇到问题,那么入门指南将为你提供一切尽可能的解决方案。

Basic Concepts

Pygame被组织成几个不同的模块,它们提供对计算机图形声音和输入硬件的抽象访问。Pygame还定义了许多类,这些类封装了与硬件无关的概念。例如,绘图是在Surface对象上完成的,其矩形限制由其Rect对象定义。

每款游戏都利用游戏循环来控制游戏玩法。这个循环会随着游戏的进展而不断迭代。Pygame提供了实现游戏循环的方法和函数,但它不自动提供。游戏作者需要实现游戏循环的功能

游戏循环的每次迭代被称为一个框架。每一,游戏执行个重要动作:

  • 处理用户输入Pygame中的用户输入是使用事件模型处理的。鼠标键盘输入生成事件,可以根据需要读取和处理事件,也可以忽略事件。Pygame本身不提供任何事件处理程序。

  • 更新游戏对象的状态。游戏对象可以使用任何Pygame数据结构或特殊的Pygame类表示。可以在Python中创建和扩展精灵图像字体颜色等对象,以提供尽可能多的必要状态信息

  • 更新显示和音频输出Pygame提供对显示声音硬件的抽象访问。显示器、混音器和音乐模块允许游戏作者在游戏设计和执行方面的灵活性。

  • 保持游戏的速度Pygame时间模块允许游戏作者控制游戏速度。通过确保每个帧在指定的时间限制内完成,游戏作者可以确保游戏在不同的硬件上运行相似。

Basic Application

首先,让我们新建一个Pygame程序pygame_basic.py,在屏幕上绘制了一些图形和一些文本:

【Python项目实战】主流游戏引擎(上)
pygame

实现上述窗口及功能对应的代码片段如下:

Show/Hide
 
"""
Basic "Hello, World!" program in Pygame

This program is designed to demonstrate the basic capabilities
of Pygame. It will:
- Create a game window
- Fill the background with white
- Draw some basic shapes in different colors
- Draw some text in a specified size and color
- Allow you to close the window
"""


# Import and initialize the pygame library
import pygame

pygame.init()

# Set the width and height of the output window, in pixels
WIDTH = 800
HEIGHT = 600

# Set up the drawing window
screen = pygame.display.set_mode([WIDTH, HEIGHT])

# Run until the user asks to quit
running = True
while running:

    # Did the user click the window close button?
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Fill the background with white
    screen.fill((255255255))

    # Draw a blue circle with a radius of 50 in the center of the screen
    pygame.draw.circle(screen, (00255), (WIDTH // 2, HEIGHT // 2), 50)

    # Draw a red-outlined square in the top-left corner of the screen
    red_square = pygame.Rect((5050), (100100))
    pygame.draw.rect(screen, (20000), red_square, 1)

    # Draw an orange caption along the bottom in 60-point font
    text_font = pygame.font.SysFont("any_font"60)
    text_block = text_font.render(
        "Hello, World! From Pygame"False, (2001000)
    )
    screen.blit(text_block, (50, HEIGHT - 50))

    # Flip the display
    pygame.display.flip()

# Done! Time to quit.
pygame.quit()



尽管它的功能很简单,但即使是这个基本的Pygame程序也需要一个游戏循环事件处理程序。游戏循环从27行开始,由运行变量控制。将此变量设置为False将结束程序。

事件处理从第30行开始,带有一个事件循环。使用pygame.event.get()从队列中检索事件,并在每次循环迭代期间一次处理一个事件。在本例中,唯一要处理的事件是pygame事件,当用户关闭游戏窗口时生成。当处理此事件时,设置running = False,这将最终结束游戏循环和程序。

Pygame提供了各种绘制基本形状的方法,如圆形和矩形。在这个示例中,第38行上画了一个蓝色的圆,第4142行上画了一个红色的正方形。注意,绘制矩形需要首先创建一个Rect对象。

在屏幕上绘制文本稍微复杂一些。首先,在第45行,选择一种字体并创建一个字体对象。在第4648行使用该字体,调用.render()方法进行渲染。这将创建一个包含以指定字体和颜色呈现的文本的Surface对象。最后,使用第49行中的.blit()方法将Surface复制到屏幕上。

游戏循环的结束发生在第52行,当之前绘制的所有内容都显示在显示器上时。如果没有这一行,就不会显示任何内容。

Advanced Application

当然,Pygame的设计初衷是用Python编写游戏。为了探索一个真正的Pygame游戏的功能和需求,我们尝试用Pygame编写一个小游戏,包含以下细节:

  • 玩家是屏幕上的一个精灵,通过移动鼠标进行控制。
  • 每隔一定的时间,硬币就会一个接一个地出现在屏幕上。
  • 当玩家移动到每一枚硬币上时,它就会消失,玩家将获得10分。
  • 随着游戏的发展,玩家会更快地添加金币。
  • 当屏幕上出现十多个硬币时,游戏结束。

完成后,游戏看起来会是这样的:

【Python项目实战】主流游戏引擎(上)
coin

完整的代码如下:

Show/Hide
 
"""
Complete Game in Pygame

This game demonstrates some of the more advanced features of
Pygame, including:
- Using sprites to render complex graphics
- Handling user mouse input
- Basic sound output
"""


# Import and initialize the pygame library
import pygame

# To randomize coin placement
from random import randint

# To find your assets
from pathlib import Path

# For type hinting
from typing import Tuple

# Set the width and height of the output window, in pixels
WIDTH = 800
HEIGHT = 600

# How quickly do you generate coins? Time is in milliseconds
coin_countdown = 2500
coin_interval = 100

# How many coins can be on the screen before you end?
COIN_COUNT = 10

# Define the Player sprite
class Player(pygame.sprite.Sprite):
  def __init__(self):
      """Initialize the player sprite"""
      super(Player, self).__init__()

      # Get the image to draw for the player
      player_image = str(
          Path.cwd() / "pygame" / "images" / "alien_green_stand.png"
      )
      # Load the image, preserve alpha channel for transparency
      self.surf = pygame.image.load(player_image).convert_alpha()
      # Save the rect so you can move it
      self.rect = self.surf.get_rect()

  def update(self, pos: Tuple):
      """Update the position of the player

      Arguments:
          pos {Tuple} -- the (X,Y) position to move the player
      """

      self.rect.center = pos

# Define the Coin sprite
class Coin(pygame.sprite.Sprite):
  def __init__(self):
      """Initialize the coin sprite"""
      super(Coin, self).__init__()

      # Get the image to draw for the coin
      coin_image = str(Path.cwd() / "pygame" / "images" / "coin_gold.png")

      # Load the image, preserve alpha channel for transparency
      self.surf = pygame.image.load(coin_image).convert_alpha()

      # The starting position is randomly generated
      self.rect = self.surf.get_rect(
          center=(
              randint(10, WIDTH - 10),
              randint(10, HEIGHT - 10),
          )
      )

# Initialize the Pygame engine
pygame.init()

# Set up the drawing window
screen = pygame.display.set_mode(size=[WIDTH, HEIGHT])

# Hide the mouse cursor
pygame.mouse.set_visible(False)

# Set up the clock for a decent frame rate
clock = pygame.time.Clock()

# Create a custom event for adding a new coin
ADDCOIN = pygame.USEREVENT + 1
pygame.time.set_timer(ADDCOIN, coin_countdown)

# Set up the coin_list
coin_list = pygame.sprite.Group()

# Initialize the score
score = 0

# Set up the coin pickup sound
coin_pickup_sound = pygame.mixer.Sound(
  str(Path.cwd() / "pygame" / "sounds" / "coin_pickup.wav")
)

# Create a player sprite and set its initial position
player = Player()
player.update(pygame.mouse.get_pos())

# Run until you get to an end condition
running = True
while running:

  # Did the user click the window close button?
  for event in pygame.event.get():
      if event.type == pygame.QUIT:
          running = False

      # Should you add a new coin?
      elif event.type == ADDCOIN:
          # Create a new coin and add it to the coin_list
          new_coin = Coin()
          coin_list.add(new_coin)

          # Speed things up if fewer than three coins are on-screen
          if len(coin_list) < 3:
              coin_countdown -= coin_interval
          # Need to have some interval
          if coin_countdown < 100:
              coin_countdown = 100

          # Stop the previous timer by setting the interval to 0
          pygame.time.set_timer(ADDCOIN, 0)

          # Start a new timer
          pygame.time.set_timer(ADDCOIN, coin_countdown)

  # Update the player position
  player.update(pygame.mouse.get_pos())

  # Check if the player has collided with a coin, removing the coin if so
  coins_collected = pygame.sprite.spritecollide(
      sprite=player, group=coin_list, dokill=True
  )
  for coin in coins_collected:
      # Each coin is worth 10 points
      score += 10
      # Play the coin collected sound
      coin_pickup_sound.play()

  # Are there too many coins on the screen?
  if len(coin_list) >= COIN_COUNT:
      # This counts as an end condition, so you end your game loop
      running = False

  # To render the screen, first fill the background with pink
  screen.fill((255170164))

  # Draw the coins next
  for coin in coin_list:
      screen.blit(coin.surf, coin.rect)

  # Then draw the player
  screen.blit(player.surf, player.rect)

  # Finally, draw the score at the bottom left
  score_font = pygame.font.SysFont("any_font"36)
  score_block = score_font.render(f"Score: {score}"False, (000))
  screen.blit(score_block, (50, HEIGHT - 50))

  # Flip the display to make everything appear
  pygame.display.flip()

  # Ensure you maintain a 30 frames per second rate
  clock.tick(30)

# Done! Print the final score
print(f"Game over! Final score: {score}")

# Make the mouse visible again
pygame.mouse.set_visible(True)

# Quit the game
pygame.quit()



Pygame中的精灵提供了一些基本功能,但它们被设计成子类,而不是单独使用。Pygame精灵在默认情况下没有与它们相关的图像,它们不能自己定位。

为了正确地绘制和管理屏幕上的玩家硬币,在第3555行创建了一个player类,在第5875行创建了一个Coin类。当每个精灵对象被创建时,它首先定位并加载它将显示的图像,并将其保存在self.surf中。self.rect属性用于定位并移动屏幕上的精灵。

定时在屏幕上添加硬币是通过计时器完成的。在·Pygame·中,每当计时器过期时就会触发事件,游戏开发者可以将自己的事件定义为整数常量ADDCOIN事件在第90行上定义,计时器在第91行上的coin_countdown毫秒后触发该事件。

因为ADDCOIN是一个事件,所以需要在事件循环中处理它,这发生在第118行到134行。该事件创建一个新的Coin对象并将其添加到现有的coin_list中。检查屏幕上的硬币数量。如果少于3个,则减少coin_countdown。最后,停止前一个计时器,开始一个新的计时器。

当玩家移动时,他们会与硬币碰撞,并收集它们。这将自动从coin_list中删除收集到的每个硬币。这也会更新乐谱并播放声音。

玩家移动发生在第137行。在第140142行检查与屏幕上硬币的碰撞。dokill=True参数自动从coin_list中删除硬币。最后,第143行到147行更新分数并播放收集到的每个硬币的声音。

当用户关闭窗口,或者当屏幕上有超过10个硬币时,游戏就结束了。在第150行到152行检查是否有超过10个硬币。

因为Pygame精灵没有内置的图像知识,它们也不知道如何在屏幕上绘制自己。游戏作者需要清除屏幕,以正确的顺序绘制所有精灵,绘制屏幕得分,然后.flip()显示所有内容。这一切都发生在第155170行。


总的来说,Pygame是一个非常强大和完善的库,但它也有缺点。实现基本的精灵行为和实现游戏循环和基本事件处理程序等关键游戏需求取决于游戏作者。接下来,你将看到其他游戏引擎如何在减少工作量的同时提供类似的结果。

今天将为大家介绍到这里,后续我们将会以相同的案例为大家介绍其它几款游戏引擎,包括Pygame ZeroArcadeadventurelibRen’Py,大家可以综合比较一下。敬请期待,下期不见不散。




往期回顾
【Python项目实战】网页爬取(上)
【Python项目实战】网页爬取(中)
【Python项目实战】网页爬取(下)
Python-Turtle库-从入门到精通
【不可不知系列】编译器|编辑器|集成开发环境的区别和联系


原文始发于微信公众号(Pytrick):【Python项目实战】主流游戏引擎(上)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/56399.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!