Godot 4 C# | Система взаимодействий

Доброго времени суток, в данной статье я бы хотел поделиться своей компактной, но достаточной для небольших проектов, реализацией системы взаимодействия между игроком и интерактивными объектами в Godot на основе 3D физики

Взаимодействия

Для выражения взаимодействия мы создадим следующие типы:

  • Класс Interaction, описывающий взаимодействие с объектом
public abstract class Interaction;
  • Класс InteractionResponse, возвращающий результат взаимодействия
public abstract class InteractionResponse;

В моей игре несколько видов:

  1. Включить фонарик(FlashOnInteraction)
  2. Выключить фонарик(FlashOffInteraction)
  3. Использовать(нажатие, UseInteraction)
  4. Использовать(удержание, HoldUseInteraction)
  5. Использовать(перемещение, DragUseInteraction)

Последнии 2 дополняют базовый класс UseInteraction и их главными отличиями являются дополнительные данные: для удержания - время, а для перемещения вектор смещения

Интерфейсы

Я решил использовать дополнительные слои абстракции в виде интерфейсов, но если вам это не будет нужно, то вы можете смело отказаться от них

public interface IInteractor { InteractResponse Interact(Interaction interaction); } public interface IInteractive { InteractResponse OnInteract(Interaction interaction); } public interface IInteractable { IInteractive GetInteractive(); }

Объекты

Первым рассмотрим класс Interactor3D, который используется контроллером персонажа и полностью изолирует реализацию взаимодействия, а наружу доступен только метод Interact(). Его главная задача - выполнять взаимодействие, здесь это реализовано через Raycast и получение Collider объекта

[GlobalClass] public partial class Interactor3D : Node3D, IInteractor { [Export] private RayCast3D _rayCast; public InteractionResponse Interact(Interaction interaction) { if (_rayCast.GetCollider() is IInteractable interactable) { return interactable.GetInteractive().OnInteract(interaction); } return InteractionResponse.None; } }

Затем перейдем в класс Interactive3D, который скрывает всю логику объекта по обработке взаимодействия. Своего рода OnInteract() это аналог метода _Input(), но для взаимодействий. У себя в игре я использую этот класс как промежуточный, он реагирует на обновления и вызывает нужный метод у, так скажем, настоящего объекта, который никак не привязан к системе и не будет затронут при необходимости изменить при каком конкретно типе использования будет выполняться действие

[GlobalClass] public abstract partial class Interactive3D : Node3D, IInteractive { public abstract InteractionResponse OnInteract(Interaction interaction); }

Последний класс - InteractableStaticBody3D, он расширяет функциональность тела, делая его интерактивным. Вы можете сделать аналоги для других тел или области при необходимости. Решение создать промежуточный объект упрощает организацию узлов и позволяет соблюдать принцип единой ответственности

[GlobalClass] public partial class InteractableStaticBody3D : StaticBody3D, IInteractable { [Export] private Interactive3D _node; public IInteractive GetInteractive() => _node; }

Примеры

Таким образом я организовал это в своем проекте

Часть структуры объекта
Часть структуры объекта
Часть структуры игрока
Часть структуры игрока

Вся система заточена на то, чтобы быть модульной и настраиваемой в редакторе, не изменяя код. Здесь используется атрибут GlobalClass, который позволяет редактору видеть наши классы, рекомендую прочитать в документации об этом

Буду рад услышать предложения и критику, это статья эксперементальная, я не уверен, что буду продолжать серию

Спасибо за внимание, друг

4
1