Предыдущие статьи:
Улучшение системы карабканья из второго выпуска и другое
Добавьте в сцену игрока узел RayCast2D

Вот так выглядит мой персонаж. RayCast добавьте в начало стрелки.
В общем покажу весь код своего персонажа и постараюсь прокомментировать его наиболее понятно.
# В этот раз будет очень много кода, потому что я не представляю себе все эти системы по отдельности. 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()
Заключение
На данный момент это самый актуальный код персонажа, который я только создал. Из-за того, что код полностью готов, мне нечего добавить к нему отдельно. А так как я вынес уроки по ловушкам в отдельный цикл, то мне также нечего сказать про примеры использования системы взаимодействий. Также мне стало интересно, что вы предпочли бы узнать в следующей части и представил ниже опрос составленный из пришедших мне в голову идей для механик. Спасибо за прочтение и до следующих публикаций.
ссылка на оригинал статьи https://habr.com/ru/post/524296/
Добавить комментарий