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

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

Paul Vincent Craven。CC BY-SA 4.0

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

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

5 条评论

非常适合开源游戏竞赛!

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

首先:Python 3.6 很痛苦,因为它太新了,大多数发行版甚至没有包含 3.anything,但至少在大多数发行版上,启动和运行 3.5.x 相当容易,据我所知,但 3.6.? 从源代码编译并费力地添加位和零碎的东西并使其真正工作?谁没有在这样的事情上浪费太多时间?

您没有帮助自己。这是 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。我不太确定您在这里寻找什么。

回复 作者 少许文字… (未验证)

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

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

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

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.