В этом уроке мы будем рисовать огромный уровень, который вмещается больше чем на один экран. И как следствие, мы будем вешать камеру на персонажа, которая будет следить за ним и выводить текстуры с учетом движения игрока.
Перерисуем уровень 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))
На этом игру можно отдавать левел-дизайнеру, чтобы он начинал рисовать уровни и пробовать по ним передвигаться.