Mass reformatting.

main
Martin Felis 2023-11-18 22:32:57 +01:00
parent 2109c6b6ec
commit bfd5eef2f5
36 changed files with 863 additions and 1321 deletions

View File

@ -1,14 +1,12 @@
using Godot; using Godot;
public static class Globals public static class Globals {
{
public const float EpsPosition = 0.01f; public const float EpsPosition = 0.01f;
public const float EpsPositionSquared = EpsPosition * EpsPosition; public const float EpsPositionSquared = EpsPosition * EpsPosition;
public const float EpsRadians = 0.1f * Mathf.Pi / 180f; public const float EpsRadians = 0.1f * Mathf.Pi / 180f;
public const float EpsRadiansSquared = EpsRadians * EpsRadians; public const float EpsRadiansSquared = EpsRadians * EpsRadians;
public static float CalcPlaneAngle(Transform worldTransform) public static float CalcPlaneAngle(Transform worldTransform) {
{
return worldTransform.basis.x.SignedAngleTo(Vector3.Right.Rotated(Vector3.Up, Mathf.Pi * 0.5f), -Vector3.Up); return worldTransform.basis.x.SignedAngleTo(Vector3.Right.Rotated(Vector3.Up, Mathf.Pi * 0.5f), -Vector3.Up);
} }
} }

View File

