Skip to content

Latest commit

 

History

History
177 lines (137 loc) · 10.2 KB

lesson7.md

File metadata and controls

177 lines (137 loc) · 10.2 KB

Урок 7 – Камера и огромный уровень

В этом уроке мы будем рисовать огромный уровень, который вмещается больше чем на один экран. И как следствие, мы будем вешать камеру на персонажа, которая будет следить за ним и выводить текстуры с учетом движения игрока.

Перерисуем уровень level.json:

  "obstacles": [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
    [1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  ],
  "front": [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 6, 6, 2, 7, 2, 0, 8, 0, 0, 0, 0, 0, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 6, 0, 0, 2, 0, 0, 0, 6, 7, 6, 0, 0, 6, 6, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 8, 8, 2, 2, 0, 0, 0, 0],
    [0, 0, 0, 2, 6, 6, 0, 0, 6, 7, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  ],
  "back": [
    [2, 2, 5, 3, 5, 2, 2, 6, 6, 8, 6, 7, 6, 6, 2, 2, 8, 2, 2, 8, 8, 2, 7, 7, 6, 2],
    [8, 6, 6, 4, 6, 6, 8, 8,12,12,12,13,12,12, 6, 2, 6,12,12,12,11,12,12,13, 6, 2],
    [2,12,12,14,13,12, 7,13, 9, 9,10,10, 9, 9,12, 2,11, 9, 9, 9, 9, 9, 9, 9, 6, 2],
    [6, 9, 9, 9,10, 9, 6, 9, 1, 1, 1, 1, 1, 1, 9, 6, 9, 1, 1, 1, 1, 1, 1, 1, 7, 2],
    [2, 1, 1, 1, 1, 1,12, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 6, 2],
    [2, 1, 1, 1, 1, 1,11, 1, 1,14, 1, 1,11, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 7, 2],
    [6, 1, 1, 1, 1, 1, 1, 1, 1,13, 1, 1,12, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2],
    [2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2],
    [2, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 2],
    [2, 2, 6, 7, 8, 2, 2, 2, 1, 1, 1, 1, 1, 1, 7, 3, 6, 1, 1, 1, 1, 1, 1, 1, 2, 2],
    [3, 3, 3, 5, 3, 5, 5, 5, 3, 2, 8, 1, 1, 2, 2, 4, 7, 7, 7, 7, 1, 1, 6, 7, 6, 2],
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 6, 5, 5, 5, 4, 5, 8, 1, 1, 8, 2, 2, 2],
    [2, 2, 2, 2, 2, 2, 2, 7, 7, 8, 2, 1, 1, 6, 2, 2, 2, 3, 2, 8, 1, 1, 7, 2, 2, 2],
    [2, 2, 6, 6, 8, 6, 2, 6,12,12,12, 1, 1,12,11,11,12,13, 2, 6, 1, 1, 6, 2, 2, 2],
    [2, 2, 2,11,11,11, 8, 8, 9, 9, 9, 1, 1, 9, 9, 9, 9, 9, 2, 2, 1, 1, 6, 2, 2, 2],
    [2, 2, 6, 9, 9, 9,12,12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 1, 1, 6, 2, 2, 2],
    [2, 2, 6, 1, 1, 1,12,12, 1, 1, 1, 1, 1, 7, 8, 6, 1, 1,11,12, 1, 1, 7, 2, 2, 2],
    [2, 2, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 2, 2, 1, 1,10,10, 1, 1, 6, 2, 2, 2],
    [2, 2, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 2, 6, 1, 1, 1, 1, 1, 1, 6, 2, 2, 2],
    [2, 2, 7, 1, 1, 1, 6, 7, 1, 1, 1, 1, 1, 6, 2, 6, 7, 2, 2, 2, 7, 2, 6, 2, 2, 2],
    [2, 2, 6, 6, 2, 2, 2, 8, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2]
  ]

И переставим персонажа на новую стартовую позицию:

  "start_position": {
    "x": 100,
    "y": 100
  },

Теперь прикрепим камеру к персонажу.

Создадим файл class_camera.py и пропишем класс:

import pygame


class Camera(object):
    def __init__(self, camera_func, width=0, height=0):
        self.camera_func = camera_func
        self.state = pygame.Rect(0, 0, width, height)

    def size(self, width, height):
        self.state.width = width
        self.state.height = height

    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target, _width, _height):
        self.state = self.camera_func(self.state, target.rect, _width, _height)


def camera_configure(camera, target_rect, win_width, win_height):
    l, t, _, _ = target_rect
    _, _, w, h = camera
    l, t = -l + win_width / 2, -t + win_height / 2

    l = min(0, l)
    l = max(-(camera.width - win_width), l)
    t = max(-(camera.height - win_height), t)
    t = min(0, t)

    return pygame.Rect(l, t, w, h)

Тут объяснять не буду, если есть желание сами поразбирайтесь. Одно скажу - камера берет координаты нашего персонажа, ставит его в центр экрана, а остальную карты смещает относительно координат игрока.

Камеру подключать будем в классе Gui:

Прежде всего импортируем класс

from class_camera import *

Далее в функции __init__ создаем объект камеры:

    def __init__(self):
    ...
        self.camera = Camera(camera_configure)

Теперь в классе Controller нам необходимо настроить камеру под размер уровня. Для этого нам необходимо задать размеры уровня в настройках камеры.

Так как это действие проводится один раз при загрузке уровня, то введем флажок в классе Controller с обозначением начала уровня.

def __init__(self):
    ...
    # Если True - уровень только что загрузился
    self.level_begin = True

Теперь в цикле while метода run зададим настройки камеры для уровня:

    while 1:
        ...
        if self.level_begin:
            # Переключаем флажок
            self.level_begin = False
            # Настраиваем камеру под уровень
            # передаем ширину и высоту уровня в пикселях
            self.gui.camera.size(len(self.level['obstacles'][0]) * self.tile, len(self.level['obstacles']) * self.tile)

        self.player.move(...)

Далее на каждой итерации цикла мы обновляем позицию игрока в камере:

        self.player.move(...)

        self.gui.camera.update(self.player.sprite, self.width * self.tile, self.height * self.tile)

        self.gui.update()

Осталось настроить Gui на правильный вывод уровня на экран:

Нужно заменить вызовы функций self.screen.blit(...) для трех слоев:

Для заднего и переднего фона:

self.screen.blit(tile_sprite.image, self.camera.apply(tile_sprite))

Для вывода игрока:

self.screen.blit(self.controller.player.sprite.image, self.camera.apply(self.controller.player.sprite))

Результат

На этом игру можно отдавать левел-дизайнеру, чтобы он начинал рисовать уровни и пробовать по ним передвигаться.