Compare commits

...

7 Commits

Author SHA1 Message Date
Martin Felis cfb731c27e Intermediate commit 2023-12-14 17:27:07 +01:00
Martin Felis bfd5eef2f5 Mass reformatting. 2023-11-18 22:32:57 +01:00
Martin Felis 2109c6b6ec Moved path planning to world class.
- Tree entities now are obstacles in navigation planning.
 - Once chopped obstacles are removed.
2023-11-18 22:11:34 +01:00
Martin Felis a37b028b39 Refactoring now functional 2023-11-15 20:57:25 +01:00
Martin Felis fcc2fdb8d3 Fixed missing ToolAttachment 2023-11-13 20:10:05 +01:00
Martin Felis 71162f9420 Minor cleanup. 2023-11-11 10:58:35 +01:00
Martin Felis d2611c6073 Interactions can now end themselves. 2023-11-10 20:22:55 +01:00
58 changed files with 2282 additions and 2230 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

@ -21,7 +21,7 @@ compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=true
flags/repeat=1
flags/filter=false
flags/mipmaps=true
flags/anisotropic=false

View File

@ -19,7 +19,7 @@ compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=2
flags/repeat=true
flags/repeat=0
flags/filter=false
flags/mipmaps=true
flags/anisotropic=false

View File

@ -2,28 +2,30 @@
importer="texture"
type="StreamTexture"
path="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.stex"
path.s3tc="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.s3tc.stex"
path.etc2="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.etc2.stex"
metadata={
"vram_texture": false
"imported_formats": [ "s3tc", "etc2" ],
"vram_texture": true
}
[deps]
source_file="res://assets/IslandMasks/IslandMask2.png"
dest_files=[ "res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.stex" ]
dest_files=[ "res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.s3tc.stex", "res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.etc2.stex" ]
[params]
compress/mode=0
compress/mode=2
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/repeat=true
flags/filter=true
flags/mipmaps=false
flags/mipmaps=true
flags/anisotropic=false
flags/srgb=2
flags/srgb=1
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
@ -31,5 +33,5 @@ process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
detect_3d=false
svg/scale=1.0

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,59 +13,47 @@ 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)
{
Vector2 planeTargetDirection = new Vector2(DirectionToTarget.x, DirectionToTarget.z);
private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition) {
Vector2 planeTargetDirection = new(DirectionToTarget.x, DirectionToTarget.z);
Vector2 planeVelocity = new Vector2(entity.Velocity.x, entity.Velocity.z);
Vector2 planeVelocity = new(entity.Velocity.x, entity.Velocity.z);
// GD.Print("-- Step: distance: " + targetDistance + " dir: " + planeTargetDirection + " speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
planeVelocity -= planeVelocity * Damping;
// GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
if (DistanceToTarget < 0.01)
{
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());
var projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
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);
}
var planeSpeed = planeVelocity.Length();
if (planeSpeed > MaxSpeed) planeVelocity *= MaxSpeed / planeSpeed;
}
else
{
float planeSpeed = planeVelocity.Length();
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,14 +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);
[Signal]
private delegate void InteractionEnd(Spatial owningEntity, Spatial targetEntity);
public void EndInteraction() {
hasStopped = true;
}
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