@ -3,18 +3,24 @@ using System.Linq;
using Godot; using Godot;
using GodotComponentTest.utils; using GodotComponentTest.utils;
public class HexCell : IEquatable<HexCell> public class HexCell : IEquatable<HexCell> {
{ public override bool Equals(object obj) {
public override bool Equals(object obj) if (ReferenceEquals(null, obj)) {
{ return false;
if (ReferenceEquals(null, obj)) return false; }
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false; if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != GetType()) {
return false;
}
return Equals((HexCell)obj); return Equals((HexCell)obj);
} }
public override int GetHashCode() public override int GetHashCode() {
{
return _cubeCoords.GetHashCode(); return _cubeCoords.GetHashCode();
} }
@ -29,8 +35,7 @@ public class HexCell : IEquatable<HexCell>
public static readonly Vector3 DIR_SW = new(-1, 0, 1); public static readonly Vector3 DIR_SW = new(-1, 0, 1);
public static readonly Vector3 DIR_NW = new(-1, 1, 0); public static readonly Vector3 DIR_NW = new(-1, 1, 0);
public static readonly Vector3[] NeighborDirections = public static readonly Vector3[] NeighborDirections = {
{
DIR_N, DIR_N,
DIR_NW, DIR_NW,
DIR_SW, DIR_SW,
@ -39,13 +44,13 @@ public class HexCell : IEquatable<HexCell>
DIR_NE DIR_NE
}; };
private static readonly Vector2 CornerNW = new Vector2(-Width / 4, -Height / 2); private static readonly Vector2 CornerNW = new(-Width / 4, -Height / 2);
private static readonly Vector2 CornerNE = new Vector2(Width / 4, -Height / 2); private static readonly Vector2 CornerNE = new(Width / 4, -Height / 2);
private static readonly Vector2 CornerE = new Vector2(Width / 2, 0); private static readonly Vector2 CornerE = new(Width / 2, 0);
private static readonly Vector2 CornerSE = new Vector2(Width / 4, Height / 2); private static readonly Vector2 CornerSE = new(Width / 4, Height / 2);
private static readonly Vector2 CornerSW = new Vector2(-Width / 4, Height / 2); private static readonly Vector2 CornerSW = new(-Width / 4, Height / 2);
private static readonly Vector2 CornerW = new Vector2(-Width / 2, 0); private static readonly Vector2 CornerW = new(-Width / 2, 0);
private static readonly Vector2 PlaneNormalN = new Vector2(0, 1); private static readonly Vector2 PlaneNormalN = new(0, 1);
private static readonly Vector2 PlaneNormalNE = private static readonly Vector2 PlaneNormalNE =
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30))); -new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
@ -53,76 +58,61 @@ public class HexCell : IEquatable<HexCell>
private static readonly Vector2 PlaneNormalSE = private static readonly Vector2 PlaneNormalSE =
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30))); -new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
private static readonly Vector2 PlaneNormalS = new Vector2(0, -1); private static readonly Vector2 PlaneNormalS = new(0, -1);
private static readonly Vector2 PlaneNormalSW = private static readonly Vector2 PlaneNormalSW = new(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
private static readonly Vector2 PlaneNormalNW = private static readonly Vector2 PlaneNormalNW = new(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
private static readonly Plane2D[] BoundaryPlanes = private static readonly Plane2D[] BoundaryPlanes = {
{ new(CornerNE, PlaneNormalN),
new Plane2D(CornerNE, PlaneNormalN), new(CornerNW, PlaneNormalNW),
new Plane2D(CornerNW, PlaneNormalNW), new(CornerW, PlaneNormalSW),
new Plane2D(CornerW, PlaneNormalSW), new(CornerSW, PlaneNormalS),
new Plane2D(CornerSW, PlaneNormalS), new(CornerSE, PlaneNormalSE),
new Plane2D(CornerSE, PlaneNormalSE), new(CornerE, PlaneNormalNE)
new Plane2D(CornerE, PlaneNormalNE),
}; };
public HexCell() public HexCell() { }
{
}
public HexCell(float cubeX, float cubeY, float cubeZ) public HexCell(float cubeX, float cubeY, float cubeZ) {
{
CubeCoords = RoundCoords(new Vector3(cubeX, cubeY, cubeZ)); CubeCoords = RoundCoords(new Vector3(cubeX, cubeY, cubeZ));
} }
public virtual bool Equals(HexCell other) public virtual bool Equals(HexCell other) {
{ if (other == null) {
if (other == null)
{
return false; return false;
} }
return CubeCoords == other.CubeCoords; return CubeCoords == other.CubeCoords;
} }
public static bool operator ==(HexCell cellA, HexCell cellB) public static bool operator ==(HexCell cellA, HexCell cellB) {
{
return Equals(cellA, cellB); return Equals(cellA, cellB);
} }
public static bool operator !=(HexCell cellA, HexCell cellB) public static bool operator !=(HexCell cellA, HexCell cellB) {
{
return !(cellA == cellB); return !(cellA == cellB);
} }
public HexCell(Vector3 cubeCoords) public HexCell(Vector3 cubeCoords) {
{
CubeCoords = cubeCoords; CubeCoords = cubeCoords;
} }
public HexCell(float axialX, float axialY) public HexCell(float axialX, float axialY) {
{
AxialCoords = new Vector2(axialX, axialY); AxialCoords = new Vector2(axialX, axialY);
} }
public HexCell(Vector2 axialCoords) public HexCell(Vector2 axialCoords) {
{
AxialCoords = axialCoords; AxialCoords = axialCoords;
} }
public HexCell(HexCell other) public HexCell(HexCell other) {
{
CubeCoords = other.CubeCoords; CubeCoords = other.CubeCoords;
} }
public static HexCell FromOffsetCoords(Vector2 offsetCoords) public static HexCell FromOffsetCoords(Vector2 offsetCoords) {
{ HexCell result = new();
HexCell result = new HexCell();
result.OffsetCoords = offsetCoords; result.OffsetCoords = offsetCoords;
return result; return result;
@ -130,13 +120,10 @@ public class HexCell : IEquatable<HexCell>
private Vector3 _cubeCoords; private Vector3 _cubeCoords;
public Vector3 CubeCoords public Vector3 CubeCoords {
{ get => _cubeCoords;
get { return _cubeCoords; } set {
set if (Mathf.Abs(value.x + value.y + value.z) > 0.0001) {
{
if (Mathf.Abs(value.x + value.y + value.z) > 0.0001)
{
GD.Print("Warning: Invalid cube coordinates for hex (x + y + z != 0): ", value.ToString()); GD.Print("Warning: Invalid cube coordinates for hex (x + y + z != 0): ", value.ToString());
} }
@ -144,25 +131,21 @@ public class HexCell : IEquatable<HexCell>
} }
} }
public Vector2 AxialCoords public Vector2 AxialCoords {
{ get => new(CubeCoords.x, CubeCoords.y);
get => new Vector2(CubeCoords.x, CubeCoords.y);
set { CubeCoords = AxialToCubeCoords(value); } set => CubeCoords = AxialToCubeCoords(value);
} }
public Vector2 OffsetCoords public Vector2 OffsetCoords {
{ get {
get
{
int x = (int)CubeCoords.x; int x = (int)CubeCoords.x;
int y = (int)CubeCoords.y; int y = (int)CubeCoords.y;
int offY = y + (x - (x & 1)) / 2; int offY = y + (x - (x & 1)) / 2;
return new Vector2(x, offY); return new Vector2(x, offY);
} }
set set {
{
int x = (int)value.x; int x = (int)value.x;
int y = (int)value.y; int y = (int)value.y;
int cubeY = y - (x - (x & 1)) / 2; int cubeY = y - (x - (x & 1)) / 2;
@ -170,51 +153,40 @@ public class HexCell : IEquatable<HexCell>
} }
} }
public Vector3 AxialToCubeCoords(Vector2 axialCoords) public Vector3 AxialToCubeCoords(Vector2 axialCoords) {
{
return new Vector3(axialCoords.x, axialCoords.y, -axialCoords.x - axialCoords.y); return new Vector3(axialCoords.x, axialCoords.y, -axialCoords.x - axialCoords.y);
} }
public Vector3 RoundCoords(Vector2 coords) public Vector3 RoundCoords(Vector2 coords) {
{
Vector3 cubeCoords = AxialToCubeCoords(coords); Vector3 cubeCoords = AxialToCubeCoords(coords);
return RoundCoords(cubeCoords); return RoundCoords(cubeCoords);
} }
public Vector3 RoundCoords(Vector3 cubeCoords) public Vector3 RoundCoords(Vector3 cubeCoords) {
{ Vector3 rounded = new(
Vector3 rounded = new Vector3(
Mathf.Round(cubeCoords.x), Mathf.Round(cubeCoords.x),
Mathf.Round(cubeCoords.y), Mathf.Round(cubeCoords.y),
Mathf.Round(cubeCoords.z)); Mathf.Round(cubeCoords.z));
Vector3 diffs = (rounded - cubeCoords).Abs(); Vector3 diffs = (rounded - cubeCoords).Abs();
if (diffs.x > diffs.y && diffs.x > diffs.z) if (diffs.x > diffs.y && diffs.x > diffs.z) {
{
rounded.x = -rounded.y - rounded.z; rounded.x = -rounded.y - rounded.z;
} } else if (diffs.y > diffs.z) {
else if (diffs.y > diffs.z)
{
rounded.y = -rounded.x - rounded.z; rounded.y = -rounded.x - rounded.z;
} } else {
else
{
rounded.z = -rounded.x - rounded.y; rounded.z = -rounded.x - rounded.y;
} }
return rounded; return rounded;
} }
public HexCell GetAdjacent(Vector3 dir) public HexCell GetAdjacent(Vector3 dir) {
{ return new HexCell(CubeCoords + dir);
return new HexCell(this.CubeCoords + dir);
} }
public HexCell[] GetAllAdjacent() public HexCell[] GetAllAdjacent() {
{ return new[] {
return new[]
{
GetAdjacent(DIR_NE), GetAdjacent(DIR_NE),
GetAdjacent(DIR_SE), GetAdjacent(DIR_SE),
GetAdjacent(DIR_S), GetAdjacent(DIR_S),
@ -224,8 +196,7 @@ public class HexCell : IEquatable<HexCell>
}; };
} }
public int DistanceTo(HexCell target) public int DistanceTo(HexCell target) {
{
return (int)( return (int)(
Mathf.Abs(_cubeCoords.x - target.CubeCoords.x) Mathf.Abs(_cubeCoords.x - target.CubeCoords.x)
+ Mathf.Abs(_cubeCoords.y - target.CubeCoords.y) + Mathf.Abs(_cubeCoords.y - target.CubeCoords.y)
@ -233,16 +204,14 @@ public class HexCell : IEquatable<HexCell>
) / 2; ) / 2;
} }
public HexCell[] LineTo(HexCell target) public HexCell[] LineTo(HexCell target) {
{ HexCell nudgedTarget = new();
HexCell nudgedTarget = new HexCell();
nudgedTarget.CubeCoords = target.CubeCoords + new Vector3(1.0e-6f, 2.0e-6f, -3.0e-6f); nudgedTarget.CubeCoords = target.CubeCoords + new Vector3(1.0e-6f, 2.0e-6f, -3.0e-6f);
int steps = DistanceTo(target); int steps = DistanceTo(target);
HexCell[] path = new HexCell[steps + 1]; HexCell[] path = new HexCell[steps + 1];
foreach (int dist in Enumerable.Range(0, steps)) foreach (int dist in Enumerable.Range(0, steps)) {
{
path[dist] = new HexCell(); path[dist] = new HexCell();
path[dist].CubeCoords = CubeCoords.LinearInterpolate(nudgedTarget.CubeCoords, (float)dist / steps); path[dist].CubeCoords = CubeCoords.LinearInterpolate(nudgedTarget.CubeCoords, (float)dist / steps);
} }
@ -252,28 +221,23 @@ public class HexCell : IEquatable<HexCell>
return path; return path;
} }
public void QueryClosestCellBoundary(Vector2 pointLocal, Vector2 dir, out int neighbourIndex, out float distance) public void QueryClosestCellBoundary(Vector2 pointLocal, Vector2 dir, out int neighbourIndex, out float distance) {
{ distance = float.PositiveInfinity;
distance = Single.PositiveInfinity;
neighbourIndex = 0; neighbourIndex = 0;
foreach (int i in Enumerable.Range(0, 6)) foreach (int i in Enumerable.Range(0, 6)) {
{ if (BoundaryPlanes[i].Normal.Dot(dir) >= Plane2D.DistancePrecision) {
if (BoundaryPlanes[i].Normal.Dot(dir) >= Plane2D.DistancePrecision)
{
continue; continue;
} }
float planeDistance = BoundaryPlanes[i].DistanceToLineSegment(pointLocal, dir); float planeDistance = BoundaryPlanes[i].DistanceToLineSegment(pointLocal, dir);
if (planeDistance > Single.NegativeInfinity && planeDistance < distance) if (planeDistance > float.NegativeInfinity && planeDistance < distance) {
{
distance = planeDistance; distance = planeDistance;
neighbourIndex = i; neighbourIndex = i;
} }
} }
} }
public HexCell NextCellAlongLine(Vector2 pointLocal, Vector2 dir) public HexCell NextCellAlongLine(Vector2 pointLocal, Vector2 dir) {
{
int planeIndex; int planeIndex;
QueryClosestCellBoundary(pointLocal, dir, out planeIndex, out _); QueryClosestCellBoundary(pointLocal, dir, out planeIndex, out _);

View File

@ -4,8 +4,7 @@ using Godot;
using Priority_Queue; using Priority_Queue;
using AxialCoordDirectionPair = System.Tuple<Godot.Vector2, Godot.Vector3>; using AxialCoordDirectionPair = System.Tuple<Godot.Vector2, Godot.Vector3>;
public class HexGrid : Resource public class HexGrid : Resource {
{
private readonly Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2); private readonly Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2);
private Rect2 _boundsAxialCoords = new(-Vector2.Inf, Vector2.Inf); private Rect2 _boundsAxialCoords = new(-Vector2.Inf, Vector2.Inf);
private Rect2 _boundsOffsetCoords = new(-Vector2.Inf, Vector2.Inf); private Rect2 _boundsOffsetCoords = new(-Vector2.Inf, Vector2.Inf);
@ -24,18 +23,15 @@ public class HexGrid : Resource
public float PathCostDefault = 1; public float PathCostDefault = 1;
public HexGrid() public HexGrid() {
{
HexScale = new Vector2(1, 1); HexScale = new Vector2(1, 1);
} }
public Vector2 HexSize => _hexSize; public Vector2 HexSize => _hexSize;
public Vector2 HexScale public Vector2 HexScale {
{
get => _hexScale; get => _hexScale;
set set {
{
_hexScale = value; _hexScale = value;
_hexSize = _baseHexSize * _hexScale; _hexSize = _baseHexSize * _hexScale;
_hexTransform = new Transform2D( _hexTransform = new Transform2D(
@ -48,135 +44,129 @@ public class HexGrid : Resource
} }
} }
public Vector2 GetHexCenter(HexCell cell) public Vector2 GetHexCenter(HexCell cell) {
{
return _hexTransform * cell.AxialCoords; return _hexTransform * cell.AxialCoords;
} }
public Vector2 GetHexCenterFromOffset(Vector2 offsetCoord) public Vector2 GetHexCenterFromOffset(Vector2 offsetCoord) {
{ HexCell cell = new HexCell();
var cell = new HexCell();
cell.OffsetCoords = offsetCoord; cell.OffsetCoords = offsetCoord;
return GetHexCenter(cell); return GetHexCenter(cell);
} }
public Vector3 GetHexCenterVec3FromOffset(Vector2 offsetCoord) public Vector3 GetHexCenterVec3FromOffset(Vector2 offsetCoord) {
{ HexCell cell = new HexCell();
var cell = new HexCell();
cell.OffsetCoords = offsetCoord; cell.OffsetCoords = offsetCoord;
var hexCenter = GetHexCenter(cell); Vector2 hexCenter = GetHexCenter(cell);
return new Vector3(hexCenter.x, 0, hexCenter.y); return new Vector3(hexCenter.x, 0, hexCenter.y);
} }
public HexCell GetHexAtOffset(Vector2 offsetCoord) public HexCell GetHexAtOffset(Vector2 offsetCoord) {
{ HexCell cell = new HexCell();
var cell = new HexCell();
cell.OffsetCoords = offsetCoord; cell.OffsetCoords = offsetCoord;
return cell; return cell;
} }
public HexCell GetHexAt(Vector2 planeCoord) public HexCell GetHexAt(Vector2 planeCoord) {
{ HexCell result = new HexCell(_hexTransformInv * planeCoord);
var result = new HexCell(_hexTransformInv * planeCoord);
return result; return result;
} }
public void SetBounds(Vector2 minAxial, Vector2 maxAxial) public void SetBounds(Vector2 minAxial, Vector2 maxAxial) {
{
SetBounds(new HexCell(minAxial), new HexCell(maxAxial)); SetBounds(new HexCell(minAxial), new HexCell(maxAxial));
} }
public void SetBounds(HexCell minCell, HexCell maxCell) public void SetBounds(HexCell minCell, HexCell maxCell) {
{
_minCoords = minCell; _minCoords = minCell;
_maxCoords = maxCell; _maxCoords = maxCell;
_boundsAxialCoords = new Rect2(_minCoords.AxialCoords, _boundsAxialCoords = new Rect2(_minCoords.AxialCoords,
_maxCoords.AxialCoords - _minCoords.AxialCoords + Vector2.One); _maxCoords.AxialCoords - _minCoords.AxialCoords + Vector2.One);
} }
public void SetBounds(HexCell center, int size) public void SetBounds(HexCell center, int size) {
{ Vector2 centerOffset = center.OffsetCoords;
var centerOffset = center.OffsetCoords;
SetBounds(GetHexAtOffset(centerOffset - Vector2.One * size / 2), SetBounds(GetHexAtOffset(centerOffset - Vector2.One * size / 2),
GetHexAtOffset(centerOffset + Vector2.One * size / 2)); GetHexAtOffset(centerOffset + Vector2.One * size / 2));
} }
public void SetBoundsOffset(HexCell cellSouthEast, Vector2 size) public void SetBoundsOffset(HexCell cellSouthEast, Vector2 size) {
{
_boundsOffsetCoords = new Rect2(cellSouthEast.OffsetCoords, size); _boundsOffsetCoords = new Rect2(cellSouthEast.OffsetCoords, size);
_boundsAxialCoords = new Rect2(-Vector2.Inf, Vector2.Inf); _boundsAxialCoords = new Rect2(-Vector2.Inf, Vector2.Inf);
} }
public void AddObstacle(Vector2 axialCoords, float cost = 0) public void AddObstacle(Vector2 axialCoords, float cost = 0) {
{
AddObstacle(new HexCell(axialCoords), cost); AddObstacle(new HexCell(axialCoords), cost);
} }
public void AddObstacle(HexCell cell, float cost = 0) public void AddObstacle(HexCell cell, float cost = 0) {
{
Obstacles[cell.AxialCoords] = cost; Obstacles[cell.AxialCoords] = cost;
} }
public void RemoveObstacle(HexCell cell) public void RemoveObstacle(HexCell cell) {
{
Obstacles.Remove(cell.AxialCoords); Obstacles.Remove(cell.AxialCoords);
} }
public void AddBarrier(Vector2 axialCoords, Vector3 directionCube, float cost = 0) public void AddBarrier(Vector2 axialCoords, Vector3 directionCube, float cost = 0) {
{
AddBarrier(new HexCell(axialCoords), directionCube, cost); AddBarrier(new HexCell(axialCoords), directionCube, cost);
} }
public void AddBarrier(HexCell cell, Vector3 directionCube, float cost = 0) public void AddBarrier(HexCell cell, Vector3 directionCube, float cost = 0) {
{
Barriers.Add((cell.AxialCoords, directionCube), cost); Barriers.Add((cell.AxialCoords, directionCube), cost);
} }
public void RemoveBarrier(HexCell cell, Vector3 directionCube) public void RemoveBarrier(HexCell cell, Vector3 directionCube) {
{ if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) {
if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) Barriers.Remove((cell.AxialCoords, directionCube)); Barriers.Remove((cell.AxialCoords, directionCube));
}
} }
public float GetHexCost(HexCell cell) public float GetHexCost(HexCell cell) {
{
return GetHexCost(cell.AxialCoords); return GetHexCost(cell.AxialCoords);
} }
public float GetHexCost(Vector2 axialCoords) public float GetHexCost(Vector2 axialCoords) {
{ if (!_boundsAxialCoords.HasPoint(axialCoords)) {
if (!_boundsAxialCoords.HasPoint(axialCoords)) return 0; return 0;
}
if (!_boundsOffsetCoords.HasPoint(new HexCell(axialCoords).OffsetCoords)) return 0; if (!_boundsOffsetCoords.HasPoint(new HexCell(axialCoords).OffsetCoords)) {
return 0;
}
float value; float value;
return Obstacles.TryGetValue(axialCoords, out value) ? value : PathCostDefault; return Obstacles.TryGetValue(axialCoords, out value) ? value : PathCostDefault;
} }
public float GetMoveCost(Vector2 axialCoords, Vector3 directionCube) public float GetMoveCost(Vector2 axialCoords, Vector3 directionCube) {
{ HexCell startCell = new HexCell(axialCoords);
var startCell = new HexCell(axialCoords); HexCell targetCell = new HexCell(startCell.CubeCoords + directionCube);
var targetCell = new HexCell(startCell.CubeCoords + directionCube);
var cost = GetHexCost(axialCoords); float cost = GetHexCost(axialCoords);
if (cost == 0) return 0; if (cost == 0) {
return 0;
}
cost = GetHexCost(targetCell.AxialCoords); cost = GetHexCost(targetCell.AxialCoords);
if (cost == 0) return 0; if (cost == 0) {
return 0;
}
float barrierCost; float barrierCost;
if (Barriers.ContainsKey((axialCoords, directionCube))) if (Barriers.ContainsKey((axialCoords, directionCube))) {
{
barrierCost = Barriers[(axialCoords, directionCube)]; barrierCost = Barriers[(axialCoords, directionCube)];
if (barrierCost == 0) return 0; if (barrierCost == 0) {
return 0;
}
cost += barrierCost; cost += barrierCost;
} }
if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube))) if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube))) {
{
barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)]; barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)];
if (barrierCost == 0) return 0; if (barrierCost == 0) {
return 0;
}
cost += barrierCost; cost += barrierCost;
} }
@ -185,32 +175,29 @@ public class HexGrid : Resource
} }
public HexCell GetClosestWalkableCell(HexCell fromCell, HexCell toCell) public HexCell GetClosestWalkableCell(HexCell fromCell, HexCell toCell) {
{ if (GetHexCost(toCell) == 0) {
if (GetHexCost(toCell) == 0) HexCell[] line = fromCell.LineTo(toCell);
{
var line = fromCell.LineTo(toCell);
foreach (var i in Enumerable.Range(1, line.Length)) foreach (int i in Enumerable.Range(1, line.Length)) {
if (GetHexCost(line[i]) == 0) if (GetHexCost(line[i]) == 0) {
{
toCell = line[i - 1]; toCell = line[i - 1];
break; break;
} }
}
} }
return toCell; return toCell;
} }
public List<HexCell> FindPath(HexCell startHex, HexCell goalHex) public List<HexCell> FindPath(HexCell startHex, HexCell goalHex) {
{ Vector2 goalAxialCoords = goalHex.AxialCoords;
var goalAxialCoords = goalHex.AxialCoords;
var frontier = new SimplePriorityQueue<Vector2, float>(); SimplePriorityQueue<Vector2, float> frontier = new SimplePriorityQueue<Vector2, float>();
frontier.Enqueue(startHex.AxialCoords, 0); frontier.Enqueue(startHex.AxialCoords, 0);
var cameFrom = Dictionary<Vector2, Vector2> cameFrom =
new Dictionary<Vector2, Vector2>(); new Dictionary<Vector2, Vector2>();
var costSoFar = Dictionary<Vector2, float> costSoFar =
new Dictionary<Vector2, float>(); new Dictionary<Vector2, float>();
cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords); cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords);
@ -218,34 +205,34 @@ public class HexGrid : Resource
FindPathCheckedCellCount = 0; FindPathCheckedCellCount = 0;
while (frontier.Any()) while (frontier.Any()) {
{
FindPathCheckedCellCount++; FindPathCheckedCellCount++;
var currentHex = new HexCell(frontier.Dequeue()); HexCell currentHex = new HexCell(frontier.Dequeue());
var currentAxial = currentHex.AxialCoords; Vector2 currentAxial = currentHex.AxialCoords;
if (currentHex == goalHex) break; if (currentHex == goalHex) {
break;
}
foreach (var nextHex in currentHex.GetAllAdjacent()) foreach (HexCell nextHex in currentHex.GetAllAdjacent()) {
{ Vector2 nextAxial = nextHex.AxialCoords;
var nextAxial = nextHex.AxialCoords; float nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords);
var nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords);
if (nextHex == goalHex && GetHexCost(nextAxial) == 0) if (nextHex == goalHex && GetHexCost(nextAxial) == 0) {
{
// Goal ist an obstacle // Goal ist an obstacle
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
frontier.Clear(); frontier.Clear();
break; break;
} }
if (nextCost == 0) continue; if (nextCost == 0) {
continue;
}
nextCost += costSoFar[currentHex.AxialCoords]; nextCost += costSoFar[currentHex.AxialCoords];
if (!costSoFar.ContainsKey(nextHex.AxialCoords) || nextCost < costSoFar[nextHex.AxialCoords]) if (!costSoFar.ContainsKey(nextHex.AxialCoords) || nextCost < costSoFar[nextHex.AxialCoords]) {
{
costSoFar[nextHex.AxialCoords] = nextCost; costSoFar[nextHex.AxialCoords] = nextCost;
var priority = nextCost + nextHex.DistanceTo(goalHex); float priority = nextCost + nextHex.DistanceTo(goalHex);
frontier.Enqueue(nextHex.AxialCoords, priority); frontier.Enqueue(nextHex.AxialCoords, priority);
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
@ -255,18 +242,18 @@ public class HexGrid : Resource
// GD.Print("Checked Cell Count: " + FindPathCheckedCellCount); // GD.Print("Checked Cell Count: " + FindPathCheckedCellCount);
var result = new List<HexCell>(); List<HexCell> result = new List<HexCell>();
if (!cameFrom.ContainsKey(goalHex.AxialCoords)) if (!cameFrom.ContainsKey(goalHex.AxialCoords)) {
{
GD.Print("Failed to find path from " + startHex + " to " + goalHex); GD.Print("Failed to find path from " + startHex + " to " + goalHex);
return result; return result;
} }
if (GetHexCost(goalAxialCoords) != 0) result.Add(goalHex); if (GetHexCost(goalAxialCoords) != 0) {
result.Add(goalHex);
}
var pathHex = goalHex; HexCell pathHex = goalHex;
while (pathHex != startHex) while (pathHex != startHex) {
{
pathHex = new HexCell(cameFrom[pathHex.AxialCoords]); pathHex = new HexCell(cameFrom[pathHex.AxialCoords]);
result.Insert(0, pathHex); result.Insert(0, pathHex);
} }
@ -275,22 +262,20 @@ public class HexGrid : Resource
} }
public List<HexCell> GetCellsForLine(Vector2 fromPlane, Vector2 toPlane) public List<HexCell> GetCellsForLine(Vector2 fromPlane, Vector2 toPlane) {
{ List<HexCell> result = new List<HexCell>();
var result = new List<HexCell>();
var distance = (toPlane - fromPlane).Length(); float distance = (toPlane - fromPlane).Length();
var direction = (toPlane - fromPlane) / distance; Vector2 direction = (toPlane - fromPlane) / distance;
var currentPointPlane = fromPlane; Vector2 currentPointPlane = fromPlane;
var currentCell = GetHexAt(currentPointPlane); HexCell currentCell = GetHexAt(currentPointPlane);
float currentDistance = 0; float currentDistance = 0;
do do {
{
result.Add(currentCell); result.Add(currentCell);
GetHexCenter(currentCell); GetHexCenter(currentCell);
var currentPointLocal = currentPointPlane - GetHexCenter(currentCell); Vector2 currentPointLocal = currentPointPlane - GetHexCenter(currentCell);
int neighbourIndex; int neighbourIndex;
float boundaryPlaneDistance; float boundaryPlaneDistance;

View File

@ -2,24 +2,21 @@ using System;
using Godot; using Godot;
using GoDotLog; using GoDotLog;
public class ClickableComponent : Spatial public class ClickableComponent : Spatial {
{
[Export] public string ClickName = "ClickName"; [Export] public string ClickName = "ClickName";
[Signal] [Signal]
delegate void Clicked(); delegate void Clicked();
public bool IsMouseOver = false; public bool IsMouseOver = false;
// private members // private members
private CollisionObject _collisionObject; private CollisionObject _collisionObject;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_collisionObject = (CollisionObject)FindNode("Area", false); _collisionObject = (CollisionObject)FindNode("Area", false);
if (_collisionObject == null) if (_collisionObject == null) {
{
GD.PrintErr("Error: could not find Area for Clickable Component!"); GD.PrintErr("Error: could not find Area for Clickable Component!");
return; return;
} }
@ -30,26 +27,21 @@ public class ClickableComponent : Spatial
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{ if (IsMouseOver && inputEvent is InputEventMouseButton) {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
{
GD.Print("Clicked on Clickable Component!"); GD.Print("Clicked on Clickable Component!");
EmitSignal("Clicked", this); EmitSignal("Clicked", this);
} }
} }
} }
public void OnAreaMouseEntered() public void OnAreaMouseEntered() {
{
IsMouseOver = true; IsMouseOver = true;
} }
public void OnAreaMouseExited() public void OnAreaMouseExited() {
{
IsMouseOver = false; IsMouseOver = false;
} }
} }

View File

@ -1,6 +1,3 @@
using Godot; using Godot;
public class Component : Node public class Component : Node { }
{
}

View File

@ -2,8 +2,7 @@ using Godot;
/// <summary> /// <summary>
/// </summary> /// </summary>
public class GroundMotionComponent : Component public class GroundMotionComponent : Component {
{
public float Accel = 50; public float Accel = 50;
public float Damping = 0.2f; public float Damping = 0.2f;
public Vector3 DirectionToTarget = Vector3.Zero; public Vector3 DirectionToTarget = Vector3.Zero;
@ -14,29 +13,21 @@ public class GroundMotionComponent : Component
public float TargetAngle; public float TargetAngle;
public float TargetDeltaAngle; public float TargetDeltaAngle;
private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, float targetAngle) private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, float targetAngle) {
{
float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle); float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle);
if (deltaAngleAbsolute > 0.001) if (deltaAngleAbsolute > 0.001) {
{ if (RotationSpeedRadPerSecond * delta >= deltaAngleAbsolute) {
if (RotationSpeedRadPerSecond * delta >= deltaAngleAbsolute)
{
entity.PlaneAngle = TargetAngle; entity.PlaneAngle = TargetAngle;
TargetDeltaAngle = 0; TargetDeltaAngle = 0;
} } else {
else
{
entity.PlaneAngle += Mathf.Sign(TargetDeltaAngle) * RotationSpeedRadPerSecond * delta; entity.PlaneAngle += Mathf.Sign(TargetDeltaAngle) * RotationSpeedRadPerSecond * delta;
} }
} } else {
else
{
TargetDeltaAngle = 0; TargetDeltaAngle = 0;
} }
} }
private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition) private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition) {
{
Vector2 planeTargetDirection = new(DirectionToTarget.x, DirectionToTarget.z); Vector2 planeTargetDirection = new(DirectionToTarget.x, DirectionToTarget.z);
Vector2 planeVelocity = new(entity.Velocity.x, entity.Velocity.z); Vector2 planeVelocity = new(entity.Velocity.x, entity.Velocity.z);
@ -44,29 +35,25 @@ public class GroundMotionComponent : Component
planeVelocity -= planeVelocity * Damping; planeVelocity -= planeVelocity * Damping;
// GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized()); // GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
if (DistanceToTarget < 0.01) if (DistanceToTarget < 0.01) {
{
planeVelocity = Vector2.Zero; planeVelocity = Vector2.Zero;
} } else if (TargetDeltaAngle == 0.0) {
else if (TargetDeltaAngle == 0.0)
{
planeVelocity = planeVelocity.Length() * planeTargetDirection + planeTargetDirection * Accel * delta; planeVelocity = planeVelocity.Length() * planeTargetDirection + planeTargetDirection * Accel * delta;
// GD.Print(" accel: speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized()); // GD.Print(" accel: speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
float projectedStep = planeTargetDirection.Dot(planeVelocity * delta); float projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
// GD.Print(" Projected step: " + projectedStep + " Speed: " + planeVelocity.Length() + " delta: " + delta); // GD.Print(" Projected step: " + projectedStep + " Speed: " + planeVelocity.Length() + " delta: " + delta);
if (projectedStep > DistanceToTarget) if (projectedStep > DistanceToTarget) {
{
planeVelocity *= DistanceToTarget / projectedStep; planeVelocity *= DistanceToTarget / projectedStep;
projectedStep = planeTargetDirection.Dot(planeVelocity * delta); projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
// GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep); // GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep);
} }
float planeSpeed = planeVelocity.Length(); float planeSpeed = planeVelocity.Length();
if (planeSpeed > MaxSpeed) planeVelocity *= MaxSpeed / planeSpeed; if (planeSpeed > MaxSpeed) {
} planeVelocity *= MaxSpeed / planeSpeed;
else }
{ } else {
planeVelocity = Vector2.Zero; planeVelocity = Vector2.Zero;
} }
@ -74,22 +61,19 @@ public class GroundMotionComponent : Component
} }
private void CalcVerticalVelocity(float delta, Entity entity, TileWorld tileWorld) private void CalcVerticalVelocity(float delta, Entity entity, TileWorld tileWorld) {
{
Vector3 entityVelocity = entity.Velocity; Vector3 entityVelocity = entity.Velocity;
Vector2 currentTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin); Vector2 currentTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin);
Vector2 nextTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin + entityVelocity * delta); Vector2 nextTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin + entityVelocity * delta);
if (nextTile != currentTile) if (nextTile != currentTile) {
{
float currentHeight = tileWorld.GetHeightAtOffset(currentTile); float currentHeight = tileWorld.GetHeightAtOffset(currentTile);
float nextHeight = tileWorld.GetHeightAtOffset(nextTile); float nextHeight = tileWorld.GetHeightAtOffset(nextTile);
bool isOnFloor = entity.IsOnFloor(); bool isOnFloor = entity.IsOnFloor();
if (nextHeight - entity.GlobalTransform.origin.y > 0.1) if (nextHeight - entity.GlobalTransform.origin.y > 0.1) {
{
GD.Print("Jump!"); GD.Print("Jump!");
entityVelocity.y = 10; entityVelocity.y = 10;
@ -105,32 +89,26 @@ public class GroundMotionComponent : Component
} }
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, float targetAngle, public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, float targetAngle,
World world) World world) {
{
DirectionToTarget = targetPosition - entity.GlobalTranslation; DirectionToTarget = targetPosition - entity.GlobalTranslation;
DirectionToTarget.y = 0; DirectionToTarget.y = 0;
DistanceToTarget = DirectionToTarget.Length(); DistanceToTarget = DirectionToTarget.Length();
if (DistanceToTarget >= Globals.EpsPosition) if (DistanceToTarget >= Globals.EpsPosition) {
{
DirectionToTarget = DirectionToTarget.Normalized(); DirectionToTarget = DirectionToTarget.Normalized();
TargetAngle = Vector3.Right.SignedAngleTo(DirectionToTarget, Vector3.Up); TargetAngle = Vector3.Right.SignedAngleTo(DirectionToTarget, Vector3.Up);
TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget); TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget);
} } else {
else
{
DirectionToTarget = Vector3.Right; DirectionToTarget = Vector3.Right;
entity.GlobalTranslation = targetPosition; entity.GlobalTranslation = targetPosition;
entity.Velocity = new Vector3(0, entity.Velocity.y, 0); entity.Velocity = new Vector3(0, entity.Velocity.y, 0);
if (entity.PlaneAngle != targetAngle) if (entity.PlaneAngle != targetAngle) {
{
Vector3 directionToTarget = Vector3.Right.Rotated(Vector3.Up, targetAngle); Vector3 directionToTarget = Vector3.Right.Rotated(Vector3.Up, targetAngle);
TargetAngle = targetAngle; TargetAngle = targetAngle;
TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(directionToTarget); TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(directionToTarget);
if (Mathf.Abs(TargetDeltaAngle) < Globals.EpsRadians) if (Mathf.Abs(TargetDeltaAngle) < Globals.EpsRadians) {
{
TargetAngle = entity.PlaneAngle; TargetAngle = entity.PlaneAngle;
TargetDeltaAngle = 0; TargetDeltaAngle = 0;

View File

@ -2,20 +2,18 @@ using Godot;
namespace GodotComponentTest.components; namespace GodotComponentTest.components;
public class InteractionComponent: Component public class InteractionComponent : Component {
{
[Signal] [Signal]
delegate void InteractionStart(Spatial owningEntity, Spatial targetEntity); private delegate void InteractionStart(Spatial owningEntity, Spatial targetEntity);
[Signal]
delegate void InteractionEnd(Spatial owningEntity, Spatial targetEntity);
public void EndInteraction() [Signal]
{ private delegate void InteractionEnd(Spatial owningEntity, Spatial targetEntity);
public void EndInteraction() {
hasStopped = true; hasStopped = true;
} }
public bool hasStopped = false; public bool hasStopped;
public Spatial OwningEntity; public Spatial OwningEntity;
public Spatial TargetEntity; public Spatial TargetEntity;
} }

View File

@ -1,15 +1,13 @@
using Godot; using Godot;
using System;
public class MovableComponent : Component public class MovableComponent : Component {
{
public Vector3 targetPosition = Vector3.Zero; public Vector3 targetPosition = Vector3.Zero;
public Vector3 currentPosition = Vector3.Zero; public Vector3 currentPosition = Vector3.Zero;
public Vector3 currentVelocity = Vector3.Zero; public Vector3 currentVelocity = Vector3.Zero;
public float targetAngle = 0; public float targetAngle;
public float currentAngle = 0; public float currentAngle;
public float currentAngularVelocity = 0; public float currentAngularVelocity;
[Export] public float maxSpeed = 3; [Export] public float maxSpeed = 3;
@ -17,62 +15,55 @@ public class MovableComponent : Component
private SpringDamper _angleSpringDamper; private SpringDamper _angleSpringDamper;
[Signal] [Signal]
delegate void PositionUpdated(Vector3 newPosition); private delegate void PositionUpdated(Vector3 newPosition);
[Signal] [Signal]
delegate void OrientationUpdated(float newAngle); private delegate void OrientationUpdated(float newAngle);
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_positionSpringDamper = new SpringDamper(4, 0.99f, 0.5f); _positionSpringDamper = new SpringDamper(4, 0.99f, 0.5f);
_angleSpringDamper = new SpringDamper(4, 0.99f, 0.5f); _angleSpringDamper = new SpringDamper(4, 0.99f, 0.5f);
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(float delta) public override void _Process(float delta) {
{
Vector3 targetError = targetPosition - currentPosition; Vector3 targetError = targetPosition - currentPosition;
if (targetError.LengthSquared() > 0.01) if (targetError.LengthSquared() > 0.01) {
{
Vector3 targetDir = targetError.Normalized(); Vector3 targetDir = targetError.Normalized();
targetAngle = new Vector3(0, 0, 1).SignedAngleTo(targetDir, Vector3.Up); targetAngle = new Vector3(0, 0, 1).SignedAngleTo(targetDir, Vector3.Up);
if (targetAngle - currentAngle > Mathf.Pi) if (targetAngle - currentAngle > Mathf.Pi) {
{
currentAngle += 2 * Mathf.Pi; currentAngle += 2 * Mathf.Pi;
} } else if (targetAngle - currentAngle < -Mathf.Pi) {
else if (targetAngle - currentAngle < -Mathf.Pi)
{
currentAngle -= 2 * Mathf.Pi; currentAngle -= 2 * Mathf.Pi;
} }
} }
if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01) if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01) {
{ (float, float) springDamperResult =
var springDamperResult =
_angleSpringDamper.Calc(currentAngle, currentAngularVelocity, targetAngle, delta); _angleSpringDamper.Calc(currentAngle, currentAngularVelocity, targetAngle, delta);
currentAngle = springDamperResult.Item1; currentAngle = springDamperResult.Item1;
currentAngularVelocity = springDamperResult.Item2; currentAngularVelocity = springDamperResult.Item2;
EmitSignal("OrientationUpdated", this.currentAngle); EmitSignal("OrientationUpdated", currentAngle);
} }
if ((Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3) if (Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3
&& (targetPosition - currentPosition).LengthSquared() > 0.01) && (targetPosition - currentPosition).LengthSquared() > 0.01) {
{ (Vector3, Vector3) springDamperResult =
var springDamperResult =
_positionSpringDamper.CalcClampedSpeedXZ(currentPosition, currentVelocity, targetPosition, delta, _positionSpringDamper.CalcClampedSpeedXZ(currentPosition, currentVelocity, targetPosition, delta,
maxSpeed); maxSpeed);
currentPosition = springDamperResult.Item1; currentPosition = springDamperResult.Item1;
currentVelocity = springDamperResult.Item2; currentVelocity = springDamperResult.Item2;
} }
currentVelocity.y = currentVelocity.y - 9.81f * delta; currentVelocity.y = currentVelocity.y - 9.81f * delta;
currentPosition.y = currentPosition.y + currentVelocity.y * delta; currentPosition.y = currentPosition.y + currentVelocity.y * delta;
EmitSignal("PositionUpdated", currentPosition); EmitSignal("PositionUpdated", currentPosition);
} }
} }

View File

@ -1,42 +1,39 @@
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
public class TaskQueueComponent : Component public class TaskQueueComponent : Component {
{
[Signal] [Signal]
private delegate void StartInteraction(Entity entity, Entity targetEntity); private delegate void StartInteraction(Entity entity, Entity targetEntity);
public Queue<Task> Queue; public Queue<Task> Queue;
public TaskQueueComponent() public TaskQueueComponent() {
{
Queue = new Queue<Task>(); Queue = new Queue<Task>();
Reset(); Reset();
} }
public void Reset() public void Reset() {
{
Queue.Clear(); Queue.Clear();
} }
public void Process(Entity entity, float delta) public void Process(Entity entity, float delta) {
{ if (Queue.Count == 0) {
if (Queue.Count == 0) return; return;
}
do do {
{ Task currentTask = Queue.Peek();
var currentTask = Queue.Peek();
if (currentTask.PerformTask(entity, this, delta)) if (currentTask.PerformTask(entity, this, delta)) {
Queue.Dequeue(); Queue.Dequeue();
else } else {
break; break;
}
} while (Queue.Count > 0); } while (Queue.Count > 0);
} }
public abstract class Task : Object public abstract class Task : Object {
{
/// <summary> /// <summary>
/// Executes a Task on the specified entity. /// Executes a Task on the specified entity.
/// </summary> /// </summary>
@ -47,33 +44,27 @@ public class TaskQueueComponent : Component
/// <summary> /// <summary>
/// Makes an entity move towards a target (translation and or orientation). /// Makes an entity move towards a target (translation and or orientation).
/// </summary> /// </summary>
public class NavigationTask : Task public class NavigationTask : Task {
{
public NavigationPoint NavigationPoint; public NavigationPoint NavigationPoint;
public bool PlanningComplete = false; public bool PlanningComplete = false;
public NavigationTask(NavigationPoint navigationPoint) public NavigationTask(NavigationPoint navigationPoint) {
{
NavigationPoint = navigationPoint; NavigationPoint = navigationPoint;
} }
public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) {
{
return NavigationPoint.IsReached(entity.GlobalTransform); return NavigationPoint.IsReached(entity.GlobalTransform);
} }
} }
public class InteractionTask : Task public class InteractionTask : Task {
{
public Entity TargetEntity; public Entity TargetEntity;
public InteractionTask(Entity entity) public InteractionTask(Entity entity) {
{
TargetEntity = entity; TargetEntity = entity;
} }
public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) {
{
taskQueueComponent.EmitSignal("StartInteraction", entity, TargetEntity); taskQueueComponent.EmitSignal("StartInteraction", entity, TargetEntity);
return true; return true;

View File

@ -1,18 +1,15 @@
using Godot; using Godot;
public class WorldInfoComponent : Component public class WorldInfoComponent : Component {
{
[Export] public NodePath WorldPath; [Export] public NodePath WorldPath;
public World World; public World World;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
World = GetNode<World>(WorldPath); World = GetNode<World>(WorldPath);
} }
public void SetWorld(World world) public void SetWorld(World world) {
{
World = world; World = world;
} }
} }

View File

@ -1,11 +1,6 @@
using Godot; using Godot;
using System;
public class Axe : StaticBody public class Axe : StaticBody {
{
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() { }
{ }
}
}

View File

@ -3,13 +3,11 @@ using Godot;
using GodotComponentTest.components; using GodotComponentTest.components;
using GodotComponentTest.entities; using GodotComponentTest.entities;
public class Chest : Entity, IInteractionInterface public class Chest : Entity, IInteractionInterface {
{
// resources // resources
private readonly PackedScene _goldBarScene = GD.Load<PackedScene>("res://entities/GoldBar.tscn"); private readonly PackedScene _goldBarScene = GD.Load<PackedScene>("res://entities/GoldBar.tscn");
public enum LidState public enum LidState {
{
Closed, Closed,
Open Open
} }
@ -29,8 +27,7 @@ public class Chest : Entity, IInteractionInterface
private delegate void ChestOpened(Entity entity); private delegate void ChestOpened(Entity entity);
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_mesh = GetNode<MeshInstance>("Geometry/Skeleton/Chest"); _mesh = GetNode<MeshInstance>("Geometry/Skeleton/Chest");
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer"); _animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
@ -42,64 +39,51 @@ public class Chest : Entity, IInteractionInterface
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{ if (IsMouseOver && inputEvent is InputEventMouseButton) {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
{
EmitSignal("EntityClicked", this); EmitSignal("EntityClicked", this);
} }
} }
} }
public void OnAreaMouseEntered() public void OnAreaMouseEntered() {
{
IsMouseOver = true; IsMouseOver = true;
SpatialMaterial overrideMaterial = new SpatialMaterial(); SpatialMaterial overrideMaterial = new();
overrideMaterial.AlbedoColor = new Color(1, 0, 0); overrideMaterial.AlbedoColor = new Color(1, 0, 0);
_mesh.MaterialOverride = overrideMaterial; _mesh.MaterialOverride = overrideMaterial;
} }
public void OnAreaMouseExited() public void OnAreaMouseExited() {
{
IsMouseOver = false; IsMouseOver = false;
_mesh.MaterialOverride = null; _mesh.MaterialOverride = null;
} }
public void OnInteractionStart() public void OnInteractionStart() {
{
_animationPlayer.Stop(); _animationPlayer.Stop();
if (State == LidState.Closed) if (State == LidState.Closed) {
{
State = LidState.Open; State = LidState.Open;
_animationPlayer.Play("ChestOpen"); _animationPlayer.Play("ChestOpen");
} } else {
else
{
_animationPlayer.PlayBackwards("ChestOpen"); _animationPlayer.PlayBackwards("ChestOpen");
State = LidState.Closed; State = LidState.Closed;
} }
} }
public void OnInteractionEnd() public void OnInteractionEnd() { }
{
}
public void OnChestOpened() public void OnChestOpened() {
{
GD.Print("Chest now opened!"); GD.Print("Chest now opened!");
foreach (int i in Enumerable.Range(0, 10)) foreach (int i in Enumerable.Range(0, 10)) {
{
GoldBar bar = (GoldBar)_goldBarScene.Instance(); GoldBar bar = (GoldBar)_goldBarScene.Instance();
bar.Transform = new Transform(Transform.basis.Rotated(Vector3.Up, GD.Randf() * 2 * Mathf.Pi), Transform.origin + Vector3.Up * 0.8f); bar.Transform = new Transform(Transform.basis.Rotated(Vector3.Up, GD.Randf() * 2 * Mathf.Pi),
Transform.origin + Vector3.Up * 0.8f);
bar.velocity = new Vector3( bar.velocity = new Vector3(
(GD.Randf() * 2f - 1f) * 2, (GD.Randf() * 2f - 1f) * 2,
5 + GD.Randf() * 0.3f, 5 + GD.Randf() * 0.3f,
@ -108,8 +92,7 @@ public class Chest : Entity, IInteractionInterface
GetParent().AddChild(bar); GetParent().AddChild(bar);
} }
if (InteractionComponent != null) if (InteractionComponent != null) {
{
InteractionComponent.EndInteraction(); InteractionComponent.EndInteraction();
} }
} }

View File

@ -1,61 +1,49 @@
using Godot; using Godot;
using System;
public class GoldBar : KinematicBody public class GoldBar : KinematicBody {
{
private Vector3 targetPosition; private Vector3 targetPosition;
private bool hasTarget = false; private bool hasTarget;
public Vector3 velocity; public Vector3 velocity;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
targetPosition = GlobalTransform.origin; targetPosition = GlobalTransform.origin;
} }
public void SetTarget(Vector3 target) public void SetTarget(Vector3 target) {
{
targetPosition = target; targetPosition = target;
hasTarget = true; hasTarget = true;
} }
public void UnsetTarget() public void UnsetTarget() {
{
hasTarget = false; hasTarget = false;
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _PhysicsProcess(float delta) public override void _PhysicsProcess(float delta) {
{ if (hasTarget) {
if (hasTarget) if (targetPosition.IsEqualApprox(GlobalTransform.origin)) {
{
if (targetPosition.IsEqualApprox(GlobalTransform.origin))
{
targetPosition = GlobalTransform.origin; targetPosition = GlobalTransform.origin;
velocity = Vector3.Zero; velocity = Vector3.Zero;
} }
Vector3 targetDirection = (targetPosition - GlobalTransform.origin).Normalized(); Vector3 targetDirection = (targetPosition - GlobalTransform.origin).Normalized();
velocity = targetDirection * (velocity.Length() + 10 * delta); velocity = targetDirection * (velocity.Length() + 10 * delta);
Transform = new Transform(this.Transform.basis.Rotated(Vector3.Up, delta * 2.0f), Transform.origin); Transform = new Transform(Transform.basis.Rotated(Vector3.Up, delta * 2.0f), Transform.origin);
} } else {
else
{
velocity.y = velocity.y - 9.81f * delta; velocity.y = velocity.y - 9.81f * delta;
} }
velocity = MoveAndSlide(velocity, Vector3.Up); velocity = MoveAndSlide(velocity, Vector3.Up);
if (IsOnFloor() || Mathf.Abs(Transform.origin.y) < 0.01) if (IsOnFloor() || Mathf.Abs(Transform.origin.y) < 0.01) {
{
// apply damping when on ground // apply damping when on ground
velocity = velocity - velocity.Normalized() * 0.9f * delta; velocity = velocity - velocity.Normalized() * 0.9f * delta;
} }
if (velocity.LengthSquared() < 0.01) if (velocity.LengthSquared() < 0.01) {
{
velocity = Vector3.Zero; velocity = Vector3.Zero;
} }
} }
} }

View File

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

View File

@ -5,8 +5,7 @@ using GodotComponentTest.components;
using GodotComponentTest.entities; using GodotComponentTest.entities;
using GodotComponentTest.utils; using GodotComponentTest.utils;
public class Player : Entity, IInteractionInterface public class Player : Entity, IInteractionInterface {
{
// public members // public members
[Export] public NodePath WorldNode; [Export] public NodePath WorldNode;
@ -30,8 +29,7 @@ public class Player : Entity, IInteractionInterface
private BoneAttachment _toolAttachement; private BoneAttachment _toolAttachement;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_groundMotion = new GroundMotionComponent(); _groundMotion = new GroundMotionComponent();
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false); _worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
NavigationComponent = (NavigationComponent)FindNode("Navigation", false); NavigationComponent = (NavigationComponent)FindNode("Navigation", false);
@ -39,33 +37,30 @@ public class Player : Entity, IInteractionInterface
TaskQueueComponent = new TaskQueueComponent(); TaskQueueComponent = new TaskQueueComponent();
_itemAttractorArea = (Area)FindNode("ItemAttractorArea", false); _itemAttractorArea = (Area)FindNode("ItemAttractorArea", false);
if (_itemAttractorArea == null) if (_itemAttractorArea == null) {
{
GD.PushWarning("No ItemAttractorArea node found for " + GetClass()); GD.PushWarning("No ItemAttractorArea node found for " + GetClass());
} } else {
else
{
_itemAttractorArea.Connect("body_entered", this, nameof(OnItemAttractorBodyEntered)); _itemAttractorArea.Connect("body_entered", this, nameof(OnItemAttractorBodyEntered));
_itemAttractorArea.Connect("body_exited", this, nameof(OnItemAttractorBodyExited)); _itemAttractorArea.Connect("body_exited", this, nameof(OnItemAttractorBodyExited));
} }
_itemPickupArea = (Area)FindNode("ItemPickupArea", false); _itemPickupArea = (Area)FindNode("ItemPickupArea", false);
if (_itemPickupArea == null) if (_itemPickupArea == null) {
GD.PushWarning("No ItemPickupArea node found for " + GetClass()); GD.PushWarning("No ItemPickupArea node found for " + GetClass());
else } else {
_itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered)); _itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered));
}
_playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer"); _playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer");
Debug.Assert(_playerAnimationPlayer != null); Debug.Assert(_playerAnimationPlayer != null);
_animationTree = GetNode<AnimationTree>("Geometry/AnimationTree"); _animationTree = GetNode<AnimationTree>("Geometry/AnimationTree");
Debug.Assert(_animationTree != null); Debug.Assert(_animationTree != null);
var stateMachine = AnimationNodeStateMachinePlayback stateMachine =
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
stateMachine.Start("Idle"); stateMachine.Start("Idle");
_toolAttachement = (BoneAttachment)FindNode("ToolAttachement"); _toolAttachement = (BoneAttachment)FindNode("ToolAttachement");
if (_toolAttachement == null) if (_toolAttachement == null) {
{
GD.PushWarning("No ToolAttachement found!"); GD.PushWarning("No ToolAttachement found!");
} }
@ -73,25 +68,20 @@ public class Player : Entity, IInteractionInterface
} }
public override void _PhysicsProcess(float delta) public override void _PhysicsProcess(float delta) {
{
base._PhysicsProcess(delta); base._PhysicsProcess(delta);
if (NavigationComponent == null) if (NavigationComponent == null) {
{
return; return;
} }
if (TaskQueueComponent != null && TaskQueueComponent.Queue.Count > 0) if (TaskQueueComponent != null && TaskQueueComponent.Queue.Count > 0) {
{
TaskQueueComponent.Process(this, delta); TaskQueueComponent.Process(this, delta);
if (TaskQueueComponent.Queue.Count > 0) if (TaskQueueComponent.Queue.Count > 0) {
{ TaskQueueComponent.Task currentTask = TaskQueueComponent.Queue.Peek();
var currentTask = TaskQueueComponent.Queue.Peek();
TaskQueueComponent.NavigationTask navigationTask = currentTask as TaskQueueComponent.NavigationTask; TaskQueueComponent.NavigationTask navigationTask = currentTask as TaskQueueComponent.NavigationTask;
if (navigationTask != null && navigationTask.PlanningComplete == false) if (navigationTask != null && navigationTask.PlanningComplete == false) {
{
// _navigationComponent.PlanGridPath(this, GlobalTransform, navigationTask.NavigationPoint); // _navigationComponent.PlanGridPath(this, GlobalTransform, navigationTask.NavigationPoint);
NavigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint); NavigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint);
@ -103,24 +93,21 @@ public class Player : Entity, IInteractionInterface
_groundMotion.PhysicsProcess(delta, this, NavigationComponent.CurrentGoalPositionWorld, _groundMotion.PhysicsProcess(delta, this, NavigationComponent.CurrentGoalPositionWorld,
NavigationComponent.CurrentGoalAngleWorld, _worldInfo.World); NavigationComponent.CurrentGoalAngleWorld, _worldInfo.World);
if (NavigationComponent.IsGoalReached()) if (NavigationComponent.IsGoalReached()) {
navigationTask.NavigationPoint = new NavigationPoint(GlobalTransform); navigationTask.NavigationPoint = new NavigationPoint(GlobalTransform);
}
} }
} }
} }
public override void _Process(float delta) public override void _Process(float delta) {
{ if (NavigationComponent != null) {
if (NavigationComponent != null)
{
NavigationComponent.UpdateCurrentGoal(GlobalTransform); NavigationComponent.UpdateCurrentGoal(GlobalTransform);
} }
foreach (Node node in _attractedItemList) foreach (Node node in _attractedItemList) {
{ if (node is GoldBar) {
if (node is GoldBar)
{
GoldBar bar = (GoldBar)node; GoldBar bar = (GoldBar)node;
bar.SetTarget(GlobalTransform.origin); bar.SetTarget(GlobalTransform.origin);
} }
@ -129,31 +116,25 @@ public class Player : Entity, IInteractionInterface
UpdateDebugGeometry(); UpdateDebugGeometry();
} }
public void UpdateDebugGeometry() public void UpdateDebugGeometry() {
{ if (_debugGeometry == null || _debugGeometry.Visible == false) {
if (_debugGeometry == null || _debugGeometry.Visible == false)
{
return; return;
} }
_debugGeometry.Clear(); _debugGeometry.Clear();
_debugGeometry.GlobalTransform = Transform.Identity; _debugGeometry.GlobalTransform = Transform.Identity;
if (NavigationComponent != null) if (NavigationComponent != null) {
{
NavigationComponent.DebugDraw(this, _debugGeometry); NavigationComponent.DebugDraw(this, _debugGeometry);
} }
} }
public void OnItemAttractorBodyEntered(Node node) public void OnItemAttractorBodyEntered(Node node) {
{
_attractedItemList.Add(node); _attractedItemList.Add(node);
} }
public void OnItemAttractorBodyExited(Node node) public void OnItemAttractorBodyExited(Node node) {
{ if (node is GoldBar) {
if (node is GoldBar)
{
GoldBar bar = (GoldBar)node; GoldBar bar = (GoldBar)node;
bar.UnsetTarget(); bar.UnsetTarget();
} }
@ -161,17 +142,14 @@ public class Player : Entity, IInteractionInterface
_attractedItemList.Remove(node); _attractedItemList.Remove(node);
} }
public void OnItemPickupAreaBodyEntered(Node body) public void OnItemPickupAreaBodyEntered(Node body) {
{
GD.Print("Picking up item: " + body.Name); GD.Print("Picking up item: " + body.Name);
if (body is Axe) if (body is Axe) {
{
SetActiveTool("Axe"); SetActiveTool("Axe");
} }
if (body is GoldBar) if (body is GoldBar) {
{
GoldCount++; GoldCount++;
EmitSignal("GoldCountChanged", GoldCount); EmitSignal("GoldCountChanged", GoldCount);
} }
@ -179,39 +157,30 @@ public class Player : Entity, IInteractionInterface
body.QueueFree(); body.QueueFree();
} }
public void SetActiveTool(string toolName) public void SetActiveTool(string toolName) {
{
Debug.Assert(_toolAttachement != null); Debug.Assert(_toolAttachement != null);
if (toolName == "Axe") if (toolName == "Axe") {
{
_toolAttachement.Visible = true; _toolAttachement.Visible = true;
} } else if (toolName == "") {
else if (toolName == "")
{
_toolAttachement.Visible = false; _toolAttachement.Visible = false;
} }
} }
public void OnInteractionStart() public void OnInteractionStart() {
{
AnimationNodeStateMachinePlayback stateMachine = AnimationNodeStateMachinePlayback stateMachine =
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
Debug.Assert(stateMachine != null); Debug.Assert(stateMachine != null);
if (InteractionComponent.TargetEntity is Chest) if (InteractionComponent.TargetEntity is Chest) {
{
GD.Print("Player Opening Box"); GD.Print("Player Opening Box");
stateMachine.Travel("Interaction"); stateMachine.Travel("Interaction");
} } else if (InteractionComponent.TargetEntity is Tree) {
else if (InteractionComponent.TargetEntity is Tree)
{
GD.Print("Player Chopping Tree"); GD.Print("Player Chopping Tree");
stateMachine.Travel("Hit"); stateMachine.Travel("Hit");
} }
} }
public void OnInteractionEnd() public void OnInteractionEnd() {
{
GD.Print("Player Stopping Interaction"); GD.Print("Player Stopping Interaction");
AnimationNodeStateMachinePlayback stateMachine = AnimationNodeStateMachinePlayback stateMachine =
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");

View File

@ -1,8 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
public class Game : Spatial public class Game : Spatial {
{
private ImageTexture _blackWhitePatternTexture; private ImageTexture _blackWhitePatternTexture;
private Camera _camera; private Camera _camera;
private Vector3 _cameraOffset; private Vector3 _cameraOffset;
@ -42,8 +41,7 @@ public class Game : Spatial
private TextureRect _worldTextureRect; private TextureRect _worldTextureRect;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
// debugStatsContainer // debugStatsContainer
Container debugStatsContainer = (Container)FindNode("DebugStatsContainer"); Container debugStatsContainer = (Container)FindNode("DebugStatsContainer");
@ -86,7 +84,7 @@ public class Game : Spatial
Debug.Assert(_tileMaterial != null); Debug.Assert(_tileMaterial != null);
_blackWhitePatternTexture = new ImageTexture(); _blackWhitePatternTexture = new ImageTexture();
Image image = new Image(); Image image = new();
image.Load("assets/4x4checker.png"); image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -108,9 +106,11 @@ public class Game : Spatial
_world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged)); _world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged));
// register entity events // register entity events
foreach (Node node in GetNode("Entities").GetChildren()) foreach (Node node in GetNode("Entities").GetChildren()) {
if (node.HasSignal("EntityClicked")) if (node.HasSignal("EntityClicked")) {
node.Connect("EntityClicked", this, nameof(OnEntityClicked)); node.Connect("EntityClicked", this, nameof(OnEntityClicked));
}
}
_world.Connect("EntityClicked", this, nameof(OnEntityClicked)); _world.Connect("EntityClicked", this, nameof(OnEntityClicked));
@ -122,15 +122,17 @@ public class Game : Spatial
} }
public override void _Input(InputEvent inputEvent) public override void _Input(InputEvent inputEvent) {
{ if (inputEvent.IsAction("Forward")) {
if (inputEvent.IsAction("Forward")) GD.Print("Forward"); GD.Print("Forward");
}
if (inputEvent.IsAction("Back")) GD.Print("Back"); if (inputEvent.IsAction("Back")) {
GD.Print("Back");
}
} }
public void UpdateCurrentTile() public void UpdateCurrentTile() {
{
// cast a ray from the camera to center // cast a ray from the camera to center
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f); Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f); Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
@ -138,12 +140,9 @@ public class Game : Spatial
Vector3 centerCoord; Vector3 centerCoord;
if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition) if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition) {
{
centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y); centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y);
} } else {
else
{
centerCoord = _camera.GlobalTranslation; centerCoord = _camera.GlobalTranslation;
centerCoord.y = 0; centerCoord.y = 0;
} }
@ -157,8 +156,7 @@ public class Game : Spatial
} }
public override void _Process(float delta) public override void _Process(float delta) {
{
_framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString(); _framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString();
_lastTile = _currentTile; _lastTile = _currentTile;
@ -176,17 +174,17 @@ public class Game : Spatial
} }
public void OnGenerateButton() public void OnGenerateButton() {
{
GD.Print("Generating"); GD.Print("Generating");
Slider worldSizeSlider = (Slider)FindNode("WorldSizeSlider"); Slider worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!"); if (worldSizeSlider == null) {
GD.PrintErr("Could not find WorldSizeSlider!");
}
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z)); HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
Transform highlightTransform = Transform.Identity; Transform highlightTransform = Transform.Identity;
@ -198,19 +196,21 @@ public class Game : Spatial
} }
public void OnTileClicked(HexTile3D tile) public void OnTileClicked(HexTile3D tile) {
{ if (_player == null) {
if (_player == null) return; return;
}
if (_player.InteractionComponent != null) _player.InteractionComponent.EmitSignal("InteractionEnd"); if (_player.InteractionComponent != null) {
_player.InteractionComponent.EmitSignal("InteractionEnd");
}
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(tile.GlobalTranslation))); new NavigationPoint(tile.GlobalTranslation)));
} }
public void OnTileHovered(HexTile3D tile) public void OnTileHovered(HexTile3D tile) {
{
Transform highlightTransform = tile.GlobalTransform; Transform highlightTransform = tile.GlobalTransform;
_mouseTileHighlight.Transform = highlightTransform; _mouseTileHighlight.Transform = highlightTransform;
_mouseWorldLabel.Text = highlightTransform.origin.ToString("F3"); _mouseWorldLabel.Text = highlightTransform.origin.ToString("F3");
@ -220,13 +220,11 @@ public class Game : Spatial
_player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
public void OnEntityClicked(Entity entity) public void OnEntityClicked(Entity entity) {
{
GD.Print("Clicked on entity at " + entity.GlobalTranslation); GD.Print("Clicked on entity at " + entity.GlobalTranslation);
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint"); Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null) if (mountPoint != null) {
{
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(mountPoint.GlobalTransform))); new NavigationPoint(mountPoint.GlobalTransform)));
@ -235,8 +233,7 @@ public class Game : Spatial
} }
public void ResetGameState() public void ResetGameState() {
{
Transform playerStartTransform = Transform.Identity; Transform playerStartTransform = Transform.Identity;
playerStartTransform.origin.y = 0; playerStartTransform.origin.y = 0;
_player.Transform = playerStartTransform; _player.Transform = playerStartTransform;
@ -246,28 +243,25 @@ public class Game : Spatial
_goldCountLabel.Text = "0"; _goldCountLabel.Text = "0";
foreach (Spatial entity in GetNode("Entities").GetChildren()) foreach (Spatial entity in GetNode("Entities").GetChildren()) {
{
Transform entityTransform = entity.Transform; Transform entityTransform = entity.Transform;
Vector2 entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z); Vector2 entityPlanePos = new(entityTransform.origin.x, entityTransform.origin.z);
Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords; Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
entityTransform.origin.y = 0; entityTransform.origin.y = 0;
entity.Transform = entityTransform; entity.Transform = entityTransform;
} }
} }
private void OnHeightmapImageChanged(Image heightmapImage) private void OnHeightmapImageChanged(Image heightmapImage) {
{ ImageTexture newHeightmapTexture = new();
ImageTexture newHeightmapTexture = new ImageTexture();
newHeightmapTexture.CreateFromImage(heightmapImage, newHeightmapTexture.CreateFromImage(heightmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_heightTextureRect.Texture = newHeightmapTexture; _heightTextureRect.Texture = newHeightmapTexture;
} }
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) {
{ ImageTexture newWorldTexture = new();
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(viewTileTypeImage, newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -279,8 +273,7 @@ public class Game : Spatial
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y); _tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y);
} }
public void OnGoldCountChanged(int goldCount) public void OnGoldCountChanged(int goldCount) {
{
AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer"); AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
_goldCountLabel.Text = goldCount.ToString(); _goldCountLabel.Text = goldCount.ToString();
animationPlayer.CurrentAnimation = "FlashLabel"; animationPlayer.CurrentAnimation = "FlashLabel";

View File

@ -1,17 +1,15 @@
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
public class HexTile3D : Spatial public class HexTile3D : Spatial {
{ public enum TileType {
public enum TileType
{
Undefined, Undefined,
Sand, Sand,
Grass, Grass,
DeepGrass DeepGrass
} }
static public TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass }; public static TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass };
// scene nodes // scene nodes
private MeshInstance _mesh; private MeshInstance _mesh;
@ -19,26 +17,24 @@ public class HexTile3D : Spatial
// signals // signals
[Signal] [Signal]
delegate void TileClicked(HexTile3D tile3d); private delegate void TileClicked(HexTile3D tile3d);
[Signal] [Signal]
delegate void TileHovered(HexTile3D tile3d); private delegate void TileHovered(HexTile3D tile3d);
// other member variables // other member variables
private SpatialMaterial _previousMaterial; private SpatialMaterial _previousMaterial;
private HexGrid _hexGrid; private readonly HexGrid _hexGrid;
public HexCell Cell = new HexCell(); public HexCell Cell = new();
public bool IsMouseOver = false; public bool IsMouseOver;
public MeshInstance Mesh; public MeshInstance Mesh;
public Vector2 OffsetCoords public Vector2 OffsetCoords {
{ get => Cell.OffsetCoords;
get { return Cell.OffsetCoords; }
set set {
{
Cell.OffsetCoords = value; Cell.OffsetCoords = value;
Transform tile3dTransform = Transform; Transform tile3dTransform = Transform;
Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell); Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell);
@ -50,14 +46,12 @@ public class HexTile3D : Spatial
public TileType Type { get; set; } public TileType Type { get; set; }
HexTile3D() private HexTile3D() {
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_mesh = GetNode<MeshInstance>("Mesh"); _mesh = GetNode<MeshInstance>("Mesh");
_staticBody = GetNode<StaticBody>("StaticBody"); _staticBody = GetNode<StaticBody>("StaticBody");
@ -65,28 +59,24 @@ public class HexTile3D : Spatial
_staticBody.Connect("mouse_entered", this, nameof(OnAreaMouseEntered)); _staticBody.Connect("mouse_entered", this, nameof(OnAreaMouseEntered));
_staticBody.Connect("mouse_exited", this, nameof(OnAreaMouseExited)); _staticBody.Connect("mouse_exited", this, nameof(OnAreaMouseExited));
Mesh = GetNode<MeshInstance>("Mesh"); Mesh = GetNode<MeshInstance>("Mesh");
Debug.Assert(Mesh != null); Debug.Assert(Mesh != null);
this.Type = TileType.Undefined; Type = TileType.Undefined;
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{ if (IsMouseOver && inputEvent is InputEventMouseButton) {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
{
EmitSignal("TileClicked", this); EmitSignal("TileClicked", this);
} }
} }
} }
public void OnAreaMouseEntered() public void OnAreaMouseEntered() {
{
IsMouseOver = true; IsMouseOver = true;
_previousMaterial = (SpatialMaterial)_mesh.MaterialOverride; _previousMaterial = (SpatialMaterial)_mesh.MaterialOverride;
@ -96,8 +86,7 @@ public class HexTile3D : Spatial
// _mesh.MaterialOverride = clonedMaterial; // _mesh.MaterialOverride = clonedMaterial;
} }
public void OnAreaMouseExited() public void OnAreaMouseExited() {
{
IsMouseOver = false; IsMouseOver = false;
_mesh.MaterialOverride = _previousMaterial; _mesh.MaterialOverride = _previousMaterial;
} }

View File

@ -1,15 +1,11 @@
using System;
using Godot;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using GoDotLog; using Godot;
public class StreamContainer : Spatial public class StreamContainer : Spatial {
{
// readonly variables // readonly variables
private readonly Transform _deactivatedTileTransform = new Transform(Basis.Identity, Vector3.Up * 1000); private readonly Transform _deactivatedTileTransform = new(Basis.Identity, Vector3.Up * 1000);
// scene nodes // scene nodes
private MeshInstance _bounds; private MeshInstance _bounds;
private Spatial _activeTiles; private Spatial _activeTiles;
@ -18,50 +14,45 @@ public class StreamContainer : Spatial
private MultiMeshInstance _tileMultiMesh; private MultiMeshInstance _tileMultiMesh;
// resources // resources
private PackedScene _hexTileScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn"); private readonly PackedScene _hexTileScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
// exports // exports
[Export] public Vector2 Dimensions = new Vector2(8, 4); [Export] public Vector2 Dimensions = new(8, 4);
[Export] public NodePath World; [Export] public NodePath World;
[Export] private bool ShowHexTiles = false; [Export] private bool ShowHexTiles;
[Signal] [Signal]
delegate void TileClicked(HexTile3D tile3d); private delegate void TileClicked(HexTile3D tile3d);
[Signal] [Signal]
delegate void TileHovered(HexTile3D tile3d); private delegate void TileHovered(HexTile3D tile3d);
// other members // other members
private Rect2 _currentOffsetCoordRect; private Rect2 _currentOffsetCoordRect;
private Rect2 _oldOffsetCoordRect; private Rect2 _oldOffsetCoordRect;
private HexGrid _hexGrid; private HexGrid _hexGrid;
private Dictionary<Vector2, HexTile3D> _coordToTile = new(); private readonly Dictionary<Vector2, HexTile3D> _coordToTile = new();
private Dictionary<HexTile3D, int> _tileToInstanceIndex = new(); private readonly Dictionary<HexTile3D, int> _tileToInstanceIndex = new();
public List<Vector2> RemovedCoords = new List<Vector2>(); public List<Vector2> RemovedCoords = new();
public List<Vector2> AddedCoords = new List<Vector2>(); public List<Vector2> AddedCoords = new();
public Rect2 CurrentOffsetCoordRect public Rect2 CurrentOffsetCoordRect => _currentOffsetCoordRect;
{
get { return _currentOffsetCoordRect; }
}
public void SetWorld(TileWorld tileWorld) public void SetWorld(TileWorld tileWorld) {
{
TileWorld = tileWorld; TileWorld = tileWorld;
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_unusedTiles = new Queue<HexTile3D>(); _unusedTiles = new Queue<HexTile3D>();
_bounds = GetNode<MeshInstance>("Bounds"); _bounds = GetNode<MeshInstance>("Bounds");
_activeTiles = GetNode<Spatial>("ActiveTiles"); _activeTiles = GetNode<Spatial>("ActiveTiles");
_tileMultiMesh = GetNode<MultiMeshInstance>("TileMultiMesh"); _tileMultiMesh = GetNode<MultiMeshInstance>("TileMultiMesh");
_tileMultiMesh.Multimesh.InstanceCount = (int)((Dimensions.x + 5) * (Dimensions.y + 5)); _tileMultiMesh.Multimesh.InstanceCount = (int)((Dimensions.x + 5) * (Dimensions.y + 5));
_tileMultiMesh.Multimesh.InstanceCount = 1810; _tileMultiMesh.Multimesh.InstanceCount = 1810;
foreach (int i in Enumerable.Range(0, _tileMultiMesh.Multimesh.InstanceCount)) foreach (int i in Enumerable.Range(0, _tileMultiMesh.Multimesh.InstanceCount)) {
{
_tileMultiMesh.Multimesh.SetInstanceTransform(i, _deactivatedTileTransform); _tileMultiMesh.Multimesh.SetInstanceTransform(i, _deactivatedTileTransform);
} }
@ -75,13 +66,12 @@ public class StreamContainer : Spatial
} }
public void UpdateRects(Vector2 centerPlane) public void UpdateRects(Vector2 centerPlane) {
{
_oldOffsetCoordRect = _currentOffsetCoordRect; _oldOffsetCoordRect = _currentOffsetCoordRect;
Vector2 bottomLeftCoord = centerPlane - new Vector2(Dimensions.x / 2, Dimensions.y / 2); Vector2 bottomLeftCoord = centerPlane - new Vector2(Dimensions.x / 2, Dimensions.y / 2);
Vector2 topRightCoord = centerPlane + new Vector2(Dimensions.x / 2, Dimensions.y / 2); Vector2 topRightCoord = centerPlane + new Vector2(Dimensions.x / 2, Dimensions.y / 2);
// GD.Print("World rect now: " + _worldRect.ToString() + " center: " + _worldRect.GetCenter()); // GD.Print("World rect now: " + _worldRect.ToString() + " center: " + _worldRect.GetCenter());
// y axis needs to be inverted as HexGrid's offset coordinates use the opposite axis direction // y axis needs to be inverted as HexGrid's offset coordinates use the opposite axis direction
@ -104,15 +94,13 @@ public class StreamContainer : Spatial
// GD.Print("Bounds Transform: " + boundsTransform.ToString()); // GD.Print("Bounds Transform: " + boundsTransform.ToString());
// GD.Print("Bounds Global : " + _bounds.GlobalTransform.ToString()); // GD.Print("Bounds Global : " + _bounds.GlobalTransform.ToString());
if (!_currentOffsetCoordRect.Equals(_oldOffsetCoordRect)) if (!_currentOffsetCoordRect.Equals(_oldOffsetCoordRect)) {
{
UpdateTileCache(); UpdateTileCache();
} }
} }
public void UpdateTileCache() public void UpdateTileCache() {
{
RemovedCoords.Clear(); RemovedCoords.Clear();
AddedCoords.Clear(); AddedCoords.Clear();
_unusedTiles.Clear(); _unusedTiles.Clear();
@ -123,8 +111,7 @@ public class StreamContainer : Spatial
MarkUnusedTiles(expandedRect, clippedRect); MarkUnusedTiles(expandedRect, clippedRect);
AddNewTiles(expandedRect, clippedRect); AddNewTiles(expandedRect, clippedRect);
foreach (HexTile3D tile3D in _unusedTiles.ToArray()) foreach (HexTile3D tile3D in _unusedTiles.ToArray()) {
{
RemovedCoords.Add(tile3D.OffsetCoords); RemovedCoords.Add(tile3D.OffsetCoords);
_activeTiles.RemoveChild(tile3D); _activeTiles.RemoveChild(tile3D);
tile3D.Disconnect("TileClicked", this, nameof(OnTileClicked)); tile3D.Disconnect("TileClicked", this, nameof(OnTileClicked));
@ -134,65 +121,53 @@ public class StreamContainer : Spatial
} }
public void MarkUnusedTiles(Rect2 expandedRect, Rect2 clippedRect) public void MarkUnusedTiles(Rect2 expandedRect, Rect2 clippedRect) {
{
foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.x) - 5, foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.x) - 5,
Mathf.CeilToInt(expandedRect.Size.x) + 20)) Mathf.CeilToInt(expandedRect.Size.x) + 20)) {
{
foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.y) - 5, foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.y) - 5,
Mathf.CeilToInt(expandedRect.Size.y) + 20)) Mathf.CeilToInt(expandedRect.Size.y) + 20)) {
{ Vector2 coord = new(coord_x, coord_y);
Vector2 coord = new Vector2(coord_x, coord_y);
if (clippedRect.HasPoint(coord)) if (clippedRect.HasPoint(coord)) {
{
continue; continue;
} }
bool isInCurrent = _currentOffsetCoordRect.HasPoint(coord); bool isInCurrent = _currentOffsetCoordRect.HasPoint(coord);
bool isInOld = _oldOffsetCoordRect.HasPoint(coord); bool isInOld = _oldOffsetCoordRect.HasPoint(coord);
if (isInOld && !isInCurrent && _coordToTile.Keys.Contains(coord)) if (isInOld && !isInCurrent && _coordToTile.Keys.Contains(coord)) {
{
HexTile3D tile3D = _coordToTile[coord]; HexTile3D tile3D = _coordToTile[coord];
OnTileDeactivate(tile3D, coord); OnTileDeactivate(tile3D, coord);
} }
} }
} }
} }
public void AddNewTiles(Rect2 expandedRect, Rect2 clippedRect) public void AddNewTiles(Rect2 expandedRect, Rect2 clippedRect) {
{
foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.x), foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.x),
Mathf.CeilToInt(_currentOffsetCoordRect.Size.x))) Mathf.CeilToInt(_currentOffsetCoordRect.Size.x))) {
{
foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.y), foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.y),
Mathf.CeilToInt(_currentOffsetCoordRect.Size.y))) Mathf.CeilToInt(_currentOffsetCoordRect.Size.y))) {
{ Vector2 coord = new(coord_x, coord_y);
Vector2 coord = new Vector2(coord_x, coord_y);
if (clippedRect.HasPoint(coord)) if (clippedRect.HasPoint(coord)) {
{
continue; continue;
} }
if (_unusedTiles.Count == 0) if (_unusedTiles.Count == 0) {
{
AddedCoords.Add(coord); AddedCoords.Add(coord);
HexTile3D tile3D = GetTile3dAt(coord); HexTile3D tile3D = GetTile3dAt(coord);
} } else {
else
{
HexTile3D tile3D = _unusedTiles.Dequeue(); HexTile3D tile3D = _unusedTiles.Dequeue();
tile3D.OffsetCoords = coord; tile3D.OffsetCoords = coord;
tile3D.Type = TileWorld.GetTileTypeAtOffset(coord); tile3D.Type = TileWorld.GetTileTypeAtOffset(coord);
Transform tileTransform = tile3D.Transform; Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = TileWorld.GetHeightAtOffset(coord); tileTransform.origin.y = TileWorld.GetHeightAtOffset(coord);
tile3D.Transform = tileTransform; tile3D.Transform = tileTransform;
OnTileActivate(tile3D); OnTileActivate(tile3D);
_coordToTile[coord] = tile3D; _coordToTile[coord] = tile3D;
} }
} }
@ -200,49 +175,42 @@ public class StreamContainer : Spatial
} }
public HexTile3D GetTile3dAt(Vector2 offsetCoords) public HexTile3D GetTile3dAt(Vector2 offsetCoords) {
{ if (!_coordToTile.Keys.Contains(offsetCoords)) {
if (!_coordToTile.Keys.Contains(offsetCoords))
{
HexTile3D tile3D = (HexTile3D)_hexTileScene.Instance(); HexTile3D tile3D = (HexTile3D)_hexTileScene.Instance();
tile3D.OffsetCoords = offsetCoords; tile3D.OffsetCoords = offsetCoords;
_activeTiles.AddChild(tile3D); _activeTiles.AddChild(tile3D);
tile3D.Connect("TileClicked", this, nameof(OnTileClicked)); tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
tile3D.Connect("TileHovered", this, nameof(OnTileHovered)); tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
if (ShowHexTiles) if (ShowHexTiles) {
{
MeshInstance HexTileMesh = tile3D.GetNode<MeshInstance>("Mesh"); MeshInstance HexTileMesh = tile3D.GetNode<MeshInstance>("Mesh");
HexTileMesh.Transform = HexTileMesh.Transform.Scaled(new Vector3(0.95f, 1, 0.95f)); HexTileMesh.Transform = HexTileMesh.Transform.Scaled(new Vector3(0.95f, 1, 0.95f));
} }
Transform tileTransform = tile3D.Transform; Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = TileWorld.GetHeightAtOffset(offsetCoords); tileTransform.origin.y = TileWorld.GetHeightAtOffset(offsetCoords);
tile3D.Transform = tileTransform; tile3D.Transform = tileTransform;
tile3D.Type = TileWorld.GetTileTypeAtOffset(offsetCoords); tile3D.Type = TileWorld.GetTileTypeAtOffset(offsetCoords);
_tileToInstanceIndex[tile3D] = _tileToInstanceIndex.Count; _tileToInstanceIndex[tile3D] = _tileToInstanceIndex.Count;
_coordToTile[offsetCoords] = tile3D; _coordToTile[offsetCoords] = tile3D;
} }
OnTileActivate(_coordToTile[offsetCoords]); OnTileActivate(_coordToTile[offsetCoords]);
return _coordToTile[offsetCoords]; return _coordToTile[offsetCoords];
} }
public void SetCenterTile(HexCell cell) public void SetCenterTile(HexCell cell) {
{
UpdateRects(_hexGrid.GetHexCenter(cell)); UpdateRects(_hexGrid.GetHexCenter(cell));
} }
public void SetTileMaterial(ShaderMaterial material) public void SetTileMaterial(ShaderMaterial material) {
{ foreach (Spatial node in _activeTiles.GetChildren()) {
foreach (Spatial node in _activeTiles.GetChildren())
{
HexTile3D tile = (HexTile3D)node; HexTile3D tile = (HexTile3D)node;
if (tile == null) if (tile == null) {
{
continue; continue;
} }
@ -250,57 +218,51 @@ public class StreamContainer : Spatial
} }
} }
public void OnTileActivate(HexTile3D tile3D) public void OnTileActivate(HexTile3D tile3D) {
{
int instanceIndex = _tileToInstanceIndex[tile3D]; int instanceIndex = _tileToInstanceIndex[tile3D];
Vector3 scale = Vector3.One; Vector3 scale = Vector3.One;
if (ShowHexTiles) if (ShowHexTiles) {
{
scale.x *= 0.96f; scale.x *= 0.96f;
scale.z *= 0.96f; scale.z *= 0.96f;
} }
Transform instanceTransform = new Transform(tile3D.GlobalTransform.basis.Rotated(Vector3.Up, Mathf.Deg2Rad(30)).Scaled(scale), tile3D.GlobalTransform.origin + Vector3.Up * -2.5f); Transform instanceTransform =
new(tile3D.GlobalTransform.basis.Rotated(Vector3.Up, Mathf.Deg2Rad(30)).Scaled(scale),
tile3D.GlobalTransform.origin + Vector3.Up * -2.5f);
_tileMultiMesh.Multimesh.SetInstanceTransform(instanceIndex, instanceTransform); _tileMultiMesh.Multimesh.SetInstanceTransform(instanceIndex, instanceTransform);
} }
public void OnTileDeactivate(HexTile3D tile3D, Vector2 coord) public void OnTileDeactivate(HexTile3D tile3D, Vector2 coord) {
{
int instanceIndex = _tileToInstanceIndex[tile3D]; int instanceIndex = _tileToInstanceIndex[tile3D];
_tileMultiMesh.Multimesh.SetInstanceTransform(instanceIndex, _deactivatedTileTransform); _tileMultiMesh.Multimesh.SetInstanceTransform(instanceIndex, _deactivatedTileTransform);
tile3D.Type = HexTile3D.TileType.Undefined; tile3D.Type = HexTile3D.TileType.Undefined;
_unusedTiles.Enqueue(tile3D); _unusedTiles.Enqueue(tile3D);
_coordToTile.Remove(coord); _coordToTile.Remove(coord);
RemovedCoords.Add(coord); RemovedCoords.Add(coord);
} }
public void OnTileClicked(HexTile3D tile) public void OnTileClicked(HexTile3D tile) {
{
EmitSignal("TileClicked", tile); EmitSignal("TileClicked", tile);
} }
public void OnTileHovered(HexTile3D tile) public void OnTileHovered(HexTile3D tile) {
{
EmitSignal("TileHovered", tile); EmitSignal("TileHovered", tile);
} }
public void OnWorldGenerated() public void OnWorldGenerated() {
{ foreach (Spatial node in _activeTiles.GetChildren()) {
foreach (Spatial node in _activeTiles.GetChildren())
{
HexTile3D tile = (HexTile3D)node; HexTile3D tile = (HexTile3D)node;
if (tile == null) if (tile == null) {
{
continue; continue;
} }
Transform tileTransform = tile.GlobalTransform; Transform tileTransform = tile.GlobalTransform;
tileTransform.origin.y = TileWorld.GetHeightAtOffset(tile.OffsetCoords); tileTransform.origin.y = TileWorld.GetHeightAtOffset(tile.OffsetCoords);
tile.GlobalTransform = tileTransform; tile.GlobalTransform = tileTransform;
OnTileActivate(tile); OnTileActivate(tile);
} }
} }

