Русский
Русский
English
Статистика
Реклама

Механики для реализации платформера на Godot engine. 2 часть

Здравствуйте, это продолжение предыдущей статьи о создании игрового персонажа в GodotEngine. Я наконец понял, как реализовать некоторые механики, такие как второй прыжок в воздухе, карабканье по, и прыжок от стены. Первая часть была более простой по насыщенности, так как с чего-то же нужно было начинать, чтобы потом доработать или переделать.

Для начала я решил собрать весь предыдущий код, чтобы те, кто использовали информацию из предыдущей статьи поняли, как я представлял себе программу полностью
extends KinematicBody2D# Константыconst GRAVITY: int = 40const MOVE_SPEED: int = 120 # Скорость перемещения персонажа в пикселяхconst JUMP_POWER: int = 80 # Скорость прыжка# Переменныеvar velocity: Vector2 = Vector2.ZEROfunc _physics_process(_delta: float) -> void:# Ниже вставлять вызовы функций перемещенияmove_character() # Перемещение персонажаjump()# Ниже можно ничего не трогатьself.velocity.y += GRAVITYself.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))func move_character() -> void:var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left") self.velocity.x = direction * MOVE_SPEEDfunc jump() -> void:if self.is_on_floor():if Input.is_action_pressed("ui_accept"): # Я вспомнил про событие ui_accept# Оно вмещает в себя нажатие прыжкаself.velocity.y -= JUMP_POWER

Надеюсь тем, кто читал предыдущую статью стало примерно понятно, как всё работает. Теперь вернёмся к разработке.

Машина состояний