@ -7,8 +7,7 @@ using GodotComponentTest.utils;
/// <summary>
/// </summary>
public class NavigationComponent : Spatial
{
public class NavigationComponent : Spatial {
public World World { set; get; }
public Vector3 CurrentGoalPositionWorld { get; private set; } = Vector3.Zero;
public float CurrentGoalAngleWorld { get; private set; }
@ -21,39 +20,29 @@ public class NavigationComponent : Spatial
private List<NavigationPoint> _planningPathWorldNavigationPoints = new();
private List<NavigationPoint> _smoothedPathWorldNavigationPoints = new();
public override void _Ready()
{
public override void _Ready() {
base._Ready();
_pathWorldNavigationPoints = new List<NavigationPoint>();
}
public override void _Process(float delta)
{
public override void _Process(float delta) {
Debug.Assert(World != null);
}
public void PlanSmoothedPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
{
public void PlanSmoothedPath(Entity body, Transform fromTransformWorld, NavigationPoint navigationPoint) {
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
{
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)) {
FindPath(body, fromTransformWorld.origin, navigationPoint);
}
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
} else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) {
FindPath(body, fromTransformWorld.origin, navigationPoint.WorldPosition);
}
else
{
} else {
throw new NotImplementedException();
}
}
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
{
var fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
if (World.HexGrid.GetHexCost(fromCell) == 0)
{
public void FindPath(Entity entity, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
HexCell fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
if (World.HexGrid.GetHexCost(fromCell) == 0) {
GD.Print("Invalid starting point for FindPath(): returning empty path.");
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
_planningPathWorldNavigationPoints.Add(new NavigationPoint(fromPositionWorld));
@ -61,11 +50,10 @@ public class NavigationComponent : Spatial
return;
}
var toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
HexCell toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
toCell = World.HexGrid.GetClosestWalkableCell(fromCell, toCell);
if (World.HexGrid.GetHexCost(toCell) == 0)
{
if (World.HexGrid.GetHexCost(toCell) == 0) {
GD.Print("Invalid target point for FindPath(): returning empty path.");
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
_planningPathWorldNavigationPoints.Add(new NavigationPoint(fromPositionWorld));
@ -73,12 +61,11 @@ public class NavigationComponent : Spatial
return;
}
var path = World.HexGrid.FindPath(fromCell, toCell);
List<HexCell> path = World.FindPath(entity, fromCell, toCell);
// Generate grid navigation points
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
foreach (var index in Enumerable.Range(0, path.Count))
{
foreach (int index in Enumerable.Range(0, path.Count)) {
_planningPathWorldNavigationPoints.Add(
new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(path[index].OffsetCoords)));
}
@ -86,40 +73,36 @@ public class NavigationComponent : Spatial
// Ensure the last point coincides with the target position
if (_planningPathWorldNavigationPoints.Count > 0 &&
(_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() <
0.5f * 0.5f)
{
0.5f * 0.5f) {
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition =
toPositionWorld;
}
// Perform smoothing
_planningPathSmoothedWorldNavigationPoints = SmoothPath(body, _planningPathWorldNavigationPoints);
_planningPathSmoothedWorldNavigationPoints = World.SmoothPath(entity, _planningPathWorldNavigationPoints);
// Ensure starting point is the current position
if (_planningPathSmoothedWorldNavigationPoints.Count > 0)
{
if (_planningPathSmoothedWorldNavigationPoints.Count > 0) {
_planningPathSmoothedWorldNavigationPoints[0] = new NavigationPoint(fromPositionWorld);
}
}
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, NavigationPoint navigationPoint)
{
FindPath(body, fromPositionWorld, navigationPoint.WorldPosition);
public void FindPath(Entity entity, Vector3 fromPositionWorld, NavigationPoint navigationPoint) {
FindPath(entity, fromPositionWorld, navigationPoint.WorldPosition);
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint;
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] =
navigationPoint;
}
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
{
var fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld);
var toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
public void PlanGridPath(Entity entity, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
Vector2 fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld);
Vector2 toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
var fromCell = new HexCell();
HexCell fromCell = new();
fromCell.OffsetCoords = fromPositionOffset;
var toCell = new HexCell();
HexCell toCell = new();
toCell.OffsetCoords = toPositionOffset;
_path = fromCell.LineTo(toCell);
@ -129,26 +112,23 @@ public class NavigationComponent : Spatial
_pathWorldNavigationPoints.Add(
new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset)));
foreach (var index in Enumerable.Range(1, _path.Length - 1))
{
foreach (int index in Enumerable.Range(1, _path.Length - 1)) {
_pathWorldNavigationPoints.Add(
new NavigationPoint(World.GetHexCenterFromOffset(_path[index].OffsetCoords)));
}
if ((fromPositionWorld - World.GetHexCenterFromOffset(toCell.OffsetCoords)).LengthSquared() >
Globals.EpsPositionSquared)
{
// Remove the last one, because it is only the position rounded to HexGrid coordinates.
if (_pathWorldNavigationPoints.Count > 0)
{
{
if (_pathWorldNavigationPoints.Count > 0) {
_pathWorldNavigationPoints.RemoveAt(_pathWorldNavigationPoints.Count - 1);
}
}
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
if (_pathWorldNavigationPoints.Count > 2)
{
_smoothedPathWorldNavigationPoints = SmoothPath(body, _pathWorldNavigationPoints);
if (_pathWorldNavigationPoints.Count > 2) {
_smoothedPathWorldNavigationPoints = SmoothPath(entity, _pathWorldNavigationPoints);
_pathWorldNavigationPoints = _smoothedPathWorldNavigationPoints;
}
@ -156,36 +136,28 @@ public class NavigationComponent : Spatial
}
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld,
Quat toWorldOrientation)
{
PlanGridPath(body, fromPositionWorld, toPositionWorld);
public void PlanGridPath(Entity entity, Vector3 fromPositionWorld, Vector3 toPositionWorld,
Quat toWorldOrientation) {
PlanGridPath(entity, fromPositionWorld, toPositionWorld);
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
}
public void PlanGridPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
{
public void PlanGridPath(Entity entity, Transform fromTransformWorld, NavigationPoint navigationPoint) {
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
{
PlanGridPath(body, fromTransformWorld.origin, navigationPoint.WorldPosition,
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)) {
PlanGridPath(entity, fromTransformWorld.origin, navigationPoint.WorldPosition,
navigationPoint.WorldOrientation);
}
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
PlanGridPath(body, fromTransformWorld.origin, navigationPoint.WorldPosition);
}
else
{
} else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) {
PlanGridPath(entity, fromTransformWorld.origin, navigationPoint.WorldPosition);
} else {
throw new NotImplementedException();
}
}
public void PlanDirectPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
{
public void PlanDirectPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
_pathWorldNavigationPoints.Clear();
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
@ -194,23 +166,20 @@ public class NavigationComponent : Spatial
public void PlanDirectPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld,
Quat toWorldOrientation)
{
Quat toWorldOrientation) {
PlanDirectPath(body, fromPositionWorld, toPositionWorld);
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
}
public bool HasPathCollision(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
{
public bool HasPathCollision(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
Vector3 fromPositionLocal = GlobalTransform.XformInv(fromPositionWorld);
Vector3 toPositionLocal = GlobalTransform.XformInv(toPositionWorld);
Vector3 relativeVelocity = GlobalTransform.basis.Xform(toPositionLocal - fromPositionLocal);
KinematicCollision moveCollision = body.MoveAndCollide(relativeVelocity, true, true, true);
if (moveCollision != null)
{
if (moveCollision != null) {
Spatial colliderSpatial = moveCollision.Collider as Spatial;
// GD.Print("Found collision: " + moveCollision.Collider + " (" + colliderSpatial.Name + ")");
return true;
@ -220,37 +189,30 @@ public class NavigationComponent : Spatial
}
public bool CheckSweptTriangleCellCollision(Vector3 startWorld, Vector3 endWorld, float radius)
{
Vector2 startPlane = new Vector2(startWorld.x, startWorld.z);
Vector2 endPlane = new Vector2(endWorld.x, endWorld.z);
public bool CheckSweptTriangleCellCollision(Vector3 startWorld, Vector3 endWorld, float radius) {
Vector2 startPlane = new(startWorld.x, startWorld.z);
Vector2 endPlane = new(endWorld.x, endWorld.z);
Vector2 directionPlane = (endPlane - startPlane).Normalized();
Vector2 sidePlane = directionPlane.Rotated(Mathf.Pi * 0.5f);
List<HexCell> cells =
World.HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius);
foreach (HexCell cell in cells)
{
if (World.HexGrid.GetHexCost(cell) == 0)
{
foreach (HexCell cell in cells) {
if (World.HexGrid.GetHexCost(cell) == 0) {
return true;
}
}
cells = World.HexGrid.GetCellsForLine(startPlane + sidePlane * radius, endPlane + sidePlane * radius);
foreach (HexCell cell in cells)
{
if (World.HexGrid.GetHexCost(cell) == 0)
{
foreach (HexCell cell in cells) {
if (World.HexGrid.GetHexCost(cell) == 0) {
return true;
}
}
cells = World.HexGrid.GetCellsForLine(startPlane - sidePlane * radius, endPlane - sidePlane * radius);
foreach (HexCell cell in cells)
{
if (World.HexGrid.GetHexCost(cell) == 0)
{
foreach (HexCell cell in cells) {
if (World.HexGrid.GetHexCost(cell) == 0) {
return true;
}
}
@ -258,29 +220,24 @@ public class NavigationComponent : Spatial
return false;
}
public List<NavigationPoint> SmoothPath(KinematicBody body, List<NavigationPoint> navigationPoints)
{
if (navigationPoints.Count <= 2)
{
public List<NavigationPoint> SmoothPath(KinematicBody body, List<NavigationPoint> navigationPoints) {
if (navigationPoints.Count <= 2) {
return navigationPoints;
}
Vector3 bodyGlobalTranslation = body.GlobalTranslation;
List<NavigationPoint> smoothedPath = new List<NavigationPoint>();
List<NavigationPoint> smoothedPath = new();
int startIndex = 0;
int endIndex = navigationPoints.Count > 1 ? 1 : 0;
smoothedPath.Add(navigationPoints[startIndex]);
Vector3 startPoint = navigationPoints[startIndex].WorldPosition;
while (endIndex != navigationPoints.Count)
{
while (endIndex != navigationPoints.Count) {
Vector3 endPoint = navigationPoints[endIndex].WorldPosition;
if (CheckSweptTriangleCellCollision(startPoint, endPoint, 0.27f))
{
if (endIndex - startIndex == 1)
{
if (CheckSweptTriangleCellCollision(startPoint, endPoint, 0.27f)) {
if (endIndex - startIndex == 1) {
GD.Print("Aborting SmoothPath: input path passes through collision geometry.");
body.GlobalTranslation = bodyGlobalTranslation;
return smoothedPath;
@ -294,8 +251,7 @@ public class NavigationComponent : Spatial
continue;
}
if (endIndex == navigationPoints.Count - 1)
{
if (endIndex == navigationPoints.Count - 1) {
break;
}
@ -308,34 +264,25 @@ public class NavigationComponent : Spatial
return smoothedPath;
}
public void PlanDirectPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
{
public void PlanDirectPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint) {
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
{
&& navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)) {
PlanDirectPath(body, fromTransformWorld.origin, navigationPoint.WorldPosition,
navigationPoint.WorldOrientation);
}
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
} else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) {
PlanDirectPath(body, fromTransformWorld.origin, navigationPoint.WorldPosition);
}
else
{
} else {
throw new NotImplementedException();
}
}
public void ActivatePlannedPath()
{
public void ActivatePlannedPath() {
_pathWorldNavigationPoints = _planningPathSmoothedWorldNavigationPoints;
UpdateCurrentGoal();
}
private void UpdateCurrentGoal()
{
if (_pathWorldNavigationPoints.Count == 0)
{
private void UpdateCurrentGoal() {
if (_pathWorldNavigationPoints.Count == 0) {
return;
}
@ -344,73 +291,56 @@ public class NavigationComponent : Spatial
CurrentGoalAngleWorld = _pathWorldNavigationPoints[0].WorldAngle;
}
private void ApplyExistingTransform(Transform worldTransform)
{
if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Orientation)
{
private void ApplyExistingTransform(Transform worldTransform) {
if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Orientation) {
CurrentGoalPositionWorld = worldTransform.origin;
}
else if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Position)
{
} else if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Position) {
CurrentGoalAngleWorld = Globals.CalcPlaneAngle(worldTransform);
}
}
public void UpdateCurrentGoal(Transform currentTransformWorld)
{
if (_currentGoal == null)
{
public void UpdateCurrentGoal(Transform currentTransformWorld) {
if (_currentGoal == null) {
_currentGoal = new NavigationPoint(currentTransformWorld);
}
if (_pathWorldNavigationPoints.Count == 0)
{
if (_pathWorldNavigationPoints.Count == 0) {
CurrentGoalAngleWorld = Globals.CalcPlaneAngle(currentTransformWorld);
CurrentGoalPositionWorld = currentTransformWorld.origin;
return;
}
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) {
CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
}
else
{
} else {
CurrentGoalAngleWorld = Globals.CalcPlaneAngle(currentTransformWorld);
}
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
{
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)) {
CurrentGoalAngleWorld = _currentGoal.WorldAngle;
}
else
{
} else {
CurrentGoalAngleWorld = Globals.CalcPlaneAngle(currentTransformWorld);
}
if (_currentGoal.IsReached(currentTransformWorld))
{
if (_currentGoal.IsReached(currentTransformWorld)) {
_pathWorldNavigationPoints.RemoveAt(0);
UpdateCurrentGoal();
ApplyExistingTransform(currentTransformWorld);
}
if (_pathWorldNavigationPoints.Count == 0)
{
if (_pathWorldNavigationPoints.Count == 0) {
CurrentGoalPositionWorld = currentTransformWorld.origin;
CurrentGoalAngleWorld = Globals.CalcPlaneAngle(currentTransformWorld);
}
}
public bool IsGoalReached()
{
public bool IsGoalReached() {
return _pathWorldNavigationPoints.Count == 0;
}
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry)
{
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry) {
Vector3 yOffset = Vector3.Up * 0.1f;
debugGeometry.GlobalTransform = Transform.Identity;
@ -428,8 +358,7 @@ public class NavigationComponent : Spatial
debugGeometry.PopTransform();
Vector3 previousPoint = parentNode.GlobalTranslation;
foreach (NavigationPoint point in _pathWorldNavigationPoints)
{
foreach (NavigationPoint point in _pathWorldNavigationPoints) {
debugGeometry.AddVertex(previousPoint + yOffset);
debugGeometry.AddVertex(point.WorldPosition + yOffset);
@ -437,8 +366,7 @@ public class NavigationComponent : Spatial
}
previousPoint = parentNode.GlobalTranslation;
foreach (NavigationPoint point in _smoothedPathWorldNavigationPoints)
{
foreach (NavigationPoint point in _smoothedPathWorldNavigationPoints) {
debugGeometry.SetColor(new Color(0, 0, 1));
debugGeometry.AddVertex(previousPoint + yOffset);
debugGeometry.AddVertex(point.WorldPosition + yOffset);
@ -447,8 +375,7 @@ public class NavigationComponent : Spatial
}
previousPoint = parentNode.GlobalTranslation;
foreach (NavigationPoint point in _planningPathWorldNavigationPoints)
{
foreach (NavigationPoint point in _planningPathWorldNavigationPoints) {
debugGeometry.SetColor(new Color(1, 0, 1));
debugGeometry.AddVertex(previousPoint + yOffset);
debugGeometry.AddVertex(point.WorldPosition + yOffset);
@ -457,8 +384,7 @@ public class NavigationComponent : Spatial
}
previousPoint = parentNode.GlobalTranslation;
foreach (NavigationPoint point in _planningPathSmoothedWorldNavigationPoints)
{
foreach (NavigationPoint point in _planningPathSmoothedWorldNavigationPoints) {
debugGeometry.SetColor(new Color(1, 1, 0));
debugGeometry.AddVertex(previousPoint + yOffset);
debugGeometry.AddVertex(point.WorldPosition + yOffset);

View File

@ -1,81 +1,72 @@
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();
var interactionTask = currentTask as InteractionTask;
if (interactionTask != null) EmitSignal("StartInteraction", entity, interactionTask.TargetEntity);
do {
Task currentTask = Queue.Peek();
if (currentTask.PerformTask(entity, 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>
/// <returns>true when the Task is complete, false otherwise</returns>
public abstract bool PerformTask(Entity entity, float delta);
public abstract bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta);
}
/// <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, 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, 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,30 +1,24 @@
using Godot;
using System;
public class Entity : KinematicBody
{
public class Entity : KinematicBody {
public Vector3 Velocity { get; set; } = Vector3.Zero;
public float RotationalVelocity { get; set; } = 0;
/** Defines the angle in plane coordinates, 0 => pointing to the right/east, pi/2 pointing up/north, range [-pi,pi]. */
public float PlaneAngle
{
/**
* Defines the angle in plane coordinates, 0 => pointing to the right/east, pi/2 pointing up/north, range [-pi,pi].
*/
public float PlaneAngle {
get => Globals.CalcPlaneAngle(GlobalTransform);
set => GlobalTransform = new Transform(new Basis(Vector3.Up, value + Mathf.Pi * 0.5f), GlobalTranslation);
}
public float CalcShortestPlaneRotationToTargetDirection(Vector3 globalTargetDirection)
{
public float CalcShortestPlaneRotationToTargetDirection(Vector3 globalTargetDirection) {
float angleToTarget = Vector3.Right.SignedAngleTo(globalTargetDirection, Vector3.Up);
float currentAngle = PlaneAngle;
float delta = angleToTarget - currentAngle;
delta += (delta > Mathf.Pi) ? -Mathf.Pi * 2 : (delta < -Mathf.Pi) ? Mathf.Pi * 2 : 0;
delta += delta > Mathf.Pi ? -Mathf.Pi * 2 : delta < -Mathf.Pi ? Mathf.Pi * 2 : 0;
return delta;
}
public override void _Ready()
{
}
}

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,56 +37,51 @@ 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) GD.PushWarning("No ToolAttachement found!");
if (_toolAttachement == null) {
GD.PushWarning("No ToolAttachement found!");
}
_debugGeometry = (DebugGeometry)FindNode("DebugGeometry");
}
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);
@ -100,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);
}
@ -126,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();
}
@ -158,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);
}
@ -176,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,12 +1,13 @@
[gd_scene load_steps=14 format=2]
[gd_scene load_steps=27 format=2]
[ext_resource path="res://entities/Player.cs" type="Script" id=1]
[ext_resource path="res://components/NavigationComponent.cs" type="Script" id=2]
[ext_resource path="res://materials/debug/PlayerPhysicsGeometry.tres" type="Material" id=3]
[ext_resource path="res://assets/Characters/Pirate.tscn" type="PackedScene" id=4]
[ext_resource path="res://assets/Objects/toolAxe.tscn" type="PackedScene" id=4]
[ext_resource path="res://components/WorldInfoComponent.cs" type="Script" id=5]
[ext_resource path="res://utils/DebugGeometry.cs" type="Script" id=6]
[ext_resource path="res://components/MovableComponent.tscn" type="PackedScene" id=7]
[ext_resource path="res://assets/CreatusPiratePack/Modified/Pirate1_Rigged.glb" type="PackedScene" id=8]
[sub_resource type="CapsuleShape" id=7]
radius = 0.27
@ -23,12 +24,315 @@ radius = 1.5
[sub_resource type="SphereShape" id=23]
radius = 0.1
[sub_resource type="AnimationNodeStateMachinePlayback" id=28]
[sub_resource type="SpatialMaterial" id=25]
flags_unshaded = true
vertex_color_use_as_albedo = true
[sub_resource type="Animation" id=29]
resource_name = "ArmatureAction"
length = 0.791667
tracks/0/type = "transform"
tracks/0/path = NodePath("Armature/Skeleton:HandTip.R")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = PoolRealArray( 0, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, 5.15244e-08, -0.167621, -2.38312e-08, 0.985852, 1, 1, 1, 0.791667, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, 5.15244e-08, -0.167621, -2.38312e-08, 0.985852, 1, 1, 1 )
tracks/1/type = "transform"
tracks/1/path = NodePath("Armature/Skeleton:LowerArm.R")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = PoolRealArray( 0, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1, 0.0666667, 1, -1.49012e-08, 1.19209e-07, 9.31323e-09, 0.00109829, -8.64304e-05, 0.0102987, 0.999946, 1, 1, 1, 0.133333, 1, -4.47035e-08, -5.96046e-08, 8.56817e-08, 0.00783155, -0.00169418, 0.0734387, 0.997268, 1, 1, 1, 0.2, 1, -1.04308e-07, -1.19209e-07, 5.58794e-09, 0.018495, -0.0080773, 0.173433, 0.984639, 1, 1, 1, 0.266667, 1, -1.49012e-08, 8.9407e-08, -1.67638e-08, 0.0280373, -0.0229293, 0.262915, 0.964139, 1, 1, 1, 0.333333, 1, -1.3411e-07, 1.78814e-07, 1.15484e-07, 0.033043, -0.0493204, 0.309854, 0.948929, 1, 1, 1, 0.4, 1, -8.9407e-08, 5.96046e-08, 7.82311e-08, -0.15378, -0.132786, 0.29174, 0.93467, 1, 1, 1, 0.466667, 1, -1.19209e-07, -2.98023e-08, 3.35276e-08, -0.407158, -0.216318, 0.236834, 0.855184, 1, 1, 1, 0.533333, 1, -1.04308e-07, 5.96046e-08, 8.56817e-08, -0.452066, -0.143661, 0.218081, 0.8529, 1, 1, 1, 0.6, 1, -8.9407e-08, -5.96046e-08, 6.89179e-08, -0.315312, -0.00422263, 0.192105, 0.929331, 1, 1, 1, 0.666667, 1, -5.96046e-08, 5.96046e-08, 1.95578e-07, -0.071726, -0.000218779, 0.0546704, 0.995925, 1, 1, 1, 0.733333, 1, -8.9407e-08, 2.98023e-08, 2.98023e-08, 1.49012e-08, -2.98023e-08, 4.44089e-16, 1, 1, 1, 1, 0.791667, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1 )
tracks/2/type = "transform"
tracks/2/path = NodePath("Armature/Skeleton:UpperArm.R")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = PoolRealArray( 0, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 1.49012e-08, -2.98023e-08, -4.47035e-08, 0.0108857, -6.76363e-05, 0.000112621, 0.999941, 1, 1, 1, 0.133333, 1, 0, -2.98023e-08, -5.96046e-08, 0.0781541, -0.00133826, 0.00222821, 0.996938, 1, 1, 1, 0.2, 1, 1.49012e-08, 2.98023e-08, -1.49012e-08, 0.185823, -0.00642065, 0.0106907, 0.982504, 1, 1, 1, 0.266667, 1, 2.98023e-08, 0, -2.98023e-08, 0.281247, -0.018186, 0.0302806, 0.958985, 1, 1, 1, 0.333333, 1, 1.19209e-07, 5.96046e-08, -5.96046e-08, 0.326813, -0.0385876, 0.0642504, 0.942113, 1, 1, 1, 0.4, 1, 0, 0, 0, 0.27597, -0.130105, 0.14414, 0.941348, 1, 1, 1, 0.466667, 1, 5.96046e-08, -5.96046e-08, 2.98023e-08, 0.155307, -0.238877, 0.223903, 0.932033, 1, 1, 1, 0.533333, 1, 8.9407e-08, 0, 0, 0.00938215, -0.220572, 0.130595, 0.966543, 1, 1, 1, 0.6, 1, 1.49012e-08, 2.98023e-08, -7.45058e-08, -0.0448046, -0.0741939, -0.0355394, 0.995603, 1, 1, 1, 0.666667, 1, -4.47035e-08, -2.98023e-08, 7.45058e-08, -0.0129521, -0.00546457, -0.0102737, 0.999848, 1, 1, 1, 0.733333, 1, -2.98023e-08, -1.19209e-07, 2.98023e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1, 0.791667, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1 )
tracks/3/type = "transform"
tracks/3/path = NodePath("Armature/Skeleton:Shoulder.R")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 4.25331e-10, 2.61906e-11, 0, 0.000588988, -0.00134978, -0.00209551, 0.999997, 1, 1, 1, 0.133333, 1, 3.42889e-09, -1.28054e-10, 0, 0.00420515, -0.00963742, -0.0149611, 0.999833, 1, 1, 1, 0.266667, 1, 1.60017e-08, 3.63217e-09, 1.19209e-07, 0.0152085, -0.0348548, -0.0541085, 0.997811, 1, 1, 1, 0.333333, 1, 1.38597e-08, -2.8136e-09, 0, 0.017721, -0.0406132, -0.0630476, 0.997026, 1, 1, 1, 0.4, 1, 4.95234e-10, -3.39933e-09, 0, -0.0677883, 0.0291052, 0.0488595, 0.996078, 1, 1, 1, 0.466667, 1, 3.0251e-08, 7.71792e-11, 0, -0.186396, 0.126963, 0.20584, 0.952243, 1, 1, 1, 0.533333, 1, 1.833e-08, 4.54755e-09, 1.19209e-07, -0.188148, 0.128888, 0.242103, 0.943066, 1, 1, 1, 0.6, 1, 8.41146e-09, -9.31308e-11, 0, -0.0808999, 0.0423329, 0.227882, 0.969398, 1, 1, 1, 0.666667, 1, 6.40912e-09, 7.10543e-15, 1.19209e-07, -0.00230095, -0.0193147, 0.175059, 0.984366, 1, 1, 1, 0.733333, 1, -4.16577e-10, 0, 0, -0.000324101, -0.00873649, 0.0635702, 0.997939, 1, 1, 1, 0.791667, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/4/type = "transform"
tracks/4/path = NodePath("Armature/Skeleton:HandTip.L")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/keys = PoolRealArray( 0, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1, 0.791667, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1 )
tracks/5/type = "transform"
tracks/5/path = NodePath("Armature/Skeleton:LowerArm.L")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/keys = PoolRealArray( 0, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1, 0.791667, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1 )
tracks/6/type = "transform"
tracks/6/path = NodePath("Armature/Skeleton:UpperArm.L")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/keys = PoolRealArray( 0, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1, 0.791667, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1 )
tracks/7/type = "transform"
tracks/7/path = NodePath("Armature/Skeleton:Shoulder.L")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/keys = PoolRealArray( 0, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1, 0.791667, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/8/type = "transform"
tracks/8/path = NodePath("Armature/Skeleton:Base")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 0, 0, 0, -0.00262969, -0.00462533, -7.91099e-05, 0.999986, 1, 1, 1, 0.133333, 1, 0, 0, 0, -0.0187834, -0.0330379, -0.000768041, 0.999277, 1, 1, 1, 0.266667, 1, 0, 0, 0, -0.0678217, -0.119291, -0.00597111, 0.990522, 1, 1, 1, 0.333333, 1, 0, 0, 0, -0.0791032, -0.139134, -0.0111504, 0.987046, 1, 1, 1, 0.4, 1, 0, 0, 0, 0.00167779, -0.0294607, -0.0182862, 0.999397, 1, 1, 1, 0.466667, 1, 0, 0, 0, 0.116381, 0.127423, -0.0233002, 0.984721, 1, 1, 1, 0.533333, 1, 0, 0, 0, 0.120038, 0.144348, -0.0211955, 0.981991, 1, 1, 1, 0.6, 1, 0, 0, 0, 0.0228862, 0.060128, -0.00882898, 0.997889, 1, 1, 1, 0.666667, 1, 0, 0, 0, -0.0459424, -8.28055e-25, 1.02013e-17, 0.998944, 1, 1, 1, 0.733333, 1, 0, 0, 0, -0.00918794, -3.32825e-25, 4.08026e-18, 0.999958, 1, 1, 1, 0.791667, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
[sub_resource type="Animation" id=30]
resource_name = "Hit-loop"
length = 0.791667
loop = true
tracks/0/type = "transform"
tracks/0/path = NodePath("Armature/Skeleton:HandTip.R")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = PoolRealArray( 0, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, 5.15244e-08, -0.167621, -2.38312e-08, 0.985852, 1, 1, 1, 0.333333, 1, -4.84288e-08, 8.9407e-08, -6.55356e-08, 2.541e-08, -0.167621, -2.04777e-08, 0.985852, 1, 1, 1, 0.4, 1, -4.84288e-08, 5.96046e-08, -1.18914e-08, 0.0547672, -0.0111317, -0.0100956, 0.998386, 1, 1, 1, 0.466667, 1, -3.72529e-08, 5.96046e-08, -7.89466e-08, 0.130055, 0.212042, -0.0239739, 0.968271, 1, 1, 1, 0.533333, 1, -6.70552e-08, 8.9407e-08, -3.72234e-08, 0.146974, 0.263647, -0.0270928, 0.952971, 1, 1, 1, 0.791667, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, 0.146974, 0.263647, -0.0270928, 0.952971, 1, 1, 1 )
tracks/1/type = "transform"
tracks/1/path = NodePath("Armature/Skeleton:LowerArm.R")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = PoolRealArray( 0, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1, 0.0666667, 1, -1.49012e-08, 0, 7.63685e-08, -0.00586834, 0.0157643, 0.00645119, 0.999838, 1, 1, 1, 0.133333, 1, -1.49012e-07, 1.19209e-07, 1.21072e-07, -0.0437508, 0.117176, 0.0471372, 0.991027, 1, 1, 1, 0.2, 1, -1.04308e-07, 0, 1.86265e-09, -0.108414, 0.289036, 0.113213, 0.944398, 1, 1, 1, 0.266667, 1, -1.49012e-07, -2.98023e-08, 1.47149e-07, -0.1704, 0.450991, 0.168978, 0.859661, 1, 1, 1, 0.333333, 1, 2.98023e-08, 2.98023e-08, 3.91155e-08, -0.211609, 0.553687, 0.192556, 0.782032, 1, 1, 1, 0.4, 1, -7.45058e-08, 5.96046e-08, 1.49012e-08, -0.230545, 0.604045, 0.0585778, 0.760623, 1, 1, 1, 0.466667, 1, -5.96046e-08, 5.96046e-08, 7.26432e-08, -0.254046, 0.611523, -0.134534, 0.737158, 1, 1, 1, 0.533333, 1, -8.9407e-08, 0, 7.26432e-08, -0.3508, 0.55588, -0.0965608, 0.747404, 1, 1, 1, 0.6, 1, -7.45058e-08, 8.9407e-08, 8.56817e-08, -0.389927, 0.324616, 0.0259209, 0.861342, 1, 1, 1, 0.666667, 1, -1.49012e-08, -8.9407e-08, 8.00937e-08, -0.113728, 0.0644829, 0.00756021, 0.991388, 1, 1, 1, 0.733333, 1, -4.47035e-08, 8.9407e-08, 5.02914e-08, 2.98023e-08, -7.45058e-08, -7.45058e-09, 1, 1, 1, 1, 0.791667, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1 )
tracks/2/type = "transform"
tracks/2/path = NodePath("Armature/Skeleton:UpperArm.R")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = PoolRealArray( 0, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 4.47035e-08, 0, -7.45058e-08, 0.011767, 0.0105079, 0.00820402, 0.999842, 1, 1, 1, 0.133333, 1, -8.9407e-08, -1.49012e-07, 4.47035e-08, 0.0859668, 0.0767673, 0.0602297, 0.991508, 1, 1, 1, 0.2, 1, 2.98023e-08, -1.19209e-07, 5.96046e-08, 0.206183, 0.184119, 0.145576, 0.949945, 1, 1, 1, 0.266667, 1, 7.45058e-08, -5.96046e-08, 7.45058e-08, 0.307592, 0.274676, 0.220059, 0.884033, 1, 1, 1, 0.333333, 1, -1.78814e-07, -1.19209e-07, 0, 0.351106, 0.313534, 0.257053, 0.844006, 1, 1, 1, 0.4, 1, 8.9407e-08, -5.96046e-08, 7.45058e-08, 0.188191, 0.208628, 0.276531, 0.919015, 1, 1, 1, 0.466667, 1, 2.98023e-08, 0, -2.98023e-08, -0.0637092, 0.0115809, 0.273677, 0.959639, 1, 1, 1, 0.533333, 1, 5.96046e-08, 0, -2.98023e-08, -0.101567, -0.0901974, 0.143595, 0.98027, 1, 1, 1, 0.6, 1, 2.98023e-08, -2.98023e-08, -2.98023e-08, -0.0373082, -0.0907643, -0.0355061, 0.99454, 1, 1, 1, 0.666667, 1, -4.47035e-08, -2.98023e-08, 7.45058e-08, -0.00362256, -0.0262561, -0.0102711, 0.999596, 1, 1, 1, 0.733333, 1, 2.98023e-08, -5.96046e-08, 2.98023e-08, -1.49012e-08, -1.49012e-08, 1.49012e-08, 1, 1, 1, 1, 0.791667, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1 )
tracks/3/type = "transform"
tracks/3/path = NodePath("Armature/Skeleton:Shoulder.R")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 4.25331e-10, 2.61906e-11, 0, 0.00658544, 0.00890813, -0.000488712, 0.999939, 1, 1, 1, 0.133333, 1, 3.42889e-09, -1.28054e-10, 0, 0.0472153, 0.0638686, -0.0035038, 0.996835, 1, 1, 1, 0.2, 1, -1.0298e-09, 1.26893e-09, 0, 0.112056, 0.15158, -0.0083152, 0.982038, 1, 1, 1, 0.266667, 1, 1.60017e-08, 3.63217e-09, 1.19209e-07, 0.169552, 0.229355, -0.0125817, 0.958379, 1, 1, 1, 0.333333, 1, 1.38597e-08, -2.8136e-09, 0, 0.197783, 0.267544, -0.0146766, 0.942914, 1, 1, 1, 0.4, 1, 4.95234e-10, -3.39933e-09, 0, 0.0396571, 0.273067, 0.068117, 0.95876, 1, 1, 1, 0.466667, 1, 3.0251e-08, 7.71792e-11, 0, -0.187959, 0.251319, 0.181668, 0.931937, 1, 1, 1, 0.533333, 1, 1.833e-08, 4.54755e-09, 1.19209e-07, -0.213406, 0.19015, 0.209136, 0.935181, 1, 1, 1, 0.6, 1, 8.41146e-09, -9.31308e-11, 0, -0.0919332, 0.0630301, 0.203615, 0.972685, 1, 1, 1, 0.666667, 1, 6.40912e-09, 7.10543e-15, 1.19209e-07, -0.00230095, -0.0193147, 0.175059, 0.984366, 1, 1, 1, 0.733333, 1, -4.16577e-10, 0, 0, -0.000323432, -0.00872148, 0.0715194, 0.997401, 1, 1, 1, 0.791667, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/4/type = "transform"
tracks/4/path = NodePath("Armature/Skeleton:HandTip.L")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/keys = PoolRealArray( 0, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1, 0.791667, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1 )
tracks/5/type = "transform"
tracks/5/path = NodePath("Armature/Skeleton:LowerArm.L")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/keys = PoolRealArray( 0, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1, 0.0666667, 1, -5.96046e-08, 1.19209e-07, 9.12696e-08, -0.00935066, 2.03391e-05, 0.000315729, 0.999956, 1, 1, 1, 0.133333, 1, -1.49012e-08, 2.98023e-08, 8.56817e-08, -0.0671479, 0.000401975, 0.00227173, 0.99774, 1, 1, 1, 0.266667, 1, -5.96046e-08, 5.96046e-08, 1.00583e-07, -0.242279, 0.00547677, 0.0082618, 0.970156, 1, 1, 1, 0.333333, 1, 1.49012e-08, 5.96046e-08, 1.69501e-07, -0.280737, 0.0115882, 0.00965797, 0.959666, 1, 1, 1, 0.4, 1, -2.98023e-08, 5.96046e-08, 6.51926e-08, -0.211918, 0.0208799, 0.00983568, 0.977015, 1, 1, 1, 0.466667, 1, 2.98023e-08, 5.96046e-08, 9.12696e-08, -0.0968075, 0.0283259, 0.00989634, 0.994851, 1, 1, 1, 0.533333, 1, 5.96046e-08, 5.96046e-08, 4.47035e-08, -0.0323694, 0.0271555, 0.0090094, 0.999066, 1, 1, 1, 0.6, 1, -1.04308e-07, 2.98023e-08, 3.53903e-08, -0.00820132, 0.0156167, 0.00518115, 0.999831, 1, 1, 1, 0.666667, 1, 1.49012e-08, 0, -9.49949e-08, -0.000421983, 0.00306127, 0.00101563, 0.999995, 1, 1, 1, 0.733333, 1, -2.98023e-08, 0, 9.31323e-08, -2.98023e-08, 4.47035e-08, -4.47035e-08, 1, 1, 1, 1, 0.791667, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1 )
tracks/6/type = "transform"
tracks/6/path = NodePath("Armature/Skeleton:UpperArm.L")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/keys = PoolRealArray( 0, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1, 0.0666667, 1, -8.9407e-08, -8.9407e-08, -1.49012e-08, -0.00947541, -0.00323637, -0.00573202, 0.999934, 1, 1, 1, 0.133333, 1, -8.9407e-08, -8.9407e-08, -2.98023e-08, -0.0679954, -0.0232241, -0.0411327, 0.996567, 1, 1, 1, 0.2, 1, -7.45058e-08, 5.96046e-08, -5.96046e-08, -0.16145, -0.0551437, -0.0976665, 0.980487, 1, 1, 1, 0.266667, 1, -2.98023e-08, 0, -5.96046e-08, -0.244128, -0.0833825, -0.147681, 0.954798, 1, 1, 1, 0.333333, 1, 2.98023e-08, -8.9407e-08, -2.98023e-08, -0.284491, -0.0971685, -0.172098, 0.938086, 1, 1, 1, 0.4, 1, -5.96046e-08, -8.9407e-08, -2.98023e-08, -0.0658813, 0.00482479, -0.0859779, 0.994105, 1, 1, 1, 0.466667, 1, 7.45058e-08, 0, 4.47035e-08, 0.258148, 0.151193, 0.0735815, 0.95136, 1, 1, 1, 0.533333, 1, -4.47035e-08, 0, -7.45058e-08, 0.266396, 0.172662, 0.156906, 0.935201, 1, 1, 1, 0.6, 1, 2.98023e-08, -2.98023e-08, 2.98023e-08, 0.0838445, 0.112623, 0.14957, 0.978731, 1, 1, 1, 0.666667, 1, 2.98023e-08, 2.98023e-08, -2.98023e-08, 0.00431782, 0.0243932, 0.0432344, 0.998758, 1, 1, 1, 0.733333, 1, -4.47035e-08, -8.9407e-08, 1.49012e-08, -1.49012e-08, -8.19564e-08, 1.49012e-08, 1, 1, 1, 1, 0.791667, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1 )
tracks/7/type = "transform"
tracks/7/path = NodePath("Armature/Skeleton:Shoulder.L")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/keys = PoolRealArray( 0, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1, 0.791667, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/8/type = "transform"
tracks/8/path = NodePath("Armature/Skeleton:Base")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.0666667, 1, 0, 0, 0, -0.00262969, -0.00462533, -7.91099e-05, 0.999986, 1, 1, 1, 0.133333, 1, 0, 0, 0, -0.0187834, -0.0330379, -0.000768041, 0.999277, 1, 1, 1, 0.266667, 1, 0, 0, 0, -0.0678217, -0.119291, -0.00597111, 0.990522, 1, 1, 1, 0.333333, 1, 0, 0, 0, -0.0791032, -0.139134, -0.0111504, 0.987046, 1, 1, 1, 0.4, 1, 0, 0, 0, 0.00167779, -0.0294607, -0.0182862, 0.999397, 1, 1, 1, 0.466667, 1, 0, 0, 0, 0.116381, 0.127423, -0.0233002, 0.984721, 1, 1, 1, 0.533333, 1, 0, 0, 0, 0.120038, 0.144348, -0.0211955, 0.981991, 1, 1, 1, 0.6, 1, 0, 0, 0, 0.0228862, 0.060128, -0.00882898, 0.997889, 1, 1, 1, 0.666667, 1, 0, 0, 0, -0.0459424, -8.28055e-25, 1.02013e-17, 0.998944, 1, 1, 1, 0.733333, 1, 0, 0, 0, -0.00918794, -3.32825e-25, 4.08026e-18, 0.999958, 1, 1, 1, 0.791667, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
[sub_resource type="Animation" id=31]
resource_name = "Idle-loop"
length = 0.333333
loop = true
tracks/0/type = "transform"
tracks/0/path = NodePath("Armature/Skeleton:HandTip.R")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = PoolRealArray( 0, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, -4.65661e-10, -3.72529e-09, -1.80444e-09, 1, 1, 1, 1, 0.333333, 1, -6.33299e-08, 5.96046e-08, -8.72095e-09, -4.65661e-10, -3.72529e-09, -1.80444e-09, 1, 1, 1, 1 )
tracks/1/type = "transform"
tracks/1/path = NodePath("Armature/Skeleton:LowerArm.R")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = PoolRealArray( 0, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1, 0.333333, 1, 2.98023e-08, 2.98023e-08, 1.02445e-07, 1.49012e-08, -2.98023e-08, -7.45058e-09, 1, 1, 1, 1 )
tracks/2/type = "transform"
tracks/2/path = NodePath("Armature/Skeleton:UpperArm.R")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = PoolRealArray( 0, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1, 0.333333, 1, -2.98023e-08, -2.98023e-08, -5.96046e-08, -1.49012e-08, 0, 0, 1, 1, 1, 1 )
tracks/3/type = "transform"
tracks/3/path = NodePath("Armature/Skeleton:Shoulder.R")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.333333, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/4/type = "transform"
tracks/4/path = NodePath("Armature/Skeleton:HandTip.L")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/keys = PoolRealArray( 0, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1, 0.333333, 1, 0, 0, 3.81116e-09, -1.86265e-09, 7.45058e-09, -1.17579e-08, 1, 1, 1, 1 )
tracks/5/type = "transform"
tracks/5/path = NodePath("Armature/Skeleton:LowerArm.L")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/keys = PoolRealArray( 0, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1, 0.333333, 1, -8.9407e-08, 2.98023e-08, 1.02445e-07, -1.49012e-08, -1.49012e-08, -2.98023e-08, 1, 1, 1, 1 )
tracks/6/type = "transform"
tracks/6/path = NodePath("Armature/Skeleton:UpperArm.L")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/keys = PoolRealArray( 0, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1, 0.333333, 1, -4.47035e-08, -2.98023e-08, 1.49012e-08, -1.49012e-08, -5.21541e-08, 1.49012e-08, 1, 1, 1, 1 )
tracks/7/type = "transform"
tracks/7/path = NodePath("Armature/Skeleton:Shoulder.L")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/keys = PoolRealArray( 0, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1, 0.333333, 1, 2.97255e-31, -2.64698e-23, 0, 0, 0, 0, 1, 1, 1, 1 )
tracks/8/type = "transform"
tracks/8/path = NodePath("Armature/Skeleton:Base")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.333333, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 )
[sub_resource type="Animation" id=32]
resource_name = "Interaction-loop"
length = 1.04167
loop = true
tracks/0/type = "transform"
tracks/0/path = NodePath("Armature/Skeleton:HandTip.R")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = PoolRealArray( 0, 1, -3.35276e-08, 0, -1.23743e-07, -0.0397685, -0.000299317, -0.0326481, 0.998675, 1, 1, 1, 0.0666667, 1, -4.09782e-08, 5.96046e-08, -4.10418e-08, -0.0388719, -0.000223445, -0.0301956, 0.998788, 1, 1, 1, 0.2, 1, -7.45058e-08, 8.9407e-08, -7.08441e-08, -0.0304912, 0.000484843, -0.00729624, 0.999508, 1, 1, 1, 0.266667, 1, -2.23517e-08, 5.96046e-08, -4.59312e-08, -0.0351766, 0.000270068, -0.0172153, 0.999233, 1, 1, 1, 0.333333, 1, -5.96046e-08, 5.96046e-08, -2.57215e-08, -0.0444978, -0.000179588, -0.0373135, 0.998312, 1, 1, 1, 0.4, 1, -2.98023e-08, 5.96046e-08, 6.6526e-08, -0.0451622, -0.000199814, -0.0386437, 0.998232, 1, 1, 1, 0.533333, 1, -2.23517e-08, 5.96046e-08, 4.92034e-08, -0.026639, 0.000803509, 0.00223312, 0.999642, 1, 1, 1, 0.6, 1, -4.84288e-08, 5.96046e-08, 4.51055e-08, -0.0276825, 0.000774615, 0.00157664, 0.999615, 1, 1, 1, 0.666667, 1, -8.56817e-08, 8.9407e-08, 6.34526e-08, -0.0308762, 0.000653472, -0.00238591, 0.99952, 1, 1, 1, 0.733333, 1, -3.72529e-08, 5.96046e-08, 2.53151e-08, -0.0314008, 0.000622581, -0.0031118, 0.999502, 1, 1, 1, 0.866667, 1, -5.21541e-08, 5.96046e-08, -4.25319e-08, -0.0278308, 0.00065537, 0.000619829, 0.999612, 1, 1, 1, 0.933333, 1, -4.84288e-08, 0, -2.25217e-09, -0.0312516, 0.000375793, -0.0090599, 0.99947, 1, 1, 1, 1, 1, -6.33299e-08, 0, 6.67123e-08, -0.0378865, -0.000149982, -0.0274314, 0.998905, 1, 1, 1, 1.04167, 1, -3.35276e-08, 0, -1.23743e-07, -0.0397685, -0.000299317, -0.0326481, 0.998675, 1, 1, 1 )
tracks/1/type = "transform"
tracks/1/path = NodePath("Armature/Skeleton:LowerArm.R")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = PoolRealArray( 0, 1, -8.9407e-08, -5.96046e-08, -5.02914e-08, -0.182251, -0.00221143, -0.121182, 0.975753, 1, 1, 1, 0.0666667, 1, -5.96046e-08, 5.96046e-08, -5.58794e-09, -0.178429, -0.000926142, -0.112629, 0.977485, 1, 1, 1, 0.2, 1, -1.49012e-08, 0, 3.72529e-09, -0.141964, 0.0110719, -0.0323065, 0.989282, 1, 1, 1, 0.266667, 1, -1.04308e-07, 1.49012e-07, 6.51926e-08, -0.162254, 0.00780114, -0.065411, 0.984548, 1, 1, 1, 0.333333, 1, -1.49012e-08, 8.9407e-08, 2.47732e-07, -0.201968, 0.000834197, -0.132512, 0.970386, 1, 1, 1, 0.4, 1, 0, 2.98023e-08, 9.12696e-08, -0.204438, 0.000619536, -0.136288, 0.969345, 1, 1, 1, 0.533333, 1, -5.96046e-08, -2.98023e-08, -2.04891e-08, -0.121433, 0.0170418, 0.00665036, 0.992431, 1, 1, 1, 0.6, 1, -1.49012e-08, 0, 3.35276e-08, -0.127274, 0.0164745, 0.00237392, 0.991728, 1, 1, 1, 0.666667, 1, -4.47035e-08, -2.98023e-08, 4.09782e-08, -0.143827, 0.0142825, -0.015193, 0.989383, 1, 1, 1, 0.733333, 1, -5.96046e-08, 2.98023e-08, 1.11759e-08, -0.146947, 0.0135418, -0.0191313, 0.988867, 1, 1, 1, 0.866667, 1, -7.45058e-08, -5.96046e-08, -2.42144e-08, -0.132294, 0.0134387, -0.00933063, 0.991075, 1, 1, 1, 0.933333, 1, -5.96046e-08, 8.9407e-08, 2.42144e-08, -0.146841, 0.0087808, -0.0420172, 0.988228, 1, 1, 1, 1, 1, -2.98023e-08, 8.9407e-08, 1.13621e-07, -0.174543, 0.000209678, -0.103758, 0.979168, 1, 1, 1, 1.04167, 1, -8.9407e-08, -5.96046e-08, -5.02914e-08, -0.182251, -0.00221143, -0.121182, 0.975753, 1, 1, 1 )
tracks/2/type = "transform"
tracks/2/path = NodePath("Armature/Skeleton:UpperArm.R")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = PoolRealArray( 0, 1, 4.47035e-08, -2.98023e-08, 0, -0.257278, 0.0958797, 0.0591961, 0.959745, 1, 1, 1, 0.0666667, 1, 1.04308e-07, 5.96046e-08, -4.47035e-08, -0.252958, 0.0959003, 0.0594266, 0.960877, 1, 1, 1, 0.2, 1, 5.96046e-08, -2.98023e-08, 0, -0.212377, 0.0968744, 0.0615156, 0.970426, 1, 1, 1, 0.266667, 1, -4.47035e-08, -5.96046e-08, 4.47035e-08, -0.233182, 0.102396, 0.0611982, 0.965089, 1, 1, 1, 0.333333, 1, 7.45058e-08, -2.98023e-08, -4.47035e-08, -0.274556, 0.111221, 0.060385, 0.953206, 1, 1, 1, 0.4, 1, 4.47035e-08, -2.98023e-08, -1.49012e-08, -0.274427, 0.112329, 0.0606839, 0.953095, 1, 1, 1, 0.533333, 1, -1.49012e-08, -5.96046e-08, -7.45058e-08, -0.162406, 0.0994839, 0.0673923, 0.97938, 1, 1, 1, 0.6, 1, 5.96046e-08, 2.98023e-08, -8.9407e-08, -0.177495, 0.10122, 0.0696684, 0.97642, 1, 1, 1, 0.666667, 1, 1.04308e-07, 1.19209e-07, -1.49012e-08, -0.213174, 0.105294, 0.0704232, 0.968768, 1, 1, 1, 0.733333, 1, -2.98023e-08, -8.9407e-08, 0, -0.221933, 0.105306, 0.070184, 0.966815, 1, 1, 1, 0.8, 1, 1.78814e-07, 2.98023e-08, -1.19209e-07, -0.215222, 0.0996311, 0.068472, 0.969054, 1, 1, 1, 0.866667, 1, 4.47035e-08, 0, -4.47035e-08, -0.209579, 0.0948555, 0.065478, 0.970975, 1, 1, 1, 0.933333, 1, 1.49012e-08, 5.96046e-08, 1.49012e-08, -0.223354, 0.0949247, 0.0622329, 0.968106, 1, 1, 1, 1, 1, 4.47035e-08, 2.98023e-08, -4.47035e-08, -0.249803, 0.0956794, 0.0597299, 0.961705, 1, 1, 1, 1.04167, 1, 4.47035e-08, -2.98023e-08, 0, -0.257278, 0.0958797, 0.0591961, 0.959745, 1, 1, 1 )
tracks/3/type = "transform"
tracks/3/path = NodePath("Armature/Skeleton:Shoulder.R")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = PoolRealArray( 0, 1, -1.05356e-08, -1.08299e-08, -1.19209e-07, -0.0304198, 0.217476, 0.165777, 0.961404, 1, 1, 1, 0.0666667, 1, -4.63332e-09, -1.65752e-09, 0, -0.0383344, 0.214758, 0.17136, 0.960752, 1, 1, 1, 0.2, 1, 3.24177e-08, 9.07121e-10, 0, -0.112003, 0.188337, 0.22262, 0.94996, 1, 1, 1, 0.266667, 1, 3.84752e-08, 2.0227e-10, 1.19209e-07, -0.0862053, 0.207715, 0.198912, 0.953864, 1, 1, 1, 0.333333, 1, 1.60653e-08, 5.66796e-09, -1.19209e-07, -0.0324234, 0.244687, 0.149931, 0.957391, 1, 1, 1, 0.4, 1, 3.37837e-08, 3.68154e-10, 0, -0.0242774, 0.246734, 0.139153, 0.958733, 1, 1, 1, 0.466667, 1, 1.92551e-08, -2.56851e-09, 0, -0.0624877, 0.207136, 0.15993, 0.963126, 1, 1, 1, 0.533333, 1, -4.49364e-09, 9.36211e-11, 0, -0.101882, 0.172775, 0.189807, 0.961115, 1, 1, 1, 0.6, 1, 2.33879e-08, -1.31112e-09, 0, -0.114725, 0.179187, 0.214338, 0.953305, 1, 1, 1, 0.666667, 1, 8.36008e-09, -5.16586e-10, 1.19209e-07, -0.118628, 0.197342, 0.233035, 0.944816, 1, 1, 1, 0.733333, 1, 8.60599e-09, -2.88125e-10, 0, -0.120042, 0.200139, 0.246325, 0.940669, 1, 1, 1, 0.8, 1, 2.80561e-09, 3.76021e-09, 0, -0.122382, 0.189051, 0.256239, 0.940013, 1, 1, 1, 0.866667, 1, -1.72062e-08, 6.89179e-09, 0, -0.12399, 0.17982, 0.260764, 0.940369, 1, 1, 1, 0.933333, 1, -6.63562e-10, 1.54396e-09, 0, -0.0967781, 0.190769, 0.233545, 0.948524, 1, 1, 1, 1, 1, 1.62981e-08, -1.06611e-08, 0, -0.0451179, 0.211756, 0.180902, 0.959374, 1, 1, 1, 1.04167, 1, -1.05356e-08, -1.10055e-08, -1.19209e-07, -0.0304198, 0.217476, 0.165777, 0.961404, 1, 1, 1 )
tracks/4/type = "transform"
tracks/4/path = NodePath("Armature/Skeleton:HandTip.L")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/keys = PoolRealArray( 0, 1, -4.47035e-08, 0, 4.84288e-08, -0.0259765, -0.000839302, -0.003693, 0.999655, 1, 1, 1, 0.0666667, 1, -2.23517e-08, -2.98023e-08, 5.06639e-08, -0.027443, -0.000736854, -0.000142364, 0.999623, 1, 1, 1, 0.2, 1, 5.21541e-08, 0, 3.94881e-08, -0.0411107, 0.000219808, 0.0329958, 0.99861, 1, 1, 1, 0.266667, 1, 4.47035e-08, 0, 8.38184e-09, -0.0392144, 0.000149269, 0.0294237, 0.998798, 1, 1, 1, 0.333333, 1, 1.49012e-08, 0, -1.00583e-07, -0.0347486, -4.67662e-05, 0.0205185, 0.999185, 1, 1, 1, 0.4, 1, 7.45058e-09, 5.96046e-08, 3.85568e-08, -0.0338174, -7.52744e-05, 0.0188424, 0.99925, 1, 1, 1, 0.466667, 1, -6.70552e-08, -2.98023e-08, -1.9744e-08, -0.0359106, 7.82064e-05, 0.0239558, 0.999068, 1, 1, 1, 0.6, 1, 2.98023e-08, 5.96046e-08, 1.57392e-08, -0.0426272, 0.000386088, 0.0380744, 0.998365, 1, 1, 1, 0.666667, 1, -6.70552e-08, 5.96046e-08, 7.91624e-09, -0.0449463, 0.000421882, 0.0419609, 0.998108, 1, 1, 1, 0.733333, 1, 3.72529e-08, 0, 2.37488e-08, -0.0453679, 0.000406673, 0.0422226, 0.998078, 1, 1, 1, 0.8, 1, 2.98023e-08, 5.96046e-08, 2.46684e-08, -0.0446239, 0.000279515, 0.0385421, 0.99826, 1, 1, 1, 0.866667, 1, 5.96046e-08, 1.19209e-07, 8.74277e-09, -0.0416562, -2.0318e-06, 0.0290418, 0.99871, 1, 1, 1, 1, 1, 8.9407e-08, 5.96046e-08, 4.47035e-08, -0.0277544, -0.000759324, -0.000433974, 0.999614, 1, 1, 1, 1.04167, 1, -4.47035e-08, 0, -7.45058e-09, -0.0259765, -0.000839302, -0.003693, 0.999655, 1, 1, 1 )
tracks/5/type = "transform"
tracks/5/path = NodePath("Armature/Skeleton:LowerArm.L")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/keys = PoolRealArray( 0, 1, -5.96046e-08, 0, -4.65661e-08, -0.118403, -0.0176198, -0.0117551, 0.99274, 1, 1, 1, 0.0666667, 1, -4.47035e-08, 2.98023e-08, 4.28408e-08, -0.125346, -0.0159979, 0.000566965, 0.991984, 1, 1, 1, 0.2, 1, -4.47035e-08, 5.96046e-08, 8.75443e-08, -0.188895, -0.000707762, 0.115526, 0.975178, 1, 1, 1, 0.266667, 1, 1.49012e-08, 1.49012e-07, 1.24797e-07, -0.180412, -0.00092285, 0.105994, 0.977863, 1, 1, 1, 0.333333, 1, 1.04308e-07, 5.96046e-08, 1.47149e-07, -0.16024, -0.00234829, 0.0804372, 0.983792, 1, 1, 1, 0.4, 1, 0, 2.98023e-08, 7.63685e-08, -0.155967, -0.00228733, 0.076109, 0.984823, 1, 1, 1, 0.466667, 1, -2.98023e-08, 2.98023e-08, 3.91155e-08, -0.165189, 7.06846e-05, 0.0933881, 0.981831, 1, 1, 1, 0.533333, 1, -1.49012e-08, 0, 3.72529e-09, -0.18027, 0.00205633, 0.118129, 0.976496, 1, 1, 1, 0.6, 1, 4.47035e-08, 8.9407e-08, 5.21541e-08, -0.195105, 0.00211556, 0.136242, 0.971271, 1, 1, 1, 0.666667, 1, -1.04308e-07, 5.96046e-08, 1.2666e-07, -0.205658, 0.00164892, 0.145659, 0.967722, 1, 1, 1, 0.733333, 1, -1.49012e-08, 0, 1.06171e-07, -0.207614, 0.000328147, 0.145968, 0.967259, 1, 1, 1, 0.8, 1, 7.45058e-08, 2.98023e-08, 2.98023e-08, -0.204855, -0.00210787, 0.13512, 0.969419, 1, 1, 1, 0.866667, 1, -4.47035e-08, 2.98023e-08, 1.13621e-07, -0.194142, -0.00615519, 0.104122, 0.975413, 1, 1, 1, 0.933333, 1, -1.49012e-08, 8.9407e-08, 4.65661e-08, -0.164634, -0.0117041, 0.0507141, 0.984981, 1, 1, 1, 1, 1, -2.98023e-08, 1.19209e-07, 7.45058e-08, -0.127867, -0.0165417, 8.73655e-06, 0.991653, 1, 1, 1, 1.04167, 1, -5.96046e-08, 0, -4.65661e-08, -0.118403, -0.0176198, -0.0117551, 0.99274, 1, 1, 1 )
tracks/6/type = "transform"
tracks/6/path = NodePath("Armature/Skeleton:UpperArm.L")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/keys = PoolRealArray( 0, 1, -7.45058e-08, -5.96046e-08, -7.45058e-08, -0.158361, -0.0989989, -0.0678321, 0.980061, 1, 1, 1, 0.0666667, 1, -1.49012e-08, -2.98023e-08, -1.49012e-08, -0.167793, -0.0992559, -0.0671212, 0.978513, 1, 1, 1, 0.2, 1, 4.47035e-08, -8.9407e-08, 7.45058e-08, -0.255067, -0.101213, -0.060189, 0.959726, 1, 1, 1, 0.266667, 1, 7.45058e-08, 0, 1.49012e-08, -0.249214, -0.0988195, -0.0599973, 0.961524, 1, 1, 1, 0.333333, 1, 0, -2.98023e-08, 0, -0.232425, -0.0942096, -0.0601037, 0.966173, 1, 1, 1, 0.4, 1, 1.49012e-08, -2.98023e-08, -2.98023e-08, -0.229419, -0.0929231, -0.060043, 0.96702, 1, 1, 1, 0.466667, 1, 5.96046e-08, -1.78814e-07, 2.98023e-08, -0.239933, -0.0934467, -0.0596002, 0.964442, 1, 1, 1, 0.533333, 1, -5.96046e-08, 2.98023e-08, -2.98023e-08, -0.255351, -0.0955354, -0.0592259, 0.960292, 1, 1, 1, 0.6, 1, -1.49012e-08, -5.96046e-08, 2.98023e-08, -0.267343, -0.100811, -0.0595827, 0.95646, 1, 1, 1, 0.666667, 1, 0, 1.19209e-07, -1.19209e-07, -0.274072, -0.107058, -0.0604036, 0.953821, 1, 1, 1, 0.733333, 1, 0, -1.78814e-07, 2.98023e-08, -0.275161, -0.108825, -0.0609397, 0.953273, 1, 1, 1, 0.8, 1, 8.9407e-08, 0, 0, -0.274679, -0.108954, -0.0611182, 0.953386, 1, 1, 1, 0.866667, 1, -4.47035e-08, 2.98023e-08, 1.49012e-08, -0.272769, -0.10898, -0.061592, 0.953901, 1, 1, 1, 0.933333, 1, 5.96046e-08, -5.96046e-08, 1.49012e-08, -0.238162, -0.106197, -0.0639192, 0.963284, 1, 1, 1, 1, 1, -4.47035e-08, 0, -4.47035e-08, -0.175957, -0.100647, -0.0670489, 0.976941, 1, 1, 1, 1.04167, 1, -7.45058e-08, -5.96046e-08, -7.45058e-08, -0.158361, -0.0989989, -0.0678321, 0.980061, 1, 1, 1 )
tracks/7/type = "transform"
tracks/7/path = NodePath("Armature/Skeleton:Shoulder.L")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/keys = PoolRealArray( 0, 1, 2.79397e-08, 1.01133e-08, -1.19209e-07, -0.105738, -0.170004, -0.193499, 0.960456, 1, 1, 1, 0.0666667, 1, 2.18279e-08, 1.09526e-09, 0, -0.0988958, -0.175343, -0.189677, 0.960988, 1, 1, 1, 0.2, 1, -3.05499e-09, -1.92449e-10, 0, -0.0346134, -0.224405, -0.153187, 0.961758, 1, 1, 1, 0.266667, 1, -1.02678e-08, -1.61381e-09, 1.19209e-07, -0.0408083, -0.217599, -0.164468, 0.961216, 1, 1, 1, 0.333333, 1, 1.15833e-08, -1.52068e-09, -1.19209e-07, -0.0566848, -0.201456, -0.18794, 0.959625, 1, 1, 1, 0.4, 1, -6.43776e-09, -4.3874e-10, 0, -0.0594242, -0.197827, -0.193253, 0.959159, 1, 1, 1, 0.466667, 1, 7.97445e-09, 2.0726e-09, 0, -0.0488153, -0.204288, -0.184498, 0.960127, 1, 1, 1, 0.533333, 1, 3.16766e-08, -7.96166e-10, 0, -0.0325078, -0.215894, -0.168064, 0.961295, 1, 1, 1, 0.6, 1, 4.29571e-09, 1.32412e-10, 0, -0.0183872, -0.228989, -0.148323, 0.961887, 1, 1, 1, 0.666667, 1, 5.67524e-09, 9.16771e-10, 1.19209e-07, -0.00943251, -0.239133, -0.132293, 0.961886, 1, 1, 1, 0.733333, 1, 6.72954e-09, 7.77073e-10, 0, -0.0118449, -0.241292, -0.13514, 0.960924, 1, 1, 1, 0.8, 1, 1.55997e-09, -4.59549e-09, 0, -0.0356173, -0.240445, -0.167061, 0.955515, 1, 1, 1, 0.866667, 1, 2.2212e-08, -8.7166e-09, 0, -0.0691231, -0.236236, -0.193717, 0.949678, 1, 1, 1, 0.933333, 1, 1.78698e-08, -1.38971e-09, 0, -0.0926686, -0.21401, -0.195148, 0.952643, 1, 1, 1, 1, 1, 1.10595e-09, 1.00945e-08, 0, -0.104118, -0.179463, -0.193885, 0.958833, 1, 1, 1, 1.04167, 1, 2.79397e-08, 1.02888e-08, -1.19209e-07, -0.105738, -0.170004, -0.193499, 0.960456, 1, 1, 1 )
tracks/8/type = "transform"
tracks/8/path = NodePath("Armature/Skeleton:Base")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/keys = PoolRealArray( 0, 1, 0, 0, -0.3, 0.0998068, -0.00311413, -0.0310306, 0.994518, 1, 1, 1, 0.0666667, 1, 0, 0, -0.3, 0.0978242, -0.00285651, -0.0286044, 0.994788, 1, 1, 1, 0.133333, 1, 0, 0, -0.3, 0.087763, -0.00136632, -0.0144903, 0.996035, 1, 1, 1, 0.2, 1, 0, 0, -0.3, 0.0792657, 0.000687168, 0.00526144, 0.996839, 1, 1, 1, 0.266667, 1, 0, 0, -0.3, 0.0804652, 0.00203443, 0.0188723, 0.996577, 1, 1, 1, 0.333333, 1, 0, 0, -0.3, 0.0853018, 0.00266966, 0.0259549, 0.996013, 1, 1, 1, 0.4, 1, 0, 0, -0.3, 0.091536, 0.00291421, 0.0289664, 0.995376, 1, 1, 1, 0.466667, 1, 0, 0, -0.3, 0.0969729, 0.00305717, 0.030523, 0.994814, 1, 1, 1, 0.533333, 1, 0, 0, -0.3, 0.0996245, 0.0031108, 0.0310037, 0.994537, 1, 1, 1, 0.6, 1, 0, 0, -0.3, 0.0867901, 0.00250436, 0.0249545, 0.995911, 1, 1, 1, 0.666667, 1, 0, 0, -0.3, 0.0623626, 0.00103457, 0.0103089, 0.998, 1, 1, 1, 0.733333, 1, 0, 0, -0.3, 0.0565602, -0.000527597, -0.00525721, 0.998385, 1, 1, 1, 0.8, 1, 0, 0, -0.3, 0.0637818, -0.0016911, -0.0168509, 0.99782, 1, 1, 1, 0.866667, 1, 0, 0, -0.3, 0.0759985, -0.00245039, -0.0244168, 0.996806, 1, 1, 1, 0.933333, 1, 0, 0, -0.3, 0.0886202, -0.00288556, -0.028753, 0.995646, 1, 1, 1, 1, 1, 0, 0, -0.3, 0.0979029, -0.00308691, -0.0307594, 0.994716, 1, 1, 1, 1.04167, 1, 0, 0, -0.3, 0.0998068, -0.00311413, -0.0310306, 0.994518, 1, 1, 1 )
[sub_resource type="AnimationNodeAnimation" id=1]
animation = "Hit-loop"
[sub_resource type="AnimationNodeAnimation" id=2]
animation = "Idle-loop"
[sub_resource type="AnimationNodeAnimation" id=3]
animation = "Interaction-loop"
[sub_resource type="AnimationNodeStateMachineTransition" id=4]
[sub_resource type="AnimationNodeStateMachineTransition" id=5]
switch_mode = 2
xfade_time = 0.2
[sub_resource type="AnimationNodeStateMachineTransition" id=6]
xfade_time = 0.1
[sub_resource type="AnimationNodeStateMachineTransition" id=33]
switch_mode = 2
xfade_time = 0.3
[sub_resource type="AnimationNodeStateMachine" id=8]
states/Hit/node = SubResource( 1 )
states/Hit/position = Vector2( 415, 45 )
states/Idle/node = SubResource( 2 )
states/Idle/position = Vector2( 149, 39 )
states/Interaction/node = SubResource( 3 )
states/Interaction/position = Vector2( 176, 157 )
transitions = [ "Idle", "Hit", SubResource( 4 ), "Hit", "Idle", SubResource( 5 ), "Idle", "Interaction", SubResource( 6 ), "Interaction", "Idle", SubResource( 33 ) ]
start_node = "Idle"
[sub_resource type="AnimationNodeStateMachinePlayback" id=34]
[node name="Player" type="KinematicBody"]
collision_mask = 0
input_ray_pickable = false
@ -53,7 +357,6 @@ material/0 = ExtResource( 3 )
[node name="WorldInfo" type="Node" parent="."]
script = ExtResource( 5 )
World = NodePath("../../TileWorld")
[node name="Navigation" type="Spatial" parent="."]
visible = false
@ -76,16 +379,9 @@ input_ray_pickable = false
monitorable = false
[node name="CollisionShape" type="CollisionShape" parent="ItemPickupArea"]
visible = false
shape = SubResource( 23 )
[node name="Geometry" parent="." groups=["GameGeometry"] instance=ExtResource( 4 )]
[node name="ToolAttachement" parent="Geometry/Armature/Skeleton" index="5"]
transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 )
[node name="AnimationTree" parent="Geometry" index="2"]
parameters/playback = SubResource( 28 )
[node name="DebugGeometry" type="Spatial" parent="."]
script = ExtResource( 6 )
@ -94,4 +390,35 @@ material_override = SubResource( 25 )
cast_shadow = 0
generate_lightmap = false
[editable path="Geometry"]
[node name="ImmediateGeometry" type="ImmediateGeometry" parent="."]
[node name="Geometry" type="Spatial" parent="." groups=["GameGeometry"]]
[node name="PirateAsset" parent="Geometry" instance=ExtResource( 8 )]
transform = Transform( 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0 )
[node name="Skeleton" parent="Geometry/PirateAsset/Armature" index="0"]
bones/4/bound_children = [ NodePath("ToolAttachement") ]
[node name="ToolAttachement" type="BoneAttachment" parent="Geometry/PirateAsset/Armature/Skeleton" index="5"]
transform = Transform( 1, 7.13626e-08, -4.47035e-08, 1.64262e-07, -1, -1.00583e-07, 1.19209e-07, 1.18278e-07, -1, -0.72, 0.45, 1.78362e-08 )
visible = false
bone_name = "HandTip.R"
[node name="toolAxe" parent="Geometry/PirateAsset/Armature/Skeleton/ToolAttachement" instance=ExtResource( 4 )]
transform = Transform( -6.98781e-14, 7.86805e-08, -1.2, 1.8, -7.86805e-08, -4.88782e-14, -7.86805e-08, -1.8, -5.24537e-08, 0.0240936, -0.144196, 0.560397 )
[node name="AnimationPlayer" type="AnimationPlayer" parent="Geometry"]
root_node = NodePath("../PirateAsset")
anims/ArmatureAction = SubResource( 29 )
anims/Hit-loop = SubResource( 30 )
anims/Idle-loop = SubResource( 31 )
anims/Interaction-loop = SubResource( 32 )
[node name="AnimationTree" type="AnimationTree" parent="Geometry"]
tree_root = SubResource( 8 )
anim_player = NodePath("../AnimationPlayer")
active = true
parameters/playback = SubResource( 34 )
[editable path="Geometry/PirateAsset"]

View File

@ -4,8 +4,7 @@ using Godot;
using GodotComponentTest.components;
using GodotComponentTest.entities;
public class Tree : StaticBody, IInteractionInterface
{
public class Tree : StaticBody, IInteractionInterface {
[Export] public float ChopDuration = 2;
public bool IsMouseOver;
public InteractionComponent InteractionComponent { get; set; }
@ -18,9 +17,11 @@ public class Tree : StaticBody, IInteractionInterface
[Signal]
public delegate void EntityClicked(Entity entity);
[Signal]
public delegate void TreeChopped(Tree entity);
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
public override void _Ready() {
_geometry = GetNode<MeshInstance>("Geometry/tree");
Debug.Assert(_geometry != null);
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
@ -33,56 +34,44 @@ public class Tree : StaticBody, IInteractionInterface
Connect("mouse_exited", this, nameof(OnAreaMouseExited));
}
public override void _Process(float delta)
{
public override void _Process(float delta) {
base._Process(delta);
if (_isBeingChopped)
{
if (_isBeingChopped) {
_health = Math.Max(0, _health - delta * 100 / ChopDuration);
}
if (_health == 0)
{
if (_health == 0) {
InteractionComponent.EndInteraction();
InteractionComponent.TargetEntity = null;
QueueFree();
GD.Print("Tree chopped!");
EmitSignal("TreeChopped", this);
}
}
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);
_geometry.MaterialOverride = overrideMaterial;
}
public void OnAreaMouseExited()
{
public void OnAreaMouseExited() {
IsMouseOver = false;
_geometry.MaterialOverride = null;
}
public void OnInteractionStart()
{
public void OnInteractionStart() {
GD.Print("Starting tree animationplayer");
_animationPlayer.CurrentAnimation = "TreeShake";
_animationPlayer.Seek(0);
@ -90,11 +79,10 @@ public class Tree : StaticBody, IInteractionInterface
_isBeingChopped = true;
}
public void OnInteractionEnd()
{
public void OnInteractionEnd() {
_animationPlayer.CurrentAnimation = "Idle";
_animationPlayer.Seek(0);
_animationPlayer.Stop();
_isBeingChopped = false;
}
}
}

View File

@ -88,6 +88,7 @@ script = ExtResource( 3 )
[node name="MountPoint" type="Spatial" parent="."]
transform = Transform( -0.524001, 0, -0.851718, 0, 1, 0, 0.851718, 0, -0.524001, 0.717306, 0, 0.400936 )
visible = false
[node name="Arrow" type="Spatial" parent="MountPoint"]
transform = Transform( -1, 0, -8.74227e-08, 0, 1, 0, 8.74227e-08, 0, -1, 2.38419e-07, 0, 0 )

View File

@ -30,7 +30,7 @@ keystore/release="/home/martin/projects/GodotComponentTest/android/keystore/debu
keystore/release_user="androiddebugkey"
keystore/release_password="android"
one_click_deploy/clear_previous_install=true
version/code=1
version/code=5
version/name="1.0"
package/unique_name="org.godotengine.$genname"
package/name=""

View File

@ -134,5 +134,9 @@ common/enable_pause_aware_picking=true
quality/directional_shadow/size.mobile=512
quality/shadow_atlas/size.mobile=1024
quality/shadow_atlas/quadrant_0_subdiv=0
quality/shadow_atlas/quadrant_1_subdiv=0
quality/shadow_atlas/quadrant_2_subdiv=0
quality/shadow_atlas/quadrant_3_subdiv=0
quality/subsurface_scattering/quality=0
environment/default_environment="res://default_env.tres"

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;
@ -13,57 +12,45 @@ public class Game : Spatial
// ui elements
private Label _framesPerSecondLabel;
private Control _gameUi;
private Button _generateWorldButton;
private Label _goldCountLabel;
private TextureRect _heightTextureRect;
// other members
private HexGrid _hexGrid;
private InteractionSystem _interactionSystem;
private HexCell _lastTile;
private Label _mouseTileCubeLabel;
private Label _mouseTileAxialLabel;
private Spatial _mouseTileHighlight;
private Label _mouseTileOffsetLabel;
private Label _mouseWorldLabel;
private Label _numCoordsAddedLabel;
private Label _numCoordsRemovedLabel;
private Label _numTilesLabel;
private Player _player;
// scene nodes
private Spatial _tileHighlight;
// Resources
private PackedScene _tileHighlightScene;
private TileInstanceManager _tileInstanceManager;
private ShaderMaterial _tileMaterial;
private Label _tileOffsetLabel;
private World _world;
private TextureRect _worldTextureRect;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
public override void _Ready() {
// debugStatsContainer
var debugStatsContainer = (Container)FindNode("DebugStatsContainer");
Container debugStatsContainer = (Container)FindNode("DebugStatsContainer");
_framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label");
_centerLabel = debugStatsContainer.GetNode<Label>("center_label");
_tileOffsetLabel = debugStatsContainer.GetNode<Label>("tile_offset_label");
_numTilesLabel = debugStatsContainer.GetNode<Label>("num_tiles_label");
_mouseWorldLabel = debugStatsContainer.GetNode<Label>("mouse_world_label");
_mouseTileOffsetLabel = debugStatsContainer.GetNode<Label>("mouse_tile_offset_label");
_mouseTileCubeLabel = debugStatsContainer.GetNode<Label>("mouse_tile_cube_label");
_mouseTileAxialLabel = debugStatsContainer.GetNode<Label>("mouse_tile_axial_label");
_numCoordsAddedLabel = debugStatsContainer.GetNode<Label>("num_coords_added_label");
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
// UI elements
var worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer");
_worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect");
_heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect");
_generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton");
Container worldGeneratorWidget = (Container)FindNode("WorldGeneratorWidget");
_worldTextureRect = worldGeneratorWidget.GetNode<TextureRect>("WorldTextureRect");
_heightTextureRect = worldGeneratorWidget.GetNode<TextureRect>("HeightTextureRect");
_gameUi = (Control)FindNode("GameUI");
_goldCountLabel = _gameUi.GetNode<Label>("GoldCount");
Debug.Assert(_goldCountLabel != null);
@ -77,75 +64,88 @@ public class Game : Spatial
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
_world = (World)FindNode("World");
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
// populate UI values
var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
// resources
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
Debug.Assert(_tileMaterial != null);
_blackWhitePatternTexture = new ImageTexture();
var image = new Image();
Image image = new();
image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
// other members
_lastTile = new HexCell();
_currentTile = new HexCell();
_hexGrid = new HexGrid();
_interactionSystem = GetNode<InteractionSystem>("InteractionSystem");
Debug.Assert(_interactionSystem != null);
// connect signals
_generateWorldButton.Connect("pressed", this, nameof(OnGenerateButton));
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
nameof(_interactionSystem.OnStartInteraction));
_player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged));
_tileInstanceManager.Connect("TileClicked", this, nameof(OnTileClicked));
_tileInstanceManager.Connect("TileHovered", this, nameof(OnTileHovered));
_world.Connect("TileClicked", this, nameof(OnTileClicked));
_world.Connect("TileHovered", this, nameof(OnTileHovered));
_world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged));
_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));
// perform dependency injection
//_streamContainer.SetWorld(_tileWorld);Clicked
var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
UpdateCurrentTile();
StartNewGame(123, 12);
}
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 StartNewGame(int seed, int chunkSize) {
_world.Seed = seed;
_world.ChunkSize = chunkSize;
_world.InitNoiseGenerator();
ResetGame();
_world.UpdateCenterChunkFromPlaneCoord(Vector2.Zero);
}
public void ResetGame() {
_player.GlobalTranslation = Vector3.Zero;
_player.PlaneAngle = -Mathf.Pi * 0.5f;
_world.Reset();
}
public void UpdateCurrentTile() {
// cast a ray from the camera to center
var cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
var cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
var cameraDir = cameraNormal - cameraPosition;
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
Vector3 cameraDir = cameraNormal - cameraPosition;
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;
}
@ -159,38 +159,26 @@ public class Game : Spatial
}
public override void _Process(float delta)
{
public override void _Process(float delta) {
_framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString();
_lastTile = _currentTile;
UpdateCurrentTile();
var tileHighlightTransform = Transform.Identity;
var currentTileCenter = _hexGrid.GetHexCenter(_currentTile);
Transform tileHighlightTransform = Transform.Identity;
Vector2 currentTileCenter = _hexGrid.GetHexCenter(_currentTile);
tileHighlightTransform.origin.x = currentTileCenter.x;
tileHighlightTransform.origin.z = currentTileCenter.y;
_tileHighlight.Transform = tileHighlightTransform;
var cameraTransform = _camera.Transform;
Transform cameraTransform = _camera.Transform;
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
_camera.Transform = cameraTransform;
}
public void OnGenerateButton()
{
GD.Print("Generating");
var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!");
}
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex)
{
var cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
var highlightTransform = Transform.Identity;
int shapeIndex) {
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
Transform highlightTransform = Transform.Identity;
_mouseWorldLabel.Text = position.ToString("F3");
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
@ -200,20 +188,22 @@ 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)
{
var highlightTransform = tile.GlobalTransform;
public void OnTileHovered(HexTile3D tile) {
Transform highlightTransform = tile.GlobalTransform;
_mouseTileHighlight.Transform = highlightTransform;
_mouseWorldLabel.Text = highlightTransform.origin.ToString("F3");
_mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N");
@ -222,13 +212,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);
var mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null)
{
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null) {
_player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(mountPoint.GlobalTransform)));
@ -237,9 +225,8 @@ public class Game : Spatial
}
public void ResetGameState()
{
var playerStartTransform = Transform.Identity;
public void ResetGameState() {
Transform playerStartTransform = Transform.Identity;
playerStartTransform.origin.y = 0;
_player.Transform = playerStartTransform;
_player.TaskQueueComponent.Reset();
@ -248,28 +235,25 @@ public class Game : Spatial
_goldCountLabel.Text = "0";
foreach (Spatial entity in GetNode("Entities").GetChildren())
{
var entityTransform = entity.Transform;
var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
foreach (Spatial entity in GetNode("Entities").GetChildren()) {
Transform entityTransform = entity.Transform;
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)
{
var 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)
{
var newWorldTexture = new ImageTexture();
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) {
ImageTexture newWorldTexture = new();
newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -281,9 +265,8 @@ public class Game : Spatial
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y);
}
public void OnGoldCountChanged(int goldCount)
{
var animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
public void OnGoldCountChanged(int goldCount) {
AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
_goldCountLabel.Text = goldCount.ToString();
animationPlayer.CurrentAnimation = "FlashLabel";
animationPlayer.Seek(0);

View File

@ -1,23 +1,16 @@
[gd_scene load_steps=22 format=2]
[gd_scene load_steps=14 format=2]
[ext_resource path="res://ui/WorldGeneratorWidget.gd" type="Script" id=1]
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=2]
[ext_resource path="res://scenes/Camera.tscn" type="PackedScene" id=3]
[ext_resource path="res://ui/EditorUI.tscn" type="PackedScene" id=4]
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=5]
[ext_resource path="res://ui/DebugStatsContainer.gd" type="Script" id=6]
[ext_resource path="res://scenes/World.cs" type="Script" id=7]
[ext_resource path="res://scenes/World.tscn" type="PackedScene" id=7]
[ext_resource path="res://scenes/Game.cs" type="Script" id=9]
[ext_resource path="res://scenes/TileInstanceManager.cs" type="Script" id=10]
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
[ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12]
[ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13]
[ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14]
[ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15]
[ext_resource path="res://entities/rockA.tscn" type="PackedScene" id=16]
[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=17]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=18]
[ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=19]
[ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=20]
[sub_resource type="Animation" id=25]
resource_name = "FlashLabel"
@ -37,13 +30,6 @@ tracks/0/keys = {
[sub_resource type="AnimationNodeStateMachinePlayback" id=26]
[sub_resource type="MultiMesh" id=27]
color_format = 1
transform_format = 1
custom_data_format = 1
visible_instance_count = 0
mesh = ExtResource( 13 )
[node name="Game" type="Spatial"]
script = ExtResource( 9 )
@ -87,8 +73,8 @@ anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = -141.0
margin_top = -172.0
margin_left = -67.0
margin_top = -34.0
grow_horizontal = 0
grow_vertical = 0
mouse_filter = 2
@ -98,8 +84,8 @@ size_flags_vertical = 3
[node name="DebugStatsContainer" type="GridContainer" parent="DebugContainer"]
margin_left = 7.0
margin_top = 7.0
margin_right = 134.0
margin_bottom = 165.0
margin_right = 60.0
margin_bottom = 27.0
grow_horizontal = 0
grow_vertical = 0
mouse_filter = 2
@ -296,18 +282,19 @@ margin_right = 40.0
margin_bottom = 40.0
mouse_filter = 2
[node name="WorldGeneratorContainer" type="VBoxContainer" parent="Generator Container"]
[node name="WorldGeneratorWidget" type="VBoxContainer" parent="Generator Container"]
margin_left = 10.0
margin_top = 10.0
margin_right = 145.0
margin_bottom = 94.0
script = ExtResource( 12 )
script = ExtResource( 1 )
[node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorWidget"]
visible = false
margin_right = 135.0
margin_bottom = 16.0
[node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorContainer/HBoxContainer"]
[node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorWidget/HBoxContainer"]
margin_right = 123.0
margin_bottom = 16.0
size_flags_horizontal = 3
@ -315,26 +302,25 @@ min_value = 1.0
max_value = 256.0
value = 1.0
[node name="WorldSizeLabel" type="Label" parent="Generator Container/WorldGeneratorContainer/HBoxContainer"]
[node name="WorldSizeLabel" type="Label" parent="Generator Container/WorldGeneratorWidget/HBoxContainer"]
margin_left = 127.0
margin_top = 1.0
margin_right = 135.0
margin_bottom = 15.0
text = "4"
[node name="WorldGenerateButton" type="Button" parent="Generator Container/WorldGeneratorContainer"]
margin_top = 20.0
[node name="WorldGenerateButton" type="Button" parent="Generator Container/WorldGeneratorWidget"]
visible = false
margin_right = 135.0
margin_bottom = 40.0
margin_bottom = 20.0
text = "Generate"
[node name="ShowTexturesCheckButton" type="CheckButton" parent="Generator Container/WorldGeneratorContainer"]
margin_top = 44.0
[node name="ShowTexturesCheckButton" type="CheckButton" parent="Generator Container/WorldGeneratorWidget"]
margin_right = 135.0
margin_bottom = 84.0
margin_bottom = 40.0
text = "Textures"
[node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"]
[node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"]
visible = false
margin_top = 88.0
margin_right = 135.0
@ -344,7 +330,7 @@ expand = true
stretch_mode = 5
flip_v = true
[node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"]
[node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"]
visible = false
margin_top = 88.0
margin_right = 135.0
@ -358,6 +344,7 @@ flip_v = true
visible = false
[node name="Camera" parent="." instance=ExtResource( 3 )]
transform = Transform( 1, 0, 0, 0, 0.60042, 0.799685, 0, -0.799685, 0.60042, -4.76837e-07, 5.16505, 3.1696 )
[node name="InteractionSystem" type="Node" parent="."]
script = ExtResource( 15 )
@ -370,55 +357,22 @@ WorldNode = NodePath("../World")
[node name="WorldInfo" parent="Player" index="2"]
WorldPath = NodePath("../../World")
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 )
[node name="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"]
transform = Transform( 1, 7.13626e-08, -4.47035e-08, 1.64262e-07, -1, -1.00583e-07, 1.19209e-07, 1.18278e-07, -1, -0.72, 0.45, 1.78362e-08 )
[node name="AnimationTree" parent="Player/Geometry" index="2"]
parameters/playback = SubResource( 26 )
[node name="Entities" type="Spatial" parent="."]
visible = false
[node name="Axe" parent="Entities" instance=ExtResource( 14 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.79762, 0, 0 )
input_ray_pickable = false
[node name="Chest" parent="Entities" instance=ExtResource( 11 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.27709, 0, 1.02593 )
transform = Transform( -0.825665, 0, 0.56416, 0, 1, 0, -0.56416, 0, -0.825665, -3.27709, 0, 1.02593 )
[node name="World" type="Spatial" parent="."]
script = ExtResource( 7 )
[node name="Chunks" type="Spatial" parent="World"]
[node name="TileInstanceManager" type="Spatial" parent="World"]
script = ExtResource( 10 )
ShowHexTiles = true
World = NodePath("..")
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World/TileInstanceManager"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 )
multimesh = SubResource( 27 )
[node name="Assets" type="Spatial" parent="World"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 )
visible = false
[node name="Rocks" type="Spatial" parent="World/Assets"]
[node name="rockA" parent="World/Assets/Rocks" instance=ExtResource( 16 )]
[node name="rockB" parent="World/Assets/Rocks" instance=ExtResource( 18 )]
[node name="rockC" parent="World/Assets/Rocks" instance=ExtResource( 17 )]
[node name="Grass" type="Spatial" parent="World/Assets"]
[node name="grassLarge" parent="World/Assets/Grass" instance=ExtResource( 20 )]
[node name="Trees" type="Spatial" parent="World/Assets"]
[node name="tree" parent="World/Assets/Trees" instance=ExtResource( 19 )]
[node name="World" parent="." instance=ExtResource( 7 )]
[node name="DirectionalLight" type="DirectionalLight" parent="."]
transform = Transform( 0.328059, -0.878387, 0.347583, 0, 0.367946, 0.929847, -0.944657, -0.305045, 0.120708, 0, 6.59293, 1.20265 )
@ -426,8 +380,8 @@ shadow_enabled = true
directional_shadow_mode = 0
[connection signal="toggled" from="DebugContainer/DebugStatsContainer/DebugMenuButton" to="DebugContainer/DebugStatsContainer" method="_on_DebugMenuButton_toggled"]
[connection signal="value_changed" from="Generator Container/WorldGeneratorContainer/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorContainer" method="_on_HSlider_value_changed"]
[connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"]
[connection signal="value_changed" from="Generator Container/WorldGeneratorWidget/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorWidget" method="_on_HSlider_value_changed"]
[connection signal="toggled" from="Generator Container/WorldGeneratorWidget/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorWidget" method="_on_ShowTexturesCheckButton_toggled"]
[editable path="Player"]
[editable path="Player/Geometry"]
[editable path="Player/Geometry/PirateAsset"]

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

@ -1,202 +0,0 @@
using System.Diagnostics;
using System.Linq;
using Godot;
using Godot.Collections;
public class TileInstanceManager : Spatial
{
// exports
[Export] public NodePath World;
[Export] public bool ShowHexTiles;
[Export] public Vector2 ViewCenterPlaneCoord;
// scene nodes
public MultiMeshInstance TileMultiMeshInstance;
// other members
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
private int _usedTileInstanceIndices;
private ImageTexture _viewTileTypeTexture;
private World _world;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_world = GetNode<World>(World);
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
TileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
Debug.Assert(TileMultiMeshInstance != null);
}
public override void _Process(float delta)
{
}
private SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex)
{
var sceneTileChunk =
new SceneTileChunk(chunkIndex, TileMultiMeshInstance, _usedTileInstanceIndices, ShowHexTiles);
_usedTileInstanceIndices += sceneTileChunk.TileNodes.Count;
foreach (var hexTile3D in sceneTileChunk.TileNodes)
{
hexTile3D.Connect("TileClicked", this, nameof(OnTileClicked));
hexTile3D.Connect("TileHovered", this, nameof(OnTileHovered));
}
return sceneTileChunk;
}
private SceneTileChunk FindSceneTileChunkAtIndex(Vector2 chunkIndex)
{
foreach (Spatial child in GetChildren())
{
var sceneTileChunk = child as SceneTileChunk;
if (sceneTileChunk == null) continue;
if (sceneTileChunk.ChunkIndex == chunkIndex) return sceneTileChunk;
}
return null;
}
private void HandleWorldTileChange(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices)
{
Array<SceneTileChunk> removedChunks = new();
foreach (var chunkIndex in removedChunkIndices)
{
var chunk = FindSceneTileChunkAtIndex(chunkIndex);
if (chunk != null) removedChunks.Add(chunk);
}
foreach (var chunkIndex in addedChunkIndices)
{
SceneTileChunk sceneTileChunk = null;
if (removedChunks.Count > 0)
{
sceneTileChunk = removedChunks[^1];
sceneTileChunk.ChunkIndex = chunkIndex;
removedChunks.RemoveAt(removedChunks.Count - 1);
}
else
{
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
AddChild(sceneTileChunk);
}
_sceneTileChunks.Add(sceneTileChunk);
}
GD.Print("Removed Chunks " + removedChunkIndices.Count);
GD.Print("Added Chunks " + addedChunkIndices.Count);
GD.Print("Removed chunk count: " + removedChunks.Count);
}
public void OnTileClicked(HexTile3D tile)
{
EmitSignal("TileClicked", tile);
}
public void OnTileHovered(HexTile3D tile)
{
EmitSignal("TileHovered", tile);
}
// signals
[Signal]
private delegate void TileClicked(HexTile3D tile3d);
[Signal]
private delegate void TileHovered(HexTile3D tile3d);
private class SceneTileChunk : Spatial
{
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
private readonly MultiMeshInstance _multiMeshInstance;
private readonly Array<int> _tileInstanceIndices = new();
private readonly HexGrid _hexGrid = new();
private readonly bool _showHexTiles;
public readonly Array<HexTile3D> TileNodes = new();
private Vector2 _chunkIndex = Vector2.Inf;
public SceneTileChunk(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance, int tileInstanceIndexStart,
bool showHexTiles)
{
_showHexTiles = showHexTiles;
var tileInstanceIndexStart1 = tileInstanceIndexStart;
var chunkSize = global::World.ChunkSize;
foreach (var i in Enumerable.Range(0, chunkSize))
foreach (var j in Enumerable.Range(0, chunkSize))
{
var tile3D = (HexTile3D)_hexTile3DScene.Instance();
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * global::World.ChunkSize + new Vector2(i, j));
var tileTransform = Transform.Identity;
var centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
tile3D.Transform = tileTransform;
TileNodes.Add(tile3D);
AddChild(tile3D);
}
_multiMeshInstance = multiMeshInstance;
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
Debug.Assert(tileInstanceIndexStart1 + chunkTileCount <= _multiMeshInstance.Multimesh.InstanceCount);
foreach (var i in Enumerable.Range(0, chunkTileCount))
_tileInstanceIndices.Add(tileInstanceIndexStart1 + i);
// _multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
ChunkIndex = chunkIndex;
}
public Vector2 ChunkIndex
{
get => _chunkIndex;
set
{
var chunkTransform = Transform.Identity;
var chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize);
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
Transform = chunkTransform;
_chunkIndex = value;
var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f);
GD.Print("Updating transforms for instances of chunk " + value + " origin: " + chunkTransform.origin);
foreach (var i in Enumerable.Range(0, _tileInstanceIndices.Count))
{
var column = i % global::World.ChunkSize;
var row = i / global::World.ChunkSize;
var tilePlaneCoord =
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
var hexTransform = new Transform(tileOrientation,
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
if (_showHexTiles)
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
hexTransform.origin);
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
}
}
}
}
}

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

@ -4,12 +4,11 @@ using System.Diagnostics;
using System.Linq;
using Godot;
using Godot.Collections;
using Priority_Queue;
public class World : Spatial
{
public enum GenerationState
{
Undefined,
public class World : Spatial {
public enum GenerationState {
Empty,
Heightmap,
TileType,
Objects,
@ -17,7 +16,7 @@ public class World : Spatial
}
// constants
public const int ChunkSize = 18;
public int ChunkSize = 14;
public const int NumChunkRows = 3;
public const int NumChunkColumns = NumChunkRows;
private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f);
@ -25,8 +24,9 @@ public class World : Spatial
private static readonly Color DarkGrassColor = new(0.05882353f, 0.5411765f, 0.05882353f);
private static readonly Color LightWaterColor = new(0.05882353f, 0.05882353f, 0.8627451f);
private readonly List<Vector2> _addedChunkIndices = new();
private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
private readonly List<Vector2> _addedChunkIndices = new();
private readonly List<WorldChunk> _deactivatedWorldChunks = new();
private readonly Image _heightmapImage = new();
private readonly List<Vector2> _removedChunkIndices = new();
private readonly Image _tileTypeMapImage = new();
@ -36,6 +36,7 @@ public class World : Spatial
private List<Vector2> _activeChunkIndices = new();
private Rect2 _centerChunkRect;
private readonly List<Spatial> _removedSpatialNodes = new();
// delegate void OnCoordClicked(Vector2 world_pos);
@ -48,7 +49,8 @@ public class World : Spatial
private OpenSimplexNoise _noiseGenerator = new();
private Array<Spatial> _rockAssets;
private TileInstanceManager _tileInstanceManager;
private MultiMeshInstance _tileMultiMeshInstance;
private int _usedTileMeshInstances;
private Array<Spatial> _treeAssets;
private ImageTexture _viewTileTypeTexture;
public Vector2 CenterChunkIndex = Vector2.Zero;
@ -57,7 +59,7 @@ public class World : Spatial
public HexGrid HexGrid = new();
public int Seed = 0;
public GenerationState State = GenerationState.Done;
public GenerationState State = GenerationState.Empty;
public Vector2 WorldTextureCoordinateOffset;
@ -80,42 +82,51 @@ public class World : Spatial
[Signal]
private delegate void OnHeightmapImageChanged(Image heightmapImage);
public World()
{
[Signal]
public delegate void EntityClicked(Entity entity);
// signals
[Signal]
private delegate void TileClicked(HexTile3D tile3d);
[Signal]
private delegate void TileHovered(HexTile3D tile3d);
public World() {
Debug.Assert(ChunkSize % 2 == 0);
_cachedWorldChunks = new Godot.Collections.Dictionary<Vector2, WorldChunk>();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
public override void _Ready() {
Chunks = (Spatial)FindNode("Chunks");
Debug.Assert(Chunks != null);
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
Debug.Assert(_tileInstanceManager != null);
_tileInstanceManager.TileMultiMeshInstance.Multimesh.InstanceCount =
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
_tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
Debug.Assert(_tileMultiMeshInstance != null);
InitNoiseGenerator();
GetNode<Spatial>("Assets").Visible = false;
_rockAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) _rockAssets.Add(asset);
foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) {
_rockAssets.Add(asset);
}
_grassAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) _grassAssets.Add(asset);
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) {
_grassAssets.Add(asset);
}
_treeAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) _treeAssets.Add(asset);
SetCenterPlaneCoord(Vector2.Zero);
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) {
_treeAssets.Add(asset);
}
}
public void InitNoiseGenerator()
{
public void InitNoiseGenerator() {
_noiseGenerator = new OpenSimplexNoise();
_noiseGenerator.Seed = Seed;
@ -125,65 +136,86 @@ public class World : Spatial
_noiseGenerator.Lacunarity = 2;
}
public WorldChunk GetOrCreateWorldChunk(int xIndex, int yIndex, Color debugColor)
{
if (IsChunkCached(xIndex, yIndex))
{
var cachedChunk = _cachedWorldChunks[new Vector2(xIndex, yIndex)];
return cachedChunk;
public void Reset() {
foreach (Spatial chunkChild in Chunks.GetChildren()) {
chunkChild.QueueFree();
}
return CreateWorldChunk(xIndex, yIndex, debugColor);
// foreach (WorldChunk chunk in _cachedWorldChunks.Values) {
// chunk.QueueFree();
// }
_cachedWorldChunks.Clear();
_addedChunkIndices.Clear();
_tileMultiMeshInstance.Multimesh.InstanceCount =
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
_usedTileMeshInstances = 0;
State = GenerationState.Empty;
}
private bool IsChunkCached(int xIndex, int yIndex)
{
return _cachedWorldChunks.ContainsKey(new Vector2(xIndex, yIndex));
public WorldChunk GetOrCreateWorldChunk(Vector2 chunkIndex, Color debugColor) {
WorldChunk chunk;
if (IsChunkCached(chunkIndex)) {
return _cachedWorldChunks[chunkIndex];
}
if (_deactivatedWorldChunks.Count > 0) {
chunk = _deactivatedWorldChunks.First();
_deactivatedWorldChunks.RemoveAt(0);
} else {
chunk = CreateWorldChunk(chunkIndex, debugColor);
}
_cachedWorldChunks[chunkIndex] = chunk;
return chunk;
}
private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor)
{
var result = (WorldChunk)_worldChunkScene.Instance();
private bool IsChunkCached(Vector2 chunkIndex) {
return _cachedWorldChunks.ContainsKey(chunkIndex);
}
private WorldChunk CreateWorldChunk(Vector2 chunkIndex, Color debugColor) {
WorldChunk result = (WorldChunk)_worldChunkScene.Instance();
result.SetSize(ChunkSize);
Chunks.AddChild(result);
result.Connect("TileClicked", this, nameof(OnTileClicked));
result.Connect("TileHovered", this, nameof(OnTileHovered));
var offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize;
var offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1);
result.SetSize(ChunkSize);
result.InitializeTileInstances(chunkIndex, _tileMultiMeshInstance, _usedTileMeshInstances);
_usedTileMeshInstances += result.Tiles.GetChildCount();
var planeCoordSouthWest = HexGrid.GetHexCenterFromOffset(offsetCoordSouthWest) +
new Vector2(-HexGrid.HexSize.x, HexGrid.HexSize.y) * 0.5f;
var planeCoordNorthEast = HexGrid.GetHexCenterFromOffset(offsetCoordNorthEast) +
new Vector2(HexGrid.HexSize.x, -HexGrid.HexSize.y) * 0.5f;
result.ChunkIndex = new Vector2(xIndex, yIndex);
result.PlaneRect = new Rect2(
new Vector2(planeCoordSouthWest.x, planeCoordNorthEast.y),
new Vector2(planeCoordNorthEast.x - planeCoordSouthWest.x, planeCoordSouthWest.y - planeCoordNorthEast.y));
result.SetChunkIndex(chunkIndex, HexGrid);
result.UpdateTileTransforms();
result.DebugColor = debugColor;
result.DebugColor.a = 0.6f;
Chunks.AddChild(result);
var chunkIndex = new Vector2(xIndex, yIndex);
_cachedWorldChunks.Add(chunkIndex, result);
return result;
}
private bool IsColorEqualApprox(Color colorA, Color colorB)
{
var colorDifference = new Vector3(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
private bool IsColorEqualApprox(Color colorA, Color colorB) {
Vector3 colorDifference = new(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
return colorDifference.LengthSquared() < 0.1 * 0.1;
}
private Spatial SelectAsset(Vector2 offsetCoord, Array<Spatial> assets, Random randomGenerator, double probability)
{
if (randomGenerator.NextDouble() < 1.0 - probability) return null;
private Spatial SelectAsset(Vector2 textureCoord, Array<Spatial> assets, Random randomGenerator,
double probability) {
if (randomGenerator.NextDouble() < 1.0 - probability) {
return null;
}
var assetIndex = randomGenerator.Next(assets.Count);
var assetInstance = (Spatial)assets[assetIndex].Duplicate();
var assetTransform = Transform.Identity;
assetTransform.origin = HexGrid.GetHexCenterVec3FromOffset(offsetCoord);
int assetIndex = randomGenerator.Next(assets.Count);
Spatial assetInstance = (Spatial)assets[assetIndex].Duplicate();
Transform assetTransform = Transform.Identity;
assetTransform.origin = HexGrid.GetHexCenterVec3FromOffset(textureCoord);
// TODO: assetTransform.origin.y = GetHeightAtOffset(offsetCoord);
assetTransform.origin.y = 0;
assetTransform.basis =
@ -193,41 +225,40 @@ public class World : Spatial
return assetInstance;
}
private void PopulateChunk(WorldChunk chunk)
{
var environmentRandom = new Random(Seed);
private void PopulateChunk(WorldChunk chunk) {
Random environmentRandom = new(Seed);
var tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData();
tileTypeImage.Lock();
chunk.CreateUnlockedTileTypeImage();
foreach (var textureCoordU in Enumerable.Range(0, chunk.Size))
foreach (var textureCoordV in Enumerable.Range(0, chunk.Size))
{
var colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV);
var textureCoord = new Vector2(textureCoordU, textureCoordV);
var offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
foreach (int textureCoordU in Enumerable.Range(0, chunk.Size)) {
foreach (int textureCoordV in Enumerable.Range(0, chunk.Size)) {
Color colorValue = chunk.TileTypeImage.GetPixel(textureCoordU, textureCoordV);
Vector2 textureCoord = new(textureCoordU, textureCoordV);
Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
if (IsColorEqualApprox(colorValue, RockColor))
{
var rockAsset = SelectAsset(offsetCoord, _rockAssets, environmentRandom, 0.15);
if (rockAsset != null) chunk.Entities.AddChild(rockAsset);
// TODO: MarkCellUnwalkable(cell);
}
else if (IsColorEqualApprox(colorValue, GrassColor) || IsColorEqualApprox(colorValue, DarkGrassColor))
{
var grassAsset = SelectAsset(offsetCoord, _grassAssets, environmentRandom, 0.15);
if (grassAsset != null) chunk.Entities.AddChild(grassAsset);
if (IsColorEqualApprox(colorValue, RockColor)) {
Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.15);
if (rockAsset != null) {
chunk.Entities.AddChild(rockAsset);
MarkCellUnwalkable(HexGrid.GetHexAtOffset(offsetCoord));
}
} else if (IsColorEqualApprox(colorValue, GrassColor) ||
IsColorEqualApprox(colorValue, DarkGrassColor)) {
Spatial grassAsset = SelectAsset(textureCoord, _grassAssets, environmentRandom, 0.15);
if (grassAsset != null) {
chunk.Entities.AddChild(grassAsset);
}
Tree treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.05) as Tree;
if (treeAsset != null)
{
chunk.Entities.AddChild(treeAsset);
treeAsset.Connect("EntityClicked", this, nameof(OnEntityClicked));
}
Tree treeAsset = SelectAsset(textureCoord, _treeAssets, environmentRandom, 0.05) as Tree;
if (treeAsset != null) {
chunk.Entities.AddChild(treeAsset);
treeAsset.Connect("EntityClicked", this, nameof(OnEntityClicked));
treeAsset.Connect("TreeChopped", this, nameof(OnBlockingSpatialRemoved));
MarkCellUnwalkable(HexGrid.GetHexAtOffset(offsetCoord));
}
// TODO: MarkCellUnwalkable(cell);
// else if (environmentRandom.NextDouble() < 0.01)
// TODO: MarkCellUnwalkable(cell);
// else if (environmentRandom.NextDouble() < 0.01)
// {
// var chestAsset = (Chest)_chestScene.Instance();
// var assetTransform = Transform.Identity;
@ -237,30 +268,25 @@ public class World : Spatial
// Entities.AddChild(chestAsset);
// MarkCellUnwalkable(cell);
// }
}
}
// else if (IsColorWater(colorValue))
// {
// MarkCellUnwalkable(cell);
// }
}
}
tileTypeImage.Unlock();
}
public Vector2 WorldToOffsetCoords(Vector3 fromPositionWorld)
{
public Vector2 WorldToOffsetCoords(Vector3 fromPositionWorld) {
return HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)).OffsetCoords;
}
public Vector3 GetHexCenterFromOffset(Vector2 fromPositionOffset)
{
public Vector3 GetHexCenterFromOffset(Vector2 fromPositionOffset) {
return HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset);
}
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
{
if (State != GenerationState.Done)
{
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord) {
if (State != GenerationState.Done && State != GenerationState.Empty) {
GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!");
return;
}
@ -269,105 +295,108 @@ public class World : Spatial
Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks);
// set new center chunk
var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
CenterChunkIndex = new Vector2(chunkIndex.Item1, chunkIndex.Item2);
CenterChunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
var currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2,
WorldChunk currentChunk = GetOrCreateWorldChunk(CenterChunkIndex,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_centerChunkRect = currentChunk.PlaneRect;
_centerChunkRect = new Rect2(
new Vector2(currentChunk.Transform.origin.x, currentChunk.Transform.origin.z)
+ currentChunk.PlaneRect.Position,
currentChunk.PlaneRect.Size);
GD.Print("Center Chunk Rect: " + _centerChunkRect.Position + " size: " + _centerChunkRect.Size);
// load or create adjacent chunks
_activeChunkIndices = new List<Vector2>();
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, -1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(0, -1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(1, -1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, 0));
_activeChunkIndices.Add(CenterChunkIndex);
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, 0));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, +1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(0, +1));
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1));
// clear unused chunks
_deactivatedWorldChunks.Clear();
_addedChunkIndices.Clear();
foreach (Vector2 oldChunkIndex in oldCachedChunks.Keys) {
if (!_activeChunkIndices.Contains(oldChunkIndex)) {
DeactivateChunk(oldCachedChunks[oldChunkIndex]);
}
}
foreach (Vector2 activeChunkIndex in _activeChunkIndices) {
WorldChunk chunk = GetOrCreateWorldChunk(activeChunkIndex,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_cachedWorldChunks[activeChunkIndex] = chunk;
}
Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns);
foreach (var activeChunkIndex in _activeChunkIndices)
GetOrCreateWorldChunk((int)activeChunkIndex.x, (int)activeChunkIndex.y,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
// unload retired chunks
_removedChunkIndices.Clear();
_addedChunkIndices.Clear();
foreach (var cachedChunkKey in oldCachedChunks.Keys)
if (!_activeChunkIndices.Contains(cachedChunkKey))
RemoveChunk(cachedChunkKey);
foreach (var chunkKey in _activeChunkIndices)
if (!oldCachedChunks.ContainsKey(chunkKey))
{
_addedChunkIndices.Add(chunkKey);
var chunk = _cachedWorldChunks[chunkKey];
GenerateChunkNoiseMap(chunk);
foreach (Vector2 chunkKey in _activeChunkIndices) {
if (!oldCachedChunks.ContainsKey(chunkKey)) {
ActivateChunk(_cachedWorldChunks[chunkKey], chunkKey);
State = GenerationState.Heightmap;
}
}
}
private void GenerateChunkNoiseMap(WorldChunk chunk)
{
var chunkIndex = chunk.ChunkIndex;
private void ActivateChunk(WorldChunk chunk, Vector2 chunkIndex) {
chunk.SetChunkIndex(chunkIndex, HexGrid);
chunk.UpdateTileTransforms();
var debugChunkColor = new Color(Mathf.Abs(chunkIndex.x) / 5, Mathf.Abs(chunkIndex.y) / 5,
Mathf.RoundToInt(Mathf.Abs(chunkIndex.x + chunkIndex.y)) % 2);
_addedChunkIndices.Add(chunk.ChunkIndex);
GenerateChunkNoiseMap(chunk);
}
var noiseImageTexture = new ImageTexture();
private void DeactivateChunk(WorldChunk chunk) {
_cachedWorldChunks.Remove(chunk.ChunkIndex);
chunk.ClearContent();
_deactivatedWorldChunks.Add(chunk);
}
private void GenerateChunkNoiseMap(WorldChunk chunk) {
Vector2 chunkIndex = chunk.ChunkIndex;
ImageTexture noiseImageTexture = new();
noiseImageTexture.CreateFromImage(_noiseGenerator.GetImage(ChunkSize, ChunkSize, chunkIndex * ChunkSize),
0);
// Debug Texture
var simpleImage = new Image();
simpleImage.Create(ChunkSize, ChunkSize, false, Image.Format.Rgb8);
simpleImage.Lock();
foreach (var i in Enumerable.Range(0, ChunkSize))
foreach (var j in Enumerable.Range(0, ChunkSize))
if ((i + j) % 2 == 0)
simpleImage.SetPixelv(new Vector2(i, j), Colors.Aqua);
else
simpleImage.SetPixelv(new Vector2(i, j), debugChunkColor);
simpleImage.Unlock();
// noiseImageTexture.CreateFromImage(simpleImage, 0);
chunk.SetNoisemap(noiseImageTexture);
}
private void RemoveChunk(Vector2 cachedChunkKey)
{
private void RemoveChunk(Vector2 cachedChunkKey) {
_cachedWorldChunks.Remove(cachedChunkKey);
_removedChunkIndices.Add(cachedChunkKey);
foreach (WorldChunk chunk in Chunks.GetChildren())
if (chunk.ChunkIndex == new Vector2(cachedChunkKey.x, cachedChunkKey.y))
foreach (WorldChunk chunk in Chunks.GetChildren()) {
if (chunk.ChunkIndex == new Vector2(cachedChunkKey.x, cachedChunkKey.y)) {
chunk.QueueFree();
}
}
}
private Tuple<int, int> GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
{
var centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
var chunkIndexFloat = (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
var chunkIndex = new Tuple<int, int>((int)chunkIndexFloat.x, (int)chunkIndexFloat.y);
return chunkIndex;
private Vector2 GetChunkTupleFromPlaneCoord(Vector2 planeCoord) {
HexCell hexCell = HexGrid.GetHexAt(planeCoord);
return GetChunkIndexFromOffsetCoord(hexCell.OffsetCoords);
}
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
{
if (!_centerChunkRect.HasPoint(centerPlaneCoord))
{
private Vector2 GetChunkIndexFromOffsetCoord(Vector2 offsetCoord) {
return (offsetCoord / ChunkSize).Floor();
}
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord) {
if (State != GenerationState.Done) {
return;
}
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) {
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
UpdateChunkBounds();
@ -376,20 +405,18 @@ public class World : Spatial
}
}
private void UpdateWorldViewTexture()
{
var worldChunkSize = ChunkSize;
var numWorldChunkRows = NumChunkRows;
var numWorldChunkColumns = NumChunkColumns;
private void UpdateWorldViewTexture() {
int worldChunkSize = ChunkSize;
int numWorldChunkRows = NumChunkRows;
int numWorldChunkColumns = NumChunkColumns;
_heightmapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
Image.Format.Rgba8);
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
Image.Format.Rgba8);
foreach (var chunkIndex in _activeChunkIndices)
{
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
foreach (Vector2 chunkIndex in _activeChunkIndices) {
WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White);
_heightmapImage.BlendRect(
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
@ -414,90 +441,287 @@ public class World : Spatial
EmitSignal("OnHeightmapImageChanged", _heightmapImage);
}
private void UpdateChunkBounds()
{
private void UpdateChunkBounds() {
_chunkIndexSouthWest = Vector2.Inf;
_chunkIndexNorthEast = -Vector2.Inf;
foreach (var chunkIndex in _activeChunkIndices)
{
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y)
foreach (Vector2 chunkIndex in _activeChunkIndices) {
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y) {
_chunkIndexSouthWest = chunkIndex;
else if (chunkIndex.x >= _chunkIndexNorthEast.x && chunkIndex.y >= _chunkIndexNorthEast.y)
} else if (chunkIndex.x >= _chunkIndexNorthEast.x && chunkIndex.y >= _chunkIndexNorthEast.y) {
_chunkIndexNorthEast = chunkIndex;
}
}
}
private void UpdateNavigationBounds()
{
var cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize);
// Chunks have their cells ordered from south west (0,0) to north east (ChunkSize, ChunkSize). For the
// north east cell we have to add the chunk size to get to the actual corner cell.
var cellNorthEast = HexGrid.GetHexAtOffset(_chunkIndexNorthEast * ChunkSize + Vector2.One * (ChunkSize - 1));
var centerCell =
HexGrid.GetHexAtOffset(((cellNorthEast.OffsetCoords - cellSouthWest.OffsetCoords) / 2).Round());
var numCells = ChunkSize * Math.Max(NumChunkColumns, NumChunkRows);
private void UpdateNavigationBounds() {
HexCell cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize);
HexGrid.SetBoundsOffset(cellSouthWest, ChunkSize * new Vector2(NumChunkColumns, NumChunkRows));
}
public override void _Process(float delta)
{
var oldState = State;
public void MarkCellUnwalkable(HexCell cell) {
HexGrid.AddObstacle(cell);
}
public float GetHexCost(Entity entity, HexCell cell) {
float nextHexCost = HexGrid.GetHexCost(cell);
if (nextHexCost != 0) {
Vector2 nextOffset = cell.OffsetCoords;
Vector2 chunkIndex = GetChunkIndexFromOffsetCoord(nextOffset);
if (!_cachedWorldChunks.ContainsKey(chunkIndex)) {
return 0;
}
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
Vector2 textureCoordinate = nextOffset - Vector2.One * ChunkSize * chunkIndex;
Color tileTypeColor = chunk.TileTypeImage.GetPixel((int)textureCoordinate.x, (int)textureCoordinate.y);
if (!IsColorEqualApprox(tileTypeColor, GrassColor) &&
!IsColorEqualApprox(tileTypeColor, DarkGrassColor)) {
nextHexCost = 0;
}
}
return nextHexCost;
}
public float GetMoveCost(Entity entity, HexCell currentHex, HexCell nextHex) {
if (GetHexCost(entity, nextHex) == 0) {
return 0;
}
return HexGrid.GetMoveCost(currentHex.AxialCoords,
new HexCell(nextHex.AxialCoords - currentHex.AxialCoords).CubeCoords);
}
public List<HexCell> FindPath(Entity entity, HexCell startHex, HexCell goalHex) {
if (State != GenerationState.Done) {
return new List<HexCell>();
}
Vector2 goalAxialCoords = goalHex.AxialCoords;
SimplePriorityQueue<Vector2, float> frontier = new();
frontier.Enqueue(startHex.AxialCoords, 0);
System.Collections.Generic.Dictionary<Vector2, Vector2> cameFrom = new();
System.Collections.Generic.Dictionary<Vector2, float> costSoFar = new();
cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords);
costSoFar.Add(startHex.AxialCoords, 0);
int FindPathCheckedCellCount = 0;
while (frontier.Any()) {
FindPathCheckedCellCount++;
HexCell currentHex = new(frontier.Dequeue());
Vector2 currentAxial = currentHex.AxialCoords;
if (currentHex == goalHex) {
break;
}
foreach (HexCell nextHex in currentHex.GetAllAdjacent()) {
Vector2 nextAxial = nextHex.AxialCoords;
float moveCost = GetMoveCost(entity, currentHex, nextHex);
if (nextHex == goalHex && moveCost == 0 && GetHexCost(entity, nextHex) == 0) {
// Goal ist an obstacle
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
frontier.Clear();
break;
}
if (moveCost == 0) {
continue;
}
moveCost += costSoFar[currentHex.AxialCoords];
if (!costSoFar.ContainsKey(nextHex.AxialCoords) || moveCost < costSoFar[nextHex.AxialCoords]) {
costSoFar[nextHex.AxialCoords] = moveCost;
float priority = moveCost + nextHex.DistanceTo(goalHex);
frontier.Enqueue(nextHex.AxialCoords, priority);
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
}
}
}
// GD.Print("Checked Cell Count: " + FindPathCheckedCellCount);
List<HexCell> result = new();
if (!cameFrom.ContainsKey(goalHex.AxialCoords)) {
GD.Print("Failed to find path from " + startHex + " to " + goalHex);
return result;
}
if (HexGrid.GetHexCost(goalAxialCoords) != 0) {
result.Add(goalHex);
}
HexCell pathHex = goalHex;
while (pathHex != startHex) {
pathHex = new HexCell(cameFrom[pathHex.AxialCoords]);
result.Insert(0, pathHex);
}
return result;
}
public bool CheckSweptTriangleCellCollision(Entity entity, Vector3 startWorld, Vector3 endWorld, float radius) {
Vector2 startPlane = new(startWorld.x, startWorld.z);
Vector2 endPlane = new(endWorld.x, endWorld.z);
Vector2 directionPlane = (endPlane - startPlane).Normalized();
Vector2 sidePlane = directionPlane.Rotated(Mathf.Pi * 0.5f);
List<HexCell> cells =
HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius);
foreach (HexCell cell in cells) {
if (GetHexCost(entity, cell) == 0) {
return true;
}
}
cells = HexGrid.GetCellsForLine(startPlane + sidePlane * radius, endPlane + sidePlane * radius);
foreach (HexCell cell in cells) {
if (GetHexCost(entity, cell) == 0) {
return true;
}
}
cells = HexGrid.GetCellsForLine(startPlane - sidePlane * radius, endPlane - sidePlane * radius);
foreach (HexCell cell in cells) {
if (GetHexCost(entity, cell) == 0) {
return true;
}
}
return false;
}
public List<NavigationPoint> SmoothPath(Entity entity, List<NavigationPoint> navigationPoints) {
if (navigationPoints.Count <= 2) {
return navigationPoints;
}
Vector3 bodyGlobalTranslation = entity.GlobalTranslation;
List<NavigationPoint> smoothedPath = new();
int startIndex = 0;
int endIndex = navigationPoints.Count > 1 ? 1 : 0;
smoothedPath.Add(navigationPoints[startIndex]);
Vector3 startPoint = navigationPoints[startIndex].WorldPosition;
while (endIndex != navigationPoints.Count) {
Vector3 endPoint = navigationPoints[endIndex].WorldPosition;
if (CheckSweptTriangleCellCollision(entity, startPoint, endPoint, 0.27f)) {
if (endIndex - startIndex == 1) {
GD.Print("Aborting SmoothPath: input path passes through collision geometry.");
entity.GlobalTranslation = bodyGlobalTranslation;
return smoothedPath;
}
smoothedPath.Add(navigationPoints[endIndex - 1]);
startIndex = endIndex - 1;
startPoint = navigationPoints[startIndex].WorldPosition;
entity.GlobalTranslation = startPoint;
continue;
}
if (endIndex == navigationPoints.Count - 1) {
break;
}
endIndex += 1;
}
smoothedPath.Add(navigationPoints[endIndex]);
entity.GlobalTranslation = bodyGlobalTranslation;
return smoothedPath;
}
public override void _Process(float delta) {
GenerationState oldState = State;
UpdateGenerationState();
if (oldState != GenerationState.Done && State == GenerationState.Done)
{
if (oldState != GenerationState.Done && State == GenerationState.Done) {
UpdateWorldViewTexture();
}
EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray());
while (_removedSpatialNodes.Count > 0) {
GD.Print("Queueing deletion of " + _removedSpatialNodes[0]);
_removedSpatialNodes[0].QueueFree();
_removedSpatialNodes.RemoveAt(0);
}
}
private void UpdateGenerationState()
{
if (State == GenerationState.Heightmap)
{
var numChunksGeneratingHeightmap = 0;
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
if (chunk.HeightMapFrameCount > 0) numChunksGeneratingHeightmap++;
private void UpdateGenerationState() {
if (State == GenerationState.Heightmap) {
int numChunksGeneratingHeightmap = 0;
foreach (Vector2 chunkIndex in _addedChunkIndices) {
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
if (chunk.HeightMapFrameCount > 0) {
numChunksGeneratingHeightmap++;
}
}
if (numChunksGeneratingHeightmap == 0)
{
if (numChunksGeneratingHeightmap == 0) {
// assign height map images
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
foreach (Vector2 chunkIndex in _addedChunkIndices) {
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture());
}
State = GenerationState.TileType;
}
}
else if (State == GenerationState.TileType)
{
var numChunksGeneratingTileType = 0;
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
if (chunk.TileTypeMapFrameCount > 0) numChunksGeneratingTileType++;
} else if (State == GenerationState.TileType) {
int numChunksGeneratingTileType = 0;
foreach (Vector2 chunkIndex in _addedChunkIndices) {
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
if (chunk.TileTypeMapFrameCount > 0) {
numChunksGeneratingTileType++;
}
}
if (numChunksGeneratingTileType == 0) State = GenerationState.Objects;
}
else if (State == GenerationState.Objects)
{
if (numChunksGeneratingTileType == 0) {
State = GenerationState.Objects;
}
} else if (State == GenerationState.Objects) {
// generate objects
foreach (var chunkIndex in _addedChunkIndices) PopulateChunk(_cachedWorldChunks[chunkIndex]);
foreach (Vector2 chunkIndex in _addedChunkIndices) {
PopulateChunk(_cachedWorldChunks[chunkIndex]);
}
_addedChunkIndices.Clear();
State = GenerationState.Done;
}
}
private void OnEntityClicked(Entity entity) {
EmitSignal("EntityClicked", entity);
}
public void OnTileClicked(HexTile3D tile) {
EmitSignal("TileClicked", tile);
}
public void OnTileHovered(HexTile3D tile) {
EmitSignal("TileHovered", tile);
}
public void OnBlockingSpatialRemoved(Spatial spatialNode) {
if (spatialNode.IsQueuedForDeletion()) {
return;
}
HexGrid.RemoveObstacle(HexGrid.GetHexAt(new Vector2(spatialNode.GlobalTranslation.x,
spatialNode.GlobalTranslation.z)));
_removedSpatialNodes.Add(spatialNode);
}
}

50
scenes/World.tscn Normal file
View File

@ -0,0 +1,50 @@
[gd_scene load_steps=10 format=2]
[ext_resource path="res://scenes/World.cs" type="Script" id=1]
[ext_resource path="res://entities/rockA.tscn" type="PackedScene" id=2]
[ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=3]
[ext_resource path="res://entities/Tree.cs" type="Script" id=4]
[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=5]
[ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=6]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=7]
[ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=8]
[sub_resource type="MultiMesh" id=27]
color_format = 1
transform_format = 1
custom_data_format = 1
visible_instance_count = 0
mesh = ExtResource( 3 )
[node name="World" type="Spatial"]
script = ExtResource( 1 )
[node name="Chunks" type="Spatial" parent="."]
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 )
multimesh = SubResource( 27 )
[node name="Assets" type="Spatial" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 )
visible = false
[node name="Rocks" type="Spatial" parent="Assets"]
[node name="rockA" type="StaticBody" parent="Assets/Rocks" instance=ExtResource( 2 )]
input_ray_pickable = false
[node name="rockB" type="StaticBody" parent="Assets/Rocks" instance=ExtResource( 7 )]
input_ray_pickable = false
[node name="rockC" type="StaticBody" parent="Assets/Rocks" instance=ExtResource( 5 )]
input_ray_pickable = false
[node name="Grass" type="Spatial" parent="Assets"]
[node name="grassLarge" type="Spatial" parent="Assets/Grass" groups=["GameGeometry"] instance=ExtResource( 6 )]
[node name="Trees" type="Spatial" parent="Assets"]
[node name="tree" type="StaticBody" parent="Assets/Trees" instance=ExtResource( 8 )]
script = ExtResource( 4 )

View File

@ -1,9 +1,13 @@
using System.Diagnostics;
using System.Linq;
using Godot;
using Godot.Collections;
public class WorldChunk : Spatial {
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
private MultiMeshInstance _multiMeshInstance;
private readonly Array<int> _tileInstanceIndices = new();
public class WorldChunk : Spatial
{
private readonly SpatialMaterial _rectMaterial = new();
private Sprite _heightmapSprite;
private TextureRect _heightmapTextureRect;
@ -11,27 +15,39 @@ public class WorldChunk : Spatial
private Sprite _noiseSprite;
private bool _showTextureOverlay;
[Export] public Vector2 ChunkIndex;
public Color DebugColor = Colors.White;
[Export] public Spatial Entities;
public Spatial Entities;
public Spatial Tiles;
private readonly HexGrid _hexGrid = new();
private readonly bool _showHexTiles;
[Export] public Texture HeightMap;
public int HeightMapFrameCount;
public Image TileTypeImage;
public Viewport HeightmapOffscreenViewport;
[Export] public Texture NavigationMap;
public bool NoiseTextureCheckerboardOverlay = true;
// signals
// delegate void OnCoordClicked(Vector2 world_pos);
[Signal]
private delegate void TileClicked(HexTile3D tile3d);
[Signal]
private delegate void TileHovered(HexTile3D tile3d);
// other members
public Rect2 PlaneRect;
// ui elements
// scene nodes
private MeshInstance PlaneRectMesh;
[Export] public int Size = 32;
public int Size = 32;
// resources
@ -40,34 +56,26 @@ public class WorldChunk : Spatial
public int TileTypeMapFrameCount;
public Viewport TileTypeOffscreenViewport;
public WorldChunk()
{
}
public WorldChunk(int size)
{
SetSize(size);
}
[Export]
public bool ShowTextureOverlay
{
public bool ShowTextureOverlay {
get => _showTextureOverlay;
set
{
if (PlaneRectMesh != null) PlaneRectMesh.Visible = value;
set {
if (PlaneRectMesh != null) {
PlaneRectMesh.Visible = value;
}
}
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
public override void _Ready() {
PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh");
Debug.Assert(PlaneRectMesh != null);
if (PlaneRectMesh.Visible) _showTextureOverlay = true;
if (PlaneRectMesh.Visible) {
_showTextureOverlay = true;
}
var planeRectTransform = Transform.Identity;
Transform planeRectTransform = Transform.Identity;
planeRectTransform =
planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x, 0.125f, PlaneRect.Size.y));
planeRectTransform.origin.x = PlaneRect.GetCenter().x;
@ -94,15 +102,14 @@ public class WorldChunk : Spatial
Entities = (Spatial)FindNode("Entities");
Debug.Assert(Entities != null);
SetSize(World.ChunkSize);
Tiles = (Spatial)FindNode("Tiles");
Debug.Assert(Tiles != null);
}
public void SetSize(int size)
{
public void SetSize(int size) {
Size = size;
if (TileTypeOffscreenViewport != null)
{
if (TileTypeOffscreenViewport != null) {
TileTypeOffscreenViewport.Size = Vector2.One * size;
HeightmapOffscreenViewport.Size = Vector2.One * size;
_noiseMask.Transform = Transform2D.Identity.Scaled(Vector2.One * size / _noiseMask.Texture.GetSize().x);
@ -112,10 +119,95 @@ public class WorldChunk : Spatial
}
}
public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid) {
ChunkIndex = chunkIndex;
float chunkSize = Size;
Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize);
Transform = new Transform(Basis.Identity, new Vector3(planeCoordSouthWest.x, 0, planeCoordSouthWest.y));
Vector2 localPlaneCoordSouthWest = new Vector2(-hexGrid.HexSize.x, hexGrid.HexSize.y) * 0.5f;
Vector2 localPlaneCoordNorthEast = hexGrid.GetHexCenterFromOffset(Vector2.One * chunkSize) +
new Vector2(hexGrid.HexSize.x, -hexGrid.HexSize.y) * 0.5f;
PlaneRect = new Rect2(
new Vector2(localPlaneCoordSouthWest.x, localPlaneCoordNorthEast.y),
new Vector2(localPlaneCoordNorthEast.x - localPlaneCoordSouthWest.x,
localPlaneCoordSouthWest.y - localPlaneCoordNorthEast.y)
);
}
public void InitializeTileInstances(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance,
int tileInstanceIndexStart) {
_multiMeshInstance = multiMeshInstance;
_tileInstanceIndices.Clear();
int chunkSize = Size;
foreach (Spatial node in Tiles.GetChildren()) {
node.QueueFree();
}
foreach (int i in Enumerable.Range(0, chunkSize)) {
foreach (int j in Enumerable.Range(0, chunkSize)) {
HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance();
tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * chunkSize + new Vector2(i, j));
_tileInstanceIndices.Add(tileInstanceIndexStart + _tileInstanceIndices.Count);
Transform tileTransform = Transform.Identity;
Vector2 centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
tile3D.Transform = tileTransform;
Tiles.AddChild(tile3D);
}
}
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
GD.Print("Chunk: " + chunkIndex + " Last index: " + _tileInstanceIndices.Last());
}
public void ClearContent() {
foreach (Spatial child in Entities.GetChildren()) {
child.QueueFree();
}
}
public void UpdateTileTransforms() {
Transform chunkTransform = Transform.Identity;
Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * Size);
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
Transform = chunkTransform;
Basis tileOrientation = new(Vector3.Up, 90f * Mathf.Pi / 180f);
foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count)) {
int column = i % Size;
int row = i / Size;
Vector2 tilePlaneCoord =
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
Transform hexTransform = new(tileOrientation,
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
if (_showHexTiles) {
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
hexTransform.origin);
}
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
}
}
// other members
public void SaveToFile(string chunkName)
{
var image = new Image();
public void SaveToFile(string chunkName) {
Image image = new();
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData());
image.SavePng(chunkName + "_tileType.png");
@ -127,13 +219,10 @@ public class WorldChunk : Spatial
image.SavePng(chunkName + "_heightMap.png");
}
public void LoadFromFile(string chunkName)
{
}
public void LoadFromFile(string chunkName) { }
public void SetNoisemap(Texture texture)
{
public void SetNoisemap(Texture texture) {
_noiseSprite.Texture = texture;
_noiseSprite.Transform =
Transform2D.Identity.Scaled(HeightmapOffscreenViewport.Size / _noiseSprite.Texture.GetSize().x);
@ -141,8 +230,7 @@ public class WorldChunk : Spatial
HeightMapFrameCount = 1;
}
public void SetHeightmap(Texture texture)
{
public void SetHeightmap(Texture texture) {
_heightmapSprite.Texture = texture;
_heightmapSprite.Transform =
Transform2D.Identity.Scaled(TileTypeOffscreenViewport.Size / _heightmapSprite.Texture.GetSize());
@ -151,29 +239,33 @@ public class WorldChunk : Spatial
TileTypeMapFrameCount = 1;
}
public override void _Process(float delta)
{
public void CreateUnlockedTileTypeImage() {
TileTypeImage = TileTypeOffscreenViewport.GetTexture().GetData();
TileTypeImage.Lock();
}
public override void _Process(float delta) {
Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture();
if (NoiseTextureCheckerboardOverlay)
{
var tileTypeImage = tileTypeTexture.GetData();
if (NoiseTextureCheckerboardOverlay) {
Image tileTypeImage = tileTypeTexture.GetData();
tileTypeImage.Lock();
foreach (var i in Enumerable.Range(0, Size))
foreach (var j in Enumerable.Range(0, Size))
{
var textureCoord = new Vector2(i, j);
var baseColor = tileTypeImage.GetPixelv(textureCoord);
foreach (int i in Enumerable.Range(0, Size)) {
foreach (int j in Enumerable.Range(0, Size)) {
Vector2 textureCoord = new(i, j);
Color baseColor = tileTypeImage.GetPixelv(textureCoord);
if ((i + j) % 2 == 0)
tileTypeImage.SetPixelv(textureCoord, baseColor);
else
tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
if ((i + j) % 2 == 0) {
tileTypeImage.SetPixelv(textureCoord, baseColor);
} else {
tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
}
}
}
tileTypeImage.Unlock();
var imageTexture = new ImageTexture();
ImageTexture imageTexture = new();
imageTexture.CreateFromImage(tileTypeImage, 0);
tileTypeTexture = imageTexture;
}
@ -188,14 +280,26 @@ public class WorldChunk : Spatial
//RectMaterial.Uv1Triplanar = true;
PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial);
if (HeightMapFrameCount == 0) HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
if (HeightMapFrameCount == 0) {
HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0;
if (TileTypeMapFrameCount == 0) TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
if (TileTypeMapFrameCount == 0) {
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
TileTypeMapFrameCount = TileTypeMapFrameCount > 0 ? TileTypeMapFrameCount - 1 : 0;
PlaneRectMesh.MaterialOverride = null;
}
public void OnTileClicked(HexTile3D tile) {
EmitSignal("TileClicked", tile);
}
public void OnTileHovered(HexTile3D tile) {
EmitSignal("TileHovered", tile);
}
}

View File

@ -24,6 +24,8 @@ blend_mode = 3
[node name="WorldChunk" type="Spatial"]
script = ExtResource( 1 )
[node name="Tiles" type="Spatial" parent="."]
[node name="Entities" type="Spatial" parent="."]
[node name="PlaneRectMesh" type="MeshInstance" parent="."]

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,65 +1,59 @@
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;
private EditorUI _editorUi;
private TileWorld _tileWorld;
private World _world;
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));
_world = GetNode<World>("World");
_world.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));
_streamContainer.Connect("TileHovered", this, nameof(OnTileHovered));
_world.Connect("TileClicked", this, nameof(OnTileClicked));
_world.Connect("TileHovered", this, nameof(OnTileHovered));
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);
HexCell entityCell = _tileWorld.HexGrid.GetHexAt(entityPlaneCoords);
_tileWorld.MarkCellUnwalkable(entityCell);
Array entities = entitiesNode.GetChildren();
foreach (Spatial entity in entities) {
Vector2 entityPlaneCoords = new(entity.GlobalTranslation.x, entity.GlobalTranslation.z);
HexCell entityCell = _world.HexGrid.GetHexAt(entityPlaneCoords);
_world.MarkCellUnwalkable(entityCell);
Vector2 cellPlaneCoords = _hexGrid.GetHexCenterFromOffset(entityCell.OffsetCoords);
Vector3 entityGlobalTranslation = entity.GlobalTranslation;
entityGlobalTranslation.x = cellPlaneCoords.x;
@ -68,79 +62,70 @@ public class NavigationTests : Spatial
}
}
public void OnWorldGenerated()
{
public void OnWorldGenerated() {
_streamContainer.OnWorldGenerated();
// Properly place the Player
Vector2 centerTileCoord = (Vector2.One * _tileWorld.Size / 2).Round();
Vector3 worldCenterTileCoords = _tileWorld.GetTileWorldCenterFromOffset(centerTileCoord);
worldCenterTileCoords.y = _tileWorld.GetHeightAtOffset(centerTileCoord);
Vector2 centerTileCoord = (Vector2.One * _world.Size / 2).Round();
Vector3 worldCenterTileCoords = _world.GetTileWorldCenterFromOffset(centerTileCoord);
worldCenterTileCoords.y = _world.GetHeightAtOffset(centerTileCoord);
Transform playerTransform = Transform.Identity;
playerTransform.origin = worldCenterTileCoords;
_player.Transform = playerTransform;
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
ImageTexture newWorldTexture = new();
newWorldTexture.CreateFromImage(_world.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
_tileMaterial.SetShaderParam("TextureSize", (int)_world.ColormapImage.GetSize().x);
CorrectEntityGridPositions();
}
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.y = _world.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,4 +1,4 @@
[gd_scene load_steps=25 format=2]
[gd_scene load_steps=26 format=2]
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=1]
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=2]
@ -11,6 +11,7 @@
[ext_resource path="res://scenes/HexTile3DPatch.tscn" type="PackedScene" id=9]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=10]
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
[ext_resource path="res://scenes/World.tscn" type="PackedScene" id=12]
[sub_resource type="AnimationNodeStateMachinePlayback" id=8]
@ -289,18 +290,21 @@ script = ExtResource( 4 )
[node name="Player" parent="." instance=ExtResource( 1 )]
collision_mask = 1
TileWorldNode = NodePath("../TileWorld")
WorldNode = NodePath("../World")
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 )
[node name="WorldInfo" parent="Player" index="2"]
WorldPath = NodePath("../../World")
[node name="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"]
transform = Transform( 1, 7.13626e-08, -4.47035e-08, 1.64262e-07, -1, -1.00583e-07, 1.19209e-07, 1.18278e-07, -1, -0.72, 0.45, 1.78362e-08 )
[node name="AnimationTree" parent="Player/Geometry" index="2"]
parameters/playback = SubResource( 8 )
[node name="TileWorld" parent="." instance=ExtResource( 2 )]
DebugMap = true
GenerationMapType = 2
Size = 20
DebugMap = true
[node name="MouseHighlight" parent="." instance=ExtResource( 3 )]
@ -374,8 +378,10 @@ anims/TreeShake = SubResource( 17 )
[node name="Geometry" parent="Entities/Tree5" index="2"]
transform = Transform( 1.5, 0, 0, 0, 0.984722, 0.266325, 0, -0.177712, 1.47574, 0, 0, 0 )
[node name="World" parent="." instance=ExtResource( 12 )]
[editable path="Player"]
[editable path="Player/Geometry"]
[editable path="Player/Geometry/PirateAsset"]
[editable path="TileWorld"]
[editable path="StreamContainer"]
[editable path="Entities/Chest"]

View File

@ -1,84 +1,68 @@
using Godot;
using System;
using System.Collections.Generic;
using Godot.Collections;
using Godot;
using GodotComponentTest.components;
using GodotComponentTest.entities;
using Array = System.Array;
using NodePair = System.Tuple<Godot.Node, Godot.Node>;
public class InteractionSystem : Node
{
public class InteractionSystem : Node {
private List<InteractionComponent> _activeInteractions;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
public override void _Ready() {
_activeInteractions = new List<InteractionComponent>();
}
public override void _Process(float delta)
{
public override void _Process(float delta) {
base._Process(delta);
List<NodePair> invalidInteractionPairs = new List<NodePair>();
List<InteractionComponent> endedInteractions = new List<InteractionComponent>();
List<InteractionComponent> endedInteractions = new();
foreach (InteractionComponent interaction in _activeInteractions)
{
foreach (InteractionComponent interaction in _activeInteractions) {
Spatial owningEntity = interaction.OwningEntity;
Spatial targetEntity = interaction.TargetEntity;
if (owningEntity == null || owningEntity.IsQueuedForDeletion() || targetEntity == null || targetEntity.IsQueuedForDeletion())
{
if (owningEntity == null || owningEntity.IsQueuedForDeletion() || targetEntity == null ||
targetEntity.IsQueuedForDeletion()) {
interaction.hasStopped = true;
}
if (interaction.hasStopped)
{
if (interaction.hasStopped) {
IInteractionInterface interactableA = owningEntity as IInteractionInterface;
if (interactableA != null)
{
if (interactableA != null) {
interactableA.OnInteractionEnd();
interactableA.InteractionComponent = null;
}
IInteractionInterface interactableB = targetEntity as IInteractionInterface;
if (interactableB != null)
{
if (interactableB != null) {
interactableB.OnInteractionEnd();
interactableB.InteractionComponent = null;
}
endedInteractions.Add(interaction);
}
}
foreach (InteractionComponent interaction in endedInteractions)
{
_activeInteractions.Remove(interaction);
}
}
public void OnStartInteraction(Entity owningEntity, Entity targetEntity)
{
InteractionComponent interactionComponent = new InteractionComponent();
public void OnStartInteraction(Entity owningEntity, Entity targetEntity) {
InteractionComponent interactionComponent = new();
interactionComponent.OwningEntity = owningEntity;
interactionComponent.TargetEntity = targetEntity;
ConnectInteractionSignals(owningEntity, interactionComponent);
ConnectInteractionSignals(targetEntity, interactionComponent);
interactionComponent.EmitSignal("InteractionStart");
_activeInteractions.Add(interactionComponent);
}
private static void ConnectInteractionSignals(Entity entity, InteractionComponent interactionComponent)
{
private static void ConnectInteractionSignals(Entity entity, InteractionComponent interactionComponent) {
IInteractionInterface interactable = entity as IInteractionInterface;
if (interactable != null)
{
if (interactable != null) {
interactable.InteractionComponent = interactionComponent;
interactionComponent.Connect("InteractionStart", entity, nameof(interactable.OnInteractionStart));
interactionComponent.Connect("InteractionEnd", entity, nameof(interactable.OnInteractionEnd));

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,21 +1,26 @@
extends VBoxContainer
extends CenterContainer
var world_size_label = null
var world_size_slider = null
onready var SeedLineEdit: LineEdit
onready var ChunkSizeSpinBox: SpinBox
onready var GameScene: PackedScene = preload ("res://scenes/Game.tscn")
onready var WorldTextureRect = $WorldTextureRect
onready var HeightTextureRect = $HeightTextureRect
# Called when the node enters the scene tree for the first time.
func _ready():
world_size_label = find_node("WorldSizeLabel")
world_size_slider = find_node("WorldSizeSlider");
SeedLineEdit = find_node("SeedLineEdit")
assert(SeedLineEdit)
world_size_slider.value = 4
ChunkSizeSpinBox = find_node("ChunkSizeSpinBox")
assert(ChunkSizeSpinBox)
func _on_HSlider_value_changed(value):
world_size_label.text = str(value)
func _on_ShowTexturesCheckButton_toggled(button_pressed):
WorldTextureRect.visible = button_pressed
HeightTextureRect.visible = button_pressed
func _on_RefreshSeedButton_pressed():
var rng = RandomNumberGenerator.new()
rng.seed = Time.get_ticks_msec()
SeedLineEdit.text = str(rng.randi())
func _on_GenerateButton_pressed():
var game_scene_instance = GameScene.instance()
self.visible = false
get_tree().get_root().add_child(game_scene_instance)
game_scene_instance.StartNewGame(int(SeedLineEdit.text), int(ChunkSizeSpinBox.value))

121
ui/WorldGeneratorUI.tscn Normal file
View File

@ -0,0 +1,121 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=1]
[node name="WorldGeneratorUI" type="CenterContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
[node name="VBoxContainer" type="VBoxContainer" parent="."]
margin_left = 16.0
margin_top = 81.0
margin_right = 303.0
margin_bottom = 319.0
custom_constants/separation = 20
[node name="Label" type="Label" parent="VBoxContainer"]
margin_right = 287.0
margin_bottom = 14.0
text = "World Generation"
align = 1
[node name="GridContainer" type="GridContainer" parent="VBoxContainer"]
margin_top = 34.0
margin_right = 287.0
margin_bottom = 198.0
columns = 3
[node name="Label" type="Label" parent="VBoxContainer/GridContainer"]
margin_top = 5.0
margin_right = 69.0
margin_bottom = 19.0
text = "Seed"
[node name="SeedLineEdit" type="LineEdit" parent="VBoxContainer/GridContainer"]
margin_left = 73.0
margin_right = 223.0
margin_bottom = 24.0
rect_min_size = Vector2( 150, 0 )
text = "0"
align = 2
caret_blink = true
[node name="RefreshSeedButton" type="Button" parent="VBoxContainer/GridContainer"]
margin_left = 227.0
margin_right = 287.0
margin_bottom = 24.0
text = "Refresh"
[node name="Label2" type="Label" parent="VBoxContainer/GridContainer"]
margin_top = 33.0
margin_right = 69.0
margin_bottom = 47.0
text = "Chunk Size"
[node name="ChunkSizeSpinBox" type="SpinBox" parent="VBoxContainer/GridContainer"]
margin_left = 73.0
margin_top = 28.0
margin_right = 223.0
margin_bottom = 52.0
min_value = 4.0
step = 2.0
value = 10.0
rounded = true
align = 2
[node name="Empty" type="Label" parent="VBoxContainer/GridContainer"]
margin_left = 227.0
margin_top = 33.0
margin_right = 287.0
margin_bottom = 47.0
[node name="Label4" type="Label" parent="VBoxContainer/GridContainer"]
margin_top = 103.0
margin_right = 69.0
margin_bottom = 117.0
text = "Objects"
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/GridContainer"]
margin_left = 73.0
margin_top = 56.0
margin_right = 223.0
margin_bottom = 164.0
[node name="ObjectTypeTreeCheckbox" type="CheckBox" parent="VBoxContainer/GridContainer/VBoxContainer"]
margin_right = 150.0
margin_bottom = 24.0
pressed = true
text = "Trees"
[node name="ObjectTypeGrassCheckbox" type="CheckBox" parent="VBoxContainer/GridContainer/VBoxContainer"]
margin_top = 28.0
margin_right = 150.0
margin_bottom = 52.0
pressed = true
text = "Grass"
[node name="ObjectTypeChestCheckbox" type="CheckBox" parent="VBoxContainer/GridContainer/VBoxContainer"]
margin_top = 56.0
margin_right = 150.0
margin_bottom = 80.0
pressed = true
text = "Chests"
[node name="ObjectTypeRockCheckbox" type="CheckBox" parent="VBoxContainer/GridContainer/VBoxContainer"]
margin_top = 84.0
margin_right = 150.0
margin_bottom = 108.0
pressed = true
text = "Rocks"
[node name="GenerateButton" type="Button" parent="VBoxContainer"]
margin_left = 216.0
margin_top = 218.0
margin_right = 287.0
margin_bottom = 238.0
size_flags_horizontal = 8
text = "Generate"
[connection signal="pressed" from="VBoxContainer/GridContainer/RefreshSeedButton" to="." method="_on_RefreshSeedButton_pressed"]
[connection signal="pressed" from="VBoxContainer/GenerateButton" to="." method="_on_GenerateButton_pressed"]

View File

@ -0,0 +1,21 @@
extends VBoxContainer
var world_size_label = null
var world_size_slider = null
onready var WorldTextureRect = $WorldTextureRect
onready var HeightTextureRect = $HeightTextureRect
# Called when the node enters the scene tree for the first time.
func _ready():
world_size_label = find_node("WorldSizeLabel")
world_size_slider = find_node("WorldSizeSlider");
world_size_slider.value = 4
func _on_HSlider_value_changed(value):
world_size_label.text = str(value)
func _on_ShowTexturesCheckButton_toggled(button_pressed):
WorldTextureRect.visible = button_pressed
HeightTextureRect.visible = button_pressed

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);
}
}