View File

@ -5,8 +5,7 @@ using Godot;
using Godot.Collections; using Godot.Collections;
using Namespace; using Namespace;
public class TileWorld : Spatial public class TileWorld : Spatial {
{
// exports // exports
[Export] public bool DebugMap; [Export] public bool DebugMap;
[ExportFlagsEnum(typeof(MapType))] public MapType GenerationMapType = MapType.Debug; [ExportFlagsEnum(typeof(MapType))] public MapType GenerationMapType = MapType.Debug;
@ -21,8 +20,7 @@ public class TileWorld : Spatial
private Spatial _environmentNode; private Spatial _environmentNode;
private readonly Array<Spatial> _grassAssets = new(); private readonly Array<Spatial> _grassAssets = new();
private enum GenerationState private enum GenerationState {
{
Heightmap, Heightmap,
Color, Color,
Objects, Objects,
@ -34,8 +32,7 @@ public class TileWorld : Spatial
private delegate void WorldGenerated(); private delegate void WorldGenerated();
// public members // public members
public enum MapType public enum MapType {
{
Noise, Noise,
Debug, Debug,
Flat Flat
@ -62,8 +59,7 @@ public class TileWorld : Spatial
private Viewport _worldOffscreenViewport; private Viewport _worldOffscreenViewport;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
HexGrid = new HexGrid(); HexGrid = new HexGrid();
_tileTypeRandom = new Random(); _tileTypeRandom = new Random();
@ -83,18 +79,15 @@ public class TileWorld : Spatial
GetNode<Spatial>("Assets").Visible = false; GetNode<Spatial>("Assets").Visible = false;
foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) {
{
_rockAssets.Add(asset); _rockAssets.Add(asset);
} }
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) {
{
_grassAssets.Add(asset); _grassAssets.Add(asset);
} }
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) {
{
_treeAssets.Add(asset); _treeAssets.Add(asset);
} }
@ -106,8 +99,7 @@ public class TileWorld : Spatial
ResetWorldImages(Size); ResetWorldImages(Size);
} }
public void ResetWorldImages(int size) public void ResetWorldImages(int size) {
{
GD.Print("Resetting World Images to size " + size); GD.Print("Resetting World Images to size " + size);
Vector2 sizeVector = new Vector2(size, size); Vector2 sizeVector = new Vector2(size, size);
@ -130,12 +122,10 @@ public class TileWorld : Spatial
} }
public void Generate(int size) public void Generate(int size) {
{
GD.Print("Triggering generation for size: " + size); GD.Print("Triggering generation for size: " + size);
if (Size != size) if (Size != size) {
{
ResetWorldImages(size); ResetWorldImages(size);
_resizeTriggered = true; _resizeTriggered = true;
_resizeExtraFrameCount = 1; _resizeExtraFrameCount = 1;
@ -153,8 +143,7 @@ public class TileWorld : Spatial
OnMapGenerationStart(); OnMapGenerationStart();
switch (GenerationMapType) switch (GenerationMapType) {
{
case MapType.Debug: case MapType.Debug:
GenerateDebugMap(); GenerateDebugMap();
break; break;
@ -167,8 +156,7 @@ public class TileWorld : Spatial
} }
} }
private void GenerateDebugMap() private void GenerateDebugMap() {
{
ColormapImage = new Image(); ColormapImage = new Image();
ColormapImage.Create(Size, Size, false, Image.Format.Rgba8); ColormapImage.Create(Size, Size, false, Image.Format.Rgba8);
@ -178,10 +166,8 @@ public class TileWorld : Spatial
HeightmapImage.Lock(); HeightmapImage.Lock();
ColormapImage.Lock(); ColormapImage.Lock();
foreach (int coordX in Enumerable.Range(0, Size)) foreach (int coordX in Enumerable.Range(0, Size)) {
{ foreach (int coordY in Enumerable.Range(0, Size)) {
foreach (int coordY in Enumerable.Range(0, Size))
{
ColormapImage.SetPixel(coordX, coordY, ColormapImage.SetPixel(coordX, coordY,
new Color((float)Mathf.Min(coordX, coordY) / Size, 0, 0)); new Color((float)Mathf.Min(coordX, coordY) / Size, 0, 0));
HeightmapImage.SetPixel(coordX, coordY, HeightmapImage.SetPixel(coordX, coordY,
@ -197,8 +183,7 @@ public class TileWorld : Spatial
} }
private void GenerateFlatMap() private void GenerateFlatMap() {
{
ColormapImage = new Image(); ColormapImage = new Image();
ColormapImage.Create(Size, Size, false, Image.Format.Rgba8); ColormapImage.Create(Size, Size, false, Image.Format.Rgba8);
@ -208,10 +193,8 @@ public class TileWorld : Spatial
HeightmapImage.Lock(); HeightmapImage.Lock();
ColormapImage.Lock(); ColormapImage.Lock();
foreach (int coordX in Enumerable.Range(0, Size)) foreach (int coordX in Enumerable.Range(0, Size)) {
{ foreach (int coordY in Enumerable.Range(0, Size)) {
foreach (int coordY in Enumerable.Range(0, Size))
{
HeightmapImage.SetPixel(coordX, coordY, HeightmapImage.SetPixel(coordX, coordY,
new Color(0.5f, 0.5f, 0.5f)); new Color(0.5f, 0.5f, 0.5f));
} }
@ -224,8 +207,7 @@ public class TileWorld : Spatial
} }
private void GenerateNoiseMap() private void GenerateNoiseMap() {
{
HeightmapImage = new Image(); HeightmapImage = new Image();
HeightmapImage.Create(Size, Size, false, Image.Format.Rgba8); HeightmapImage.Create(Size, Size, false, Image.Format.Rgba8);
@ -245,28 +227,23 @@ public class TileWorld : Spatial
OnHeightMapChanged(); OnHeightMapChanged();
} }
private void OnMapGenerationStart() private void OnMapGenerationStart() {
{ foreach (Node child in _environmentNode.GetChildren()) {
foreach (Node child in _environmentNode.GetChildren())
{
child.QueueFree(); child.QueueFree();
} }
foreach (Node child in Entities.GetChildren()) foreach (Node child in Entities.GetChildren()) {
{
child.QueueFree(); child.QueueFree();
} }
} }
private void OnHeightMapChanged() private void OnHeightMapChanged() {
{
GD.Print("Triggering rendering of height map"); GD.Print("Triggering rendering of height map");
_currentGenerationState = GenerationState.Heightmap; _currentGenerationState = GenerationState.Heightmap;
_heightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once; _heightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
} }
private void OnMapGenerationComplete() private void OnMapGenerationComplete() {
{
_currentGenerationState = GenerationState.Done; _currentGenerationState = GenerationState.Done;
_worldOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled; _worldOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
_heightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled; _heightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
@ -276,10 +253,9 @@ public class TileWorld : Spatial
EmitSignal("WorldGenerated"); EmitSignal("WorldGenerated");
} }
private Spatial SelectAsset(Vector2 offsetCoord, Array<Spatial> assets, Random randomGenerator, double probability) private Spatial SelectAsset(Vector2 offsetCoord, Array<Spatial> assets, Random randomGenerator,
{ double probability) {
if (randomGenerator.NextDouble() < 1.0 - probability) if (randomGenerator.NextDouble() < 1.0 - probability) {
{
return null; return null;
} }
@ -295,66 +271,52 @@ public class TileWorld : Spatial
return assetInstance; return assetInstance;
} }
private bool IsColorEqualApprox(Color colorA, Color colorB) private bool IsColorEqualApprox(Color colorA, Color colorB) {
{
Vector3 colorDifference = new Vector3(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b); Vector3 colorDifference = new Vector3(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
return colorDifference.LengthSquared() < 0.1 * 0.1; return colorDifference.LengthSquared() < 0.1 * 0.1;
} }
private bool IsColorWater(Color color) private bool IsColorWater(Color color) {
{
return (color.r == 0 && color.g == 0 && color.b > 0.01); return (color.r == 0 && color.g == 0 && color.b > 0.01);
} }
public void MarkCellUnwalkable(HexCell cell) public void MarkCellUnwalkable(HexCell cell) {
{
HexGrid.AddObstacle(cell); HexGrid.AddObstacle(cell);
NavigationmapImage.Lock(); NavigationmapImage.Lock();
NavigationmapImage.SetPixelv(OffsetToTextureCoord(cell.OffsetCoords), Colors.Red); NavigationmapImage.SetPixelv(OffsetToTextureCoord(cell.OffsetCoords), Colors.Red);
NavigationmapImage.Unlock(); NavigationmapImage.Unlock();
} }
private void PopulateEnvironment() private void PopulateEnvironment() {
{
Random environmentRandom = new Random(Seed); Random environmentRandom = new Random(Seed);
ColormapImage.Lock(); ColormapImage.Lock();
HeightmapImage.Lock(); HeightmapImage.Lock();
foreach (int textureCoordU in Enumerable.Range(0, Size)) foreach (int textureCoordU in Enumerable.Range(0, Size)) {
{ foreach (int textureCoordV in Enumerable.Range(0, Size)) {
foreach (int textureCoordV in Enumerable.Range(0, Size))
{
Color colorValue = ColormapImage.GetPixel(textureCoordU, textureCoordV); Color colorValue = ColormapImage.GetPixel(textureCoordU, textureCoordV);
Vector2 textureCoord = new Vector2(textureCoordU, textureCoordV); Vector2 textureCoord = new Vector2(textureCoordU, textureCoordV);
HexCell cell = TextureCoordToCell(textureCoord); HexCell cell = TextureCoordToCell(textureCoord);
Vector2 offsetCoord = cell.OffsetCoords; Vector2 offsetCoord = cell.OffsetCoords;
if (IsColorEqualApprox(colorValue, RockColor)) if (IsColorEqualApprox(colorValue, RockColor)) {
{
Spatial rockAsset = SelectAsset(offsetCoord, _rockAssets, environmentRandom, 0.15); Spatial rockAsset = SelectAsset(offsetCoord, _rockAssets, environmentRandom, 0.15);
if (rockAsset != null) if (rockAsset != null) {
{
_environmentNode.AddChild(rockAsset); _environmentNode.AddChild(rockAsset);
MarkCellUnwalkable(cell); MarkCellUnwalkable(cell);
} }
} } else if (IsColorEqualApprox(colorValue, GrassColor)) {
else if (IsColorEqualApprox(colorValue, GrassColor))
{
Spatial grassAsset = SelectAsset(offsetCoord, _grassAssets, environmentRandom, 0.35); Spatial grassAsset = SelectAsset(offsetCoord, _grassAssets, environmentRandom, 0.35);
if (grassAsset != null) if (grassAsset != null) {
{
_environmentNode.AddChild(grassAsset); _environmentNode.AddChild(grassAsset);
} }
Spatial treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.10); Spatial treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.10);
if (treeAsset != null) if (treeAsset != null) {
{
Entities.AddChild(treeAsset); Entities.AddChild(treeAsset);
MarkCellUnwalkable(cell); MarkCellUnwalkable(cell);
} } else if (environmentRandom.NextDouble() < 0.01) {
else if (environmentRandom.NextDouble() < 0.01)
{
Chest chestAsset = (Chest)_chestScene.Instance(); Chest chestAsset = (Chest)_chestScene.Instance();
Transform assetTransform = Transform.Identity; Transform assetTransform = Transform.Identity;
assetTransform.origin = GetHexCenterFromOffset(offsetCoord); assetTransform.origin = GetHexCenterFromOffset(offsetCoord);
@ -363,9 +325,7 @@ public class TileWorld : Spatial
Entities.AddChild(chestAsset); Entities.AddChild(chestAsset);
MarkCellUnwalkable(cell); MarkCellUnwalkable(cell);
} }
} } else if (IsColorWater(colorValue)) {
else if (IsColorWater(colorValue))
{
MarkCellUnwalkable(cell); MarkCellUnwalkable(cell);
} }
} }
@ -375,16 +335,13 @@ public class TileWorld : Spatial
ColormapImage.Unlock(); ColormapImage.Unlock();
} }
public override void _Process(float delta) public override void _Process(float delta) {
{ if (_resizeTriggered && _resizeExtraFrameCount > 0) {
if (_resizeTriggered && _resizeExtraFrameCount > 0)
{
_resizeExtraFrameCount--; _resizeExtraFrameCount--;
return; return;
} }
if (_currentGenerationState == GenerationState.Heightmap) if (_currentGenerationState == GenerationState.Heightmap) {
{
HeightmapImage.CopyFrom(_heightmapOffscreenViewport.GetTexture().GetData()); HeightmapImage.CopyFrom(_heightmapOffscreenViewport.GetTexture().GetData());
_currentGenerationState = GenerationState.Color; _currentGenerationState = GenerationState.Color;
@ -397,9 +354,7 @@ public class TileWorld : Spatial
_worldOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once; _worldOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
_resizeExtraFrameCount = 1; _resizeExtraFrameCount = 1;
} } else if (_currentGenerationState == GenerationState.Color) {
else if (_currentGenerationState == GenerationState.Color)
{
ColormapImage = new Image(); ColormapImage = new Image();
ColormapImage.Create(Size, Size, false, Image.Format.Rgb8); ColormapImage.Create(Size, Size, false, Image.Format.Rgb8);
ColormapImage.CopyFrom(_worldOffscreenViewport.GetTexture().GetData()); ColormapImage.CopyFrom(_worldOffscreenViewport.GetTexture().GetData());
@ -407,24 +362,19 @@ public class TileWorld : Spatial
_currentGenerationState = GenerationState.Objects; _currentGenerationState = GenerationState.Objects;
PopulateEnvironment(); PopulateEnvironment();
} } else if (_currentGenerationState == GenerationState.Objects) {
else if (_currentGenerationState == GenerationState.Objects)
{
OnMapGenerationComplete(); OnMapGenerationComplete();
} }
} }
public bool IsOffsetCoordValid(Vector2 offsetCoord) public bool IsOffsetCoordValid(Vector2 offsetCoord) {
{
return ((int)Math.Clamp(offsetCoord.x, -(float)Size / 2, (float)Size / 2 - 1) == (int)offsetCoord.x return ((int)Math.Clamp(offsetCoord.x, -(float)Size / 2, (float)Size / 2 - 1) == (int)offsetCoord.x
&& (int)Math.Clamp(offsetCoord.y, -(float)Size / 2, (float)Size / 2 - 1) == (int)offsetCoord.y); && (int)Math.Clamp(offsetCoord.y, -(float)Size / 2, (float)Size / 2 - 1) == (int)offsetCoord.y);
} }
public HexTile3D.TileType GetTileTypeAtOffset(Vector2 offsetCoord) public HexTile3D.TileType GetTileTypeAtOffset(Vector2 offsetCoord) {
{ if (!IsOffsetCoordValid(offsetCoord)) {
if (!IsOffsetCoordValid(offsetCoord))
{
return HexTile3D.TileType.Undefined; return HexTile3D.TileType.Undefined;
} }
@ -432,24 +382,20 @@ public class TileWorld : Spatial
} }
public Vector2 OffsetToTextureCoord(Vector2 offsetCoord) public Vector2 OffsetToTextureCoord(Vector2 offsetCoord) {
{
Vector2 mapSize = Vector2.One * Size; Vector2 mapSize = Vector2.One * Size;
Vector2 textureCoord = (offsetCoord + mapSize / 2).PosMod(mapSize); Vector2 textureCoord = (offsetCoord + mapSize / 2).PosMod(mapSize);
return textureCoord; return textureCoord;
} }
public void SetHeightAtOffset(Vector2 offsetCoord, float height) public void SetHeightAtOffset(Vector2 offsetCoord, float height) {
{
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord); Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
HeightmapImage.SetPixel((int)textureCoord.x, (int)textureCoord.y, new Color(height, 0f, 0f)); HeightmapImage.SetPixel((int)textureCoord.x, (int)textureCoord.y, new Color(height, 0f, 0f));
} }
public float GetHeightAtOffset(Vector2 offsetCoord) public float GetHeightAtOffset(Vector2 offsetCoord) {
{ if (_currentGenerationState != GenerationState.Done) {
if (_currentGenerationState != GenerationState.Done)
{
return 0f; return 0f;
} }
@ -467,8 +413,7 @@ public class TileWorld : Spatial
} }
public void SetTileColorAtOffset(Vector2 offsetCoord, Color color) public void SetTileColorAtOffset(Vector2 offsetCoord, Color color) {
{
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord); Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
ColormapImage.Lock(); ColormapImage.Lock();
@ -476,32 +421,27 @@ public class TileWorld : Spatial
ColormapImage.Unlock(); ColormapImage.Unlock();
} }
public Vector2 WorldToOffsetCoords(Vector3 worldCoord) public Vector2 WorldToOffsetCoords(Vector3 worldCoord) {
{
return HexGrid.GetHexAt(new Vector2(worldCoord.x, worldCoord.z)).OffsetCoords + return HexGrid.GetHexAt(new Vector2(worldCoord.x, worldCoord.z)).OffsetCoords +
Vector2.One * Mathf.Round(Size / 2f); Vector2.One * Mathf.Round(Size / 2f);
} }
public Vector3 GetTileWorldCenterFromOffset(Vector2 offsetCoord) public Vector3 GetTileWorldCenterFromOffset(Vector2 offsetCoord) {
{
Vector2 tileCenter = HexGrid.GetHexCenterFromOffset(offsetCoord - Vector2.One * Mathf.Round(Size / 2f)); Vector2 tileCenter = HexGrid.GetHexCenterFromOffset(offsetCoord - Vector2.One * Mathf.Round(Size / 2f));
return new Vector3(tileCenter.x, GetHeightAtOffset(offsetCoord), tileCenter.y); return new Vector3(tileCenter.x, GetHeightAtOffset(offsetCoord), tileCenter.y);
} }
public Vector3 GetHexCenterFromOffset(Vector2 offsetCoord) public Vector3 GetHexCenterFromOffset(Vector2 offsetCoord) {
{
Vector2 tileCenter = HexGrid.GetHexCenterFromOffset(offsetCoord); Vector2 tileCenter = HexGrid.GetHexCenterFromOffset(offsetCoord);
return new Vector3(tileCenter.x, GetHeightAtOffset(offsetCoord), tileCenter.y); return new Vector3(tileCenter.x, GetHeightAtOffset(offsetCoord), tileCenter.y);
} }
public HexCell TextureCoordToCell(Vector2 textureCoord) public HexCell TextureCoordToCell(Vector2 textureCoord) {
{
return HexCell.FromOffsetCoords(textureCoord - Vector2.One * _halfSize); return HexCell.FromOffsetCoords(textureCoord - Vector2.One * _halfSize);
} }
public Vector2 TextureCoordToOffsetCoord(Vector2 textureCoord) public Vector2 TextureCoordToOffsetCoord(Vector2 textureCoord) {
{
return TextureCoordToCell(textureCoord).OffsetCoords; return TextureCoordToCell(textureCoord).OffsetCoords;
} }
} }

View File

@ -1,9 +1,8 @@
using Godot; using Godot;
using Godot.Collections;
public class EditorUI : Control public class EditorUI : Control {
{ public enum InputMode {
public enum InputMode
{
None, None,
Grass, Grass,
Sand, Sand,
@ -35,8 +34,7 @@ public class EditorUI : Control
public Vector2 currentTileOffset = Vector2.Zero; public Vector2 currentTileOffset = Vector2.Zero;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
// signals // signals
@ -69,74 +67,65 @@ public class EditorUI : Control
} }
public void OnResetButton() public void OnResetButton() {
{
GD.Print("Resetting Map"); GD.Print("Resetting Map");
_tileWorld.Seed = _tileWorld.Seed + 1; _tileWorld.Seed = _tileWorld.Seed + 1;
_tileWorld.Generate(24); _tileWorld.Generate(24);
} }
public void OnGrassButton() public void OnGrassButton() {
{
CurrentInputMode = InputMode.Grass; CurrentInputMode = InputMode.Grass;
} }
public void OnSandButton() public void OnSandButton() {
{
CurrentInputMode = InputMode.Sand; CurrentInputMode = InputMode.Sand;
} }
public void OnWaterButton() public void OnWaterButton() {
{
CurrentInputMode = InputMode.Water; CurrentInputMode = InputMode.Water;
} }
public void OnObstacleButton() public void OnObstacleButton() {
{
CurrentInputMode = InputMode.Obstacle; CurrentInputMode = InputMode.Obstacle;
} }
public void OnNavigateButton() public void OnNavigateButton() {
{
CurrentInputMode = InputMode.Navigate; CurrentInputMode = InputMode.Navigate;
} }
public void OnGameGeometryCheckBoxToggled(bool pressed) public void OnGameGeometryCheckBoxToggled(bool pressed) {
{ Array gameGeometries = GetTree().GetNodesInGroup("GameGeometry");
var gameGeometries = GetTree().GetNodesInGroup("GameGeometry"); foreach (Spatial mesh in gameGeometries) {
foreach (Spatial mesh in gameGeometries) if (mesh != null) {
if (mesh != null)
mesh.Visible = pressed; mesh.Visible = pressed;
}
}
} }
public void OnPhysicsGeometryCheckBoxToggled(bool pressed) public void OnPhysicsGeometryCheckBoxToggled(bool pressed) {
{ Array physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry");
var physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry"); foreach (Spatial mesh in physicsGeometries) {
foreach (Spatial mesh in physicsGeometries) if (mesh != null) {
if (mesh != null)
mesh.Visible = pressed; mesh.Visible = pressed;
}
}
} }
public void OnNavigationGeometryCheckBoxToggled(bool pressed) public void OnNavigationGeometryCheckBoxToggled(bool pressed) {
{
UpdateTileMaterial(); UpdateTileMaterial();
} }
public void UpdateTileMaterial() public void UpdateTileMaterial() {
{ if (_navigationGeometryCheckBox.Pressed) {
if (_navigationGeometryCheckBox.Pressed) ImageTexture newWorldTexture = new();
{
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.NavigationmapImage, newWorldTexture.CreateFromImage(_tileWorld.NavigationmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.NavigationmapImage.GetSize().x); _tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.NavigationmapImage.GetSize().x);
} } else {
else ImageTexture newWorldTexture = new();
{
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage, newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
@ -145,10 +134,8 @@ public class EditorUI : Control
} }
public void OnTileClicked(Vector2 offsetCoord) public void OnTileClicked(Vector2 offsetCoord) {
{ switch (CurrentInputMode) {
switch (CurrentInputMode)
{
case InputMode.Grass: case InputMode.Grass:
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green); _tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
break; break;

View File

@ -1,9 +1,7 @@
using Godot;
using System;
using System.Diagnostics; using System.Diagnostics;
using Godot;
public class HexTile3DMaterialAssign : Spatial public class HexTile3DMaterialAssign : Spatial {
{
// Declare member variables here. Examples: // Declare member variables here. Examples:
// private int a = 2; // private int a = 2;
// private string b = "text"; // private string b = "text";
@ -15,10 +13,9 @@ public class HexTile3DMaterialAssign : Spatial
private ShaderMaterial _customTileMaterial; private ShaderMaterial _customTileMaterial;
private ImageTexture _blackWhitePatternTexture; private ImageTexture _blackWhitePatternTexture;
private ImageTexture _colorPatternTexture; private ImageTexture _colorPatternTexture;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_blackWhitePatternButton = (Button)FindNode("BlackWhitePatternButton"); _blackWhitePatternButton = (Button)FindNode("BlackWhitePatternButton");
Debug.Assert(_blackWhitePatternButton != null); Debug.Assert(_blackWhitePatternButton != null);
_blackWhitePatternButton.Connect("pressed", this, nameof(OnBlackWhitePatternButton)); _blackWhitePatternButton.Connect("pressed", this, nameof(OnBlackWhitePatternButton));
@ -30,42 +27,39 @@ public class HexTile3DMaterialAssign : Spatial
_textureSizeSpinBox = (SpinBox)FindNode("TextureSizeSpinBox"); _textureSizeSpinBox = (SpinBox)FindNode("TextureSizeSpinBox");
Debug.Assert(_textureSizeSpinBox != null); Debug.Assert(_textureSizeSpinBox != null);
_textureSizeSpinBox.Connect("value_changed", this, nameof(OnTextureSizeChanged)); _textureSizeSpinBox.Connect("value_changed", this, nameof(OnTextureSizeChanged));
_hexTile = (HexTile3D)FindNode("HexTile3D"); _hexTile = (HexTile3D)FindNode("HexTile3D");
Debug.Assert(_hexTile != null); Debug.Assert(_hexTile != null);
_customTileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _customTileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
_blackWhitePatternTexture = new ImageTexture(); _blackWhitePatternTexture = new ImageTexture();
Image image = new Image(); Image image = new();
image.Load("assets/4x4checker.png"); image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint) (Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_colorPatternTexture = new ImageTexture(); _colorPatternTexture = new ImageTexture();
image.Load("assets/4x4checkerColor.png"); image.Load("assets/4x4checkerColor.png");
_colorPatternTexture.CreateFromImage(image, (uint) (Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _colorPatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
} }
public void OnBlackWhitePatternButton() public void OnBlackWhitePatternButton() {
{
GD.Print("Apply Black White Pattern!"); GD.Print("Apply Black White Pattern!");
_customTileMaterial.SetShaderParam("MapAlbedoTexture", _blackWhitePatternTexture); _customTileMaterial.SetShaderParam("MapAlbedoTexture", _blackWhitePatternTexture);
} }
public void OnColorPatternButton() public void OnColorPatternButton() {
{
GD.Print("Apply Collor Pattern!"); GD.Print("Apply Collor Pattern!");
//currentMaterial.SetShaderParam("MapAlbedoTexture", _colorPatternTexture); //currentMaterial.SetShaderParam("MapAlbedoTexture", _colorPatternTexture);
_customTileMaterial.SetShaderParam("MapAlbedoTexture", _colorPatternTexture); _customTileMaterial.SetShaderParam("MapAlbedoTexture", _colorPatternTexture);
// _customTileMaterial.SetShaderParam("MapAlbedoTexture", _imageTexture); // _customTileMaterial.SetShaderParam("MapAlbedoTexture", _imageTexture);
// _hexTile.Mesh.SetSurfaceMaterial(0, _customTileMaterial); // _hexTile.Mesh.SetSurfaceMaterial(0, _customTileMaterial);
} }
public void OnTextureSizeChanged(float value) public void OnTextureSizeChanged(float value) {
{ _customTileMaterial.SetShaderParam("TextureSize", (int)value);
_customTileMaterial.SetShaderParam("TextureSize", (int) value);
GD.Print("Texture size: " + _customTileMaterial.GetShaderParam("TextureSize")); GD.Print("Texture size: " + _customTileMaterial.GetShaderParam("TextureSize"));
} }
} }

View File

@ -1,14 +1,12 @@
using Godot;
using System;
using System.Diagnostics; using System.Diagnostics;
using Godot;
using Godot.Collections; using Godot.Collections;
public class NavigationTests : Spatial public class NavigationTests : Spatial {
{
private HexGrid _hexGrid; private HexGrid _hexGrid;
private HexCell _currentTile; private HexCell _currentTile;
private HexCell _lastTile; private HexCell _lastTile;
private Spatial _mouseHighlight; private Spatial _mouseHighlight;
private ShaderMaterial _tileMaterial; private ShaderMaterial _tileMaterial;
@ -17,27 +15,26 @@ public class NavigationTests : Spatial
private StreamContainer _streamContainer; private StreamContainer _streamContainer;
private Player _player; private Player _player;
private NavigationComponent _playerNavigationComponent; private NavigationComponent _playerNavigationComponent;
public override void _Ready() public override void _Ready() {
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
_currentTile = new HexCell(); _currentTile = new HexCell();
_lastTile = new HexCell(); _lastTile = new HexCell();
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
_mouseHighlight = GetNode<Spatial>("MouseHighlight"); _mouseHighlight = GetNode<Spatial>("MouseHighlight");
_editorUi = GetNode<EditorUI>("EditorUI"); _editorUi = GetNode<EditorUI>("EditorUI");
_tileWorld = GetNode<TileWorld>("TileWorld"); _tileWorld = GetNode<TileWorld>("TileWorld");
_tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated)); _tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated));
_streamContainer = GetNode<StreamContainer>("StreamContainer"); _streamContainer = GetNode<StreamContainer>("StreamContainer");
_streamContainer.SetCenterTile(_currentTile); _streamContainer.SetCenterTile(_currentTile);
_player = GetNode<Player>("Player"); _player = GetNode<Player>("Player");
_playerNavigationComponent = _player.GetNode<NavigationComponent>("Navigation"); _playerNavigationComponent = _player.GetNode<NavigationComponent>("Navigation");
// input handling // input handling
// _groundLayer.Connect("input_event", this, nameof(OnGroundLayerInputEvent)); // _groundLayer.Connect("input_event", this, nameof(OnGroundLayerInputEvent));
_streamContainer.Connect("TileClicked", this, nameof(OnTileClicked)); _streamContainer.Connect("TileClicked", this, nameof(OnTileClicked));
@ -46,18 +43,15 @@ public class NavigationTests : Spatial
CorrectEntityGridPositions(); CorrectEntityGridPositions();
} }
public void CorrectEntityGridPositions() public void CorrectEntityGridPositions() {
{
Spatial entitiesNode = GetNode<Spatial>("Entities"); Spatial entitiesNode = GetNode<Spatial>("Entities");
if (entitiesNode == null) if (entitiesNode == null) {
{
return; return;
} }
var entities = entitiesNode.GetChildren(); Array entities = entitiesNode.GetChildren();
foreach (Spatial entity in entities) foreach (Spatial entity in entities) {
{ Vector2 entityPlaneCoords = new(entity.GlobalTranslation.x, entity.GlobalTranslation.z);
Vector2 entityPlaneCoords = new Vector2(entity.GlobalTranslation.x, entity.GlobalTranslation.z);
HexCell entityCell = _tileWorld.HexGrid.GetHexAt(entityPlaneCoords); HexCell entityCell = _tileWorld.HexGrid.GetHexAt(entityPlaneCoords);
_tileWorld.MarkCellUnwalkable(entityCell); _tileWorld.MarkCellUnwalkable(entityCell);
Vector2 cellPlaneCoords = _hexGrid.GetHexCenterFromOffset(entityCell.OffsetCoords); Vector2 cellPlaneCoords = _hexGrid.GetHexCenterFromOffset(entityCell.OffsetCoords);
@ -68,8 +62,7 @@ public class NavigationTests : Spatial
} }
} }
public void OnWorldGenerated() public void OnWorldGenerated() {
{
_streamContainer.OnWorldGenerated(); _streamContainer.OnWorldGenerated();
// Properly place the Player // Properly place the Player
@ -80,7 +73,7 @@ public class NavigationTests : Spatial
playerTransform.origin = worldCenterTileCoords; playerTransform.origin = worldCenterTileCoords;
_player.Transform = playerTransform; _player.Transform = playerTransform;
ImageTexture newWorldTexture = new ImageTexture(); ImageTexture newWorldTexture = new();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage, newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
@ -90,57 +83,49 @@ public class NavigationTests : Spatial
} }
public void UpdateCurrentTile(HexCell tile) public void UpdateCurrentTile(HexCell tile) {
{ if (_currentTile.AxialCoords == tile.AxialCoords) {
if (_currentTile.AxialCoords == tile.AxialCoords)
{
return; return;
} }
_lastTile = _currentTile; _lastTile = _currentTile;
_currentTile = tile; _currentTile = tile;
GD.Print("Current tile: " + _currentTile.OffsetCoords); GD.Print("Current tile: " + _currentTile.OffsetCoords);
if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null) if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null) {
{
_editorUi.currentTileOffset = _currentTile.OffsetCoords; _editorUi.currentTileOffset = _currentTile.OffsetCoords;
} }
Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords); Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords);
Transform tileTransform = Transform.Identity; Transform tileTransform = Transform.Identity;
tileTransform.origin.x = planeCoords.x; tileTransform.origin.x = planeCoords.x;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(_currentTile.OffsetCoords) + 0.1f; tileTransform.origin.y = _tileWorld.GetHeightAtOffset(_currentTile.OffsetCoords) + 0.1f;
tileTransform.origin.z = planeCoords.y; tileTransform.origin.z = planeCoords.y;
_mouseHighlight.Transform = tileTransform; _mouseHighlight.Transform = tileTransform;
} }
public void OnGroundLayerInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnGroundLayerInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{
UpdateCurrentTile(_hexGrid.GetHexAt(new Vector2(position.x, position.z))); UpdateCurrentTile(_hexGrid.GetHexAt(new Vector2(position.x, position.z)));
} }
public void OnTileClicked(HexTile3D tile) public void OnTileClicked(HexTile3D tile) {
{ if (_editorUi != null) {
if (_editorUi != null)
{
_editorUi.OnTileClicked(tile.OffsetCoords); _editorUi.OnTileClicked(tile.OffsetCoords);
if (_editorUi.CurrentInputMode == EditorUI.InputMode.Navigate) if (_editorUi.CurrentInputMode == EditorUI.InputMode.Navigate) {
{
_playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
} }
} }
public void OnTileHovered(HexTile3D tile) public void OnTileHovered(HexTile3D tile) {
{
UpdateCurrentTile(tile.Cell); UpdateCurrentTile(tile.Cell);
Debug.Assert(_playerNavigationComponent != null); Debug.Assert(_playerNavigationComponent != null);
_playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
} }

View File

@ -1,12 +1,11 @@
public class LookupWorldSystem : IWorldSystemInterface using System;
{
public void RegisterEntityComponent(Entity entity, Component component) public class LookupWorldSystem : IWorldSystemInterface {
{ public void RegisterEntityComponent(Entity entity, Component component) {
throw new System.NotImplementedException(); throw new NotImplementedException();
} }
public void Update(float delta) public void Update(float delta) {
{ throw new NotImplementedException();
throw new System.NotImplementedException();
} }
} }

View File

@ -1,5 +1,4 @@
public interface IWorldSystemInterface public interface IWorldSystemInterface {
{
void RegisterEntityComponent(Entity entity, Component component); void RegisterEntityComponent(Entity entity, Component component);
void Update(float delta); void Update(float delta);
} }

View File

@ -1,27 +1,22 @@
using Godot;
using GoDotTest;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Godot;
using GoDotTest;
using Xunit; using Xunit;
public class HexCellTests : TestClass public class HexCellTests : TestClass {
{ public HexCellTests(Node testScene) : base(testScene) { }
public HexCellTests(Node testScene) : base(testScene)
{
}
[Test] [Test]
public void TestHexCellSimple() public void TestHexCellSimple() {
{ HexCell cell = new();
HexCell cell = new HexCell();
Debug.Assert(cell.CubeCoords == new Vector3(0, 0, 0)); Debug.Assert(cell.CubeCoords == new Vector3(0, 0, 0));
} }
[Test] [Test]
public void TestHexCellEqualityInequality() public void TestHexCellEqualityInequality() {
{ HexCell cellA = new();
HexCell cellA = new HexCell(); HexCell cellB = new();
HexCell cellB = new HexCell();
cellA.AxialCoords = new Vector2(2, 3); cellA.AxialCoords = new Vector2(2, 3);
cellB.AxialCoords = new Vector2(2, 3); cellB.AxialCoords = new Vector2(2, 3);
@ -37,9 +32,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestAxialCoords() public void TestAxialCoords() {
{ HexCell cell = new(1, 1, -2);
HexCell cell = new HexCell(1, 1, -2);
Debug.Assert(cell.AxialCoords == new Vector2(1, 1)); Debug.Assert(cell.AxialCoords == new Vector2(1, 1));
cell = new HexCell(1, -1); cell = new HexCell(1, -1);
@ -51,9 +45,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestAxialCoordsRounded() public void TestAxialCoordsRounded() {
{ HexCell cell = new(new Vector2(-0.1f, 0.6f));
HexCell cell = new HexCell(new Vector2(-0.1f, 0.6f));
Debug.Assert(cell.CubeCoords == new Vector3(0, 1, -1)); Debug.Assert(cell.CubeCoords == new Vector3(0, 1, -1));
cell = new HexCell(new Vector2(4.2f, -5.5f)); cell = new HexCell(new Vector2(4.2f, -5.5f));
@ -61,17 +54,15 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestConversion() public void TestConversion() {
{ HexCell cell = new();
HexCell cell = new HexCell();
Debug.Assert(cell.AxialToCubeCoords(new Vector2(2, 1)) == new Vector3(2, 1, -3)); Debug.Assert(cell.AxialToCubeCoords(new Vector2(2, 1)) == new Vector3(2, 1, -3));
Debug.Assert(cell.AxialToCubeCoords(new Vector2(-1, -1)) == new Vector3(-1, -1, 2)); Debug.Assert(cell.AxialToCubeCoords(new Vector2(-1, -1)) == new Vector3(-1, -1, 2));
} }
[Test] [Test]
public void TestRounding() public void TestRounding() {
{ HexCell cell = new();
HexCell cell = new HexCell();
Debug.Assert(cell.RoundCoords(new Vector3(0.1f, 0.5f, -0.6f)) == new Vector3(0, 1, -1)); Debug.Assert(cell.RoundCoords(new Vector3(0.1f, 0.5f, -0.6f)) == new Vector3(0, 1, -1));
Debug.Assert(cell.RoundCoords(new Vector3(-0.4f, -1.3f, 1.7f)) == new Vector3(-1, -1, 2)); Debug.Assert(cell.RoundCoords(new Vector3(-0.4f, -1.3f, 1.7f)) == new Vector3(-1, -1, 2));
@ -80,9 +71,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestCoords() public void TestCoords() {
{ HexCell cell = new();
HexCell cell = new HexCell();
// from cubic positive // from cubic positive
cell.CubeCoords = new Vector3(2, 1, -3); cell.CubeCoords = new Vector3(2, 1, -3);
@ -104,9 +94,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestNearby() public void TestNearby() {
{ HexCell cell = new(new Vector2(1, 2));
HexCell cell = new HexCell(new Vector2(1, 2));
// adjacent // adjacent
HexCell otherCell = cell.GetAdjacent(HexCell.DIR_N); HexCell otherCell = cell.GetAdjacent(HexCell.DIR_N);
@ -128,9 +117,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestDistance() public void TestDistance() {
{ HexCell cell = new();
HexCell cell = new HexCell();
cell.OffsetCoords = new Vector2(1, 2); cell.OffsetCoords = new Vector2(1, 2);
Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(0, 0))) == 3); Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(0, 0))) == 3);
@ -140,80 +128,71 @@ public class HexCellTests : TestClass
[Test] [Test]
public void TestLineTo() public void TestLineTo() {
{ HexCell cell = new();
HexCell cell = new HexCell();
cell.OffsetCoords = new Vector2(1, 2); cell.OffsetCoords = new Vector2(1, 2);
HexCell[] path = cell.LineTo(new HexCell(5, 2)); HexCell[] path = cell.LineTo(new HexCell(5, 2));
HexCell[] pathExpected = HexCell[] pathExpected = {
{ new(1, 2),
new HexCell(1, 2), new(2, 2),
new HexCell(2, 2), new(3, 2),
new HexCell(3, 2), new(4, 2),
new HexCell(4, 2), new(5, 2)
new HexCell(5, 2)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length)) {
{
Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords); Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
} }
} }
[Test] [Test]
public void TestLineToAngled() public void TestLineToAngled() {
{ HexCell cell = new();
HexCell cell = new HexCell();
cell.OffsetCoords = new Vector2(1, 2); cell.OffsetCoords = new Vector2(1, 2);
HexCell[] path = cell.LineTo(new HexCell(5, 4)); HexCell[] path = cell.LineTo(new HexCell(5, 4));
HexCell[] pathExpected = HexCell[] pathExpected = {
{ new(1, 2),
new HexCell(1, 2), new(2, 2),
new HexCell(2, 2), new(2, 3),
new HexCell(2, 3), new(3, 3),
new HexCell(3, 3), new(4, 3),
new HexCell(4, 3), new(4, 4),
new HexCell(4, 4), new(5, 4)
new HexCell(5, 4)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length)) {
{
Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords); Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
} }
} }
[Test] [Test]
public void TestLineEdge() public void TestLineEdge() {
{ HexCell cell = new();
HexCell cell = new HexCell();
cell.OffsetCoords = new Vector2(1, 2); cell.OffsetCoords = new Vector2(1, 2);
HexCell[] path = cell.LineTo(new HexCell(3, 4)); HexCell[] path = cell.LineTo(new HexCell(3, 4));
HexCell[] pathExpected = HexCell[] pathExpected = {
{ new(1, 2),
new HexCell(1, 2), new(2, 2),
new HexCell(2, 2), new(2, 3),
new HexCell(2, 3), new(2, 4),
new HexCell(2, 4), new(3, 4)
new HexCell(3, 4)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length)) {
{
Debug.Print("index: " + index + " path: " + path[index].AxialCoords + " expected: " + Debug.Print("index: " + index + " path: " + path[index].AxialCoords + " expected: " +
pathExpected[index].AxialCoords); pathExpected[index].AxialCoords);
Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords); Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
@ -221,9 +200,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestCellDirections() public void TestCellDirections() {
{ HexCell cell = new();
HexCell cell = new HexCell();
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1)); HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1));
HexCell cellNW = HexCell.FromOffsetCoords(new Vector2(-1, 0)); HexCell cellNW = HexCell.FromOffsetCoords(new Vector2(-1, 0));
@ -252,8 +230,7 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestCellDirectionsNonzeroReference() public void TestCellDirectionsNonzeroReference() {
{
HexCell cell = HexCell.FromOffsetCoords(new Vector2(-4, -3)); HexCell cell = HexCell.FromOffsetCoords(new Vector2(-4, -3));
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(-4, -2)); HexCell cellN = HexCell.FromOffsetCoords(new Vector2(-4, -2));
@ -283,9 +260,8 @@ public class HexCellTests : TestClass
} }
[Test] [Test]
public void TestNextCellAlongLine() public void TestNextCellAlongLine() {
{ HexCell cell = new();
HexCell cell = new HexCell();
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1)); HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1));
HexCell cellNE = HexCell.FromOffsetCoords(new Vector2(1, 0)); HexCell cellNE = HexCell.FromOffsetCoords(new Vector2(1, 0));
HexCell cellSE = HexCell.FromOffsetCoords(new Vector2(1, -1)); HexCell cellSE = HexCell.FromOffsetCoords(new Vector2(1, -1));

View File

@ -7,50 +7,43 @@ using Xunit;
namespace GodotComponentTest.tests; namespace GodotComponentTest.tests;
public class HexGridPathFindingTests : TestClass public class HexGridPathFindingTests : TestClass {
{
private HexGrid _hexGrid; private HexGrid _hexGrid;
private HexCell _hexCell; private HexCell _hexCell;
private HexCell _positionA = new HexCell(new Vector2(2, 0)); private readonly HexCell _positionA = new(new Vector2(2, 0));
private HexCell _positionB = new HexCell(new Vector2(4, 2)); private readonly HexCell _positionB = new(new Vector2(4, 2));
private HexCell _positionC = new HexCell(new Vector2(7, 0)); private readonly HexCell _positionC = new(new Vector2(7, 0));
private HexCell _positionD = new HexCell(new Vector2(5, 0)); private readonly HexCell _positionD = new(new Vector2(5, 0));
private HexCell _positionE = new HexCell(new Vector2(2, 2)); private HexCell _positionE = new(new Vector2(2, 2));
private HexCell _positionF = new HexCell(new Vector2(1, 3)); private HexCell _positionF = new(new Vector2(1, 3));
private HexCell _positionG = new HexCell(new Vector2(1, 0)); private readonly HexCell _positionG = new(new Vector2(1, 0));
private Vector2[] _obstacles = private readonly Vector2[] _obstacles = {
{ new(2, 1),
new Vector2(2, 1), new(3, 1),
new Vector2(3, 1), new(4, 1),
new Vector2(4, 1), new(1, 2),
new Vector2(1, 2), new(3, 2),
new Vector2(3, 2), new(1, 3),
new Vector2(1, 3), new(2, 3)
new Vector2(2, 3),
}; };
public HexGridPathFindingTests(Node testScene) : base(testScene) public HexGridPathFindingTests(Node testScene) : base(testScene) { }
{
}
[Setup] [Setup]
public void Setup() public void Setup() {
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
_hexCell = new HexCell(); _hexCell = new HexCell();
_hexGrid.SetBounds(new Vector2(0, 0), new Vector2(7, 4)); _hexGrid.SetBounds(new Vector2(0, 0), new Vector2(7, 4));
foreach (Vector2 obstacle in _obstacles) foreach (Vector2 obstacle in _obstacles) {
{
_hexGrid.AddObstacle(new HexCell(obstacle)); _hexGrid.AddObstacle(new HexCell(obstacle));
} }
} }
[Test] [Test]
public void TestBounds() public void TestBounds() {
{
Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 0))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 0)));
Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 4))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(0, 4)));
Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(7, 0))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(7, 0)));
@ -63,9 +56,8 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestNegativeBounds() public void TestNegativeBounds() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
grid.SetBounds(new Vector2(-5, -5), new Vector2(-2, -2)); grid.SetBounds(new Vector2(-5, -5), new Vector2(-2, -2));
Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-2, -2))); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-2, -2)));
@ -76,9 +68,8 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestNegativeBoundsAlt() public void TestNegativeBoundsAlt() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
grid.SetBounds(new Vector2(-3, -3), new Vector2(2, 2)); grid.SetBounds(new Vector2(-3, -3), new Vector2(2, 2));
Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-3, -3))); Assert.Equal(grid.PathCostDefault, grid.GetHexCost(new Vector2(-3, -3)));
@ -89,8 +80,7 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestGridObstacles() public void TestGridObstacles() {
{
Assert.Equal(_obstacles.Length, _hexGrid.Obstacles.Count); Assert.Equal(_obstacles.Length, _hexGrid.Obstacles.Count);
// Adding an obstacle // Adding an obstacle
@ -110,8 +100,7 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestHexCost() public void TestHexCost() {
{
Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(1, 1))); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetHexCost(new Vector2(1, 1)));
Assert.Equal(0, _hexGrid.GetHexCost(new HexCell(new Vector3(2, 1, -3)))); Assert.Equal(0, _hexGrid.GetHexCost(new HexCell(new Vector3(2, 1, -3))));
@ -120,14 +109,12 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestMoveCost() public void TestMoveCost() {
{
Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N)); Assert.Equal(_hexGrid.PathCostDefault, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N));
} }
[Test] [Test]
public void TestMovieCostCumulative() public void TestMovieCostCumulative() {
{
_hexGrid.AddObstacle(new Vector2(0, 0), 1); _hexGrid.AddObstacle(new Vector2(0, 0), 1);
_hexGrid.AddObstacle(new Vector2(0, 1), 2); _hexGrid.AddObstacle(new Vector2(0, 1), 2);
_hexGrid.AddBarrier(new Vector2(0, 0), HexCell.DIR_N, 4); _hexGrid.AddBarrier(new Vector2(0, 0), HexCell.DIR_N, 4);
@ -138,12 +125,10 @@ public class HexGridPathFindingTests : TestClass
Assert.Equal(14, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N)); Assert.Equal(14, _hexGrid.GetMoveCost(new Vector2(0, 0), HexCell.DIR_N));
} }
void ComparePath(List<HexCell> expected, List<HexCell> path) private void ComparePath(List<HexCell> expected, List<HexCell> path) {
{
Assert.Equal(expected.Count, path.Count()); Assert.Equal(expected.Count, path.Count());
foreach (int i in Enumerable.Range(0, Math.Min(expected.Count, path.Count()))) foreach (int i in Enumerable.Range(0, Math.Min(expected.Count, path.Count()))) {
{
HexCell pathCell = path[i]; HexCell pathCell = path[i];
HexCell expectedCell = expected[i]; HexCell expectedCell = expected[i];
Assert.Equal(expectedCell.AxialCoords, pathCell.AxialCoords); Assert.Equal(expectedCell.AxialCoords, pathCell.AxialCoords);
@ -151,10 +136,8 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestStraightLine() public void TestStraightLine() {
{ List<HexCell> expectedPath = new() {
List<HexCell> expectedPath = new List<HexCell>()
{
_positionA, _positionA,
new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(3, 0)),
new HexCell(new Vector2(4, 0)), new HexCell(new Vector2(4, 0)),
@ -184,10 +167,8 @@ public class HexGridPathFindingTests : TestClass
// } // }
[Test] [Test]
public void TestObstacle() public void TestObstacle() {
{ List<HexCell> expectedPath = new() {
List<HexCell> expectedPath = new List<HexCell>()
{
_positionA, _positionA,
new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(3, 0)),
new HexCell(new Vector2(4, 0)), new HexCell(new Vector2(4, 0)),
@ -200,10 +181,8 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestWalls() public void TestWalls() {
{ Vector3[] walls = {
Vector3[] walls =
{
HexCell.DIR_N, HexCell.DIR_N,
HexCell.DIR_NE, HexCell.DIR_NE,
HexCell.DIR_SE, HexCell.DIR_SE,
@ -212,13 +191,11 @@ public class HexGridPathFindingTests : TestClass
HexCell.DIR_NW HexCell.DIR_NW
}; };
foreach (Vector3 wall in walls) foreach (Vector3 wall in walls) {
{
_hexGrid.AddBarrier(_positionG, wall); _hexGrid.AddBarrier(_positionG, wall);
} }
List<HexCell> expectedPath = new List<HexCell>() List<HexCell> expectedPath = new() {
{
_positionA, _positionA,
new HexCell(new Vector2(1, 1)), new HexCell(new Vector2(1, 1)),
new HexCell(new Vector2(0, 1)), new HexCell(new Vector2(0, 1)),
@ -230,13 +207,11 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestSlopes() public void TestSlopes() {
{
_hexGrid.AddBarrier(_positionG, HexCell.DIR_NE, 3); _hexGrid.AddBarrier(_positionG, HexCell.DIR_NE, 3);
_hexGrid.AddBarrier(_positionG, HexCell.DIR_N, _hexGrid.PathCostDefault - 0.1f); _hexGrid.AddBarrier(_positionG, HexCell.DIR_N, _hexGrid.PathCostDefault - 0.1f);
List<HexCell> expectedPath = new List<HexCell>() List<HexCell> expectedPath = new() {
{
_positionA, _positionA,
new HexCell(new Vector2(1, 1)), new HexCell(new Vector2(1, 1)),
_positionG _positionG
@ -246,20 +221,17 @@ public class HexGridPathFindingTests : TestClass
} }
[Test] [Test]
public void TestRoughTerrain() public void TestRoughTerrain() {
{ List<HexCell> shortPath = new() {
List<HexCell> shortPath = new List<HexCell>()
{
_positionA, _positionA,
new HexCell(new Vector2(3, 0)), new HexCell(new Vector2(3, 0)),
new HexCell(new Vector2(4, 0)), new HexCell(new Vector2(4, 0)),
_positionD, _positionD,
new HexCell(new Vector2(5, 1)), new HexCell(new Vector2(5, 1)),
_positionB, _positionB
}; };
List<HexCell> longPath = new List<HexCell>() List<HexCell> longPath = new() {
{
_positionA, _positionA,
new HexCell(new Vector2(1, 1)), new HexCell(new Vector2(1, 1)),
new HexCell(new Vector2(0, 2)), new HexCell(new Vector2(0, 2)),
@ -268,7 +240,7 @@ public class HexGridPathFindingTests : TestClass
new HexCell(new Vector2(1, 4)), new HexCell(new Vector2(1, 4)),
new HexCell(new Vector2(2, 4)), new HexCell(new Vector2(2, 4)),
new HexCell(new Vector2(3, 3)), new HexCell(new Vector2(3, 3)),
_positionB, _positionB
}; };
_hexGrid.PathCostDefault = 1f; _hexGrid.PathCostDefault = 1f;

View File

@ -1,19 +1,15 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using Godot; using Godot;
using GoDotTest; using GoDotTest;
using System.Diagnostics;
using Xunit; using Xunit;
public class HexGridTests : TestClass public class HexGridTests : TestClass {
{ public HexGridTests(Node testScene) : base(testScene) { }
public HexGridTests(Node testScene) : base(testScene)
{
}
[Test] [Test]
public void TestGetAt() public void TestGetAt() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
float w = grid.HexSize.x; float w = grid.HexSize.x;
float h = grid.HexSize.y; float h = grid.HexSize.y;
@ -24,9 +20,8 @@ public class HexGridTests : TestClass
} }
[Test] [Test]
public void TestGetCellsForLineSimple() public void TestGetCellsForLineSimple() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
List<HexCell> lineCells = List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(0, 2))); grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(0, 2)));
@ -46,9 +41,8 @@ public class HexGridTests : TestClass
} }
[Test] [Test]
public void TestGetCellsDiagonal() public void TestGetCellsDiagonal() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
List<HexCell> lineCells = List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(2, 1))); grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(2, 1)));
@ -68,9 +62,8 @@ public class HexGridTests : TestClass
} }
[Test] [Test]
public void TestGetCellsForLineAlongEdge() public void TestGetCellsForLineAlongEdge() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
List<HexCell> lineCells = List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, -0.0001f), grid.GetHexCenterFromOffset(new Vector2(2, 0))); grid.GetCellsForLine(new Vector2(0, -0.0001f), grid.GetHexCenterFromOffset(new Vector2(2, 0)));
@ -90,13 +83,12 @@ public class HexGridTests : TestClass
} }
[Test] [Test]
public void GetTestsInfiniteLoop() public void GetTestsInfiniteLoop() {
{ HexGrid grid = new();
HexGrid grid = new HexGrid();
Vector2 fromPlane = new(-2.31678f, -5.024752f);
Vector2 fromPlane = new Vector2(-2.31678f, -5.024752f); Vector2 toPlane = new(-2.599937f, -6.134028f);
Vector2 toPlane = new Vector2(-2.599937f, -6.134028f);
List<HexCell> cellList = grid.GetCellsForLine(fromPlane, toPlane); List<HexCell> cellList = grid.GetCellsForLine(fromPlane, toPlane);
} }
} }

