GodotComponentTest/components/NavigationComponent.cs

268 lines
8.8 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using Godot;
using Vector2 = Godot.Vector2;
using Vector3 = Godot.Vector3;
using GoDotLog;
/// <summary>
/// </summary>
public class NavigationComponent : Node
{
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;
}
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;
}
}
public TileWorld TileWorld { set; get; }
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
private List<NavigationPoint> _pathWorldNavigationPoints;
private HexCell[] _path;
public override void _Ready()
{
base._Ready();
_pathWorldNavigationPoints = new List<NavigationPoint>();
}
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)
{
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);
_pathWorldNavigationPoints = new List<NavigationPoint>();
foreach (int index in Enumerable.Range(1, _path.Length - 1))
{
_pathWorldNavigationPoints.Add(
2023-06-29 23:07:29 +02:00
new NavigationPoint(TileWorld.GetHexCenterFromOffset(_path[index].OffsetCoords)));
}
2023-06-29 23:07:29 +02:00
if ((fromPositionWorld - TileWorld.GetHexCenterFromOffset(toCell.OffsetCoords)).LengthSquared() >
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-05-03 12:01:02 +02:00
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
2023-06-29 23:07:29 +02:00
UpdateCurrentGoal();
}
2023-05-03 12:10:01 +02:00
public void PlanGridPath(Vector3 fromPositionWorld, Vector3 toPositionWorld, Quat toWorldOrientation)
{
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);
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
}
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();
}
}
private void UpdateCurrentGoal()
{
if (_pathWorldNavigationPoints.Count == 0)
{
return;
}
_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-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-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-06-29 23:07:29 +02:00
public void UpdateCurrentGoal(Transform currentTransformWorld)
{
if (_pathWorldNavigationPoints.Count == 0)
{
2023-05-01 18:37:35 +02:00
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
_currentGoalPositionWorld = currentTransformWorld.origin;
return;
}
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
2023-06-29 23:07:29 +02:00
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
}
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))
{
_pathWorldNavigationPoints.RemoveAt(0);
2023-06-29 23:07:29 +02:00
UpdateCurrentGoal();
2023-05-01 18:37:35 +02:00
ApplyExistingTransform(currentTransformWorld);
}
2023-02-15 20:59:22 +01:00
if (_pathWorldNavigationPoints.Count == 0)
{
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
_currentGoalPositionWorld = currentTransformWorld.origin;
}
}
}