Создаём игрока

Игрок должен уметь передвигаться и сталкиваться с существующими объектами. Сначала запишем его движения: переходим в player.py и прописываем функцию перемещения. Код в player.py:

import pygame
from settings import *

class Player(pygame.sprite.Sprite):
    def __init__(self, pos, groups):
        super().__init__(groups)
        self.image = pygame.image.load('../graphic/link.png').convert_alpha()
        self.rect = self.image.get_rect(topleft = pos)

        self.direction = pygame.math.Vector2() #обращаемся к направлению через вектор

    def input(self): #варьируем кнопки
        keys = pygame.key.get_pressed()

        if keys[pygame.K_UP]:
            self.direction.y = -1
        elif keys[pygame.K_DOWN]:
            self.direction.y = 1
        else:
            self.direction.y = 0

        if keys[pygame.K_LEFT]:
            self.direction.x = -1
        elif keys[pygame.K_RIGHT]:
            self.direction.x = 1
        else:
            self.direction.x = 0

    def update(self):
        self.input()

Тут с кодом достаточно просто. В функции self.direction = pygame.math.Vector2() мы задаём вектор точке. Точка — это наш герой, а его вектор болтается в диапазоне от -1 до 1. Функция keys = pygame.key.get_pressed() позволяет продолжать нажатие и тогда, герой должен разгоняться. Если нажатия нет — вектор равен 0 и герой тормозит. Это очень условное, но приписывание инерции.

Ещё в файле level.py в функции run я записал обновление всех спрайтов. Это строчка кода вида:

self.visible_sprites.update()

Ещё я переписал в том же файле вызов героя. Мне это нужно для глобализации позиции героя. Из строки

Player((x, y), [self.visible_sprites])

Я сделал

self.player = Player((x, y), [self.visible_sprites])

Продолжим прорисовку движений в файле player.py. Создадим ещё один параметр — скорость. Для этого в глобальные параметры (__init__) добавим строчку self.speed = 5

Также напишем функцию move:

def move(self, speed):
        self.rect.center += self.direction * speed

И в update-функции пропишем движения персонажа: self.move(self.speed).

Результат:

Если приглядеться, то по диагонали Линк бежит чуть быстрее. Дело в том, что он бежит по диагонали со скоростью корень из двух, что примерно равно 1.4. Мы прописали скорость вверх, вниз, влево и вправо равной 1, а вот по диагонали из правил математики, можно понять, что длина гипотенузы равна сумме квадратов длин оснований квадрата под корнем, то есть корень из (1^2 + 1^2). Исправим данный баг нормализацией от PyGame (да-да, как в Unity). Исправленная функция движения:

def move(self, speed):
    if self.direction.magnitude() != 0:
        self.direction = self.direction.normalize()
    self.rect.center += self.direction * speed

Теперь вторая проблема. Линк — танк. Он сбивает всё на своём пути. Нам нужны объекты, с которыми он будет сталкиваться Не забывайте, что все объекты у нас — квадраты тайлами.

Сейчас есть проблема — файл player.py не знает о наличии тайлов, которые отображаются через файл уровня. Поэтому дадим файлу новый аргумент. Добавим его в init и назовём obstacle_sprites. Также не забудьте в файле уровня сослаться на obstacle_sprites в отрисовке точки игрока. Для этого в файле level.py замените строку:

self.player = Player((x, y), [self.visible_sprites])

На строку:

self.player = Player((x, y), [self.visible_sprites], self.obstacle_sprites)

В файле игрока создадим метод столкновений под название collision. Функция:

def collision(self, direction):
    if direction == 'horizontal':
        for sprite in self.obstacle_sprites:
            if sprite.rect.colliderect(self.rect):
                if self.direction.x > 0: #двигаем вправо
                    self.rect.right = sprite.rect.left
                if self.direction.x < 0: #двигаем влево
                    self.rect.left = sprite.rect.right

    if direction == 'vertical':
        for sprite in self.obstacle_sprites:
            if sprite.rect.colliderect(self.rect):
                if self.direction.y > 0: #двигаем вниз
                    self.rect.bottom = sprite.rect.top
                if self.direction.y < 0: #двигаем вверх
                    self.rect.top = sprite.rect.bottom

В ней всё разделено на две координаты. По горизонтальной оси — x, по вертикальной — y. Если столкновение произошло по горизонтали, то делаем смещения (вправо или влево). Тоже самое по вертикали, но там вниз и вверх. Последний шаг — разделить метод в движении на два варианта: вертикальный и горизонтальный. То есть из строки:

self.rect.center += self.direction * speed

Делаем структуру:

self.rect.x += self.direction.x * speed
self.collision('horizontal')
self.rect.y += self.direction.y * speed
self.collision('vertical')

Получаем героя Хайрула без наклонностей в приведение:

Из предыдущей гифки видна следующая задача — создание камеры. Этим и займёмся. Ссылка на этот этап.

Last updated