View File

@ -1,8 +1,7 @@
using Godot; using Godot;
using GoDotTest; using GoDotTest;
public class NavigationComponentTests : TestClass public class NavigationComponentTests : TestClass {
{
private readonly Node _testScene; private readonly Node _testScene;
private readonly PackedScene _WorldScene = GD.Load<PackedScene>("res://scenes/World.tscn"); private readonly PackedScene _WorldScene = GD.Load<PackedScene>("res://scenes/World.tscn");
@ -10,14 +9,12 @@ public class NavigationComponentTests : TestClass
private NavigationComponent _navigationComponent; private NavigationComponent _navigationComponent;
private World _world; private World _world;
public NavigationComponentTests(Node testScene) : base(testScene) public NavigationComponentTests(Node testScene) : base(testScene) {
{
_testScene = testScene; _testScene = testScene;
} }
[Setup] [Setup]
public void Setup() public void Setup() {
{
_world = (World)_WorldScene.Instance(); _world = (World)_WorldScene.Instance();
_world.HexGrid = new HexGrid(); _world.HexGrid = new HexGrid();
_testScene.AddChild(_world); _testScene.AddChild(_world);

View File

@ -6,41 +6,35 @@ using Xunit;
namespace GodotComponentTest.tests; namespace GodotComponentTest.tests;
public class Plane2DTests : TestClass public class Plane2DTests : TestClass {
{ public Plane2DTests(Node testScene) : base(testScene) { }
public Plane2DTests(Node testScene) : base(testScene)
{ [Test]
public void Plane2DDistSimple() {
Plane2D plane2D = new(new Vector2(0, 1), new Vector2(0, -1));
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - 1) < float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 2)) + 1) < float.Epsilon);
} }
[Test] [Test]
public void Plane2DDistSimple() public void Plane2DDistAngled() {
{ Plane2D plane2D = new(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(0, -1));
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - 1) < Single.Epsilon); Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < Single.Epsilon); Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - MathF.Sqrt(2) / 2) < float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 2)) + 1) < Single.Epsilon); Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(-1, 0))) < float.Epsilon);
} }
[Test] [Test]
public void Plane2DDistAngled() public void Plane2DDistLineSegment() {
{ Plane2D plane2D = new(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - MathF.Sqrt(2) / 2) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(-1, 0))) < Single.Epsilon);
}
[Test]
public void Plane2DDistLineSegment()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True( Assert.True(
Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 0)) - 1) < Single.Epsilon); Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 0)) - 1) < float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(0, 1)) - 1) < Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(0, 1)) - 1) <
Single.Epsilon); float.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, -1).Normalized()) + Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, -1).Normalized()) +
MathF.Sqrt(2) / 2) < Plane2D.DistancePrecision); MathF.Sqrt(2) / 2) < Plane2D.DistancePrecision);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 1).Normalized()) - Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 1).Normalized()) -
@ -48,22 +42,20 @@ public class Plane2DTests : TestClass
} }
[Test] [Test]
public void Plane2DTestIsParallel() public void Plane2DTestIsParallel() {
{ Plane2D plane2D = new(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True(plane2D.IsParallelToDir(new Vector2(1, 1.00001f).Normalized())); Assert.True(plane2D.IsParallelToDir(new Vector2(1, 1.00001f).Normalized()));
Assert.True(plane2D.IsParallelToDir(new Vector2(1, 0.99999f).Normalized())); Assert.True(plane2D.IsParallelToDir(new Vector2(1, 0.99999f).Normalized()));
} }
[Test] [Test]
public void Plane2DDistLineSegmentParallel() public void Plane2DDistLineSegmentParallel() {
{ Plane2D plane2D = new(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.Equal(Single.PositiveInfinity, Assert.Equal(float.PositiveInfinity,
plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 1.00001f).Normalized())); plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 1.00001f).Normalized()));
Assert.Equal(Single.NegativeInfinity, Assert.Equal(float.NegativeInfinity,
plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 0.99999f).Normalized())); plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 0.99999f).Normalized()));
} }
} }

