From f2ff758dcb3d8bd5ac6f1230b0322f7a45f919e4 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 30 Oct 2025 01:31:20 +0100 Subject: [PATCH] Add door behavior interface and implementations for animation and linear movement --- Code/Interfaces/IDoorBehavior.cs | 10 +++ Code/Interfaces/IDoorBehavior.cs.uid | 1 + Code/Presenters/DoorPresenter.cs | 29 +++------ Code/Resources/AnimationPlayerDoorBehavior.cs | 49 ++++++++++++++ .../AnimationPlayerDoorBehavior.cs.uid | 1 + Code/Resources/DoorBehaviorResource.cs | 16 +++++ Code/Resources/DoorBehaviorResource.cs.uid | 1 + Code/Resources/LerpDoorBehavior.cs | 64 +++++++++++++++++++ Code/Resources/LerpDoorBehavior.cs.uid | 1 + Objects/green_door.tscn | 56 +++------------- 10 files changed, 162 insertions(+), 66 deletions(-) create mode 100644 Code/Interfaces/IDoorBehavior.cs create mode 100644 Code/Interfaces/IDoorBehavior.cs.uid create mode 100644 Code/Resources/AnimationPlayerDoorBehavior.cs create mode 100644 Code/Resources/AnimationPlayerDoorBehavior.cs.uid create mode 100644 Code/Resources/DoorBehaviorResource.cs create mode 100644 Code/Resources/DoorBehaviorResource.cs.uid create mode 100644 Code/Resources/LerpDoorBehavior.cs create mode 100644 Code/Resources/LerpDoorBehavior.cs.uid diff --git a/Code/Interfaces/IDoorBehavior.cs b/Code/Interfaces/IDoorBehavior.cs new file mode 100644 index 0000000..9553676 --- /dev/null +++ b/Code/Interfaces/IDoorBehavior.cs @@ -0,0 +1,10 @@ +using GameCore.ECS; +using Godot; + +namespace cryptonymthunder.Code.Interfaces; + +public interface IDoorBehavior +{ + void Initialize(Node3D doorNode, World world); + void UpdateProgress(float progress, float delta); +} \ No newline at end of file diff --git a/Code/Interfaces/IDoorBehavior.cs.uid b/Code/Interfaces/IDoorBehavior.cs.uid new file mode 100644 index 0000000..f007312 --- /dev/null +++ b/Code/Interfaces/IDoorBehavior.cs.uid @@ -0,0 +1 @@ +uid://cligtc5v1wl7o diff --git a/Code/Presenters/DoorPresenter.cs b/Code/Presenters/DoorPresenter.cs index 1208463..e1d3a34 100644 --- a/Code/Presenters/DoorPresenter.cs +++ b/Code/Presenters/DoorPresenter.cs @@ -1,3 +1,4 @@ +using CryptonymThunder.Code.Resources; using GameCore.ECS; using GameCore.ECS.Interfaces; using GameCore.Interaction; @@ -6,13 +7,11 @@ using Godot; namespace CryptonymThunder.Code.Presenters; [GlobalClass] -public partial class DoorPresenter : AnimatableBody3D, IEntityPresenter, IPresenterComponent +public partial class DoorPresenter : CharacterBody3D, IEntityPresenter, IPresenterComponent { - [Export] private AnimationPlayer _animationPlayer; - [Export] private string _openAnimationName = "Open"; + [Export] private DoorBehaviorResource _behavior; private DoorComponent _doorComponent; - private Animation _openAnimation; public Entity CoreEntity { get; set; } @@ -21,31 +20,21 @@ public partial class DoorPresenter : AnimatableBody3D, IEntityPresenter, IPresen CoreEntity = coreEntity; _doorComponent = world.GetComponent(CoreEntity); - if (_animationPlayer == null) + if (_behavior == null) { - world.Logger.Error($"DoorPresenter '{Name}' is missing an AnimationPlayer!"); - return; - } - - if (!_animationPlayer.HasAnimation(_openAnimationName)) - { - world.Logger.Error($"DoorPresenter '{Name}' AnimationPlayer is missing animation: '{_openAnimationName}'"); + world.Logger.Error($"DoorPresenter '{Name}' is missing a DoorBehaviorResource!"); return; } - _openAnimation = _animationPlayer.GetAnimation(_openAnimationName); - _animationPlayer.Play(_openAnimationName); - _animationPlayer.Pause(); + _behavior.Initialize(this, world); + SyncToPresentation(0f); } public void SyncToPresentation(float delta) { - if (_doorComponent == null || _openAnimation == null) return; - - var targetTime = _doorComponent.OpenProgress * _openAnimation.Length; - - _animationPlayer.Seek(targetTime, true); + if (_doorComponent == null || _behavior == null) return; + _behavior.UpdateProgress(_doorComponent.OpenProgress, delta); } public void SyncToCore(float delta) diff --git a/Code/Resources/AnimationPlayerDoorBehavior.cs b/Code/Resources/AnimationPlayerDoorBehavior.cs new file mode 100644 index 0000000..6bf8663 --- /dev/null +++ b/Code/Resources/AnimationPlayerDoorBehavior.cs @@ -0,0 +1,49 @@ +using cryptonymthunder.Code.Interfaces; +using GameCore.ECS; +using Godot; + +namespace CryptonymThunder.Code.Resources; + +[GlobalClass] +public partial class AnimationPlayerDoorBehavior : DoorBehaviorResource, IDoorBehavior +{ + [Export] private NodePath _animationPlayerPath; + [Export] private string _animationName = "Open"; + + private AnimationPlayer _animationPlayer; + private Animation _animation; + + public override void Initialize(Node3D doorNode, World world) + { + if (_animationPlayerPath == null) + { + world.Logger.Error($"[AnimationPlayerDoorBehavior] NodePath is not set for door '{doorNode.Name}'"); + return; + } + + _animationPlayer = doorNode.GetNode(_animationPlayerPath); + if (_animationPlayer == null) + { + world.Logger.Error($"[AnimationPlayerDoorBehavior] Could not find AnimationPlayer at path '{_animationPlayerPath}' on door '{doorNode.Name}'"); + return; + } + + if (!_animationPlayer.HasAnimation(_animationName)) + { + world.Logger.Error($"[AnimationPlayerDoorBehavior] AnimationPlayer on '{doorNode.Name}' is missing animation: '{_animationName}'"); + return; + } + + _animation = _animationPlayer.GetAnimation(_animationName); + _animationPlayer.Play(_animationName); + _animationPlayer.Pause(); + } + + public override void UpdateProgress(float progress, float delta) + { + if (_animationPlayer == null || _animation == null) return; + + var targetTime = progress * _animation.Length; + _animationPlayer.Seek(targetTime, true); + } +} \ No newline at end of file diff --git a/Code/Resources/AnimationPlayerDoorBehavior.cs.uid b/Code/Resources/AnimationPlayerDoorBehavior.cs.uid new file mode 100644 index 0000000..a8ffa5a --- /dev/null +++ b/Code/Resources/AnimationPlayerDoorBehavior.cs.uid @@ -0,0 +1 @@ +uid://b8b28akam7skl diff --git a/Code/Resources/DoorBehaviorResource.cs b/Code/Resources/DoorBehaviorResource.cs new file mode 100644 index 0000000..0a90bab --- /dev/null +++ b/Code/Resources/DoorBehaviorResource.cs @@ -0,0 +1,16 @@ +using cryptonymthunder.Code.Interfaces; +using GameCore.ECS; +using Godot; + +namespace CryptonymThunder.Code.Resources; + +public partial class DoorBehaviorResource : Resource, IDoorBehavior +{ + public virtual void Initialize(Node3D doorNode, World world) + { + } + + public virtual void UpdateProgress(float progress, float delta) + { + } +} \ No newline at end of file diff --git a/Code/Resources/DoorBehaviorResource.cs.uid b/Code/Resources/DoorBehaviorResource.cs.uid new file mode 100644 index 0000000..4df226b --- /dev/null +++ b/Code/Resources/DoorBehaviorResource.cs.uid @@ -0,0 +1 @@ +uid://b35aei3jl2524 diff --git a/Code/Resources/LerpDoorBehavior.cs b/Code/Resources/LerpDoorBehavior.cs new file mode 100644 index 0000000..142bb09 --- /dev/null +++ b/Code/Resources/LerpDoorBehavior.cs @@ -0,0 +1,64 @@ +using cryptonymthunder.Code.Interfaces; +using GameCore.ECS; +using Godot; + +namespace CryptonymThunder.Code.Resources; + +[GlobalClass] +public partial class LerpDoorBehavior : DoorBehaviorResource, IDoorBehavior +{ + [Export] private NodePath _nodeToMovePath; + + [ExportGroup("Position")] + [Export] private Vector3 _closedPosition = Vector3.Zero; + [Export] private Vector3 _openPosition = Vector3.Up * 3f; + + [ExportGroup("Rotation")] + [Export] private Vector3 _closedRotationDegrees = Vector3.Zero; + [Export] private Vector3 _openRotationDegrees = Vector3.Zero; + + private Node3D _nodeToMove; + + public override void Initialize(Node3D doorNode, World world) + { + if (_nodeToMovePath == null) + { + _nodeToMove = doorNode; + } + else + { + _nodeToMove = doorNode.GetNode(_nodeToMovePath); + if (_nodeToMove == null) + { + world.Logger.Error($"[LerpDoorBehavior] Could not find NodeToMove at path '{_nodeToMovePath}' on door '{doorNode.Name}'"); + } + } + } + + public override void UpdateProgress(float progress, float delta) + { + if (_nodeToMove == null) return; + + _nodeToMove.RotationDegrees = _closedRotationDegrees.Lerp(_openRotationDegrees, progress); + + var targetPosition = _closedPosition.Lerp(_openPosition, progress); + + if (_nodeToMove is CharacterBody3D doorBody) + { + if (delta > 0f) + { + var velocity = (targetPosition - doorBody.Position) / delta; + doorBody.Velocity = velocity; + doorBody.MoveAndSlide(); + } + else + { + doorBody.Position = targetPosition; + } + } + else + { + _nodeToMove.Position = targetPosition; + } + } +} \ No newline at end of file diff --git a/Code/Resources/LerpDoorBehavior.cs.uid b/Code/Resources/LerpDoorBehavior.cs.uid new file mode 100644 index 0000000..c6aa10e --- /dev/null +++ b/Code/Resources/LerpDoorBehavior.cs.uid @@ -0,0 +1 @@ +uid://cratjw4trngpr diff --git a/Objects/green_door.tscn b/Objects/green_door.tscn index 40d5c10..9691da2 100644 --- a/Objects/green_door.tscn +++ b/Objects/green_door.tscn @@ -1,10 +1,17 @@ -[gd_scene load_steps=13 format=3 uid="uid://b1d2gc8goj6gx"] +[gd_scene load_steps=12 format=3 uid="uid://b1d2gc8goj6gx"] [ext_resource type="Script" uid="uid://bxqite0b1di2b" path="res://Code/Presenters/DoorPresenter.cs" id="1_0k8gl"] +[ext_resource type="Script" uid="uid://cratjw4trngpr" path="res://Code/Resources/LerpDoorBehavior.cs" id="2_3dbp0"] [ext_resource type="Script" uid="uid://b6x8llipvutqs" path="res://Code/Presenters/SceneEntity.cs" id="2_l5dry"] [ext_resource type="Script" uid="uid://dnyfoaprv6bhw" path="res://Code/Resources/RequiresItemRequirementResource.cs" id="3_3dbp0"] [ext_resource type="Script" uid="uid://ymtyxkea76mv" path="res://Code/Resources/DoorComponentResource.cs" id="4_g4ryb"] +[sub_resource type="Resource" id="Resource_3dbp0"] +script = ExtResource("2_3dbp0") +_nodeToMovePath = NodePath(".") +_openRotationDegrees = Vector3(0, 45, 0) +metadata/_custom_type_script = "uid://cratjw4trngpr" + [sub_resource type="BoxMesh" id="BoxMesh_u2oqr"] size = Vector3(1, 2, 0.3) @@ -14,42 +21,6 @@ albedo_color = Color(0.14891627, 0.744422, 0, 1) [sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_l5dry"] points = PackedVector3Array(-0.5, -1, -0.15, -0.5, -1, 0.15, -0.5, 1, -0.15, 0.5, -1, -0.15, 0.5, -1, 0.15, -0.5, 1, 0.15, 0.5, 1, -0.15, 0.5, 1, 0.15) -[sub_resource type="Animation" id="Animation_rv1pd"] -resource_name = "open" -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:position") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 1), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [Vector3(0, 0, 0), Vector3(0, 3, 0)] -} - -[sub_resource type="Animation" id="Animation_me0pg"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:position") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [Vector3(0, 0, 0)] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_dgrt5"] -_data = { -&"RESET": SubResource("Animation_me0pg"), -&"open": SubResource("Animation_rv1pd") -} - [sub_resource type="Resource" id="Resource_g2hal"] script = ExtResource("3_3dbp0") metadata/_custom_type_script = "uid://dnyfoaprv6bhw" @@ -60,11 +31,9 @@ InitialState = 0 Requirements = Array[Object]([SubResource("Resource_g2hal")]) metadata/_custom_type_script = "uid://ymtyxkea76mv" -[node name="GreenDoor" type="AnimatableBody3D" node_paths=PackedStringArray("_animationPlayer")] +[node name="GreenDoor" type="CharacterBody3D"] script = ExtResource("1_0k8gl") -_animationPlayer = NodePath("AnimationPlayer") -_openAnimationName = "open" -metadata/_custom_type_script = "uid://bxqite0b1di2b" +_behavior = SubResource("Resource_3dbp0") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] mesh = SubResource("BoxMesh_u2oqr") @@ -73,11 +42,6 @@ surface_material_override/0 = SubResource("StandardMaterial3D_l4yuh") [node name="CollisionShape3D" type="CollisionShape3D" parent="."] shape = SubResource("ConvexPolygonShape3D_l5dry") -[node name="AnimationPlayer" type="AnimationPlayer" parent="."] -libraries = { -&"": SubResource("AnimationLibrary_dgrt5") -} - [node name="SceneEntity" type="Node" parent="." groups=["SceneEntities"]] script = ExtResource("2_l5dry") ComponentResources = Array[Resource]([SubResource("Resource_fyehb")])