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;
public static class Globals
{
public static class Globals {
public const float EpsPosition = 0.01f;
public const float EpsPositionSquared = EpsPosition * EpsPosition;
public const float EpsRadians = 0.1f * Mathf.Pi / 180f;
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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,13 @@
using Godot;
using System;
public class MovableComponent : Component
{
public class MovableComponent : Component {
public Vector3 targetPosition = Vector3.Zero;
public Vector3 currentPosition = Vector3.Zero;
public Vector3 currentVelocity = Vector3.Zero;
public float targetAngle = 0;
public float currentAngle = 0;
public float currentAngularVelocity = 0;
public float targetAngle;
public float currentAngle;
public float currentAngularVelocity;
[Export] public float maxSpeed = 3;
@ -17,62 +15,55 @@ public class MovableComponent : Component
private SpringDamper _angleSpringDamper;
[Signal]
delegate void PositionUpdated(Vector3 newPosition);
private delegate void PositionUpdated(Vector3 newPosition);
[Signal]
delegate void OrientationUpdated(float newAngle);
private delegate void OrientationUpdated(float newAngle);
// 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);
_angleSpringDamper = new SpringDamper(4, 0.99f, 0.5f);
}
// 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;
if (targetError.LengthSquared() > 0.01)
{
if (targetError.LengthSquared() > 0.01) {
Vector3 targetDir = targetError.Normalized();
targetAngle = new Vector3(0, 0, 1).SignedAngleTo(targetDir, Vector3.Up);
if (targetAngle - currentAngle > Mathf.Pi)
{
if (targetAngle - currentAngle > Mathf.Pi) {
currentAngle += 2 * Mathf.Pi;
}
else if (targetAngle - currentAngle < -Mathf.Pi)
{
} else if (targetAngle - currentAngle < -Mathf.Pi) {
currentAngle -= 2 * Mathf.Pi;
}
}
if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01)
{
var springDamperResult =
if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01) {
(float, float) springDamperResult =
_angleSpringDamper.Calc(currentAngle, currentAngularVelocity, targetAngle, delta);
currentAngle = springDamperResult.Item1;
currentAngularVelocity = springDamperResult.Item2;
EmitSignal("OrientationUpdated", this.currentAngle);
EmitSignal("OrientationUpdated", currentAngle);
}
if ((Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3)
&& (targetPosition - currentPosition).LengthSquared() > 0.01)
{
var springDamperResult =
if (Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3
&& (targetPosition - currentPosition).LengthSquared() > 0.01) {
(Vector3, Vector3) springDamperResult =
_positionSpringDamper.CalcClampedSpeedXZ(currentPosition, currentVelocity, targetPosition, delta,
maxSpeed);
currentPosition = springDamperResult.Item1;
currentVelocity = springDamperResult.Item2;
}
currentVelocity.y = currentVelocity.y - 9.81f * delta;
currentPosition.y = currentPosition.y + currentVelocity.y * delta;
EmitSignal("PositionUpdated", currentPosition);
}
}

View File

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

View File

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

View File

@ -1,11 +1,6 @@
using Godot;
using System;
public class Axe : StaticBody
{
public class Axe : StaticBody {
// 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.entities;
public class Chest : Entity, IInteractionInterface
{
public class Chest : Entity, IInteractionInterface {
// resources
private readonly PackedScene _goldBarScene = GD.Load<PackedScene>("res://entities/GoldBar.tscn");
public enum LidState
{
public enum LidState {
Closed,
Open
}
@ -29,8 +27,7 @@ public class Chest : Entity, IInteractionInterface
private delegate void ChestOpened(Entity entity);
// 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");
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
@ -42,64 +39,51 @@ public class Chest : Entity, IInteractionInterface
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex)
{
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
int shapeIndex) {
if (IsMouseOver && inputEvent is InputEventMouseButton) {
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed)
{
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
EmitSignal("EntityClicked", this);
}
}
}
public void OnAreaMouseEntered()
{
public void OnAreaMouseEntered() {
IsMouseOver = true;
SpatialMaterial overrideMaterial = new SpatialMaterial();
SpatialMaterial overrideMaterial = new();
overrideMaterial.AlbedoColor = new Color(1, 0, 0);
_mesh.MaterialOverride = overrideMaterial;
}
public void OnAreaMouseExited()
{
public void OnAreaMouseExited() {
IsMouseOver = false;
_mesh.MaterialOverride = null;
}
public void OnInteractionStart()
{
public void OnInteractionStart() {
_animationPlayer.Stop();
if (State == LidState.Closed)
{
if (State == LidState.Closed) {
State = LidState.Open;
_animationPlayer.Play("ChestOpen");
}
else
{
} else {
_animationPlayer.PlayBackwards("ChestOpen");
State = LidState.Closed;
}
}
public void OnInteractionEnd()
{
}
public void OnInteractionEnd() { }
public void OnChestOpened()
{
public void OnChestOpened() {
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();
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(
(GD.Randf() * 2f - 1f) * 2,
5 + GD.Randf() * 0.3f,
@ -108,8 +92,7 @@ public class Chest : Entity, IInteractionInterface
GetParent().AddChild(bar);
}
if (InteractionComponent != null)
{
if (InteractionComponent != null) {
InteractionComponent.EndInteraction();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,76 +1,63 @@
using System.Collections.Generic;
using System.Linq;
using Godot;
namespace GodotComponentTest.utils;
public class DebugGeometry : Spatial
{
public class DebugGeometry : Spatial {
private ImmediateGeometry _immediateGeometry;
private List<Transform> _transformStack;
private Transform _currentTransform = Transform.Identity;
private Transform _currentTransform = Transform.Identity;
public override void _Ready()
{
public override void _Ready() {
base._Ready();
_immediateGeometry = (ImmediateGeometry)FindNode("ImmediateGeometry");
Clear();
}
public void Clear()
{
public void Clear() {
_immediateGeometry.Clear();
_transformStack = new List<Transform>();
_transformStack.Add(Transform.Identity);
}
public void PushTransform(Transform transform)
{
public void PushTransform(Transform transform) {
_transformStack.Add(transform);
_currentTransform = transform;
}
public void PushTranslated(Vector3 offset)
{
public void PushTranslated(Vector3 offset) {
PushTransform(_currentTransform.Translated(offset));
}
public void PopTransform()
{
public void PopTransform() {
_transformStack.RemoveAt(_transformStack.Count - 1);
_currentTransform = PeekTransform();
}
public Transform PeekTransform()
{
public Transform PeekTransform() {
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);
}
public void End()
{
public void End() {
_immediateGeometry.End();
}
public void AddVertex(Vector3 vertex)
{
public void AddVertex(Vector3 vertex) {
_immediateGeometry.AddVertex(_currentTransform.Xform(vertex));
}
public void SetColor(Color color)
{
public void SetColor(Color color) {
_immediateGeometry.SetColor(color);
}
public void AddBox(Vector3 extents)
{
public void AddBox(Vector3 extents) {
Transform currentTransform = PeekTransform();
// bottom square
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));
// 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));
@ -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));
// 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));
}
}

View File

@ -5,43 +5,39 @@ using System.Collections.Generic;
using System.Linq;
using Godot;
namespace Namespace
{
using UnderlyingType = UInt64;
namespace Namespace;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ExportFlagsEnumAttribute : ExportAttribute
{
public ExportFlagsEnumAttribute(Type enumType)
: base(PropertyHint.Flags, GetFlagsEnumHintString(enumType))
{ }
using UnderlyingType = UInt64;
private static string GetFlagsEnumHintString(Type enumType)
{
Dictionary<UnderlyingType, List<string>> flagNamesByFlag = new Dictionary<UnderlyingType, List<string>>();
UnderlyingType flag = (UnderlyingType)1;
foreach (string name in Enum.GetNames(enumType))
{
UnderlyingType value = (UnderlyingType)Convert.ChangeType(Enum.Parse(enumType, name), typeof(UnderlyingType));
while (value > flag)
{
if (!flagNamesByFlag.ContainsKey(flag))
{
flagNamesByFlag.Add(flag, new List<string>());
}
flag <<= 1;
}
if (value == flag)
{
if (!flagNamesByFlag.TryGetValue(flag, out List<string> names))
{
names = new List<string>();
flagNamesByFlag.Add(flag, names);
}
names.Add(name);
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ExportFlagsEnumAttribute : ExportAttribute {
public ExportFlagsEnumAttribute(Type enumType)
: base(PropertyHint.Flags, GetFlagsEnumHintString(enumType)) { }
private static string GetFlagsEnumHintString(Type enumType) {
Dictionary<UnderlyingType, List<string>> flagNamesByFlag = new();
UnderlyingType flag = 1;
foreach (string name in Enum.GetNames(enumType)) {
UnderlyingType value =
(UnderlyingType)Convert.ChangeType(Enum.Parse(enumType, name), typeof(UnderlyingType));
while (value > flag) {
if (!flagNamesByFlag.ContainsKey(flag)) {
flagNamesByFlag.Add(flag, new List<string>());
}
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 Godot;
public class NavigationPoint
{
public class NavigationPoint {
[Flags]
public enum NavigationFlags
{
public enum NavigationFlags {
Position = 1,
Orientation = 2
}
@ -16,52 +14,43 @@ public class NavigationPoint
public Vector3 WorldPosition = Vector3.Zero;
public NavigationPoint(Vector3 worldPosition)
{
public NavigationPoint(Vector3 worldPosition) {
WorldPosition = worldPosition;
Flags = NavigationFlags.Position;
}
public NavigationPoint(Quat worldOrientation)
{
public NavigationPoint(Quat worldOrientation) {
WorldOrientation = worldOrientation;
Flags = NavigationFlags.Orientation;
}
public NavigationPoint(Transform worldTransform)
{
public NavigationPoint(Transform worldTransform) {
WorldPosition = worldTransform.origin;
WorldOrientation = worldTransform.basis.Quat();
WorldAngle = Globals.CalcPlaneAngle(worldTransform);
Flags = NavigationFlags.Position | NavigationFlags.Orientation;
}
public bool IsReached(Transform worldTransform)
{
var goalReached = false;
var positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
public bool IsReached(Transform worldTransform) {
bool goalReached = false;
Vector2 positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
WorldPosition.z - worldTransform.origin.z);
var positionErrorSquared = positionError.LengthSquared();
float positionErrorSquared = positionError.LengthSquared();
worldTransform.basis.Quat();
var orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
var angleError = Mathf.Abs(Globals.CalcPlaneAngle(worldTransform) - WorldAngle);
float orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
float angleError = Mathf.Abs(Globals.CalcPlaneAngle(worldTransform) - WorldAngle);
if (Flags.HasFlag(NavigationFlags.Position)
&& Flags.HasFlag(NavigationFlags.Orientation)
&& positionErrorSquared < Globals.EpsPositionSquared
&& angleError < Globals.EpsRadians)
{
&& angleError < Globals.EpsRadians) {
goalReached = true;
}
else if (Flags == NavigationFlags.Position &&
positionErrorSquared < Globals.EpsPositionSquared)
{
} else if (Flags == NavigationFlags.Position &&
positionErrorSquared < Globals.EpsPositionSquared) {
goalReached = true;
}
else if (Flags == NavigationFlags.Orientation &&
angleError < Globals.EpsRadians)
{
} else if (Flags == NavigationFlags.Orientation &&
angleError < Globals.EpsRadians) {
goalReached = true;
}

View File

@ -1,47 +1,39 @@
using System;
using Godot;
namespace GodotComponentTest.utils;
public class Plane2D
{
public class Plane2D {
public static readonly float DistancePrecision = 1.0e-5f;
private Vector2 _planePoint;
public Vector2 Normal;
public Plane2D(Vector2 planePoint, Vector2 normal)
{
public Plane2D(Vector2 planePoint, Vector2 normal) {
_planePoint = planePoint;
Normal = normal;
}
public float DistanceToPoint(Vector2 point)
{
public float DistanceToPoint(Vector2 point) {
return (point - _planePoint).Dot(Normal);
}
public bool IsParallelToDir(Vector2 dir)
{
public bool IsParallelToDir(Vector2 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);
if (Mathf.Abs(normalDotDir) > Plane2D.DistancePrecision)
{
if (Mathf.Abs(normalDotDir) > DistancePrecision) {
return (_planePoint.Dot(Normal) - point.Dot(Normal)) / normalDotDir;
}
if (normalDotDir < 0)
{
return Single.PositiveInfinity;
if (normalDotDir < 0) {
return float.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/
using Godot;
using System;
using System.Diagnostics;
using Godot;
public class SpringDamper
{
public class SpringDamper {
public float omega = 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);
omega = osc_freq * 2 * Mathf.Pi;
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 oo = omega * omega;
float hoo = oo * h;
@ -30,8 +26,7 @@ public class SpringDamper
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 oo = omega * omega;
float hoo = oo * h;
@ -44,27 +39,24 @@ public class SpringDamper
return (det_x * det_inv, det_v * det_inv);
}
public (Vector3, Vector3) CalcClampedSpeed(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax)
{
var defaultResult = Calc(x, v, xt, h);
public (Vector3, Vector3) CalcClampedSpeed(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax) {
(Vector3, Vector3) defaultResult = Calc(x, v, xt, h);
Vector3 x_new = defaultResult.Item1;
Vector3 vel_new = (x_new - x) / h;
float speed_new = vel_new.Length();
if (speed_new > speedMax)
{
vel_new = (vel_new / speed_new) * speedMax;
if (speed_new > speedMax) {
vel_new = vel_new / speed_new * speedMax;
x_new = x + vel_new * h;
}
return (x_new, vel_new);
}
public (Vector3, Vector3) CalcClampedSpeedXZ(Vector3 x, Vector3 v, Vector3 xt, float h, float speedMax)
{
var result_x = Calc(x.x, v.x, xt.x, h);
var result_z = Calc(x.z, v.z, xt.z, h);
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);
(float, float) result_z = Calc(x.z, v.z, xt.z, h);
Vector3 x_new = x;
Vector3 v_new = v;
@ -74,24 +66,23 @@ public class SpringDamper
x_new.z = result_z.Item1;
v_new.z = result_z.Item2;
Vector3 result_v_xz = new Vector3(
Vector3 result_v_xz = new(
(result_x.Item1 - x.x) / h,
0,
(result_z.Item1 - x.z) / h);
float speed_new = result_v_xz.Length();
if (speed_new > speedMax)
{
result_v_xz = (result_v_xz) / speed_new * speedMax;
if (speed_new > speedMax) {
result_v_xz = result_v_xz / speed_new * speedMax;
x_new.x = x.x + result_v_xz.x * h;
x_new.z = x.z + result_v_xz.z * h;
}
v.x = result_v_xz.x;
v.z = result_v_xz.z;
return (x_new, v_new);
}
}