View File

@ -1,37 +1,29 @@
using System;
using Godot; using Godot;
using GodotComponentTest.utils;
using GoDotTest; using GoDotTest;
using Xunit;
namespace GodotComponentTest.tests; namespace GodotComponentTest.tests;
public class StreamContainerTests : TestClass public class StreamContainerTests : TestClass {
{ private readonly Node _testScene;
private Node _testScene = null;
private TileWorld _tileWorld; private TileWorld _tileWorld;
private StreamContainer _streamContainer; private StreamContainer _streamContainer;
private PackedScene _tileWorldScene = GD.Load<PackedScene>("res://scenes/TileWorld.tscn"); private readonly PackedScene _tileWorldScene = GD.Load<PackedScene>("res://scenes/TileWorld.tscn");
private PackedScene _streamContainerScene = GD.Load<PackedScene>("res://scenes/StreamContainer.tscn"); private readonly PackedScene _streamContainerScene = GD.Load<PackedScene>("res://scenes/StreamContainer.tscn");
public StreamContainerTests(Node testScene) : base(testScene) public StreamContainerTests(Node testScene) : base(testScene) {
{
_testScene = testScene; _testScene = testScene;
} }
[Setup] [Setup]
public void Setup() public void Setup() {
{ _tileWorld = (TileWorld)_tileWorldScene.Instance();
_tileWorld = (TileWorld) _tileWorldScene.Instance();
_tileWorld.HexGrid = new HexGrid(); _tileWorld.HexGrid = new HexGrid();
_testScene.AddChild(_tileWorld); _testScene.AddChild(_tileWorld);
_streamContainer = (StreamContainer) _streamContainerScene.Instance(); _streamContainer = (StreamContainer)_streamContainerScene.Instance();
foreach (Node node in _testScene.GetChildren()) foreach (Node node in _testScene.GetChildren()) {
{ if (node is TileWorld) {
if (node is TileWorld)
{
_streamContainer.World = node.GetPath(); _streamContainer.World = node.GetPath();
} }
} }
@ -40,20 +32,17 @@ public class StreamContainerTests : TestClass
} }
[Cleanup] [Cleanup]
public void Cleanup() public void Cleanup() {
{ foreach (Node node in _testScene.GetChildren()) {
foreach (Node node in _testScene.GetChildren())
{
node.QueueFree(); node.QueueFree();
} }
_streamContainer.QueueFree(); _streamContainer.QueueFree();
_tileWorld.QueueFree(); _tileWorld.QueueFree();
} }
[Test] [Test]
public void Plane2DDistSimple() public void Plane2DDistSimple() {
{
// _streamContainer.UpdateRects(new Vector2(0, 0)); // _streamContainer.UpdateRects(new Vector2(0, 0));
// _streamContainer.UpdateRects(new Vector2(1, 0)); // _streamContainer.UpdateRects(new Vector2(1, 0));
} }

View File

@ -2,8 +2,8 @@ using System.Reflection;
using Godot; using Godot;
using GoDotTest; using GoDotTest;
public class Tests : Control public class Tests : Control {
{ public override async void _Ready() {
public override async void _Ready() await GoTest.RunTests(Assembly.GetExecutingAssembly(), this);
=> await GoTest.RunTests(Assembly.GetExecutingAssembly(), this); }
} }

View File

@ -1,76 +1,63 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Godot; using Godot;
namespace GodotComponentTest.utils; namespace GodotComponentTest.utils;
public class DebugGeometry : Spatial public class DebugGeometry : Spatial {
{
private ImmediateGeometry _immediateGeometry; private ImmediateGeometry _immediateGeometry;
private List<Transform> _transformStack; private List<Transform> _transformStack;
private Transform _currentTransform = Transform.Identity; private Transform _currentTransform = Transform.Identity;
public override void _Ready() public override void _Ready() {
{
base._Ready(); base._Ready();
_immediateGeometry = (ImmediateGeometry)FindNode("ImmediateGeometry"); _immediateGeometry = (ImmediateGeometry)FindNode("ImmediateGeometry");
Clear(); Clear();
} }
public void Clear() public void Clear() {
{
_immediateGeometry.Clear(); _immediateGeometry.Clear();
_transformStack = new List<Transform>(); _transformStack = new List<Transform>();
_transformStack.Add(Transform.Identity); _transformStack.Add(Transform.Identity);
} }
public void PushTransform(Transform transform) public void PushTransform(Transform transform) {
{
_transformStack.Add(transform); _transformStack.Add(transform);
_currentTransform = transform; _currentTransform = transform;
} }
public void PushTranslated(Vector3 offset) public void PushTranslated(Vector3 offset) {
{
PushTransform(_currentTransform.Translated(offset)); PushTransform(_currentTransform.Translated(offset));
} }
public void PopTransform() public void PopTransform() {
{
_transformStack.RemoveAt(_transformStack.Count - 1); _transformStack.RemoveAt(_transformStack.Count - 1);
_currentTransform = PeekTransform(); _currentTransform = PeekTransform();
} }
public Transform PeekTransform() public Transform PeekTransform() {
{
return _transformStack[_transformStack.Count - 1]; return _transformStack[_transformStack.Count - 1];
} }
public void Begin(Mesh.PrimitiveType primitive, Texture texture = null) public void Begin(Mesh.PrimitiveType primitive, Texture texture = null) {
{
_immediateGeometry.Begin(primitive, texture); _immediateGeometry.Begin(primitive, texture);
} }
public void End() public void End() {
{
_immediateGeometry.End(); _immediateGeometry.End();
} }
public void AddVertex(Vector3 vertex) public void AddVertex(Vector3 vertex) {
{
_immediateGeometry.AddVertex(_currentTransform.Xform(vertex)); _immediateGeometry.AddVertex(_currentTransform.Xform(vertex));
} }
public void SetColor(Color color) public void SetColor(Color color) {
{
_immediateGeometry.SetColor(color); _immediateGeometry.SetColor(color);
} }
public void AddBox(Vector3 extents) public void AddBox(Vector3 extents) {
{
Transform currentTransform = PeekTransform(); Transform currentTransform = PeekTransform();
// bottom square // bottom square
AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f));
@ -81,7 +68,7 @@ public class DebugGeometry : Spatial
AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f));
// top square // top square
AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * 0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * 0.5f));
@ -91,15 +78,15 @@ public class DebugGeometry : Spatial
AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f));
// side // side
AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * 0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * 0.5f, extents.z * 0.5f));
AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * 0.5f)); AddVertex(new Vector3(extents.x * -0.5f, extents.y * -0.5f, extents.z * 0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * 0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * 0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * 0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * 0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * 0.5f, extents.z * -0.5f));
AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f)); AddVertex(new Vector3(extents.x * 0.5f, extents.y * -0.5f, extents.z * -0.5f));
} }
} }

