Add door behavior interface and implementations for animation and linear movement

This commit is contained in:
2025-10-30 01:31:20 +01:00
parent 5ae8b6f08c
commit f2ff758dcb
10 changed files with 162 additions and 66 deletions

View File

@@ -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);
}

View File

@@ -0,0 +1 @@
uid://cligtc5v1wl7o

View File

@@ -1,3 +1,4 @@
using CryptonymThunder.Code.Resources;
using GameCore.ECS; using GameCore.ECS;
using GameCore.ECS.Interfaces; using GameCore.ECS.Interfaces;
using GameCore.Interaction; using GameCore.Interaction;
@@ -6,13 +7,11 @@ using Godot;
namespace CryptonymThunder.Code.Presenters; namespace CryptonymThunder.Code.Presenters;
[GlobalClass] [GlobalClass]
public partial class DoorPresenter : AnimatableBody3D, IEntityPresenter, IPresenterComponent public partial class DoorPresenter : CharacterBody3D, IEntityPresenter, IPresenterComponent
{ {
[Export] private AnimationPlayer _animationPlayer; [Export] private DoorBehaviorResource _behavior;
[Export] private string _openAnimationName = "Open";
private DoorComponent _doorComponent; private DoorComponent _doorComponent;
private Animation _openAnimation;
public Entity CoreEntity { get; set; } public Entity CoreEntity { get; set; }
@@ -21,31 +20,21 @@ public partial class DoorPresenter : AnimatableBody3D, IEntityPresenter, IPresen
CoreEntity = coreEntity; CoreEntity = coreEntity;
_doorComponent = world.GetComponent<DoorComponent>(CoreEntity); _doorComponent = world.GetComponent<DoorComponent>(CoreEntity);
if (_animationPlayer == null) if (_behavior == null)
{ {
world.Logger.Error($"DoorPresenter '{Name}' is missing an AnimationPlayer!"); world.Logger.Error($"DoorPresenter '{Name}' is missing a DoorBehaviorResource!");
return; return;
} }
if (!_animationPlayer.HasAnimation(_openAnimationName)) _behavior.Initialize(this, world);
{
world.Logger.Error($"DoorPresenter '{Name}' AnimationPlayer is missing animation: '{_openAnimationName}'");
return;
}
_openAnimation = _animationPlayer.GetAnimation(_openAnimationName);
_animationPlayer.Play(_openAnimationName);
_animationPlayer.Pause();
SyncToPresentation(0f); SyncToPresentation(0f);
} }
public void SyncToPresentation(float delta) public void SyncToPresentation(float delta)
{ {
if (_doorComponent == null || _openAnimation == null) return; if (_doorComponent == null || _behavior == null) return;
_behavior.UpdateProgress(_doorComponent.OpenProgress, delta);
var targetTime = _doorComponent.OpenProgress * _openAnimation.Length;
_animationPlayer.Seek(targetTime, true);
} }
public void SyncToCore(float delta) public void SyncToCore(float delta)

View File

@@ -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<AnimationPlayer>(_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);
}
}

View File

@@ -0,0 +1 @@
uid://b8b28akam7skl

View File

@@ -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)
{
}
}

View File

@@ -0,0 +1 @@
uid://b35aei3jl2524

View File

@@ -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<Node3D>(_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;
}
}
}

View File

@@ -0,0 +1 @@
uid://cratjw4trngpr

View File

@@ -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://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://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://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"] [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"] [sub_resource type="BoxMesh" id="BoxMesh_u2oqr"]
size = Vector3(1, 2, 0.3) 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"] [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) 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"] [sub_resource type="Resource" id="Resource_g2hal"]
script = ExtResource("3_3dbp0") script = ExtResource("3_3dbp0")
metadata/_custom_type_script = "uid://dnyfoaprv6bhw" metadata/_custom_type_script = "uid://dnyfoaprv6bhw"
@@ -60,11 +31,9 @@ InitialState = 0
Requirements = Array[Object]([SubResource("Resource_g2hal")]) Requirements = Array[Object]([SubResource("Resource_g2hal")])
metadata/_custom_type_script = "uid://ymtyxkea76mv" metadata/_custom_type_script = "uid://ymtyxkea76mv"
[node name="GreenDoor" type="AnimatableBody3D" node_paths=PackedStringArray("_animationPlayer")] [node name="GreenDoor" type="CharacterBody3D"]
script = ExtResource("1_0k8gl") script = ExtResource("1_0k8gl")
_animationPlayer = NodePath("AnimationPlayer") _behavior = SubResource("Resource_3dbp0")
_openAnimationName = "open"
metadata/_custom_type_script = "uid://bxqite0b1di2b"
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_u2oqr") mesh = SubResource("BoxMesh_u2oqr")
@@ -73,11 +42,6 @@ surface_material_override/0 = SubResource("StandardMaterial3D_l4yuh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("ConvexPolygonShape3D_l5dry") shape = SubResource("ConvexPolygonShape3D_l5dry")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
&"": SubResource("AnimationLibrary_dgrt5")
}
[node name="SceneEntity" type="Node" parent="." groups=["SceneEntities"]] [node name="SceneEntity" type="Node" parent="." groups=["SceneEntities"]]
script = ExtResource("2_l5dry") script = ExtResource("2_l5dry")
ComponentResources = Array[Resource]([SubResource("Resource_fyehb")]) ComponentResources = Array[Resource]([SubResource("Resource_fyehb")])