如何使用 Python 和 Arcade 库创建 2D 游戏

了解如何开始使用 Arcade,这是一个易于使用的 Python 库,用于创建 2D 视频游戏。
575 位读者喜欢这个。
2d arcade game sprite

Paul Vincent Craven。CC BY-SA 4.0

Python 是一种非常适合编程初学者的语言,也非常适合任何想要“完成工作”而不花费大量时间在样板代码上的人。Arcade 是一个用于创建 2D 视频游戏的 Python 库,它易于上手,并且随着您经验的积累,功能非常强大。在本文中,我将解释如何开始使用 Python 和 Arcade 编写视频游戏程序。

在使用 PyGame 库教授学生后,我开始了 Arcade 的开发。我使用 PyGame 进行了近 10 年的面对面教学,并且我开发了 ProgramArcadeGames.com 用于在线教学。PyGame 很棒,但最终我觉得我浪费了时间来弥补从未修复的错误

我担心教授诸如事件循环之类的东西,这已不再是我们编写代码的方式。我有一个整个章节,我在其中解释了为什么 y 坐标是反向的。由于 PyGame 很少更新,并且它基于旧的 SDL 1 库,而不是像更现代的 OpenGL,我对它的未来并没有抱太大的希望。

我想要一个更易于使用、更强大且使用 Python 3 新特性(如装饰器和类型提示)的库。Arcade 就是它。这就是如何开始使用它。

安装

Arcade,像许多其他软件包一样,可以通过 PyPi 获得,这意味着您可以使用 pip 命令(或 pipenv 命令)安装 Arcade。如果您已经安装了 Python,您可能只需打开 Windows 上的命令提示符并键入

pip install arcade

或者在 MacOS 和 Linux 上键入

pip3 install arcade

有关更详细的安装说明,您可以参考 Arcade 安装文档

简单绘图

您只需几行代码即可打开窗口并创建简单的图形。让我们创建一个示例,绘制一个笑脸,如下图所示

smile face image

下面的脚本展示了如何使用 Arcade 的绘图命令来做到这一点。请注意,您不需要知道如何使用甚至定义函数。对于任何想要开始学习编程的人来说,通过快速的视觉反馈进行编程非常棒。

import arcade

# Set constants for the screen size
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600

# Open the window. Set the window title and dimensions (width and height)
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")

# Set the background color to white.
# For a list of named colors see:
# http://arcade.academy/arcade.color.html
# Colors can also be specified in (red, green, blue) format and
# (red, green, blue, alpha) format.
arcade.set_background_color(arcade.color.WHITE)

# Start the render process. This must be done before any drawing commands.
arcade.start_render()

# Draw the face
x = 300
y = 300
radius = 200
arcade.draw_circle_filled(x, y, radius, arcade.color.YELLOW)

# Draw the right eye
x = 370
y = 350
radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)

# Draw the left eye
x = 230
y = 350
radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)

# Draw the smile
x = 300
y = 280
width = 120
height = 100
start_angle = 190
end_angle = 350
arcade.draw_arc_outline(x, y, width, height, arcade.color.BLACK, start_angle, end_angle, 10)

# Finish drawing and display the result
arcade.finish_render()

# Keep the window open until the user hits the 'close' button
arcade.run()

使用函数

当然,在全局上下文中编写代码不是好的形式。值得庆幸的是,通过使用函数来改进您的程序很容易。在这里,我们可以看到一个使用函数在特定 (x, y) 位置绘制松树的示例

def draw_pine_tree(x, y):
    """ This function draws a pine tree at the specified location. """
    
    # Draw the triangle on top of the trunk.
    # We need three x, y points for the triangle.
    arcade.draw_triangle_filled(x + 40, y,       # Point 1
                                x, y - 100,      # Point 2
                                x + 80, y - 100, # Point 3
                                arcade.color.DARK_GREEN)

    # Draw the trunk
    arcade.draw_lrtb_rectangle_filled(x + 30, x + 50, y - 100, y - 140,
                                      arcade.color.DARK_BROWN)

有关完整示例,请参阅使用函数绘图

classes and functions

更有经验的程序员会知道,现代图形程序首先将绘图信息加载到图形卡上,然后要求图形卡稍后批量绘制。Arcade 也支持这一点。单独绘制 10,000 个矩形大约需要 0.800 秒。批量绘制它们的时间不到 0.001 秒。

Window 类

较大的程序通常会从 Window 类派生,或者使用装饰器。这允许程序员编写代码来处理绘图、更新和处理来自用户的输入。下面是基于 Window 的程序的启动模板。

import arcade

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600


