2023-01-03 17:46:55 +01:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2023-01-04 21:29:42 +01:00
|
|
|
using System.Diagnostics;
|
2023-01-03 17:46:55 +01:00
|
|
|
using System.Linq;
|
|
|
|
using System.Numerics;
|
|
|
|
using Godot;
|
2023-07-07 17:27:05 +02:00
|
|
|
using GodotComponentTest.utils;
|
2023-01-03 17:46:55 +01:00
|
|
|
using Vector2 = Godot.Vector2;
|
|
|
|
using Vector3 = Godot.Vector3;
|
2023-01-04 21:29:42 +01:00
|
|
|
using GoDotLog;
|
2023-01-03 17:46:55 +01:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// </summary>
|
2023-01-04 21:29:42 +01:00
|
|
|
public class NavigationComponent : Node
|
2023-01-03 17:46:55 +01:00
|
|
|
{
|
2023-02-12 16:30:56 +01:00
|
|
|
public class NavigationPoint
|
|
|
|
{
|
|
|
|
[Flags]
|
|
|
|
public enum NavigationFlags
|
|
|
|
{
|
|
|
|
Position = 1,
|
|
|
|
Orientation = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3 WorldPosition = Vector3.Zero;
|
|
|
|
public Quat WorldOrientation = Quat.Identity;
|
|
|
|
public NavigationFlags Flags = NavigationFlags.Position | NavigationFlags.Orientation;
|
|
|
|
|
|
|
|
public NavigationPoint(Vector3 worldPosition)
|
|
|
|
{
|
|
|
|
WorldPosition = worldPosition;
|
|
|
|
Flags = NavigationFlags.Position;
|
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
public NavigationPoint(Quat worldOrientation)
|
|
|
|
{
|
|
|
|
WorldOrientation = worldOrientation;
|
|
|
|
Flags = NavigationFlags.Orientation;
|
|
|
|
}
|
2023-02-12 21:10:28 +01:00
|
|
|
|
|
|
|
public NavigationPoint(Transform worldTransform)
|
|
|
|
{
|
|
|
|
WorldPosition = worldTransform.origin;
|
|
|
|
WorldOrientation = worldTransform.basis.Quat();
|
|
|
|
Flags = NavigationFlags.Position | NavigationFlags.Orientation;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsReached(Transform worldTransform)
|
|
|
|
{
|
|
|
|
bool goalReached = false;
|
|
|
|
float positionErrorSquared = (worldTransform.origin - WorldPosition).LengthSquared();
|
2023-02-15 20:59:22 +01:00
|
|
|
Quat own_orientation = worldTransform.basis.Quat();
|
2023-02-12 21:10:28 +01:00
|
|
|
float orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
|
|
|
|
|
|
|
|
if (Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
|
|
|
|
&& Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)
|
|
|
|
&& positionErrorSquared < Globals.EpsPositionSquared
|
|
|
|
&& orientationError < Globals.EpsRadians)
|
|
|
|
{
|
|
|
|
goalReached = true;
|
|
|
|
}
|
2023-05-01 18:37:35 +02:00
|
|
|
else if (Flags == NavigationPoint.NavigationFlags.Position &&
|
2023-02-12 21:10:28 +01:00
|
|
|
positionErrorSquared < Globals.EpsPositionSquared)
|
|
|
|
{
|
|
|
|
goalReached = true;
|
|
|
|
}
|
2023-05-01 18:37:35 +02:00
|
|
|
else if (Flags == NavigationPoint.NavigationFlags.Orientation &&
|
2023-02-12 21:10:28 +01:00
|
|
|
orientationError < Globals.EpsRadians)
|
|
|
|
{
|
|
|
|
goalReached = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return goalReached;
|
|
|
|
}
|
2023-02-12 16:30:56 +01:00
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
public TileWorld TileWorld { set; get; }
|
2023-01-04 21:29:42 +01:00
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
public Vector3 CurrentGoalPositionWorld => _currentGoalPositionWorld;
|
|
|
|
public Quat CurrentGoalOrientationWorld => _currentGoalOrientationWorld;
|
|
|
|
|
|
|
|
private NavigationPoint _currentGoal;
|
|
|
|
private Vector3 _currentGoalPositionWorld = Vector3.Zero;
|
|
|
|
private Quat _currentGoalOrientationWorld = Quat.Identity;
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
private List<NavigationPoint> _pathWorldNavigationPoints;
|
2023-01-04 21:29:42 +01:00
|
|
|
private HexCell[] _path;
|
|
|
|
|
|
|
|
public override void _Ready()
|
|
|
|
{
|
|
|
|
base._Ready();
|
2023-02-12 16:30:56 +01:00
|
|
|
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
2023-01-04 21:29:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void _Process(float delta)
|
|
|
|
{
|
|
|
|
Debug.Assert(TileWorld != null);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanGridPath(Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
2023-01-03 17:46:55 +01:00
|
|
|
{
|
2023-01-04 21:29:42 +01:00
|
|
|
Vector2 fromPositionOffset = TileWorld.WorldToOffsetCoords(fromPositionWorld);
|
|
|
|
Vector2 toPositionOffset = TileWorld.WorldToOffsetCoords(toPositionWorld);
|
|
|
|
|
|
|
|
HexCell fromCell = new HexCell();
|
|
|
|
fromCell.OffsetCoords = fromPositionOffset;
|
|
|
|
|
|
|
|
HexCell toCell = new HexCell();
|
|
|
|
toCell.OffsetCoords = toPositionOffset;
|
|
|
|
|
|
|
|
_path = fromCell.LineTo(toCell);
|
|
|
|
Debug.Assert(_path.Length > 0);
|
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
2023-01-04 21:29:42 +01:00
|
|
|
foreach (int index in Enumerable.Range(1, _path.Length - 1))
|
|
|
|
{
|
2023-02-12 16:30:56 +01:00
|
|
|
_pathWorldNavigationPoints.Add(
|
2023-06-29 23:07:29 +02:00
|
|
|
new NavigationPoint(TileWorld.GetHexCenterFromOffset(_path[index].OffsetCoords)));
|
2023-02-12 16:30:56 +01:00
|
|
|
}
|
|
|
|
|
2023-06-29 23:07:29 +02:00
|
|
|
if ((fromPositionWorld - TileWorld.GetHexCenterFromOffset(toCell.OffsetCoords)).LengthSquared() >
|
2023-02-12 16:30:56 +01:00
|
|
|
Globals.EpsPositionSquared)
|
|
|
|
{
|
2023-02-12 16:44:48 +01:00
|
|
|
// Remove the last one, because it is only the position rounded to HexGrid coordinates.
|
2023-02-12 16:49:47 +01:00
|
|
|
if (_pathWorldNavigationPoints.Count > 0)
|
2023-02-12 16:44:48 +01:00
|
|
|
{
|
|
|
|
_pathWorldNavigationPoints.RemoveAt(_pathWorldNavigationPoints.Count - 1);
|
|
|
|
}
|
2023-01-04 21:29:42 +01:00
|
|
|
}
|
|
|
|
|
2023-05-03 12:01:02 +02:00
|
|
|
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-01-04 21:29:42 +01:00
|
|
|
UpdateCurrentGoal();
|
|
|
|
}
|
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanGridPath(Vector3 fromPositionWorld, Vector3 toPositionWorld, Quat toWorldOrientation)
|
2023-02-12 16:30:56 +01:00
|
|
|
{
|
2023-05-03 12:10:01 +02:00
|
|
|
PlanGridPath(fromPositionWorld, toPositionWorld);
|
|
|
|
|
|
|
|
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
|
|
|
|
}
|
2023-06-29 23:07:29 +02:00
|
|
|
|
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanGridPath(Transform fromTransformWorld, NavigationPoint navigationPoint)
|
|
|
|
{
|
|
|
|
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
|
|
|
|
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
|
|
|
{
|
|
|
|
PlanGridPath(fromTransformWorld.origin, navigationPoint.WorldPosition, navigationPoint.WorldOrientation);
|
2023-06-29 23:07:29 +02:00
|
|
|
}
|
|
|
|
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
|
2023-05-03 12:10:01 +02:00
|
|
|
{
|
|
|
|
PlanGridPath(fromTransformWorld.origin, navigationPoint.WorldPosition);
|
2023-06-29 23:07:29 +02:00
|
|
|
}
|
|
|
|
else
|
2023-05-03 12:10:01 +02:00
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
}
|
2023-06-29 23:07:29 +02:00
|
|
|
|
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanDirectPath(Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
|
|
|
{
|
|
|
|
_pathWorldNavigationPoints.Clear();
|
|
|
|
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
UpdateCurrentGoal();
|
|
|
|
}
|
|
|
|
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanDirectPath(Vector3 fromPositionWorld, Vector3 toPositionWorld, Quat toWorldOrientation)
|
|
|
|
{
|
|
|
|
PlanDirectPath(fromPositionWorld, toPositionWorld);
|
2023-02-12 16:30:56 +01:00
|
|
|
|
|
|
|
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-01 09:50:41 +02:00
|
|
|
bool SweptSphereHasCollision(Vector3 fromPosition, Vector3 toPosition, float radius)
|
|
|
|
{
|
|
|
|
if ((fromPosition - toPosition).LengthSquared() < 0.001)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector3 direction = (toPosition - fromPosition).Normalized();
|
|
|
|
|
|
|
|
// TODO: Complete Implementation
|
|
|
|
Debug.Assert(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<NavigationPoint> SmoothPath(List<NavigationPoint> navigationPoints)
|
|
|
|
{
|
|
|
|
List<NavigationPoint> smoothedPath = new List<NavigationPoint>();
|
|
|
|
|
|
|
|
int startIndex = 0;
|
|
|
|
smoothedPath.Add(navigationPoints[startIndex]);
|
|
|
|
|
|
|
|
int endIndex = navigationPoints.Count > 1 ? 1 : 0;
|
|
|
|
|
|
|
|
while (endIndex != navigationPoints.Count - 1)
|
|
|
|
{
|
|
|
|
Vector3 startPoint = navigationPoints[startIndex].WorldPosition;
|
|
|
|
Vector3 endPoint = navigationPoints[endIndex].WorldPosition;
|
|
|
|
|
|
|
|
if (SweptSphereHasCollision(startPoint, endPoint, 0.25f))
|
|
|
|
{
|
|
|
|
smoothedPath.Add(navigationPoints[endIndex-1]);
|
|
|
|
smoothedPath.Add(navigationPoints[endIndex]);
|
|
|
|
startIndex = endIndex;
|
|
|
|
endIndex += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smoothedPath.Add(navigationPoints[endIndex]);
|
|
|
|
|
|
|
|
return smoothedPath;
|
|
|
|
}
|
|
|
|
|
2023-05-03 12:10:01 +02:00
|
|
|
public void PlanDirectPath(Transform fromTransformWorld, NavigationPoint navigationPoint)
|
2023-02-12 21:10:28 +01:00
|
|
|
{
|
|
|
|
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
|
|
|
|
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
|
|
|
{
|
2023-05-03 12:10:01 +02:00
|
|
|
PlanDirectPath(fromTransformWorld.origin, navigationPoint.WorldPosition, navigationPoint.WorldOrientation);
|
2023-06-29 23:07:29 +02:00
|
|
|
}
|
|
|
|
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
|
2023-02-12 21:10:28 +01:00
|
|
|
{
|
2023-05-03 12:10:01 +02:00
|
|
|
PlanDirectPath(fromTransformWorld.origin, navigationPoint.WorldPosition);
|
2023-06-29 23:07:29 +02:00
|
|
|
}
|
|
|
|
else
|
2023-02-12 21:10:28 +01:00
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-04 21:29:42 +01:00
|
|
|
private void UpdateCurrentGoal()
|
|
|
|
{
|
2023-02-12 16:30:56 +01:00
|
|
|
if (_pathWorldNavigationPoints.Count == 0)
|
2023-01-03 17:46:55 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
_currentGoal = _pathWorldNavigationPoints[0];
|
2023-06-29 23:07:29 +02:00
|
|
|
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
2023-02-15 20:59:22 +01:00
|
|
|
_currentGoalOrientationWorld = _pathWorldNavigationPoints[0].WorldOrientation;
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-04-28 14:19:48 +02:00
|
|
|
// GD.Print("Navigation Goal: pos " + _currentGoal.WorldPosition + " " + " rot: " + _currentGoal.WorldOrientation +
|
|
|
|
// " flags: " + _currentGoal.Flags + " path length: " +
|
|
|
|
// _pathWorldNavigationPoints.Count);
|
2023-01-04 21:29:42 +01:00
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-05-01 18:37:35 +02:00
|
|
|
private void ApplyExistingTransform(Transform worldTransform)
|
|
|
|
{
|
|
|
|
if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Orientation)
|
|
|
|
{
|
|
|
|
_currentGoalPositionWorld = worldTransform.origin;
|
2023-06-29 23:07:29 +02:00
|
|
|
}
|
|
|
|
else if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Position)
|
2023-05-01 18:37:35 +02:00
|
|
|
{
|
|
|
|
_currentGoalOrientationWorld = worldTransform.basis.Quat();
|
|
|
|
}
|
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-02-12 16:30:56 +01:00
|
|
|
public void UpdateCurrentGoal(Transform currentTransformWorld)
|
2023-01-04 21:29:42 +01:00
|
|
|
{
|
2023-02-12 16:30:56 +01:00
|
|
|
if (_pathWorldNavigationPoints.Count == 0)
|
|
|
|
{
|
2023-05-01 18:37:35 +02:00
|
|
|
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
|
|
|
_currentGoalPositionWorld = currentTransformWorld.origin;
|
2023-02-12 16:30:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
|
|
|
|
{
|
2023-06-29 23:07:29 +02:00
|
|
|
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
2023-02-12 16:30:56 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_currentGoalPositionWorld = currentTransformWorld.origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
|
|
|
{
|
|
|
|
_currentGoalOrientationWorld = _currentGoal.WorldOrientation;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
|
|
|
}
|
|
|
|
|
2023-02-12 21:10:28 +01:00
|
|
|
if (_currentGoal.IsReached(currentTransformWorld))
|
2023-01-03 17:46:55 +01:00
|
|
|
{
|
2023-02-12 16:30:56 +01:00
|
|
|
_pathWorldNavigationPoints.RemoveAt(0);
|
2023-06-29 23:07:29 +02:00
|
|
|
|
2023-01-04 21:29:42 +01:00
|
|
|
UpdateCurrentGoal();
|
2023-05-01 18:37:35 +02:00
|
|
|
ApplyExistingTransform(currentTransformWorld);
|
2023-01-03 17:46:55 +01:00
|
|
|
}
|
2023-02-15 20:59:22 +01:00
|
|
|
|
|
|
|
if (_pathWorldNavigationPoints.Count == 0)
|
|
|
|
{
|
|
|
|
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
|
|
|
_currentGoalPositionWorld = currentTransformWorld.origin;
|
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
}
|
2023-07-07 17:27:05 +02:00
|
|
|
|
|
|
|
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry)
|
|
|
|
{
|
|
|
|
|
|
|
|
Vector3 yOffset = Vector3.Up * 0.1f;
|
|
|
|
|
|
|
|
debugGeometry.GlobalTransform = Transform.Identity;
|
|
|
|
debugGeometry.Begin(Mesh.PrimitiveType.Lines);
|
|
|
|
debugGeometry.SetColor(Colors.Pink);
|
|
|
|
debugGeometry.AddVertex(parentNode.GlobalTranslation + yOffset);
|
|
|
|
debugGeometry.SetColor(Colors.Pink);
|
|
|
|
debugGeometry.AddVertex(CurrentGoalPositionWorld + yOffset);
|
|
|
|
debugGeometry.SetColor(Colors.Pink);
|
|
|
|
|
|
|
|
debugGeometry.PushTranslated(CurrentGoalPositionWorld);
|
|
|
|
debugGeometry.AddBox(Vector3.One * 1);
|
|
|
|
debugGeometry.PopTransform();
|
|
|
|
|
|
|
|
Vector3 previousPoint = parentNode.GlobalTranslation;
|
|
|
|
foreach (NavigationPoint point in _pathWorldNavigationPoints)
|
|
|
|
{
|
|
|
|
debugGeometry.AddVertex(previousPoint + yOffset);
|
|
|
|
debugGeometry.AddVertex(point.WorldPosition + yOffset);
|
|
|
|
|
|
|
|
previousPoint = point.WorldPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
debugGeometry.End();
|
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
}
|