Предисловие.
У нас есть игра с 2-мя уровнями, которая выглядит максимально бледно, что ж, в этом уроке исправим эту проблему, добавив:
- Анимированный спрайт
- Более качественное управление игроком
Подготовка.
Скачайте спрайт персонажа (player.png).
И закиньте его в проект на Godot.
Теперь, уберем мыло, зайдите в настройки проекта.
Выберите Рендериг->Текстуры, там у Default Texture Filter выберите Nearest. Теперь, все ресурсы будут загружаться без размытия.
Включите расширенные настройки, измените разрешение игры на 320×240, и разрешение окна на 1280×720, где:
- разрешение игры — разрешение в котором рендерится игра, и размер области видимости камеры.
- разрешение окна — разрешения окна с игрой, в нем разрешение игры растягивается до разрешения окна.
Подготовка завершена, теперь добавим анимацию игроку.
Настраиваем анимации игроку
В сцене игрока, удалите ноду Sprite2D, и добавьте AnimatedSprite2D.
В инспекторе, создайте SpriteFrames и нажмите на него
Внизу, у вас появится редактор анимаций.
- Список анимаций
- Кнопка добавить новую анимацию в список (1)
- Кнопки проигрывания анимации
- Добавить кадры анимации из спрайтшита (сетки с кадрами)
- Длительность 1-го кадра анимации
- Список кадров выбранной выбранной анимации.
Ок, добавьте анимации:
- idle — бездействие.
- run — бег
- jump — прыжок
Нажатием кнопки документа с плюсом (2).
Далее, нажмите на кнопку в форме сетки (4). Там выберите спрайт player.png скачанный ранее.
Укажите количество кадров:
- по горизонтали — 2
- по вертикали — 3
Выбираете кадр, и нажимаете Добавить кадров: 1
То же самое сделайте для других анимаций, по кадрам показанных на картинках.
Теперь, будем переключать анимации через код игрока.
Улучшаем логику игрока
Откройте код игрока, и замените код на следующее:
extends CharacterBody2D
const SPEED = 200.0
const JUMP_VELOCITY = -200.0
const GRAVITY = 500.0
const ACCELERATION = 900.0
const FRICTION = 1000.0
#положили ноду AnimatedSprite2D в переменную sprite
@onready var sprite = $AnimatedSprite2D
func _physics_process(delta):
if not is_on_floor():
velocity.y += GRAVITY*delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
var direction = 0
if Input.is_action_pressed("move_left"):
direction += 1
if Input.is_action_pressed("move_right"):
direction -= 1
if(direction != 0):
#Если игрок двигается влево или вправо, то применяем ускорение
apply_acceleration(direction, delta)
else:
#Иначе трение, или другими словами, ускорение направленной в 0
apply_friction(direction, delta)
#обновляем анимацию в зависимости от направления.
update_animations(direction)
move_and_slide()
func apply_acceleration(direction, delta):
#Увеличиваем скорость velocity.x с ускорением ACCELERATION до скорости SPEED
velocity.x = move_toward(velocity.x, SPEED*direction, ACCELERATION*delta)
func apply_friction(direction, delta):
#Уменьшаем скорость velocity.x с ускорением FRICTION до скорости 0
velocity.x = move_toward(velocity.x, 0, FRICTION*delta)
func update_animations(direction):
#Если игрок стоит на месте
if(direction == 0):
#Проигрываем анимацию бездействия
sprite.play("idle")
else:
#Если двигается, проигрываем анимацию ходьбы и поворачиваем спрайт в направлении движения
sprite.play("run")
sprite.flip_h = direction > 0
#Если игрок в воздухе, проигрываем анимаю прыжка
if(!is_on_floor()):
sprite.play("jump")
Весь код я кратко объяснил в комментариях, а теперь подробнее.
Мы добавили константы ACCELERATION и FRICTION
const ACCELERATION = 900.0
const FRICTION = 1000.0
которые отвечают, за ускорение и силу трения (ускорение в 0). Они больше чем SPEED, из-за того что они будут потом умножаться на delta.
Далее onready
@onready var sprite = $AnimatedSprite2D
Эта запись аналогична следующей:
var sprite
func _ready():
sprite = $AnimatedSprite2D
Т.е. присваивает значение переменной в методе _ready. Запись с onready оказалась короче и проще, поэтому её и используем.
Дальше идут функции, начнем с update_animations
func update_animations(direction):
#Если игрок стоит на месте
if(direction == 0):
#Проигрываем анимацию бездействия
sprite.play("idle")
else:
#Если двигается, проигрываем анимацию ходьбы и поворачиваем спрайт в направлении движения
sprite.play("run")
sprite.flip_h = direction > 0
#Если игрок в воздухе, проигрываем анимаю прыжка
if(!is_on_floor()):
sprite.play("jump")
- Если игрок стоит на месте, то проигрываем анимацию idle, проигрывание анимации работает с помощью функции play объекта AnimatedSprite.
- Если игрок двигается, то проигрываем анимацию движения run
- В зависимости от направления, отражаем спрайт по горизонтали flip_h
- Если игрок в воздухе, проигрываем анимацию прыжка jump
Далее, в apply_acceleration, которая вызывается если игрок двигается.
func apply_acceleration(direction, delta):
#Увеличиваем скорость velocity.x с ускорением ACCELERATION до скорости SPEED
velocity.x = move_toward(velocity.x, SPEED*direction, ACCELERATION*delta)
С помощью move_toward применяем ускорение к скорости по следующему шаблону
move_toward(начальное_значение, конечное_значение, шаг)
Таким образом, функция apply_acceleration плавно увеличивает скорость игрока от текущей velocity.x до SPEED с ускорением ACCELERATION
Так же работает и apply_friction, который вызывается при бездействии. Только скорость стремится к 0, а ни к SPEED.
Ок, не забудьте подогнать CollisionShape у игрока под размеры спрайта.
Теперь, запустите.
Как видите, управлять игроком стало намного приятней, а еще, у него появилась анимация.
Добавляем Coyote jump.
Бывало у вас бывало так, что вы нажимаете на кнопку прыжка с края платформы а она не срабатывает, и вы падаете в пропасть? Что-ж, в реальности она еще как срабатывает, вы просто в тайминги не попали. Но, в любом случае, это неприятный опыт, который можно пофиксить добавив Coyote jump.
Смысл его в дарении игроку, небольшого времени на прыжок, после того, как он упал с платформы. Если это время будет маленьким, то игрок ничего не заметит, и играть станет намного приятней.
Ок, добавьте ноду Timer к игроку, назовите CoyoteTimer. В инспекторе, установите:
- Wait Time = 0.15s, указывает на сколько засечь таймер
- One Shot = True, определяет должен-ли повторно запускаться таймер, в нашем случае, не должен
Теперь, откройте код игрока. Присвойте там CoyoteTimer через onready.
@onready var coyoteTimer = $CoyoteTimer
Код прыжка замените на следующее:
if Input.is_action_just_pressed("jump") and (is_on_floor() or coyoteTimer.time_left > 0.0):
velocity.y = JUMP_VELOCITY
Если нажата кнопка прыжка и, либо игрок на земле, либо coyoteTimer не закончился, тогда прыгаем.
В конце _physics_process добавьте следующее:
update_animations(direction)
var was_on_floor = is_on_floor() #Получаем значение is_on_floor до применения скорости
move_and_slide() #Обновляем is_on_floor и позицию игрока
if(was_on_floor and !is_on_floor() and velocity.y >= 0): #Если игрок только что оказался в воздухе, и падает вниз, то
coyoteTimer.start() #Запускаем coyoteTimer
Код, объяснил в комментариях.
Готово, теперь запустите.