[Разработка игр, Godot] Механики для реализации платформера на Godot engine. 5 часть
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Здравствуйте. В предыдущем опросе читатели выбрали следующие пункты на момент создания данной статьи: система характеристик оружия, система здоровья персонажа. А вот дерево навыков я так и не сообразил как правильно реализовать… Точнее как создать интерфейс, чтобы отображать и управлять деревом.Предыдущие части:
Система здоровьяНу начнём пожалуй с системы здоровья персонажа. Я решил создать отдельный файл для всего необходимого. Он назван «health_system.gd» и лежит в папке скриптов. Важно: это не синглтон, а просто скрипт. Дальше вы поймёте почему.
# Теперь я буду использовать синтаксис javascript. В нём хоть var подсвечивается.
extends Object # Расширяем базовый объект
class_name Health # Подписываем класс как Health и после он доступен из любого места игры как тот-же Node, или Node2D.
signal death # Добавляем сигнал "death", чтобы испускать его, когда персонаж умирает. Этим персонажем может быть не обязательно даже игрок, или вообще что-то живое.
var health: int = 100 setget set_health, get_health # Здоровье и setget для его изменения
var max_health: int = 100 # верхний и
var min_health: int = 0 # нижний предел здоровья
func set_health(new_health: int) -> void: # Функция для того, чтобы ставить new_health здоровья игроку при вызове, что будет не больше или меньше чем можно
# warning-ignore:narrowing_conversion # О том что clamp возвращает целое значение, отрезая дробную часть.
health = clamp(new_health, min_health, max_health) # Ограничиваем здоровье
if health == min_health: # А если здоровья - заданный минимум - мы умираем, меньше не будет.
emit_signal("death") # Испускаем сигнал о том что нечто с этим здоровьем умерло
return
func get_health() -> int:
return health # получаем текущее здоровье. Если возвращать константу - игрок будет бессмертным, но константа должна быть больше максимального получаемого урона в игре, иначе мы всё равно умрём.
func set_min_max_health(new_max: int = 100, new_min: int = 0) -> void: # Задача верхнего и нижнего предела здоровья. Тут поддерживается отрицательный минимум, что позволит создать эффект грани жизни и смерти, что поднимет адреналин игрока, как было в первой части того же Assasin's creed, что улучшало ощущение хорошей игры.
self.max_health = new_max
self.min_health = new_min
return
func damaged(dmg: int = 0) -> void: # Получение урона.
self.health -= dmg # После некоторых разбирательств я выяснил следующее: set_health() вызывается вместо стандартного "=" и подобных, что позволяет не писать логику смерти в получении урона.
return
Чтобы его добавить к персонажу нужно всего-лишь добавить к переменным
# ...
var health: Health = Health.new()
# ...
# И подключаем сигнал смерти в _ready
func _ready():
health.connect("death", self, "dead") #сигнал "смерть" к self.dead(). Смерть я писал в предыдущих выпусках, а если точнее в 3-ей части.
И теперь игрок имеет здоровье. Правда нас всё по-прежнему убивает с 1 удара, потому что я не рассказал об оружии, что будет наносить N урона, а не просто убивать.Система оружияА если точнее базовое оружие, которое можно прикреплять к разным объектам и брать данные от него.Ближе к делу, поэтому начну сразу с кода.
extends Node
class_name Weapon # Подписываем как Weapon, чтобы можно было прикреплять
enum WeaponTypes {MILLITARY, RANGED, BLOCKER} # Ближний бой, дальний и блокировщик
export (NodePath) var weapon_owner_node_path: NodePath # Путь к владельцу
export (int) var damage: int = 10 # урон
export (int) var critical_damage: int = 20 # критический урон
export (float) var critical_damage_chance = 0.2 # шанс критического урона. Бросаем кубик, если число меньше этого - атакуем критическим уроном
export (WeaponTypes) var weapon_type: int = 0 # Тип оружия из перечисления
export (int) var max_damage_distance: int = 0x20 # Дальность атаки чтобы нанести максимальный урон, если больше - урон будет меньше. В разработке. По умолчанию 32 пикселя
var weapon_owner: Node # Заготовка weapon_owner, чтобы в _ready задать из NodePath, что позволит немного оптимизировать множественные вызовы
var use: FuncRef
func _ready() -> void:
weapon_owner = WeaponController.get_node_(weapon_owner_node_path)
match weapon_type: # ставим в use нужный FuncRef, что вызывается с помощью use.call_func() аргументы в скобки.
WeaponTypes.MILLITARY:
use = funcref(self, "attack") # Обычная атака
WeaponTypes.RANGED:
use = funcref(self, "ranged_attack") # Дальняя атака
WeaponTypes.BLOCKER:
use = funcref(self, "block") # Блок атаки врага
func attack(enemy: Entity) -> bool:
print("attack")
enemy.health.damaged(self.damage)
return true # Удачность нанесения урона
func ranged_attack(enemy: Entity) -> bool:
var dmg: float = damage
if weapon_owner.global_position.distance_to(enemy.global_position) > max_damage_distance:
dmg -= damage / 4
enemy.health.damaged(round(dmg))
return true
func block(enemy: Entity) -> bool:
print(randi() > critical_damage_chance) # В разработке. Нужно чтобы гасить урон от атак врагов, но я до конца не придумал как реализовать его.
return true
Добавляем этот узел к объекту, к примеру к пуле:
extends Entity # Тут я не буду рассказывать обо всём, потому что этот урок о том как прикреплять скрипты здоровья и сцены оружия, а не о создании пушек и других ловушек. О пушке расскажу в следующем уроке о механизмах и ловушках.
var direction = Vector2(-1, 0) setget set_direction
const SPEED: int = 40
onready var weapon: Weapon = $Weapon # Задаём оружие
func _ready():
health.connect("death", self, "dead") # health есть уже в Entity, но это единственное что есть в нём.
func _physics_process(delta):
var motion: Vector2 = direction * SPEED * delta
var collision = move_and_collide(motion, false)
if collision != null and collision.collider != null:
var coll = collision.collider
if coll.name == "TileMap":
self.dead()
else:
weapon.use.call_func(coll) # Атакуем объект со здоровьем
self.dead()
print(coll.health.get_health())
func set_direction(vec: Vector2) -> void:
direction = vec
func dead():
self.queue_free() # Метод смерти просто освобождает этот объект через queue_free()
Вот как выглядит сцена пули:
Надеюсь этой картинки хватит чтобы понять как работает сцена Weapon.ЗаключениеНа этот раз на этом всё. Вышла довольно короткая статья, так как в этот раз я не смог подготовить весь запланированный материал, то есть дерево навыков, а то что показал довольно просто реализовать. Удачной разработки и до следующих выпусков.
===========
Источник:
habr.com
===========
Похожие новости:
- [Python, Node.JS, Amazon Web Services, Google Cloud Platform, Видеоконференцсвязь] Психопомощь [online] | Product Weekend
- [C#, Промышленное программирование] Как я при помощи Google сделал OPC2WEB клиент
- [Python, Программирование, ООП, Учебный процесс в IT] Как совместить парадигму Объектно-ориентированного программирования и Python в голове новичка?
- [Анализ и проектирование систем, Проектирование и рефакторинг, Математика, Параллельное программирование, Промышленное программирование] Что сказал-то?
- [Разработка игр, Google App Engine, Разработка под Android, Unity, DevOps] OPPO, Huawei, Xiaomi. Chinese app stores join forces to take on Google
- [Разработка игр, Монетизация игр] Рекламный доход разработчиков мобильных игр ВКонтакте и сервисов VK Mini Apps увеличится более чем в 2 раза
- [Разработка игр, Профессиональная литература] Книга «Игровой движок. Программирование и внутреннее устройство. Третье издание»
- [Ненормальное программирование, Программирование, Rust] Пишем ОС на Rust. Настройка среды. Бинарник для «голого» железа
- [Разработка игр, Локализация продуктов, Монетизация игр, Продвижение игр] Как локализовать игру? Пошаговое руководство (перевод)
- [Алгоритмы] Укрощаем динамику в задаче о палиндромах
Теги для поиска: #_razrabotka_igr (Разработка игр), #_godot, #_gdscript, #_programmirovanie (программирование), #_razrabotka_igr (
Разработка игр
), #_godot
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:32
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Здравствуйте. В предыдущем опросе читатели выбрали следующие пункты на момент создания данной статьи: система характеристик оружия, система здоровья персонажа. А вот дерево навыков я так и не сообразил как правильно реализовать… Точнее как создать интерфейс, чтобы отображать и управлять деревом.Предыдущие части: Система здоровьяНу начнём пожалуй с системы здоровья персонажа. Я решил создать отдельный файл для всего необходимого. Он назван «health_system.gd» и лежит в папке скриптов. Важно: это не синглтон, а просто скрипт. Дальше вы поймёте почему. # Теперь я буду использовать синтаксис javascript. В нём хоть var подсвечивается.
extends Object # Расширяем базовый объект class_name Health # Подписываем класс как Health и после он доступен из любого места игры как тот-же Node, или Node2D. signal death # Добавляем сигнал "death", чтобы испускать его, когда персонаж умирает. Этим персонажем может быть не обязательно даже игрок, или вообще что-то живое. var health: int = 100 setget set_health, get_health # Здоровье и setget для его изменения var max_health: int = 100 # верхний и var min_health: int = 0 # нижний предел здоровья func set_health(new_health: int) -> void: # Функция для того, чтобы ставить new_health здоровья игроку при вызове, что будет не больше или меньше чем можно # warning-ignore:narrowing_conversion # О том что clamp возвращает целое значение, отрезая дробную часть. health = clamp(new_health, min_health, max_health) # Ограничиваем здоровье if health == min_health: # А если здоровья - заданный минимум - мы умираем, меньше не будет. emit_signal("death") # Испускаем сигнал о том что нечто с этим здоровьем умерло return func get_health() -> int: return health # получаем текущее здоровье. Если возвращать константу - игрок будет бессмертным, но константа должна быть больше максимального получаемого урона в игре, иначе мы всё равно умрём. func set_min_max_health(new_max: int = 100, new_min: int = 0) -> void: # Задача верхнего и нижнего предела здоровья. Тут поддерживается отрицательный минимум, что позволит создать эффект грани жизни и смерти, что поднимет адреналин игрока, как было в первой части того же Assasin's creed, что улучшало ощущение хорошей игры. self.max_health = new_max self.min_health = new_min return func damaged(dmg: int = 0) -> void: # Получение урона. self.health -= dmg # После некоторых разбирательств я выяснил следующее: set_health() вызывается вместо стандартного "=" и подобных, что позволяет не писать логику смерти в получении урона. return # ...
var health: Health = Health.new() # ... # И подключаем сигнал смерти в _ready func _ready(): health.connect("death", self, "dead") #сигнал "смерть" к self.dead(). Смерть я писал в предыдущих выпусках, а если точнее в 3-ей части. extends Node
class_name Weapon # Подписываем как Weapon, чтобы можно было прикреплять enum WeaponTypes {MILLITARY, RANGED, BLOCKER} # Ближний бой, дальний и блокировщик export (NodePath) var weapon_owner_node_path: NodePath # Путь к владельцу export (int) var damage: int = 10 # урон export (int) var critical_damage: int = 20 # критический урон export (float) var critical_damage_chance = 0.2 # шанс критического урона. Бросаем кубик, если число меньше этого - атакуем критическим уроном export (WeaponTypes) var weapon_type: int = 0 # Тип оружия из перечисления export (int) var max_damage_distance: int = 0x20 # Дальность атаки чтобы нанести максимальный урон, если больше - урон будет меньше. В разработке. По умолчанию 32 пикселя var weapon_owner: Node # Заготовка weapon_owner, чтобы в _ready задать из NodePath, что позволит немного оптимизировать множественные вызовы var use: FuncRef func _ready() -> void: weapon_owner = WeaponController.get_node_(weapon_owner_node_path) match weapon_type: # ставим в use нужный FuncRef, что вызывается с помощью use.call_func() аргументы в скобки. WeaponTypes.MILLITARY: use = funcref(self, "attack") # Обычная атака WeaponTypes.RANGED: use = funcref(self, "ranged_attack") # Дальняя атака WeaponTypes.BLOCKER: use = funcref(self, "block") # Блок атаки врага func attack(enemy: Entity) -> bool: print("attack") enemy.health.damaged(self.damage) return true # Удачность нанесения урона func ranged_attack(enemy: Entity) -> bool: var dmg: float = damage if weapon_owner.global_position.distance_to(enemy.global_position) > max_damage_distance: dmg -= damage / 4 enemy.health.damaged(round(dmg)) return true func block(enemy: Entity) -> bool: print(randi() > critical_damage_chance) # В разработке. Нужно чтобы гасить урон от атак врагов, но я до конца не придумал как реализовать его. return true extends Entity # Тут я не буду рассказывать обо всём, потому что этот урок о том как прикреплять скрипты здоровья и сцены оружия, а не о создании пушек и других ловушек. О пушке расскажу в следующем уроке о механизмах и ловушках.
var direction = Vector2(-1, 0) setget set_direction const SPEED: int = 40 onready var weapon: Weapon = $Weapon # Задаём оружие func _ready(): health.connect("death", self, "dead") # health есть уже в Entity, но это единственное что есть в нём. func _physics_process(delta): var motion: Vector2 = direction * SPEED * delta var collision = move_and_collide(motion, false) if collision != null and collision.collider != null: var coll = collision.collider if coll.name == "TileMap": self.dead() else: weapon.use.call_func(coll) # Атакуем объект со здоровьем self.dead() print(coll.health.get_health()) func set_direction(vec: Vector2) -> void: direction = vec func dead(): self.queue_free() # Метод смерти просто освобождает этот объект через queue_free() Надеюсь этой картинки хватит чтобы понять как работает сцена Weapon.ЗаключениеНа этот раз на этом всё. Вышла довольно короткая статья, так как в этот раз я не смог подготовить весь запланированный материал, то есть дерево навыков, а то что показал довольно просто реализовать. Удачной разработки и до следующих выпусков. =========== Источник: habr.com =========== Похожие новости:
Разработка игр ), #_godot |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:32
Часовой пояс: UTC + 5