class MyGame(arcade.Window):
    """ Main application class. """

    def __init__(self, width, height):
        super().__init__(width, height)

        arcade.set_background_color(arcade.color.AMAZON)

    def setup(self):
        # Set up your game here
        pass

    def on_draw(self):
        """ Render the screen. """
        arcade.start_render()
        # Your drawing code goes here

    def update(self, delta_time):
        """ All the logic to move, and the game logic goes here. """
        pass


def main():
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
    game.setup()
    arcade.run()


if __name__ == "__main__":
    main()

Window 类有几个方法,您的程序可以覆盖这些方法来为程序提供功能。以下是一些最常用的方法

  • on_draw:所有绘制屏幕的代码都在这里。
  • update:所有移动项目和执行游戏逻辑的代码都在这里。这大约每秒调用 60 次。
  • on_key_press:处理按下键时的事件,例如给玩家一个速度。
  • on_key_release:处理释放键时的情况,在这里您可能会阻止玩家移动。
  • on_mouse_motion:每次鼠标移动时都会调用此方法。
  • on_mouse_press:在按下鼠标按钮时调用。
  • set_viewport:此函数用于滚动游戏中,当您拥有一个比一个屏幕上可见的世界大得多的世界时。调用 set_viewport 允许程序员设置当前可见的世界部分。

精灵

精灵是在 Arcade 中创建 2D 位图对象的简单方法。Arcade 具有使精灵易于绘制、移动和动画化的方法。您还可以轻松地使用精灵来检测对象之间的碰撞。

创建精灵

从图形创建 Arcade 的 Sprite 类的实例非常容易。程序员只需要基于精灵的图像文件名,以及可选的用于放大或缩小图像的数字。例如

SPRITE_SCALING_COIN = 0.2

coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

此代码将使用存储在 coin_01.png 中的图像创建一个精灵。图像将缩小到其原始高度和宽度的 20%。

sprites collecting coins

精灵列表

精灵通常组织成列表。这些列表使管理精灵更容易。列表中的精灵将使用 OpenGL 批量绘制精灵组。下面的代码设置了一个带有玩家和一堆硬币的游戏供玩家收集。我们使用两个列表,一个用于玩家,一个用于硬币。

def setup(self):
    """ Set up the game and initialize the variables. """

    # Create the sprite lists
    self.player_list = arcade.SpriteList()
    self.coin_list = arcade.SpriteList()

    # Score
    self.score = 0

    # Set up the player
    # Character image from kenney.nl
    self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER)
    self.player_sprite.center_x = 50 # Starting position
    self.player_sprite.center_y = 50
    self.player_list.append(self.player_sprite)

    # Create the coins
    for i in range(COIN_COUNT):

        # Create the coin instance
        # Coin image from kenney.nl
        coin = arcade.Sprite("images/coin_01.png", SPRITE_SCALING_COIN)

        # Position the coin
        coin.center_x = random.randrange(SCREEN_WIDTH)
        coin.center_y = random.randrange(SCREEN_HEIGHT)

        # Add the coin to the lists
        self.coin_list.append(coin)

我们可以轻松地绘制硬币列表中的所有硬币

def on_draw(self):
    """ Draw everything """
    arcade.start_render()
    self.coin_list.draw()
    self.player_list.draw()

检测精灵碰撞

函数 check_for_collision_with_list 允许我们查看一个精灵是否撞到了列表中的另一个精灵。我们可以使用它来查看玩家精灵接触的所有硬币。使用简单的 for 循环,我们可以从游戏中删除硬币并增加我们的分数。

def update(self, delta_time):
    # Generate a list of all coin sprites that collided with the player.
    coins_hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.coin_list)

    # Loop through each colliding sprite, remove it, and add to the score.
    for coin in coins_hit_list:
        coin.kill()
        self.score += 1

有关完整示例,请参阅collect_coins.py

游戏物理

许多游戏都包含某种物理效果。最简单的是自上而下的程序,可防止玩家穿墙而过。平台游戏通过重力和移动平台增加了复杂性。一些游戏使用完整的 2D 物理引擎,具有质量、摩擦力、弹簧等等。

自上而下的游戏

spring moving to walls

对于简单的自上而下的游戏,Arcade 程序需要一个玩家(或任何其他东西)无法穿过的墙壁列表。我通常称之为 wall_list。然后在 Window 类的设置代码中使用以下代码创建物理引擎

self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)

player_sprite 被赋予一个运动向量,其中包含它的两个属性 change_xchange_y。执行此操作的一个简单示例是让玩家通过键盘移动。例如,这可能在 Window 类的自定义子类中

MOVEMENT_SPEED = 5

def on_key_press(self, key, modifiers):
    """Called whenever a key is pressed. """

    if key == arcade.key.UP:
        self.player_sprite.change_y = MOVEMENT_SPEED
    elif key == arcade.key.DOWN:
        self.player_sprite.change_y = -MOVEMENT_SPEED
    elif key == arcade.key.LEFT:
        self.player_sprite.change_x = -MOVEMENT_SPEED
    elif key == arcade.key.RIGHT:
        self.player_sprite.change_x = MOVEMENT_SPEED

def on_key_release(self, key, modifiers):
    """Called when the user releases a key. """

    if key == arcade.key.UP or key == arcade.key.DOWN:
        self.player_sprite.change_y = 0
    elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
        self.player_sprite.change_x = 0

尽管该代码设置了玩家的速度,但它并没有移动玩家。在 Window 类的 update 方法中,调用 physics_engine.update() 将移动玩家,但不会穿过墙壁。

def update(self, delta_time):
    """ Movement and game logic """

     self.physics_engine.update()

有关完整示例,请参阅sprite_move_walls.py

平台游戏

sprite tiled map

切换到侧视图平台游戏相当容易。程序员只需要将物理引擎切换到 PhysicsEnginePlatformer 并添加重力常数即可。

self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
                                                     self.wall_list, 
                                                     gravity_constant=GRAVITY)

您可以使用像 Tiled 这样的程序来铺设构成关卡的图块/块。

有关示例,请参阅sprite_tiled_map.py

对于完整的 2D 物理效果,您可以集成 PyMunk 库。

通过示例学习

最好的学习方法之一是通过示例。Arcade 库有很长的示例程序列表,人们可以借鉴这些示例程序来创建游戏。这些示例都展示了多年来学生在我的课堂上或在线上要求的游戏概念。

一旦安装了 Arcade,运行这些演示中的任何一个都很容易。每个示例的程序开头都有一个注释,其中包含您可以在命令行中键入以运行示例的命令,例如

python -m arcade.examples.sprite_moving_platforms

总结

Arcade 让您可以使用易于理解的代码开始编写图形和游戏程序。许多新程序员在入门后不久就创建了很棒的游戏。试一试!


要了解更多信息,请参加 Paul Vincent Craven 在 PyCon Cleveland 2018 上的演讲 使用 Arcade 轻松创建 2D 游戏

User profile image.
Paul Craven 是《Program Arcade Games with Python and Pygame》的作者,这是一本在线和印刷资源,用于学习编程。他是 Arcade Python 2D 图形游戏库的主要维护者。Paul 还是爱荷华州辛普森学院的教授。

5 条评论

非常适合开源游戏竞赛!

Tl;dr:在替换 PyGame 时,您设法做了完全相同的核心错误,并且使您的库启动和运行变得过于烦人,至少在我认为的合理方式中(当然是主观的)。

首先:Python 3.6 很痛苦,因为它太新了,大多数发行版甚至没有包含 3.x,但至少让 3.5.x 启动和运行在大多数发行版上都相当容易,据我所知,但 3.6.? 从源代码编译并与添加的 bits and bobs 以及实际工作的其他东西作斗争?谁没有在这样的事情上浪费太多时间?

你没有帮助自己。这是 PyGame 不支持 Python 人们已经拥有的东西的完美镜像。在 PyGame 中,这是因为 PyGame 已经过时(并且已损坏),而在 Arcade 中,这是因为 Python 太新了。没什么区别。如果您在专用环境等中运行,那还可以,但不适用于想要窥视并以最低成本直接进入的普通人在家中使用。

第二个问题:pip,根据捷克情报机构一年前的报告,它仍然是一个完全开放且未经审查的安全问题吗?任何人应该使用 pip 吗?

Python 使用 pip 失去了方向。这不是你的错

很抱歉在只使用永远不会被读取的邮件地址(因为 Google 是邪恶的)的驱动器评论中出现如此负面的问题,但应该有人指出这些事情,而人们通常不会,所以这一次我调整了 opensource.com 的临时 JavaScript 设置并发表了评论。

尽管如此,您的实际库看起来很有趣,可惜我已经厌倦了障碍。

您说得对,对于某些 Linux 发行版来说,3.6 很难启动和运行。对于 Windows、MacOS 和 Ubuntu,它将是最近安装的人的默认版本。Linux Mint 或其他一些发行版则不然。我会认真考虑你的评论。我非常喜欢新的打印格式,但它在代码中并没有大量使用。

如果您不喜欢 pip,请使用 setup.py 安装 Arcade。我不太确定您在这里寻找什么。

回复 作者:A few bits sho… (未验证)

这看起来太棒了。我迫不及待想尝试一下。

我刚刚在 Linux 上安装了它,很快就会试用。它看起来很棒。我喜欢 PyGame,但它*确实*显示出它的年代,所以这是一个非常受欢迎的开发。

哇,Paul,这个库看起来真的很有前途。去年我给 10-13 岁的孩子开了一个 Python 课,使用了 PyGame。虽然进展顺利,但我有时会对 PyGame 库的局限性和文档的混乱状态感到沮丧。迫不及待想尝试 Arcade!

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.