Improved interactions.

- Interactions can now be stopped by both participating Entities.
- Player animation now controlled by AnimationTree.
WorldChunkRefactoring
Martin Felis 2023-06-26 22:34:48 +02:00
parent 22ad86319d
commit 2e7f30e214
9 changed files with 148 additions and 27 deletions

View File

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

View File

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

View File

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

View File

@ -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"
} ]
}

View File

@ -1,7 +1,15 @@
using GodotComponentTest.components;
namespace GodotComponentTest.entities;
public interface IInteractionInterface
{
void OnInteractionStart();
void OnInteractionEnd();
InteractionComponent InteractionComponent
{
get;
set;
}
}

View File

@ -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<Node> _attractedItemList = new List<Node>();
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<AnimationPlayer>("Geometry/AnimationPlayer");
_playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer");
Debug.Assert(_playerAnimationPlayer != null);
_animationTree = GetNode<AnimationTree>("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");
}
}

View File

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

View File

@ -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="."]

View File

@ -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<Godot.Node, Godot.Node>;
@ -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;
IInteractionInterface interactableB = nodeB as IInteractionInterface;
if (interactableB != null)
{
interactableB.OnInteractionStart();
}
ConnectInteractionSignals(owningEntity, interactionComponent);
ConnectInteractionSignals(targetEntity, interactionComponent);
NodePair pair = new NodePair(nodeA, nodeB);
_interactionPairs.Add(new NodePair(nodeA, nodeB));
interactionComponent.EmitSignal("InteractionStart");
NodePair pair = new NodePair(owningEntity, targetEntity);
_interactionPairs.Add(new NodePair(owningEntity, targetEntity));
}
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));
}
}
}