View File

@ -5,43 +5,39 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Godot; using Godot;
namespace Namespace namespace Namespace;
{
using UnderlyingType = UInt64;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] using UnderlyingType = UInt64;
public class ExportFlagsEnumAttribute : ExportAttribute
{
public ExportFlagsEnumAttribute(Type enumType)
: base(PropertyHint.Flags, GetFlagsEnumHintString(enumType))
{ }
private static string GetFlagsEnumHintString(Type enumType) [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
{ public class ExportFlagsEnumAttribute : ExportAttribute {
Dictionary<UnderlyingType, List<string>> flagNamesByFlag = new Dictionary<UnderlyingType, List<string>>(); public ExportFlagsEnumAttribute(Type enumType)
UnderlyingType flag = (UnderlyingType)1; : base(PropertyHint.Flags, GetFlagsEnumHintString(enumType)) { }
foreach (string name in Enum.GetNames(enumType))
{ private static string GetFlagsEnumHintString(Type enumType) {
UnderlyingType value = (UnderlyingType)Convert.ChangeType(Enum.Parse(enumType, name), typeof(UnderlyingType)); Dictionary<UnderlyingType, List<string>> flagNamesByFlag = new();
while (value > flag) UnderlyingType flag = 1;
{ foreach (string name in Enum.GetNames(enumType)) {
if (!flagNamesByFlag.ContainsKey(flag)) UnderlyingType value =
{ (UnderlyingType)Convert.ChangeType(Enum.Parse(enumType, name), typeof(UnderlyingType));
flagNamesByFlag.Add(flag, new List<string>()); while (value > flag) {
} if (!flagNamesByFlag.ContainsKey(flag)) {
flag <<= 1; flagNamesByFlag.Add(flag, new List<string>());
}
if (value == flag)
{
if (!flagNamesByFlag.TryGetValue(flag, out List<string> names))
{
names = new List<string>();
flagNamesByFlag.Add(flag, names);
}
names.Add(name);
} }
flag <<= 1;
}
if (value == flag) {
if (!flagNamesByFlag.TryGetValue(flag, out List<string> names)) {
names = new List<string>();
flagNamesByFlag.Add(flag, names);
}
names.Add(name);
} }
return string.Join(", ", flagNamesByFlag.Values.Select(flagNames => string.Join(" / ", flagNames)));
} }
return string.Join(", ", flagNamesByFlag.Values.Select(flagNames => string.Join(" / ", flagNames)));
} }
} }

View File

@ -1,11 +1,9 @@
using System; using System;
using Godot; using Godot;
public class NavigationPoint public class NavigationPoint {
{
[Flags] [Flags]
public enum NavigationFlags public enum NavigationFlags {
{
Position = 1, Position = 1,
Orientation = 2 Orientation = 2
} }
@ -16,52 +14,43 @@ public class NavigationPoint
public Vector3 WorldPosition = Vector3.Zero; public Vector3 WorldPosition = Vector3.Zero;
public NavigationPoint(Vector3 worldPosition) public NavigationPoint(Vector3 worldPosition) {
{
WorldPosition = worldPosition; WorldPosition = worldPosition;
Flags = NavigationFlags.Position; Flags = NavigationFlags.Position;
} }
public NavigationPoint(Quat worldOrientation) public NavigationPoint(Quat worldOrientation) {
{
WorldOrientation = worldOrientation; WorldOrientation = worldOrientation;
Flags = NavigationFlags.Orientation; Flags = NavigationFlags.Orientation;
} }
public NavigationPoint(Transform worldTransform) public NavigationPoint(Transform worldTransform) {
{
WorldPosition = worldTransform.origin; WorldPosition = worldTransform.origin;
WorldOrientation = worldTransform.basis.Quat(); WorldOrientation = worldTransform.basis.Quat();
WorldAngle = Globals.CalcPlaneAngle(worldTransform); WorldAngle = Globals.CalcPlaneAngle(worldTransform);
Flags = NavigationFlags.Position | NavigationFlags.Orientation; Flags = NavigationFlags.Position | NavigationFlags.Orientation;
} }
public bool IsReached(Transform worldTransform) public bool IsReached(Transform worldTransform) {
{ bool goalReached = false;
var goalReached = false; Vector2 positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
var positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
WorldPosition.z - worldTransform.origin.z); WorldPosition.z - worldTransform.origin.z);
var positionErrorSquared = positionError.LengthSquared(); float positionErrorSquared = positionError.LengthSquared();
worldTransform.basis.Quat(); worldTransform.basis.Quat();
var orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation)); float orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
var angleError = Mathf.Abs(Globals.CalcPlaneAngle(worldTransform) - WorldAngle); float angleError = Mathf.Abs(Globals.CalcPlaneAngle(worldTransform) - WorldAngle);
if (Flags.HasFlag(NavigationFlags.Position) if (Flags.HasFlag(NavigationFlags.Position)
&& Flags.HasFlag(NavigationFlags.Orientation) && Flags.HasFlag(NavigationFlags.Orientation)
&& positionErrorSquared < Globals.EpsPositionSquared && positionErrorSquared < Globals.EpsPositionSquared
&& angleError < Globals.EpsRadians) && angleError < Globals.EpsRadians) {
{
goalReached = true; goalReached = true;
} } else if (Flags == NavigationFlags.Position &&
else if (Flags == NavigationFlags.Position && positionErrorSquared < Globals.EpsPositionSquared) {
positionErrorSquared < Globals.EpsPositionSquared)
{
goalReached = true; goalReached = true;
} } else if (Flags == NavigationFlags.Orientation &&
else if (Flags == NavigationFlags.Orientation && angleError < Globals.EpsRadians) {
angleError < Globals.EpsRadians)
{
goalReached = true; goalReached = true;
} }

