[Godot, Разработка игр] Механики для реализации платформера на Godot engine. 4 часть
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Здравствуйте снова. В этом выпуске я расскажу о том, как исправил механику карабканья, показанную во втором выпуске, покажу механику взаимодействия, для создания интерактива. Это по-прежнему будет доработка персонажа, так что окружающий мир будет подвергнут минимальным изменениям, но главный герой будет очень сильно улучшен. Правда до дерева навыков ещё далеко, поэтому оставайтесь на связи и я покажу как можно реализовать всё, что придёт нам в голову.Предыдущие статьи:
Улучшение системы карабканья из второго выпуска и другоеВ общем покажу весь код своего персонажа и постараюсь прокомментировать его наиболее понятно.
# В этот раз будет очень много кода, потому что я не представляю себе все эти системы по отдельности.
extends KinematicBody2D
signal timer_ended # Сигнал о отключении таймера в _process
const UP_VECTOR: Vector2 = Vector2(0, -1) # Направление вверх
const GRAVITY: int = 40 # Скорость падения
const MOVE_SPEED: int = 100 # Скорость перемещения
const JUMP_POWER: int = 480 # Сила прыжка
const CLIMB_SPEED: int = 40 # Скорость карабканья
const WALL_JUMP_SPEED: int = 80 # Скорость прыжка от стены
enum States {ON_FLOOR, ON_WALL} # Как я выяснил, этому скрипту нужно только 2 состояния
onready var ray_cast: Object = $RayCast2D # Для реализации взаимодействия с другими объектами. Будет пояснён позже
var velocity: Vector2 = Vector2.ZERO # Ускорение.
var walls: Array = [false, false, false] # Для определения стен. Стена слева, стена сверху, стена справа.
var timer_enabled: bool = false # Отвечает за включение таймера
var climbing: bool = false # Поднимаемся мы по стене, или просто падаем вдоль неё
var is_wall_jump: bool = false # Прыгаем ли мы от стены, или нет
var is_double_jump: bool = true # Двойной ли прыжок
var right_pressed: float = 0 # Трансляция силы нажатия на стрелки влево и вправо, что позволяет подменить значения
var left_pressed: float = 0
var timer: float = 0 # Таймер
var prev_direction: float = 0 # Предыдущее направление. Нужно для того чтобы анимация бездействия воспроизводилась в обоих направлениях
var direction: float = 0 # Текущее направление движения.
var keys: int = 0 # Количество ключей. Нужно для открытия дверей, соответственно
var current_state: int = States.ON_FLOOR # Текущее состояние персонажа
func _ready():
ray_cast.add_exception($WallLeft) # говорит что не нужно обрабатывать лучу ray_cast
ray_cast.add_exception($WallRight)
ray_cast.add_exception(self)
func _process(_delta: float) -> void: # метод _process
if timer > 0 or timer_enabled:
timer -= _delta # Уменьшаем таймер на _delta
if timer <= 0 and timer_enabled:
timer_enabled = false
timer = 0 # Сбрасываем значение и выключаем таймер
emit_signal("timer_ended") # Испускаем сигнал таймера.
if self.direction != 0:
self.ray_cast.cast_to *= -1
self.prev_direction = self.direction # обновляем предыдущее направление если текущее не равно 0
func _physics_process(_delta: float) -> void:
self.control_character()
self.pause_opened() # Вызываем для проверки - открыта ли пауза
if (!self.climbing): # Если не карабкаемся, то проверяем
if (!self.is_wall_jump): # Если прыжок от стены то увеличиваем self.velocity.y на гравитацию
self.velocity.y += GRAVITY
else: # Иначе падаем в 4 раза медленнее
self.velocity.y += float(GRAVITY) / 4
self.velocity = self.move_and_slide(self.velocity, UP_VECTOR) # Обновить self.velocity из текущего состояния
func check_states() -> void:
if self.is_on_floor():
self.current_state = States.ON_FLOOR
is_double_jump = true
elif self.is_on_wall():
self.current_state = States.ON_WALL
is_double_jump = true
elif self.is_on_floor() and self.is_on_wall():
self.current_state = States.ON_WALL
func fall() -> void:
self.velocity.y += GRAVITY
func update_controls(): # Обновляем информации о нажатиях на кнопки "влево" и "вправо"
if !is_wall_jump: # Если не прыгаем от стены сейчас - обновляем
self.left_pressed = Input.get_action_strength("ui_left")
self.right_pressed = Input.get_action_strength("ui_right")
func control_character() -> void: # Об этом я уже рассказывал
check_states() # Проверить состояния
update_controls() # Обновить данные о нажатии на стрелки влево и вправо
self.interact_with() # Взаимодействие с другими объектами+
match current_state:
States.ON_WALL:
self.climb()
self.move()
if !climbing:
self.jump()
self.fall()
self.wall_jump()
# States.IN_AIR: # Данный раздел совсем не нужен
# self.jump()
# self.move()
# self.fall()
States.ON_FLOOR:
self.jump()
self.move()
func climb():
if (walls[0] or walls[2]): # Если стена слева или справа - self.climbing = нажато ли событие "ui_climb". Тут вам самим нужно создать событие
self.climbing = Input.is_action_pressed("ui_climb")
else: # Иначе просто не карабкаемся
self.climbing = false
func climb_up() -> void: # Ползем вверх по стене
self.velocity.y = (CLIMB_SPEED)
func climb_down() -> void: # ползем вниз по стене
self.velocity.y = (-CLIMB_SPEED)
func move() -> void: # Перемещение. Я его доделал, чтобы по левой стене
self.direction = self.right_pressed - self.left_pressed
if (self.climbing and !self.is_wall_jump):
if self.walls[0]: # Если левая стена
if direction > 0: # Если движемся вправо - карабкаемся вверх
climb_up()
elif direction < 0: # Иначе если движемся влево - спускаемся вниз
climb_down()
else: # Иначе никак не двигаемся по вертикали
self.velocity.y = 0
elif self.walls[2]: # Почти то же самое что с движением по левой стене, только направления местами поменял
if direction < 0:
climb_up()
elif direction > 0:
climb_down()
else:
self.velocity.y = 0
# else: # Я думал что это будет нужно, но видимо это осталось лишним
# self.velocity.y = 0
else: # Иначе если не карабкаемся по стене и от неё не прыгаем просто передвигаемся
self.velocity.x = self.direction * float(MOVE_SPEED) * (1 + (float(self.is_wall_jump) / 2))
if !(climbing): # Анимации
if direction == 0:
$AnimatedSprite.flip_h = (-self.prev_direction >= 0)
$AnimatedSprite.play("idle")
else:
$AnimatedSprite.flip_h = direction < 0
$AnimatedSprite.play("run")
return
func jump() -> void: # Совершенно никаких изменений со второго выпуска в прыжке
if Input.is_action_just_pressed("ui_accept"):
if is_on_floor():
self.velocity.y = -JUMP_POWER
if !is_on_floor() and is_double_jump:
is_double_jump = false
self.velocity.y = -JUMP_POWER
func wall_jump() -> void:
if Input.is_action_just_pressed("ui_accept") and Input.is_action_pressed("ui_climb"):
self.is_wall_jump = true
self.velocity.y = -JUMP_POWER
if walls[0]:
self.timer = 0.3
self.timer_enabled = true
self.right_pressed = 1 # Это приравнивание как я понял вынужденная мера из-за слишком простого механизма перемещения
yield(self, "timer_ended") # Подождать сигнал таймера
self.right_pressed = Input.get_action_strength("ui_right")
# Сбросить перемещение влево
elif walls[2]:
self.timer = 0.3
self.timer_enabled = true
self.left_pressed = 1 # Это приравнивание как я понял вынужденная мера из-за слишком простого механизма перемещения
yield(self, "timer_ended")
self.left_pressed = Input.get_action_strength("ui_left")
# Сбросить перемещение вправо
self.is_wall_jump = false # Перестаём прыгать от стены
func interact_with() -> void: # Метод взаимодействия
if Input.is_action_pressed("ui_use"): # Если нужная кнопка нажата
var coll: Object = self.ray_cast.get_collider() # Определяем что столкнулось
if coll: # И если это не null
if coll.has_method("open"): # Проверяем, дверь это или объект взаимодействия
use_key(coll)
elif coll.has_method("interact"):
use_object(coll)
func use_object(collider: Object) -> void: # Используй объект
collider.interact(self) # В дополнительном уроке так активировались порталы
func use_key(collider: Object) -> void: # Метод открывает все двери.
if self.keys > 0: # Если ключи есть
collider.open() # Открой объект
self.keys -= 1 # И убери ключ из инвентаря за ненадобностью
func key_picked_up():
self.keys += 1
func _on_WallRight_body_entered(_body): # Я уже рассказывал об этих определителях стен.
if (_body.name != self.name): # Если с ними что-то столкнулось - они изменят соответствующую
self.walls[2] = true # переменную в массиве walls на true или false.
func _on_WallRight_body_exited(_body): #
self.walls[2] = false #
func _on_WallLeft_body_entered(_body): #
if (_body.name != self.name): #
self.walls[0] = true #
func _on_WallLeft_body_exited(_body): #
self.walls[0] = false #
func dead():
# $Particles2D.emitting = true # Если вы добавили частицы крови - можете убрать комментарий
LevelMgr.goto_scene("res://scenes/dead_screen/dead_screen.tscn") # Переход на экран смерти. Сделать чтобы отлет частиц был виден пока не придумал как
func pause_opened(): # Открывает окно паузы
if Input.is_action_just_pressed("ui_cancel"): # Если соответствующая кнопка нажата
$PositionResetter/WindowDialog.popup_centered()
ЗаключениеНа данный момент это самый актуальный код персонажа, который я только создал. Из-за того, что код полностью готов, мне нечего добавить к нему отдельно. А так как я вынес уроки по ловушкам в отдельный цикл, то мне также нечего сказать про примеры использования системы взаимодействий. Также мне стало интересно, что вы предпочли бы узнать в следующей части и представил ниже опрос составленный из пришедших мне в голову идей для механик. Спасибо за прочтение и до следующих публикаций.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка игр] Сборка ArmorPaint из исходников
- [Карьера в IT-индустрии, Разработка под Android, Учебный процесс в IT] Стоит ли увольнять разраба за большую и дорогую ошибку? Думаю, нет, но менеджмент хотел крови
- [Программирование, Совершенный код, C++, Rust, История IT] Интереснейшее влияние Cyclone (перевод)
- [JavaScript, Программирование, Разработка веб-сайтов] Настройка Webpack 5 с нуля (перевод)
- [Разработка игр, Прототипирование, Дизайн игр, Godot] Демо-версии Невангеров для Unigine и Godot
- [C, C++, Программирование, Функциональное программирование] Сравнение встраиваемых ЯП по размеру в исполняемом файле
- [PostgreSQL, SQL, Алгоритмы, Ненормальное программирование] SQL HowTo: обрабатываем дерево — упорядочиваем иерархию с рекурсией и без
- [Разработка игр, Godot] Механики ловушек и механизмов в Godot Engine
- [Unity, C#, Разработка игр] Как мы переосмыслили работу со сценами в Unity
- [Занимательные задачки, Python, Учебный процесс в IT, Логические игры] DataArt запустил бесплатную платформу Kiddo — онлайн-задачник для школьников, изучающих Питон
Теги для поиска: #_godot, #_razrabotka_igr (Разработка игр), #_gdscript, #_programmirovanie (программирование), #_godot, #_razrabotka_igr (
Разработка игр
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:26
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Здравствуйте снова. В этом выпуске я расскажу о том, как исправил механику карабканья, показанную во втором выпуске, покажу механику взаимодействия, для создания интерактива. Это по-прежнему будет доработка персонажа, так что окружающий мир будет подвергнут минимальным изменениям, но главный герой будет очень сильно улучшен. Правда до дерева навыков ещё далеко, поэтому оставайтесь на связи и я покажу как можно реализовать всё, что придёт нам в голову.Предыдущие статьи: Улучшение системы карабканья из второго выпуска и другоеВ общем покажу весь код своего персонажа и постараюсь прокомментировать его наиболее понятно. # В этот раз будет очень много кода, потому что я не представляю себе все эти системы по отдельности.
extends KinematicBody2D signal timer_ended # Сигнал о отключении таймера в _process const UP_VECTOR: Vector2 = Vector2(0, -1) # Направление вверх const GRAVITY: int = 40 # Скорость падения const MOVE_SPEED: int = 100 # Скорость перемещения const JUMP_POWER: int = 480 # Сила прыжка const CLIMB_SPEED: int = 40 # Скорость карабканья const WALL_JUMP_SPEED: int = 80 # Скорость прыжка от стены enum States {ON_FLOOR, ON_WALL} # Как я выяснил, этому скрипту нужно только 2 состояния onready var ray_cast: Object = $RayCast2D # Для реализации взаимодействия с другими объектами. Будет пояснён позже var velocity: Vector2 = Vector2.ZERO # Ускорение. var walls: Array = [false, false, false] # Для определения стен. Стена слева, стена сверху, стена справа. var timer_enabled: bool = false # Отвечает за включение таймера var climbing: bool = false # Поднимаемся мы по стене, или просто падаем вдоль неё var is_wall_jump: bool = false # Прыгаем ли мы от стены, или нет var is_double_jump: bool = true # Двойной ли прыжок var right_pressed: float = 0 # Трансляция силы нажатия на стрелки влево и вправо, что позволяет подменить значения var left_pressed: float = 0 var timer: float = 0 # Таймер var prev_direction: float = 0 # Предыдущее направление. Нужно для того чтобы анимация бездействия воспроизводилась в обоих направлениях var direction: float = 0 # Текущее направление движения. var keys: int = 0 # Количество ключей. Нужно для открытия дверей, соответственно var current_state: int = States.ON_FLOOR # Текущее состояние персонажа func _ready(): ray_cast.add_exception($WallLeft) # говорит что не нужно обрабатывать лучу ray_cast ray_cast.add_exception($WallRight) ray_cast.add_exception(self) func _process(_delta: float) -> void: # метод _process if timer > 0 or timer_enabled: timer -= _delta # Уменьшаем таймер на _delta if timer <= 0 and timer_enabled: timer_enabled = false timer = 0 # Сбрасываем значение и выключаем таймер emit_signal("timer_ended") # Испускаем сигнал таймера. if self.direction != 0: self.ray_cast.cast_to *= -1 self.prev_direction = self.direction # обновляем предыдущее направление если текущее не равно 0 func _physics_process(_delta: float) -> void: self.control_character() self.pause_opened() # Вызываем для проверки - открыта ли пауза if (!self.climbing): # Если не карабкаемся, то проверяем if (!self.is_wall_jump): # Если прыжок от стены то увеличиваем self.velocity.y на гравитацию self.velocity.y += GRAVITY else: # Иначе падаем в 4 раза медленнее self.velocity.y += float(GRAVITY) / 4 self.velocity = self.move_and_slide(self.velocity, UP_VECTOR) # Обновить self.velocity из текущего состояния func check_states() -> void: if self.is_on_floor(): self.current_state = States.ON_FLOOR is_double_jump = true elif self.is_on_wall(): self.current_state = States.ON_WALL is_double_jump = true elif self.is_on_floor() and self.is_on_wall(): self.current_state = States.ON_WALL func fall() -> void: self.velocity.y += GRAVITY func update_controls(): # Обновляем информации о нажатиях на кнопки "влево" и "вправо" if !is_wall_jump: # Если не прыгаем от стены сейчас - обновляем self.left_pressed = Input.get_action_strength("ui_left") self.right_pressed = Input.get_action_strength("ui_right") func control_character() -> void: # Об этом я уже рассказывал check_states() # Проверить состояния update_controls() # Обновить данные о нажатии на стрелки влево и вправо self.interact_with() # Взаимодействие с другими объектами+ match current_state: States.ON_WALL: self.climb() self.move() if !climbing: self.jump() self.fall() self.wall_jump() # States.IN_AIR: # Данный раздел совсем не нужен # self.jump() # self.move() # self.fall() States.ON_FLOOR: self.jump() self.move() func climb(): if (walls[0] or walls[2]): # Если стена слева или справа - self.climbing = нажато ли событие "ui_climb". Тут вам самим нужно создать событие self.climbing = Input.is_action_pressed("ui_climb") else: # Иначе просто не карабкаемся self.climbing = false func climb_up() -> void: # Ползем вверх по стене self.velocity.y = (CLIMB_SPEED) func climb_down() -> void: # ползем вниз по стене self.velocity.y = (-CLIMB_SPEED) func move() -> void: # Перемещение. Я его доделал, чтобы по левой стене self.direction = self.right_pressed - self.left_pressed if (self.climbing and !self.is_wall_jump): if self.walls[0]: # Если левая стена if direction > 0: # Если движемся вправо - карабкаемся вверх climb_up() elif direction < 0: # Иначе если движемся влево - спускаемся вниз climb_down() else: # Иначе никак не двигаемся по вертикали self.velocity.y = 0 elif self.walls[2]: # Почти то же самое что с движением по левой стене, только направления местами поменял if direction < 0: climb_up() elif direction > 0: climb_down() else: self.velocity.y = 0 # else: # Я думал что это будет нужно, но видимо это осталось лишним # self.velocity.y = 0 else: # Иначе если не карабкаемся по стене и от неё не прыгаем просто передвигаемся self.velocity.x = self.direction * float(MOVE_SPEED) * (1 + (float(self.is_wall_jump) / 2)) if !(climbing): # Анимации if direction == 0: $AnimatedSprite.flip_h = (-self.prev_direction >= 0) $AnimatedSprite.play("idle") else: $AnimatedSprite.flip_h = direction < 0 $AnimatedSprite.play("run") return func jump() -> void: # Совершенно никаких изменений со второго выпуска в прыжке if Input.is_action_just_pressed("ui_accept"): if is_on_floor(): self.velocity.y = -JUMP_POWER if !is_on_floor() and is_double_jump: is_double_jump = false self.velocity.y = -JUMP_POWER func wall_jump() -> void: if Input.is_action_just_pressed("ui_accept") and Input.is_action_pressed("ui_climb"): self.is_wall_jump = true self.velocity.y = -JUMP_POWER if walls[0]: self.timer = 0.3 self.timer_enabled = true self.right_pressed = 1 # Это приравнивание как я понял вынужденная мера из-за слишком простого механизма перемещения yield(self, "timer_ended") # Подождать сигнал таймера self.right_pressed = Input.get_action_strength("ui_right") # Сбросить перемещение влево elif walls[2]: self.timer = 0.3 self.timer_enabled = true self.left_pressed = 1 # Это приравнивание как я понял вынужденная мера из-за слишком простого механизма перемещения yield(self, "timer_ended") self.left_pressed = Input.get_action_strength("ui_left") # Сбросить перемещение вправо self.is_wall_jump = false # Перестаём прыгать от стены func interact_with() -> void: # Метод взаимодействия if Input.is_action_pressed("ui_use"): # Если нужная кнопка нажата var coll: Object = self.ray_cast.get_collider() # Определяем что столкнулось if coll: # И если это не null if coll.has_method("open"): # Проверяем, дверь это или объект взаимодействия use_key(coll) elif coll.has_method("interact"): use_object(coll) func use_object(collider: Object) -> void: # Используй объект collider.interact(self) # В дополнительном уроке так активировались порталы func use_key(collider: Object) -> void: # Метод открывает все двери. if self.keys > 0: # Если ключи есть collider.open() # Открой объект self.keys -= 1 # И убери ключ из инвентаря за ненадобностью func key_picked_up(): self.keys += 1 func _on_WallRight_body_entered(_body): # Я уже рассказывал об этих определителях стен. if (_body.name != self.name): # Если с ними что-то столкнулось - они изменят соответствующую self.walls[2] = true # переменную в массиве walls на true или false. func _on_WallRight_body_exited(_body): # self.walls[2] = false # func _on_WallLeft_body_entered(_body): # if (_body.name != self.name): # self.walls[0] = true # func _on_WallLeft_body_exited(_body): # self.walls[0] = false # func dead(): # $Particles2D.emitting = true # Если вы добавили частицы крови - можете убрать комментарий LevelMgr.goto_scene("res://scenes/dead_screen/dead_screen.tscn") # Переход на экран смерти. Сделать чтобы отлет частиц был виден пока не придумал как func pause_opened(): # Открывает окно паузы if Input.is_action_just_pressed("ui_cancel"): # Если соответствующая кнопка нажата $PositionResetter/WindowDialog.popup_centered() =========== Источник: habr.com =========== Похожие новости:
Разработка игр ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:26
Часовой пояс: UTC + 5