From 2e7f30e2145b15d0a90c89c647aca4f4bb6ff35f Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Mon, 26 Jun 2023 22:34:48 +0200 Subject: [PATCH] Improved interactions. - Interactions can now be stopped by both participating Entities. - Player animation now controlled by AnimationTree. --- assets/Characters/Pirate.tscn | 44 +++++++++++++++++++++++++++++- components/InteractionComponent.cs | 15 ++++++++++ entities/Chest.cs | 13 +++++++++ entities/Chest.tscn | 7 +++-- entities/IInteractionInterface.cs | 8 ++++++ entities/Player.cs | 33 ++++++++++++++++++---- entities/Tree.cs | 14 ++++++++-- entities/Tree.tscn | 2 +- systems/InteractionSystem.cs | 39 ++++++++++++++++---------- 9 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 components/InteractionComponent.cs diff --git a/assets/Characters/Pirate.tscn b/assets/Characters/Pirate.tscn index c32e421..6d3a512 100644 --- a/assets/Characters/Pirate.tscn +++ b/assets/Characters/Pirate.tscn @@ -1,11 +1,47 @@ -[gd_scene load_steps=3 format=2] +[gd_scene load_steps=12 format=2] [ext_resource path="res://assets/Characters/Pirate.glb" type="PackedScene" id=1] [ext_resource path="res://assets/Objects/toolAxe.tscn" type="PackedScene" id=2] +[sub_resource type="AnimationNodeAnimation" id=1] +animation = "Hit-loop" + +[sub_resource type="AnimationNodeAnimation" id=2] +animation = "Idle-loop" + +[sub_resource type="AnimationNodeAnimation" id=3] +animation = "Interaction-loop" + +[sub_resource type="AnimationNodeStateMachineTransition" id=4] + +[sub_resource type="AnimationNodeStateMachineTransition" id=5] +switch_mode = 2 +xfade_time = 0.2 + +[sub_resource type="AnimationNodeStateMachineTransition" id=6] + +[sub_resource type="AnimationNodeStateMachineTransition" id=7] +switch_mode = 2 +xfade_time = 0.2 + +[sub_resource type="AnimationNodeStateMachine" id=8] +states/Hit/node = SubResource( 1 ) +states/Hit/position = Vector2( 415, 45 ) +states/Idle/node = SubResource( 2 ) +states/Idle/position = Vector2( 149, 39 ) +states/Interaction/node = SubResource( 3 ) +states/Interaction/position = Vector2( 176, 157 ) +transitions = [ "Idle", "Hit", SubResource( 4 ), "Hit", "Idle", SubResource( 5 ), "Idle", "Interaction", SubResource( 6 ), "Interaction", "Idle", SubResource( 7 ) ] +start_node = "Idle" + +[sub_resource type="AnimationNodeStateMachinePlayback" id=9] + [node name="Pirate" instance=ExtResource( 1 )] transform = Transform( 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0 ) +[node name="Skeleton" parent="Armature" index="0"] +bones/4/bound_children = [ NodePath("ToolAttachement") ] + [node name="ToolAttachement" type="BoneAttachment" parent="Armature/Skeleton" index="5"] transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 ) visible = false @@ -15,3 +51,9 @@ bone_name = "HandTip.R" transform = Transform( -1.12419e-14, 8.74228e-08, -2, 2, -8.74228e-08, -1.39841e-13, -8.74228e-08, -2, -8.74228e-08, 2.38419e-07, -0.151768, 0.615043 ) [node name="toolAxe" parent="Armature/Skeleton/ToolAttachement/Tool" index="0" instance=ExtResource( 2 )] + +[node name="AnimationTree" type="AnimationTree" parent="." index="2"] +tree_root = SubResource( 8 ) +anim_player = NodePath("../AnimationPlayer") +active = true +parameters/playback = SubResource( 9 ) diff --git a/components/InteractionComponent.cs b/components/InteractionComponent.cs new file mode 100644 index 0000000..d4462d4 --- /dev/null +++ b/components/InteractionComponent.cs @@ -0,0 +1,15 @@ +using Godot; + +namespace GodotComponentTest.components; + +public class InteractionComponent: Component +{ + [Signal] + delegate void InteractionStart(Entity owningEntity, Entity targetEntity); + + [Signal] + delegate void InteractionEnd(Entity owningEntity, Entity targetEntity); + + public Entity OwningEntity; + public Entity TargetEntity; +} \ No newline at end of file diff --git a/entities/Chest.cs b/entities/Chest.cs index 66baade..fcbf940 100644 --- a/entities/Chest.cs +++ b/entities/Chest.cs @@ -1,6 +1,7 @@ using Godot; using System; using System.Linq; +using GodotComponentTest.components; using GodotComponentTest.entities; public class Chest : Entity, IInteractionInterface @@ -20,6 +21,13 @@ public class Chest : Entity, IInteractionInterface private MeshInstance _mesh; private AnimationPlayer _animationPlayer; + private InteractionComponent _interactionComponent; + public InteractionComponent InteractionComponent + { + get => _interactionComponent; + set => _interactionComponent = value; + } + [Signal] delegate void EntityClicked(Entity entity); @@ -105,5 +113,10 @@ public class Chest : Entity, IInteractionInterface ); GetParent().AddChild(bar); } + + if (_interactionComponent != null) + { + _interactionComponent.EmitSignal("InteractionEnd"); + } } } \ No newline at end of file diff --git a/entities/Chest.tscn b/entities/Chest.tscn index 8442dc3..45f0254 100644 --- a/entities/Chest.tscn +++ b/entities/Chest.tscn @@ -65,11 +65,14 @@ tracks/2/loop_wrap = true tracks/2/imported = false tracks/2/enabled = true tracks/2/keys = { -"times": PoolRealArray( 0.3 ), -"transitions": PoolRealArray( 1 ), +"times": PoolRealArray( 0.3, 1 ), +"transitions": PoolRealArray( 1, 1 ), "values": [ { "args": [ ], "method": "OnChestOpened" +}, { +"args": [ ], +"method": "OnChestOpened" } ] } diff --git a/entities/IInteractionInterface.cs b/entities/IInteractionInterface.cs index da035b0..cc5ee91 100644 --- a/entities/IInteractionInterface.cs +++ b/entities/IInteractionInterface.cs @@ -1,7 +1,15 @@ +using GodotComponentTest.components; + namespace GodotComponentTest.entities; public interface IInteractionInterface { void OnInteractionStart(); void OnInteractionEnd(); + + InteractionComponent InteractionComponent + { + get; + set; + } } \ No newline at end of file diff --git a/entities/Player.cs b/entities/Player.cs index 03ef6f9..c3c2219 100644 --- a/entities/Player.cs +++ b/entities/Player.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using GodotComponentTest.components; using GodotComponentTest.entities; public class Player : Entity, IInteractionInterface @@ -30,6 +31,13 @@ public class Player : Entity, IInteractionInterface private List _attractedItemList = new List(); private BoneAttachment _toolAttachement; private AnimationPlayer _playerAnimationPlayer; + private AnimationTree _animationTree; + private InteractionComponent _interactionComponent; + public InteractionComponent InteractionComponent + { + get => _interactionComponent; + set => _interactionComponent = value; + } // Called when the node enters the scene tree for the first time. public override void _Ready() @@ -61,8 +69,12 @@ public class Player : Entity, IInteractionInterface _itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered)); } - _playerAnimationPlayer = (AnimationPlayer)GetNode("Geometry/AnimationPlayer"); + _playerAnimationPlayer = GetNode("Geometry/AnimationPlayer"); Debug.Assert(_playerAnimationPlayer != null); + _animationTree = GetNode("Geometry/AnimationTree"); + Debug.Assert(_animationTree != null); + AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); + stateMachine.Start("Idle"); _toolAttachement = (BoneAttachment)FindNode("ToolAttachement"); if (_toolAttachement == null) @@ -168,14 +180,25 @@ public class Player : Entity, IInteractionInterface public void OnInteractionStart() { - GD.Print("Player Starting Interaction"); - _playerAnimationPlayer.CurrentAnimation = "Hit-loop"; - _playerAnimationPlayer.Play(); + AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); + Debug.Assert(stateMachine != null); + + if (_interactionComponent.TargetEntity is Chest) + { + GD.Print("Player Opening Box"); + stateMachine.Travel("Interaction"); + } else if (_interactionComponent.TargetEntity is Tree) + { + GD.Print("Player Chopping Tree"); + stateMachine.Travel("Hit"); + } } public void OnInteractionEnd() { GD.Print("Player Stopping Interaction"); - _playerAnimationPlayer.CurrentAnimation = "Idle-loop"; + AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); + Debug.Assert(stateMachine != null); + stateMachine.Travel("Idle"); } } \ No newline at end of file diff --git a/entities/Tree.cs b/entities/Tree.cs index d03fec9..98767ed 100644 --- a/entities/Tree.cs +++ b/entities/Tree.cs @@ -1,17 +1,25 @@ using Godot; using System; using System.Diagnostics; +using GodotComponentTest.components; using GodotComponentTest.entities; -public class Tree : StaticBody, IInteractionInterface +public class Tree : Entity, IInteractionInterface { [Export] public float ChopDuration = 2; - public bool IsMouseOver = false; + public bool IsMouseOver; private MeshInstance _geometry; private AnimationPlayer _animationPlayer; private float _health = 100; - private bool _isBeingChopped = false; + private bool _isBeingChopped; + + private InteractionComponent _interactionComponent; + public InteractionComponent InteractionComponent + { + get => _interactionComponent; + set => _interactionComponent = value; + } [Signal] delegate void EntityClicked(Entity entity); diff --git a/entities/Tree.tscn b/entities/Tree.tscn index efa0d2a..ac5cb92 100644 --- a/entities/Tree.tscn +++ b/entities/Tree.tscn @@ -50,7 +50,7 @@ tracks/0/imported = false tracks/0/enabled = true tracks/0/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1.5, 1, 1.5, 0.5, 1, 0, 0, 0, 0, 0, 0, 1, 1.5, 0.6, 1.5, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1.5, 1, 1.5 ) -[node name="Tree" type="StaticBody"] +[node name="Tree" type="KinematicBody"] script = ExtResource( 3 ) [node name="Geometry" type="MeshInstance" parent="."] diff --git a/systems/InteractionSystem.cs b/systems/InteractionSystem.cs index be3d3b6..fa7aecd 100644 --- a/systems/InteractionSystem.cs +++ b/systems/InteractionSystem.cs @@ -2,6 +2,7 @@ using Godot; using System; using System.Collections.Generic; using Godot.Collections; +using GodotComponentTest.components; using GodotComponentTest.entities; using Array = System.Array; using NodePair = System.Tuple; @@ -38,6 +39,7 @@ public class InteractionSystem : Node if (interactableB != null) { interactableB.OnInteractionEnd(); + interactableB.InteractionComponent = null; } } @@ -47,10 +49,9 @@ public class InteractionSystem : Node if (interactableA != null) { interactableA.OnInteractionEnd(); + interactableA.InteractionComponent = null; } } - - } foreach (NodePair pair in invalidInteractionPairs) @@ -59,21 +60,29 @@ public class InteractionSystem : Node } } - public void OnStartInteraction(Node nodeA, Node nodeB) + public void OnStartInteraction(Entity owningEntity, Entity targetEntity) { - IInteractionInterface interactableA = nodeA as IInteractionInterface; - if (interactableA != null) - { - interactableA.OnInteractionStart(); - } + InteractionComponent interactionComponent = new InteractionComponent(); + interactionComponent.OwningEntity = owningEntity; + interactionComponent.TargetEntity = targetEntity; + + ConnectInteractionSignals(owningEntity, interactionComponent); + ConnectInteractionSignals(targetEntity, interactionComponent); + + interactionComponent.EmitSignal("InteractionStart"); - IInteractionInterface interactableB = nodeB as IInteractionInterface; - if (interactableB != null) - { - interactableB.OnInteractionStart(); - } + NodePair pair = new NodePair(owningEntity, targetEntity); + _interactionPairs.Add(new NodePair(owningEntity, targetEntity)); + } - NodePair pair = new NodePair(nodeA, nodeB); - _interactionPairs.Add(new NodePair(nodeA, nodeB)); + private static void ConnectInteractionSignals(Entity entity, InteractionComponent interactionComponent) + { + IInteractionInterface interactable = entity as IInteractionInterface; + if (interactable != null) + { + interactable.InteractionComponent = interactionComponent; + interactionComponent.Connect("InteractionStart", entity, nameof(interactable.OnInteractionStart)); + interactionComponent.Connect("InteractionEnd", entity, nameof(interactable.OnInteractionEnd)); + } } } \ No newline at end of file