Машина состояний(в моём понимании) часть программы, что определяет состояние чего либо: в воздухе, на полу, на потолке, или на стене, а также определяет что должно происходить с персонажем в том или ином месте. В GodotEngine есть такая вещь как enum, что создаёт перечисление, где каждый элемент является, заданной в коде, константой. Думаю лучше покажу это на примере:
enum States { # Создаётся перечисление States, к константам которого можно обращаться через States.IN_AIR, States.ON_FLOOR...IN_AIR, # В воздухеON_FLOOR, # На полу ON_WALL # На стене}

Данный код можно смело положить в самое начало скрипта игрового персонажа и держать в голове, что он существует. Следом инициализируем переменную в нужном месте var current_state: int = States.IN_AIR, которая равна нулю, если использовать print. Далее нужно как-то определять что игрок в текущем состоянии будет делать. Думаю многим опытным разработчикам пришедшим из C++ знакома конструкция switch () {case:}. В GDScript есть похожая адаптированная конструкция, хотя и switch также есть в планах у разработчиков. Конструкция называется match. Думаю будет правильнее показать данную конструкцию в деле, так как рассказывать будет сложнее, чем показывать:
func _physics_process(_delta: float) -> void:# Ниже функции перемещенияmatch (self.current_state):States.IN_AIR:# Вызов методов что доступны в воздухе.self.move_character()States.ON_FLOOR:# Вызов методов, что доступны на земле.self.move_character()self.jump()States.ON_WALL:# вызов методов, что доступны, если мы упремся лицом в стену. Пока кроме перемещения ничего нет.self.move_character()# Ниже будет остальной код

Но мы до сих пор не меняем состояния. Нужно создать отдельную функцию, которую будем вызывать перед match-ем, чтобы изменять переменную current_state которую стоит добавить в код к остальным переменным. А функцию назовём update_state().
func update_state() -> void:# Тут всё зависит от запланированных разработчиком возможностей персонажа.if self.is_on_floor():self.current_state = self.States.ON_FLOORelif self.is_on_wall() and !self.is_on_floor():# Когда персонаж только на стене.self.current_state = self.States.ON_WALLelif self.is_on_wall() and self.is_on_floor():# Ситуация угла. Будем в данном случае на стене.self.current_state = self.States.ON_WALLelse: # Во всех других случаях будем в воздухеself.current_state = self.states.IN_AIR

Теперь, когда машина состояний готова, мы можем добавлять уйму функций. В том числе и добавить анимации к персонажу Даже не так Мы можем добавить тонну анимаций персонажу. Система стала модульной. Но на этом мы не закончили с кодом. Я сказал в начале, что покажу, как делать дополнительный прыжок в воздухе, карабканье по, и прыжок от стены. Начнём по порядку.

Дополнительный прыжок в воздухе


Во-первых, добавьте вызов прыжка в состоянии States.IN_AIR в наш match, который мы чуток доработаем.
Вот код нашего прыжка, который я исправил:
func jump() -> void:# Старую проверку в мусор. Мы сделаем её позже.if Input.is_action_pressed("ui_accept"): # Назначаем в настройках событиеif self.current_state == self.States.ON_FLOOR:# Как раньше, но добавляем проверку через текущее_состояниеself.velocity.y -= JUMP_POWERelif (self.current_state == self.States.IN_AIR or self.current_state == self.States.ON_WALL)and self.second_jump == true:# Тут проверяем на другие состояния и можем ли мы вообще прыгнуть второй разself.velocity.y = -JUMP_POWER# Сбрасываем накопленное ускорение падения и совершаем прыжокself.second_jump = false# Не забудьте добавить var second_jump: bool = true в самый верх. и в update_state()# Добавьте после if self.is_on_floor(): self.second_jump = true # чтобы сбрасывать состояние прыжка после приземления на пол.

В комментариях к коду в принципе сказано, как я переделал программу, надеюсь вы понимаете мои слова там. Но фактически этих исправлений хватит чтобы изменить механику прыжка и улучшить до двойного. На то чтобы изобрести следующие методы мне потребовалась пара месяцев. Я их написал фактически позавчера, 1 октября 2020 года.

Карабканье по стенам


К нашему сожалению, Нормаль стены GodotEngine не позволяет узнать, из чего следует, что нам придётся создать небольшой костыль. Для начала я сделаю сноску имеющихся на данный момент переменных, чтобы можно было проще сказать что изменилось.
extends KinematicBody2D# Сигналыsignal timer_ended # нужно чтобы заставить работать yield в wall_jump, что основан на обмане управления.# Константыconst GRAVITY: int = 40const MOVE_SPEED: int = 120 # Скорость перемещения персонажа в пикселяхconst JUMP_POWER: int = 80 # Скорость прыжкаconst WALL_JUMP_POWER: int = 60 # Сила прыжка от стены. Нужно для соответственной функцииconst CLIMB_SPEED: int = 30 # Скорость вскарабкивания# Переменныеvar velocity: Vector2 = Vector2.ZEROvar second_jump: bool = truevar climbing: bool = false # Нужно чтобы определять, карабкается ли игрок по стене, или нет.var timer_working: bool = falsevar is_wall_jump: bool = false # Нужно, чтобы определить, а от стены ли мы прыгаемvar left_pressed: bool = false # Для искусственного зажатия кнопки влевоvar right_pressed: bool = false # Для искусственного зажатия кнопки вправоvar current_state: int = States.IN_AIRvar timer: float = 0 # счётчик таймера, что будет встроен в _process(delta: float)var walls = [false, false, false] # определения стен и потолка. Нулевой и второй - стены. Первый - потолок.# Пока нужны только нулевой и второй# Перечисленияenum States {IN_AIR, # В воздухеON_FLOOR, # На полу ON_WALL # На стене}# И я сделаю чуть больше чем сказал, добавив метод _process() с самодельным таймеромfunc _process(delta: float):if timer_working:timer -= deltaif timer <= 0:emit_signal("timer_ended")timer = 0

Теперь нужно определять по какой стене игрок карабкается.
Вот дерево сцены, что вам стоит подготовить для реализации определителя стороны стены
image
Разместите 2 Area2D по бокам персонажа и CollisionShape2D обоих не должны пересекаться с персонажем. Подпишите соответственно объекты WallLeft/WallRight и присоедините сигналы _on_body_endered и _on_body_exited к единственному скрипту персонажа. Вот код который нужен чтобы определять стены(Добавить в самый конец скрипта):
# Надеюсь тут всё интуитивно понятно# Если нет, то комментарии вам в помощьfunc _on_WallRight_body_entered(_body):if (_body.name != self.name):self.walls[0] = true # Если засечённый объект не мы, объект слева - стенаfunc _on_WallRight_body_exited(_body):self.walls[0] = false # Когда тело вышло из коллизии другого объекта - стены слева нетfunc _on_WallLeft_body_entered(_body):if (_body.name != self.name):self.walls[2] = true # Если засечённый объект не мы, объект справа - стенаfunc _on_WallLeft_body_exited(_body):self.walls[2] = false # Когда тело вышло из коллизии другого объекта - стены справа нет

Приступим к методу карабканья. В коде всё будет сказано за меня
func climbing() -> void:if (self.walls[0] or self.walls[2]): # Если стена слева или стена справа есть# Создайте новый action в настройках и назовите ui_climb. Об этом я уже говорил в первой части.self.climbing = Input.is_action_pressed("ui_climb")else:self.climbing = false

И нужно переписать управление move_character() для того, чтобы можно было не просто держаться, а карабкаться вверх вниз, благо у нас есть direction
func move_character() -> void:var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left") if !self.climbing:self.velocity.x = direction * MOVE_SPEEDelse:self.velocity.y = direction * CLIMB_SPEED

И исправляем наш _physics_process()
func _physics_process(_delta: float) -> void:# Ниже функции перемещенияmatch (self.current_state):States.IN_AIR:self.move_character()States.ON_FLOOR:self.move_character()self.jump()States.ON_WALL:self.move_character()# Ниже можно ничего не трогатьif !self.climbing:self.velocity.y += GRAVITYself.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))

Теперь персонаж должен уметь карабкаться по стенам.

Прыжок от стены


Теперь реализуем прыжок от стены.
func wall_jump() -> void:if Input.is_action_just_pressed("ui_accept") and Input.is_action_pressed("ui_climb"): # Если нажата 1 раз кнопка прыжка и зажата кнопка карабканьяself.is_wall_jump = true # Мы прыгаем от стены = даself.velocity.y = -JUMP_POWER # Изменяем ускорение до -JUMP_POWERif walls[0]: # Если стена слеваself.timer = 0.5 # Установить self.timer на 0.5 секундыself.timer_enabled = true # Включаем таймерself.left_pressed = true # ставим переменную left_pressed на даyield(self, "timer_ended") # Дожидаемся срабатывания сигнала timer_endedself.left_pressed = false # отпускаем left_pressedif walls[2]: # Если стена справаself.timer = 0.5 # Установить self.timer на 0.5 секундыself.timer_enabled = true # Включаем таймерself.right_pressed = true # ставим переменную right_pressed на даyield(self, "timer_ended") # Дожидаемся срабатывания сигнала timer_endedself.right_pressed = false # отпускаем right_pressedself.is_wall_jump = false # Прыгнули. Больше не на стене

Добавляем вызов этого метода в наш match -> States.ON_WALL и мы присоединили наш метод к остальной части _physics_process().

Заключение


В данной статье я показал реализацию относительно сложных механик(для начинающих) в GodotEngine. Но это ещё не последняя часть серии статей, поэтому попрошу тех, кто знает как реализовать мною показанные методы в этой статье лучше, писать о них в комментарии. Я, да и многие читающие, будем благодарны за качественные и быстрые решения.
Источник: habr.com
К списку статей
Опубликовано: 03.10.2020 20:19:18
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка игр

Godot

Чисто программирование

Gdscript

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru