From c59d92618bdda5b3736ecb221ca047775335800e Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Fri, 10 Nov 2023 11:11:08 +0100 Subject: [PATCH] Tile instancing now working, also fixed navigation bounds. --- components/GroundMotionComponent.cs | 62 +++---- components/NavigationComponent.cs | 263 ++++++++++++++-------------- components/TaskQueueComponent.cs | 85 ++++----- components/WorldInfoComponent.cs | 12 +- entities/Player.cs | 86 ++++----- scenes/Game.cs | 93 +--------- scenes/Game.tscn | 51 ++++-- scenes/TileInstanceManager.cs | 78 ++++----- scenes/TileWorld.cs | 79 ++++----- scenes/TileWorld.tscn | 5 - scenes/World.cs | 104 +++++++---- scenes/tests/EditorUI.cs | 127 ++++++-------- tests/NavigationComponentTests.cs | 21 +-- 13 files changed, 490 insertions(+), 576 deletions(-) diff --git a/components/GroundMotionComponent.cs b/components/GroundMotionComponent.cs index 916be89..b2cba84 100644 --- a/components/GroundMotionComponent.cs +++ b/components/GroundMotionComponent.cs @@ -1,8 +1,4 @@ -using System; -using System.Numerics; using Godot; -using Vector2 = Godot.Vector2; -using Vector3 = Godot.Vector3; /// /// @@ -10,23 +6,21 @@ public class GroundMotionComponent : Component { public float Accel = 50; public float Damping = 0.2f; - public float MaxSpeed = 8; - - public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180; public Vector3 DirectionToTarget = Vector3.Zero; - public float DistanceToTarget = 0; - public float TargetAngle = 0; - public float TargetDeltaAngle = 0; - - + public float DistanceToTarget; + public float MaxSpeed = 8; + + public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180; + public float TargetAngle; + public float TargetDeltaAngle; + private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation) { float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle); if (deltaAngleAbsolute > 0.001) { - if (RotationSpeedRadPerSecond * delta + 0.001 >= deltaAngleAbsolute) + if (RotationSpeedRadPerSecond * delta >= deltaAngleAbsolute) { - GD.Print("Target Angle " + TargetAngle + " reached! Current Angle: " + entity.PlaneAngle + " TargetDeltaAngle = " + TargetDeltaAngle); entity.PlaneAngle = TargetAngle; TargetDeltaAngle = 0; } @@ -40,24 +34,26 @@ public class GroundMotionComponent : Component TargetDeltaAngle = 0; } } - + private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition) { Vector2 planeTargetDirection = new Vector2(DirectionToTarget.x, DirectionToTarget.z); - + Vector2 planeVelocity = new Vector2(entity.Velocity.x, entity.Velocity.z); // GD.Print("-- Step: distance: " + targetDistance + " dir: " + planeTargetDirection + " speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized()); planeVelocity -= planeVelocity * Damping; // GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized()); - + if (DistanceToTarget < 0.01) { planeVelocity = Vector2.Zero; - } else if (TargetDeltaAngle == 0.0) { + } + else if (TargetDeltaAngle == 0.0) + { planeVelocity = planeVelocity.Length() * planeTargetDirection + planeTargetDirection * Accel * delta; // GD.Print(" accel: speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized()); - float projectedStep = planeTargetDirection.Dot(planeVelocity * delta); + var projectedStep = planeTargetDirection.Dot(planeVelocity * delta); // GD.Print(" Projected step: " + projectedStep + " Speed: " + planeVelocity.Length() + " delta: " + delta); if (projectedStep > DistanceToTarget) { @@ -65,12 +61,9 @@ public class GroundMotionComponent : Component projectedStep = planeTargetDirection.Dot(planeVelocity * delta); // GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep); } - - float planeSpeed = planeVelocity.Length(); - if (planeSpeed > MaxSpeed) - { - planeVelocity *= MaxSpeed / planeSpeed; - } + + var planeSpeed = planeVelocity.Length(); + if (planeSpeed > MaxSpeed) planeVelocity *= MaxSpeed / planeSpeed; } else { @@ -94,7 +87,7 @@ public class GroundMotionComponent : Component float nextHeight = tileWorld.GetHeightAtOffset(nextTile); bool isOnFloor = entity.IsOnFloor(); - + if (nextHeight - entity.GlobalTransform.origin.y > 0.1) { GD.Print("Jump!"); @@ -107,14 +100,14 @@ public class GroundMotionComponent : Component } entityVelocity.y -= 9.81f * delta; - + entity.Velocity = entityVelocity; } - - - public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation, TileWorld tileWorld) + + public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation, + World world) { - DirectionToTarget = (targetPosition - entity.GlobalTranslation); + DirectionToTarget = targetPosition - entity.GlobalTranslation; DirectionToTarget.y = 0; DistanceToTarget = DirectionToTarget.Length(); @@ -122,7 +115,7 @@ public class GroundMotionComponent : Component { DirectionToTarget = DirectionToTarget.Normalized(); TargetAngle = Vector3.Right.SignedAngleTo(DirectionToTarget, Vector3.Up); - TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget); + TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget); } else { @@ -134,15 +127,14 @@ public class GroundMotionComponent : Component entity.Velocity = new Vector3(0, entity.Velocity.y, 0); return; } - + CalcAndApplyOrientation(delta, entity, targetPosition, targetOrientation); CalcPlaneVelocity(delta, entity, targetPosition); // CalcVerticalVelocity(delta, entity, tileWorld); entity.Velocity = entity.MoveAndSlide(entity.Velocity); - + //GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity); //GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity); } - } \ No newline at end of file diff --git a/components/NavigationComponent.cs b/components/NavigationComponent.cs index 7478462..e76a62b 100644 --- a/components/NavigationComponent.cs +++ b/components/NavigationComponent.cs @@ -4,92 +4,23 @@ using System.Diagnostics; using System.Linq; using Godot; using GodotComponentTest.utils; -using Vector2 = Godot.Vector2; -using Vector3 = Godot.Vector3; /// /// public class NavigationComponent : Spatial { - public class NavigationPoint - { - [Flags] - public enum NavigationFlags - { - Position = 1, - Orientation = 2 - } - - public Vector3 WorldPosition = Vector3.Zero; - public Quat WorldOrientation = Quat.Identity; - public readonly NavigationFlags Flags; - - public NavigationPoint(Vector3 worldPosition) - { - WorldPosition = worldPosition; - Flags = NavigationFlags.Position; - } - - public NavigationPoint(Quat worldOrientation) - { - WorldOrientation = worldOrientation; - Flags = NavigationFlags.Orientation; - } - - public NavigationPoint(Transform worldTransform) - { - WorldPosition = worldTransform.origin; - WorldOrientation = worldTransform.basis.Quat(); - Flags = NavigationFlags.Position | NavigationFlags.Orientation; - } - - public bool IsReached(Transform worldTransform) - { - bool goalReached = false; - Vector2 positionError = new Vector2(WorldPosition.x - worldTransform.origin.x, - WorldPosition.z - worldTransform.origin.z); - float positionErrorSquared = positionError.LengthSquared(); - worldTransform.basis.Quat(); - float orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation)); - - if (Flags.HasFlag(NavigationFlags.Position) - && Flags.HasFlag(NavigationFlags.Orientation) - && positionErrorSquared < Globals.EpsPositionSquared - && orientationError < Globals.EpsRadians) - { - goalReached = true; - } - else if (Flags == NavigationFlags.Position && - positionErrorSquared < Globals.EpsPositionSquared) - { - goalReached = true; - } - else if (Flags == NavigationFlags.Orientation && - orientationError < Globals.EpsRadians) - { - goalReached = true; - } - - return goalReached; - } - } - - public TileWorld TileWorld { set; get; } - - public Vector3 CurrentGoalPositionWorld => _currentGoalPositionWorld; - public Quat CurrentGoalOrientationWorld => _currentGoalOrientationWorld; + public World World { set; get; } + public Vector3 CurrentGoalPositionWorld { get; private set; } = Vector3.Zero; + public float CurrentGoalAngleWorld { get; } = 0; + public Quat CurrentGoalOrientationWorld { get; private set; } = Quat.Identity; private NavigationPoint _currentGoal; - private Vector3 _currentGoalPositionWorld = Vector3.Zero; - private float _currentGoalAngleWorld = 0; - private Quat _currentGoalOrientationWorld = Quat.Identity; - - private List _planningPathWorldNavigationPoints = new List(); - private List _planningPathSmoothedWorldNavigationPoints = new List(); - private List _pathWorldNavigationPoints = new List(); - private List _smoothedPathWorldNavigationPoints = new List(); - private HexCell[] _path; + private List _pathWorldNavigationPoints = new(); + private List _planningPathSmoothedWorldNavigationPoints = new(); + + private List _planningPathWorldNavigationPoints = new(); + private List _smoothedPathWorldNavigationPoints = new(); public override void _Ready() { @@ -99,9 +30,9 @@ public class NavigationComponent : Spatial public override void _Process(float delta) { - Debug.Assert(TileWorld != null); + Debug.Assert(World != null); } - + public void PlanSmoothedPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint) { if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position) @@ -121,8 +52,8 @@ public class NavigationComponent : Spatial public void FindPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) { - HexCell fromCell = TileWorld.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)); - if (TileWorld.HexGrid.GetHexCost(fromCell) == 0) + var fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)); + if (World.HexGrid.GetHexCost(fromCell) == 0) { GD.Print("Invalid starting point for FindPath(): returning empty path."); _planningPathWorldNavigationPoints = new List(); @@ -130,11 +61,11 @@ public class NavigationComponent : Spatial _planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints; return; } - - HexCell toCell = TileWorld.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z)); - toCell = TileWorld.HexGrid.GetClosestWalkableCell(fromCell, toCell); - - if (TileWorld.HexGrid.GetHexCost(toCell) == 0) + + var toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z)); + toCell = World.HexGrid.GetClosestWalkableCell(fromCell, toCell); + + if (World.HexGrid.GetHexCost(toCell) == 0) { GD.Print("Invalid target point for FindPath(): returning empty path."); _planningPathWorldNavigationPoints = new List(); @@ -142,25 +73,26 @@ public class NavigationComponent : Spatial _planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints; return; } - - TileWorld.HexGrid.SetBounds(fromCell, 40); - List path = TileWorld.HexGrid.FindPath(fromCell, toCell); + + var path = World.HexGrid.FindPath(fromCell, toCell); // Generate grid navigation points _planningPathWorldNavigationPoints = new List(); - foreach (int index in Enumerable.Range(0, path.Count)) + foreach (var index in Enumerable.Range(0, path.Count)) { _planningPathWorldNavigationPoints.Add( - new NavigationPoint(TileWorld.GetHexCenterFromOffset(path[index].OffsetCoords))); + new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(path[index].OffsetCoords))); } // Ensure the last point coincides with the target position - if (_planningPathWorldNavigationPoints.Count > 0 && (_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() < + if (_planningPathWorldNavigationPoints.Count > 0 && + (_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() < 0.5f * 0.5f) { - _planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition = toPositionWorld; + _planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition = + toPositionWorld; } - + // Perform smoothing _planningPathSmoothedWorldNavigationPoints = SmoothPath(body, _planningPathWorldNavigationPoints); @@ -176,18 +108,19 @@ public class NavigationComponent : Spatial FindPath(body, fromPositionWorld, navigationPoint.WorldPosition); _planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint; - _planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] = navigationPoint; + _planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] = + navigationPoint; } - + public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) { - Vector2 fromPositionOffset = TileWorld.WorldToOffsetCoords(fromPositionWorld); - Vector2 toPositionOffset = TileWorld.WorldToOffsetCoords(toPositionWorld); + var fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld); + var toPositionOffset = World.WorldToOffsetCoords(toPositionWorld); - HexCell fromCell = new HexCell(); + var fromCell = new HexCell(); fromCell.OffsetCoords = fromPositionOffset; - HexCell toCell = new HexCell(); + var toCell = new HexCell(); toCell.OffsetCoords = toPositionOffset; _path = fromCell.LineTo(toCell); @@ -195,15 +128,15 @@ public class NavigationComponent : Spatial _pathWorldNavigationPoints = new List(); _pathWorldNavigationPoints.Add( - new NavigationPoint(TileWorld.GetHexCenterFromOffset(fromPositionOffset))); + new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset))); - foreach (int index in Enumerable.Range(1, _path.Length - 1)) + foreach (var index in Enumerable.Range(1, _path.Length - 1)) { _pathWorldNavigationPoints.Add( - new NavigationPoint(TileWorld.GetHexCenterFromOffset(_path[index].OffsetCoords))); + new NavigationPoint(World.GetHexCenterFromOffset(_path[index].OffsetCoords))); } - if ((fromPositionWorld - TileWorld.GetHexCenterFromOffset(toCell.OffsetCoords)).LengthSquared() > + if ((fromPositionWorld - World.GetHexCenterFromOffset(toCell.OffsetCoords)).LengthSquared() > Globals.EpsPositionSquared) { // Remove the last one, because it is only the position rounded to HexGrid coordinates. @@ -287,7 +220,7 @@ public class NavigationComponent : Spatial return false; } - + public bool CheckSweptTriangleCellCollision(Vector3 startWorld, Vector3 endWorld, float radius) { Vector2 startPlane = new Vector2(startWorld.x, startWorld.z); @@ -295,36 +228,37 @@ public class NavigationComponent : Spatial Vector2 directionPlane = (endPlane - startPlane).Normalized(); Vector2 sidePlane = directionPlane.Rotated(Mathf.Pi * 0.5f); - List cells = TileWorld.HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius); + List cells = + World.HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius); foreach (HexCell cell in cells) { - if (TileWorld.HexGrid.GetHexCost(cell) == 0) + if (World.HexGrid.GetHexCost(cell) == 0) { return true; } } - - cells = TileWorld.HexGrid.GetCellsForLine(startPlane + sidePlane * radius, endPlane + sidePlane * radius); + + cells = World.HexGrid.GetCellsForLine(startPlane + sidePlane * radius, endPlane + sidePlane * radius); foreach (HexCell cell in cells) { - if (TileWorld.HexGrid.GetHexCost(cell) == 0) + if (World.HexGrid.GetHexCost(cell) == 0) { return true; } } - - cells = TileWorld.HexGrid.GetCellsForLine(startPlane - sidePlane * radius, endPlane - sidePlane * radius); + + cells = World.HexGrid.GetCellsForLine(startPlane - sidePlane * radius, endPlane - sidePlane * radius); foreach (HexCell cell in cells) { - if (TileWorld.HexGrid.GetHexCost(cell) == 0) + if (World.HexGrid.GetHexCost(cell) == 0) { return true; } } - + return false; } - + public List SmoothPath(KinematicBody body, List navigationPoints) { if (navigationPoints.Count <= 2) @@ -360,7 +294,7 @@ public class NavigationComponent : Spatial continue; } - + if (endIndex == navigationPoints.Count - 1) { break; @@ -407,7 +341,7 @@ public class NavigationComponent : Spatial } _currentGoal = _pathWorldNavigationPoints[0]; - _currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition; + CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition; //_currentGoalOrientationWorld = Vector3.Right.SignedAngleTo(_pathWorldNavigationPoints[0].WorldOrientation); // GD.Print("Navigation Goal: pos " + _currentGoal.WorldPosition + " " + " rot: " + _currentGoal.WorldOrientation + @@ -419,11 +353,11 @@ public class NavigationComponent : Spatial { if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Orientation) { - _currentGoalPositionWorld = worldTransform.origin; + CurrentGoalPositionWorld = worldTransform.origin; } else if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Position) { - _currentGoalOrientationWorld = worldTransform.basis.Quat(); + CurrentGoalOrientationWorld = worldTransform.basis.Quat(); } } @@ -434,32 +368,36 @@ public class NavigationComponent : Spatial { _currentGoal = new NavigationPoint(currentTransformWorld); } - + if (_pathWorldNavigationPoints.Count == 0) { - _currentGoalOrientationWorld = currentTransformWorld.basis.Quat(); - _currentGoalPositionWorld = currentTransformWorld.origin; + CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat(); + CurrentGoalPositionWorld = currentTransformWorld.origin; return; } if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) { - _currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition; + CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition; } else { - _currentGoalPositionWorld = currentTransformWorld.origin; + CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat(); } if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)) { - _currentGoalOrientationWorld = _currentGoal.WorldOrientation; + CurrentGoalOrientationWorld = _currentGoal.WorldOrientation; } else { - _currentGoalOrientationWorld = currentTransformWorld.basis.Quat(); + CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat(); } + // Vector3 currentWorldXAxis = currentTransformWorld.basis.x; + // Debug.Assert(Mathf.Abs(currentWorldXAxis.y) < 0.9); + // float angle = Mathf.Atan2(currentWorldXAxis.y, currentWorldXAxis.x); + // _currentGoalOrientationWorld = Basis.Identity.Rotated(Vector3.Up, angle).Quat(); if (_currentGoal.IsReached(currentTransformWorld)) { _pathWorldNavigationPoints.RemoveAt(0); @@ -470,11 +408,16 @@ public class NavigationComponent : Spatial if (_pathWorldNavigationPoints.Count == 0) { - _currentGoalOrientationWorld = currentTransformWorld.basis.Quat(); - _currentGoalPositionWorld = currentTransformWorld.origin; + CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat(); + CurrentGoalPositionWorld = currentTransformWorld.origin; } } + public bool IsGoalReached() + { + return _pathWorldNavigationPoints.Count == 0; + } + public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry) { Vector3 yOffset = Vector3.Up * 0.1f; @@ -511,7 +454,7 @@ public class NavigationComponent : Spatial previousPoint = point.WorldPosition; } - + previousPoint = parentNode.GlobalTranslation; foreach (NavigationPoint point in _planningPathWorldNavigationPoints) { @@ -521,7 +464,7 @@ public class NavigationComponent : Spatial previousPoint = point.WorldPosition; } - + previousPoint = parentNode.GlobalTranslation; foreach (NavigationPoint point in _planningPathSmoothedWorldNavigationPoints) { @@ -534,4 +477,62 @@ public class NavigationComponent : Spatial debugGeometry.End(); } + + public class NavigationPoint + { + [Flags] + public enum NavigationFlags + { + Position = 1, + Orientation = 2 + } + + public readonly NavigationFlags Flags; + public Quat WorldOrientation = Quat.Identity; + + public Vector3 WorldPosition = Vector3.Zero; + + public NavigationPoint(Vector3 worldPosition) + { + WorldPosition = worldPosition; + Flags = NavigationFlags.Position; + } + + public NavigationPoint(Quat worldOrientation) + { + WorldOrientation = worldOrientation; + Flags = NavigationFlags.Orientation; + } + + public NavigationPoint(Transform worldTransform) + { + WorldPosition = worldTransform.origin; + WorldOrientation = worldTransform.basis.Quat(); + Flags = NavigationFlags.Position | NavigationFlags.Orientation; + } + + public bool IsReached(Transform worldTransform) + { + var goalReached = false; + var positionError = new Vector2(WorldPosition.x - worldTransform.origin.x, + WorldPosition.z - worldTransform.origin.z); + var positionErrorSquared = positionError.LengthSquared(); + worldTransform.basis.Quat(); + var orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation)); + + if (Flags.HasFlag(NavigationFlags.Position) + && Flags.HasFlag(NavigationFlags.Orientation) + && positionErrorSquared < Globals.EpsPositionSquared + && orientationError < Globals.EpsRadians) + goalReached = true; + else if (Flags == NavigationFlags.Position && + positionErrorSquared < Globals.EpsPositionSquared) + goalReached = true; + else if (Flags == NavigationFlags.Orientation && + orientationError < Globals.EpsRadians) + goalReached = true; + + return goalReached; + } + } } \ No newline at end of file diff --git a/components/TaskQueueComponent.cs b/components/TaskQueueComponent.cs index c77efa3..885dc08 100644 --- a/components/TaskQueueComponent.cs +++ b/components/TaskQueueComponent.cs @@ -1,24 +1,54 @@ -using Godot; -using System; using System.Collections.Generic; -using System.Diagnostics; -using GodotComponentTest.entities; -using Object = Godot.Object; +using Godot; public class TaskQueueComponent : Component { [Signal] - delegate void StartInteraction(Entity entity, Entity targetEntity); + private delegate void StartInteraction(Entity entity, Entity targetEntity); + public Queue Queue; + + public TaskQueueComponent() + { + Queue = new Queue(); + + Reset(); + } + + public void Reset() + { + Queue.Clear(); + } + + public void Process(Entity entity, float delta) + { + if (Queue.Count == 0) return; + + do + { + var currentTask = Queue.Peek(); + var interactionTask = currentTask as InteractionTask; + if (interactionTask != null) EmitSignal("StartInteraction", entity, interactionTask.TargetEntity); + + if (currentTask.PerformTask(entity, delta)) + Queue.Dequeue(); + else + break; + } while (Queue.Count > 0); + } + public abstract class Task : Object { /// - /// Executes a Task on the specified entity. + /// Executes a Task on the specified entity. /// /// true when the Task is complete, false otherwise public abstract bool PerformTask(Entity entity, float delta); } + /// + /// Makes an entity move towards a target (translation and or orientation). + /// public class NavigationTask : Task { public NavigationComponent.NavigationPoint NavigationPoint; @@ -49,45 +79,4 @@ public class TaskQueueComponent : Component return true; } } - - public Queue Queue; - - public TaskQueueComponent() - { - Queue = new Queue(); - - Reset(); - } - - public void Reset() - { - Queue.Clear(); - } - - public void Process(Entity entity, float delta) - { - if (Queue.Count == 0) - { - return; - } - - do - { - Task currentTask = Queue.Peek(); - InteractionTask interactionTask = currentTask as InteractionTask; - if (interactionTask != null) - { - EmitSignal("StartInteraction", entity, interactionTask.TargetEntity); - } - - if (currentTask.PerformTask(entity, delta)) - { - Queue.Dequeue(); - } - else - { - break; - } - } while (Queue.Count > 0); - } } \ No newline at end of file diff --git a/components/WorldInfoComponent.cs b/components/WorldInfoComponent.cs index 18cacf8..1c071fc 100644 --- a/components/WorldInfoComponent.cs +++ b/components/WorldInfoComponent.cs @@ -1,20 +1,18 @@ using Godot; -using System; public class WorldInfoComponent : Component { - [Export] public NodePath World; - - public TileWorld TileWorld; + [Export] public NodePath WorldPath; + public World World; // Called when the node enters the scene tree for the first time. public override void _Ready() { - TileWorld = GetNode(World); + World = GetNode(WorldPath); } - public void SetWorld(TileWorld tileWorld) + public void SetWorld(World world) { - TileWorld = tileWorld; + World = world; } } \ No newline at end of file diff --git a/entities/Player.cs b/entities/Player.cs index 8a27500..b0de207 100644 --- a/entities/Player.cs +++ b/entities/Player.cs @@ -1,8 +1,6 @@ -using Godot; -using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; +using Godot; using GodotComponentTest.components; using GodotComponentTest.entities; using GodotComponentTest.utils; @@ -10,52 +8,40 @@ using GodotComponentTest.utils; public class Player : Entity, IInteractionInterface { // public members - [Export] public NodePath TileWorldNode; - - public int GoldCount = 0; + [Export] public NodePath WorldNode; + public int GoldCount; public TaskQueueComponent TaskQueueComponent; - - public NavigationComponent Navigation - { - get { return _navigationComponent; } - } + public NavigationComponent NavigationComponent { get; private set; } + public InteractionComponent InteractionComponent { get; set; } [Signal] - delegate void GoldCountChanged(int goldCount); + private delegate void GoldCountChanged(int goldCount); // private members private WorldInfoComponent _worldInfo; - private GroundMotionComponent _groundMotion; - private NavigationComponent _navigationComponent; - private Area _itemAttractorArea; - private Area _itemPickupArea; - private List _attractedItemList = new List(); - private BoneAttachment _toolAttachement; - private AnimationPlayer _playerAnimationPlayer; + private readonly List _attractedItemList = new(); private AnimationTree _animationTree; private DebugGeometry _debugGeometry; - private InteractionComponent _interactionComponent; - - public InteractionComponent InteractionComponent - { - get => _interactionComponent; - set => _interactionComponent = value; - } + private GroundMotionComponent _groundMotion; + private Area _itemAttractorArea; + private Area _itemPickupArea; + private AnimationPlayer _playerAnimationPlayer; + private BoneAttachment _toolAttachement; // Called when the node enters the scene tree for the first time. public override void _Ready() { _groundMotion = new GroundMotionComponent(); _worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false); - _navigationComponent = (NavigationComponent)FindNode("Navigation", false); - _navigationComponent.TileWorld = GetNode(TileWorldNode); + NavigationComponent = (NavigationComponent)FindNode("Navigation", false); + NavigationComponent.World = GetNode(WorldNode); TaskQueueComponent = new TaskQueueComponent(); _itemAttractorArea = (Area)FindNode("ItemAttractorArea", false); if (_itemAttractorArea == null) { - GD.PushWarning("No ItemAttractorArea node found for " + this.GetClass()); + GD.PushWarning("No ItemAttractorArea node found for " + GetClass()); } else { @@ -65,27 +51,20 @@ public class Player : Entity, IInteractionInterface _itemPickupArea = (Area)FindNode("ItemPickupArea", false); if (_itemPickupArea == null) - { - GD.PushWarning("No ItemPickupArea node found for " + this.GetClass()); - } + GD.PushWarning("No ItemPickupArea node found for " + GetClass()); else - { _itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered)); - } _playerAnimationPlayer = GetNode("Geometry/AnimationPlayer"); Debug.Assert(_playerAnimationPlayer != null); _animationTree = GetNode("Geometry/AnimationTree"); Debug.Assert(_animationTree != null); - AnimationNodeStateMachinePlayback stateMachine = + var stateMachine = (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); stateMachine.Start("Idle"); _toolAttachement = (BoneAttachment)FindNode("ToolAttachement"); - if (_toolAttachement == null) - { - GD.PushWarning("No ToolAttachement found!"); - } + if (_toolAttachement == null) GD.PushWarning("No ToolAttachement found!"); _debugGeometry = (DebugGeometry)FindNode("DebugGeometry"); } @@ -95,7 +74,7 @@ public class Player : Entity, IInteractionInterface { base._PhysicsProcess(delta); - if (_navigationComponent == null) + if (NavigationComponent == null) { return; } @@ -111,15 +90,18 @@ public class Player : Entity, IInteractionInterface if (navigationTask != null && navigationTask.PlanningComplete == false) { // _navigationComponent.PlanGridPath(this, GlobalTransform, navigationTask.NavigationPoint); - _navigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint); + NavigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint); - _navigationComponent.ActivatePlannedPath(); + NavigationComponent.ActivatePlannedPath(); navigationTask.PlanningComplete = true; } - _navigationComponent.UpdateCurrentGoal(GlobalTransform); - _groundMotion.PhysicsProcess(delta, this, _navigationComponent.CurrentGoalPositionWorld, - _navigationComponent.CurrentGoalOrientationWorld, _worldInfo.TileWorld); + NavigationComponent.UpdateCurrentGoal(GlobalTransform); + _groundMotion.PhysicsProcess(delta, this, NavigationComponent.CurrentGoalPositionWorld, + NavigationComponent.CurrentGoalOrientationWorld, _worldInfo.World); + + if (NavigationComponent.IsGoalReached()) + navigationTask.NavigationPoint = new NavigationComponent.NavigationPoint(GlobalTransform); } } } @@ -127,9 +109,9 @@ public class Player : Entity, IInteractionInterface public override void _Process(float delta) { - if (_navigationComponent != null) + if (NavigationComponent != null) { - _navigationComponent.UpdateCurrentGoal(GlobalTransform); + NavigationComponent.UpdateCurrentGoal(GlobalTransform); } foreach (Node node in _attractedItemList) @@ -154,9 +136,9 @@ public class Player : Entity, IInteractionInterface _debugGeometry.Clear(); _debugGeometry.GlobalTransform = Transform.Identity; - if (_navigationComponent != null) + if (NavigationComponent != null) { - _navigationComponent.DebugDraw(this, _debugGeometry); + NavigationComponent.DebugDraw(this, _debugGeometry); return; } } @@ -198,7 +180,7 @@ public class Player : Entity, IInteractionInterface body.QueueFree(); } - public void SetActiveTool(String toolName) + public void SetActiveTool(string toolName) { Debug.Assert(_toolAttachement != null); if (toolName == "Axe") @@ -217,12 +199,12 @@ public class Player : Entity, IInteractionInterface (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); Debug.Assert(stateMachine != null); - if (_interactionComponent.TargetEntity is Chest) + if (InteractionComponent.TargetEntity is Chest) { GD.Print("Player Opening Box"); stateMachine.Travel("Interaction"); } - else if (_interactionComponent.TargetEntity is Tree) + else if (InteractionComponent.TargetEntity is Tree) { GD.Print("Player Chopping Tree"); stateMachine.Travel("Hit"); diff --git a/scenes/Game.cs b/scenes/Game.cs index 3b662fc..e8fe5a8 100644 --- a/scenes/Game.cs +++ b/scenes/Game.cs @@ -22,6 +22,7 @@ public class Game : Spatial private InteractionSystem _interactionSystem; private HexCell _lastTile; private Label _mouseTileCubeLabel; + private Label _mouseTileAxialLabel; private Spatial _mouseTileHighlight; private Label _mouseTileOffsetLabel; private Label _mouseWorldLabel; @@ -29,9 +30,6 @@ public class Game : Spatial private Label _numCoordsRemovedLabel; private Label _numTilesLabel; private Player _player; - private StreamContainer _streamContainer; - private Spatial _streamContainerActiveTiles; - private Area _streamContainerArea; // scene nodes private Spatial _tileHighlight; @@ -41,7 +39,6 @@ public class Game : Spatial private TileInstanceManager _tileInstanceManager; private ShaderMaterial _tileMaterial; private Label _tileOffsetLabel; - private TileWorld _tileWorld; private World _world; private TextureRect _worldTextureRect; @@ -58,6 +55,7 @@ public class Game : Spatial _mouseWorldLabel = debugStatsContainer.GetNode