View File

@ -1,47 +1,39 @@
using System;
using Godot; using Godot;
namespace GodotComponentTest.utils; namespace GodotComponentTest.utils;
public class Plane2D public class Plane2D {
{
public static readonly float DistancePrecision = 1.0e-5f; public static readonly float DistancePrecision = 1.0e-5f;
private Vector2 _planePoint; private Vector2 _planePoint;
public Vector2 Normal; public Vector2 Normal;
public Plane2D(Vector2 planePoint, Vector2 normal) public Plane2D(Vector2 planePoint, Vector2 normal) {
{
_planePoint = planePoint; _planePoint = planePoint;
Normal = normal; Normal = normal;
} }
public float DistanceToPoint(Vector2 point) public float DistanceToPoint(Vector2 point) {
{
return (point - _planePoint).Dot(Normal); return (point - _planePoint).Dot(Normal);
} }
public bool IsParallelToDir(Vector2 dir) public bool IsParallelToDir(Vector2 dir) {
{
float normalDotDir = Normal.Dot(dir); float normalDotDir = Normal.Dot(dir);
return (Mathf.Abs(normalDotDir) <= Plane2D.DistancePrecision); return Mathf.Abs(normalDotDir) <= DistancePrecision;
} }
public float DistanceToLineSegment(Vector2 point, Vector2 dir) public float DistanceToLineSegment(Vector2 point, Vector2 dir) {
{
float normalDotDir = Normal.Dot(dir); float normalDotDir = Normal.Dot(dir);
if (Mathf.Abs(normalDotDir) > Plane2D.DistancePrecision) if (Mathf.Abs(normalDotDir) > DistancePrecision) {
{
return (_planePoint.Dot(Normal) - point.Dot(Normal)) / normalDotDir; return (_planePoint.Dot(Normal) - point.Dot(Normal)) / normalDotDir;
} }
if (normalDotDir < 0) if (normalDotDir < 0) {
{ return float.PositiveInfinity;
return Single.PositiveInfinity;
} }
return Single.NegativeInfinity; return float.NegativeInfinity;
} }
} }

View File

@ -1,23 +1,19 @@
// Based on: allenchou.net/2015/04/game-math-precise-control-over-numeric-springing/ // Based on: allenchou.net/2015/04/game-math-precise-control-over-numeric-springing/
using Godot;
using System;
using System.Diagnostics; using System.Diagnostics;
using Godot;
public class SpringDamper public class SpringDamper {
{
public float omega = 1; public float omega = 1;
public float zeta = 1; public float zeta = 1;
public SpringDamper(float osc_freq = 1.0f, float osc_red = 0.1f, float osc_red_h = 1.0f) public SpringDamper(float osc_freq = 1.0f, float osc_red = 0.1f, float osc_red_h = 1.0f) {
{
Debug.Assert(osc_red > 0.001 && osc_red < 0.999); Debug.Assert(osc_red > 0.001 && osc_red < 0.999);
omega = osc_freq * 2 * Mathf.Pi; omega = osc_freq * 2 * Mathf.Pi;
zeta = Mathf.Log(1.0f - osc_red) / (-omega * osc_red_h); zeta = Mathf.Log(1.0f - osc_red) / (-omega * osc_red_h);
} }
public (float, float) Calc(float x, float v, float xt, float h) public (float, float) Calc(float x, float v, float xt, float h) {
{
float f = 1 + 2 * h * zeta * omega; float f = 1 + 2 * h * zeta * omega;
float oo = omega * omega; float oo = omega * omega;
float hoo = oo * h; float hoo = oo * h;
@ -30,8 +26,7 @@ public class SpringDamper
return (det_x * det_inv, det_v * det_inv); return (det_x * det_inv, det_v * det_inv);
} }
public (Vector3, Vector3) Calc(Vector3 x, Vector3 v, Vector3 xt, float h) public (Vector3, Vector3) Calc(Vector3 x, Vector3 v, Vector3 xt, float h) {
{
float f = 1 + 2 * h * zeta * omega; float f = 1 + 2 * h * zeta * omega;
float oo = omega * omega; float oo = omega * omega;
float hoo = oo * h; float hoo = oo * h;
@ -44,27 +39,24 @@ public class SpringDamper
return (det_x * det_inv, det_v * det_inv); return (det_x * det_inv, det_v * det_inv);
} }
public (Vector3, Vector3) CalcClampedSpeed(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax) public (Vector3, Vector3) CalcClampedSpeed(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax) {
{ (Vector3, Vector3) defaultResult = Calc(x, v, xt, h);
var defaultResult = Calc(x, v, xt, h);
Vector3 x_new = defaultResult.Item1; Vector3 x_new = defaultResult.Item1;
Vector3 vel_new = (x_new - x) / h; Vector3 vel_new = (x_new - x) / h;
float speed_new = vel_new.Length(); float speed_new = vel_new.Length();
if (speed_new > speedMax) if (speed_new > speedMax) {
{ vel_new = vel_new / speed_new * speedMax;
vel_new = (vel_new / speed_new) * speedMax;
x_new = x + vel_new * h; x_new = x + vel_new * h;
} }
return (x_new, vel_new); return (x_new, vel_new);
} }
public (Vector3, Vector3) CalcClampedSpeedXZ(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax) public (Vector3, Vector3) CalcClampedSpeedXZ(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax) {
{ (float, float) result_x = Calc(x.x, v.x, xt.x, h);
var result_x = Calc(x.x, v.x, xt.x, h); (float, float) result_z = Calc(x.z, v.z, xt.z, h);
var result_z = Calc(x.z, v.z, xt.z, h);
Vector3 x_new = x; Vector3 x_new = x;
Vector3 v_new = v; Vector3 v_new = v;
@ -74,24 +66,23 @@ public class SpringDamper
x_new.z = result_z.Item1; x_new.z = result_z.Item1;
v_new.z = result_z.Item2; v_new.z = result_z.Item2;
Vector3 result_v_xz = new Vector3( Vector3 result_v_xz = new(
(result_x.Item1 - x.x) / h, (result_x.Item1 - x.x) / h,
0, 0,
(result_z.Item1 - x.z) / h); (result_z.Item1 - x.z) / h);
float speed_new = result_v_xz.Length(); float speed_new = result_v_xz.Length();
if (speed_new > speedMax) if (speed_new > speedMax) {
{ result_v_xz = result_v_xz / speed_new * speedMax;
result_v_xz = (result_v_xz) / speed_new * speedMax;
x_new.x = x.x + result_v_xz.x * h; x_new.x = x.x + result_v_xz.x * h;
x_new.z = x.z + result_v_xz.z * h; x_new.z = x.z + result_v_xz.z * h;
} }
v.x = result_v_xz.x; v.x = result_v_xz.x;
v.z = result_v_xz.z; v.z = result_v_xz.z;
return (x_new, v_new); return (x_new, v_new);
} }
} }