Topic 6 of 6

Course materials are currently available in Russian only.

Уровень, камера и платформер

Уровень из строк

Уровень удобно нарисовать прямо в коде — строками символов:

LEVEL = [
    "....o.o.....",
    "...#####....",
    ".P.......o..",
    "############",
]

Каждая клетка карты становится спрайтом. Строки считаются сверху, а y в arcade растёт вверх — поэтому номер строки переворачивают:

rows = len(LEVEL)
for row, line in enumerate(LEVEL):
    for col, ch in enumerate(line):
        x = col * SIZE + SIZE // 2
        y = (rows - 1 - row) * SIZE + SIZE // 2
        if ch == "#":
            self.walls.append(arcade.SpriteSolidColor(SIZE, SIZE, center_x=x, center_y=y))
        elif ch == "o":
            self.coins.append(arcade.SpriteCircle(SIZE // 4, arcade.color.GOLD, center_x=x, center_y=y))

Для больших уровней рисуют карты в бесплатном редакторе Tiled и загружают их одной строкой: arcade.load_tilemap("level.tmx") и arcade.Scene.from_tilemap(...). Внутри — та же идея: слои клеток превращаются в списки спрайтов.

Камера

Уровень больше окна, поэтому нужна камера — она показывает часть мира вокруг своего центра.

self.camera = arcade.Camera2D()       # камера мира
self.gui = arcade.Camera2D()          # неподвижная камера для счёта и жизней

def on_update(self, delta_time):
    self.camera.position = (self.player.center_x, self.player.center_y)

def on_draw(self):
    self.clear()
    with self.camera.activate():      # всё внутри рисуется «в мире»
        self.walls.draw()
        self.players.draw()
    with self.gui.activate():         # а это — поверх, на месте экрана
        arcade.draw_text(f"Счёт: {self.score}", 10, 570, arcade.color.WHITE, 16)

Чтобы у краёв уровня не была видна пустота, центр камеры ограничивают: не ближе половины окна к краю.

x = arcade.math.clamp(player.center_x, VIEW_W // 2, LEVEL_W - VIEW_W // 2)

Если уровень меньше окна, ограничение теряет смысл — камеру просто ставят в центр уровня.

Движущиеся платформы

Лифт — стена, у которой есть скорость. Игрок, стоящий на нём, должен ехать вместе с ним. Порядок важен:

  1. проверить, стоит ли игрок на лифте (player.bottom == lift.top и есть перекрытие по горизонтали);
  2. сдвинуть лифт;
  3. если стоял — сдвинуть игрока на столько же.

В PhysicsEnginePlatformer такие платформы уже поддерживаются: у стены задают change_x или change_y и границы boundary_left, boundary_right, boundary_bottom, boundary_top.

Итог

  • карта из строк → спрайты; строку переворачивают: (rows - 1 - row) * size;
  • arcade.Camera2D, camera.position, with camera.activate():; отдельная камера для интерфейса;
  • центр камеры ограничивают clamp с отступом в половину окна;
  • лифт: проверить → сдвинуть лифт → сдвинуть игрока.

Вы прошли все части платформера: рисование, спрайты, монеты и шипы, гравитацию и прыжки, камеру и уровень. Попробуйте добавить в игру из последней задачи шипы, врагов-патрульных и лифт — всё это вы уже чинили.