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

от автора

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

Предыдущие статьи:

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

Добавьте в сцену игрока узел 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *