Compare commits

..

No commits in common. "main" and "WorldChunkRefactoring" have entirely different histories.

92 changed files with 2390 additions and 5032 deletions

24
.gitignore vendored
View File

@ -1,28 +1,8 @@
# Godot 4+ specific ignores .import/*
.godot/ .mono/*
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# JetBrains Rider ignores
.idea/* .idea/*
android/* android/*
# project specific ignores
addons/gdhexgrid/addons/gut
ass
assets/KenneySurvivalKit/Previews
*.swp *.swp
*.apk *.apk
*.idsig *.idsig

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,50 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://c7dpvyathtwcn"
path="res://.godot/imported/Pirate1_Rigged.blend-ea530b9073238ab11e8923a91a430d84.scn"
[deps]
source_file="res://assets/CreatusPiratePack/Modified/Pirate1_Rigged.blend"
dest_files=["res://.godot/imported/Pirate1_Rigged.blend-ea530b9073238ab11e8923a91a430d84.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1
blender/nodes/visible=0
blender/nodes/punctual_lights=true
blender/nodes/cameras=true
blender/nodes/custom_properties=true
blender/nodes/modifiers=1
blender/meshes/colors=false
blender/meshes/uvs=true
blender/meshes/normals=true
blender/meshes/tangents=true
blender/meshes/skins=2
blender/meshes/export_bones_deforming_mesh_only=false
blender/materials/unpack_enabled=true
blender/materials/export_materials=1
blender/animation/limit_playback=true
blender/animation/always_sample=true
blender/animation/group_tracks=true

File diff suppressed because it is too large Load Diff

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,46 +0,0 @@
using Godot;
public class CollectibleComponent : Component {
private Vector3 targetPosition;
private bool hasTarget;
// Called when the node enters the scene tree for the first time.
public override void _Ready() { }
public void SetTarget(Vector3 target) {
targetPosition = target;
hasTarget = true;
}
public void UnsetTarget() {
hasTarget = false;
}
public void PhysicsProcess(float delta, Entity entity) {
if (hasTarget) {
if (targetPosition.IsEqualApprox(entity.GlobalTransform.origin)) {
targetPosition = entity.GlobalTransform.origin;
entity.Velocity = Vector3.Zero;
}
Vector3 targetDirection = (targetPosition - entity.GlobalTransform.origin).Normalized();
entity.Velocity = targetDirection * (entity.Velocity.Length() + 10 * delta);
entity.Transform = new Transform(entity.Transform.basis.Rotated(Vector3.Up, delta * 2.0f),
entity.Transform.origin);
} else {
entity.Velocity = entity.Velocity - 9.81f * delta * Vector3.Up;
}
entity.Velocity = entity.MoveAndSlide(entity.Velocity, Vector3.Up);
if (entity.IsOnFloor() || Mathf.Abs(entity.Transform.origin.y) < 0.01) {
// apply damping when on ground
entity.Velocity = entity.Velocity - entity.Velocity.Normalized() * 0.9f * delta;
}
if (entity.Velocity.LengthSquared() < 0.01) {
entity.Velocity = Vector3.Zero;
}
}
}

View File

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://components/CollectibleComponent.cs" type="Script" id=1]
[node name="CollectibleComponent" type="Node"]
script = ExtResource( 1 )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,13 @@
[gd_scene load_steps=5 format=2] [gd_scene load_steps=4 format=2]
[ext_resource path="res://assets/Objects/toolAxe.tscn" type="PackedScene" id=1] [ext_resource path="res://assets/Objects/toolAxe.tscn" type="PackedScene" id=1]
[ext_resource path="res://entities/Axe.cs" type="Script" id=2] [ext_resource path="res://entities/Axe.cs" type="Script" id=2]
[ext_resource path="res://components/CollectibleComponent.tscn" type="PackedScene" id=3]
[sub_resource type="CylinderShape" id=1] [sub_resource type="CylinderShape" id=1]
height = 0.846435 height = 0.846435
radius = 0.687167 radius = 0.687167
[node name="Axe" type="KinematicBody"] [node name="Axe" type="StaticBody"]
collision_layer = 9 collision_layer = 9
collision_mask = 0 collision_mask = 0
script = ExtResource( 2 ) script = ExtResource( 2 )
@ -19,5 +18,3 @@ shape = SubResource( 1 )
[node name="toolAxe" parent="." instance=ExtResource( 1 )] [node name="toolAxe" parent="." instance=ExtResource( 1 )]
transform = Transform( 0.707107, 0.707107, -3.09086e-08, 4.37114e-08, 1.91069e-15, 1, 0.707107, -0.707107, -3.09086e-08, -0.323064, 0.0760467, 0.348457 ) transform = Transform( 0.707107, 0.707107, -3.09086e-08, 4.37114e-08, 1.91069e-15, 1, 0.707107, -0.707107, -3.09086e-08, -0.323064, 0.0760467, 0.348457 )
[node name="CollectibleComponent" parent="." instance=ExtResource( 3 )]

View File

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

View File

@ -79,8 +79,8 @@ tracks/2/keys = {
[sub_resource type="PrismMesh" id=15] [sub_resource type="PrismMesh" id=15]
[sub_resource type="SphereShape" id=19] [sub_resource type="BoxShape" id=16]
radius = 0.359562 extents = Vector3( 0.19, 0.19, 0.33 )
[sub_resource type="CubeMesh" id=17] [sub_resource type="CubeMesh" id=17]
size = Vector3( 0.38, 0.38, 0.66 ) size = Vector3( 0.38, 0.38, 0.66 )
@ -133,8 +133,8 @@ skeleton = NodePath("../..")
[node name="ResourceContainer" type="Node" parent="."] [node name="ResourceContainer" type="Node" parent="."]
[node name="CollisionShape" type="CollisionShape" parent="."] [node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( -0.866026, 0, 0.5, 0, 1, 0, -0.5, 0, -0.866026, 0, 0.110576, 0 ) transform = Transform( -0.866026, 0, 0.5, 0, 1, 0, -0.5, 0, -0.866026, 0, 0.240716, 0 )
shape = SubResource( 19 ) shape = SubResource( 16 )
[node name="MeshInstance" type="MeshInstance" parent="CollisionShape" groups=["PhysicsGeometry"]] [node name="MeshInstance" type="MeshInstance" parent="CollisionShape" groups=["PhysicsGeometry"]]
mesh = SubResource( 17 ) mesh = SubResource( 17 )

View File

@ -1,61 +1,30 @@
using System;
using System.Diagnostics;
using Godot; using Godot;
using System;
public class Entity : KinematicBody { public class Entity : KinematicBody
[Flags] {
public enum EntityMaskEnum {
Obstacle = 1 << 0,
Ground = 1 << 1,
Water = 1 << 2
}
[Export(PropertyHint.Flags, "Obstacle,Ground,Water")]
public int EntityMask { get; set; }
public Vector3 Velocity { get; set; } = Vector3.Zero; public Vector3 Velocity { get; set; } = Vector3.Zero;
public float RotationalVelocity { get; set; } = 0; public float RotationalVelocity { get; set; } = 0;
private CollectibleComponent _collectibleComponent; /** Defines the angle in plane coordinates, 0 => pointing to the right/east, pi/2 pointing up/north, range [-pi,pi]. */
public float PlaneAngle
public override void _Ready() { {
base._Ready();
foreach (Node node in GetChildren()) {
if (node is CollectibleComponent) {
Debug.Assert(_collectibleComponent == null);
_collectibleComponent = node as CollectibleComponent;
}
}
}
public override void _PhysicsProcess(float delta) {
base._PhysicsProcess(delta);
if (_collectibleComponent != null) {
_collectibleComponent.PhysicsProcess(delta, this);
}
}
public CollectibleComponent GetCollectibleComponent() {
return _collectibleComponent;
}
/**
* 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); get => Globals.CalcPlaneAngle(GlobalTransform);
set => GlobalTransform = new Transform(new Basis(Vector3.Up, value + Mathf.Pi * 0.5f), GlobalTranslation); 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 angleToTarget = Vector3.Right.SignedAngleTo(globalTargetDirection, Vector3.Up);
float currentAngle = PlaneAngle; float currentAngle = PlaneAngle;
float delta = angleToTarget - currentAngle; 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; return delta;
} }
public override void _Ready()
{
}
} }

View File

@ -1 +1,61 @@
public class GoldBar : Entity { } using Godot;
using System;
public class GoldBar : KinematicBody
{
private Vector3 targetPosition;
private bool hasTarget = false;
public Vector3 velocity;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
targetPosition = GlobalTransform.origin;
}
public void SetTarget(Vector3 target)
{
targetPosition = target;
hasTarget = true;
}
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))
{
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
{
velocity.y = velocity.y - 9.81f * delta;
}
velocity = MoveAndSlide(velocity, Vector3.Up);
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)
{
velocity = Vector3.Zero;
}
}
}

View File

@ -1,8 +1,7 @@
[gd_scene load_steps=5 format=2] [gd_scene load_steps=4 format=2]
[ext_resource path="res://entities/GoldBar.cs" type="Script" id=1] [ext_resource path="res://entities/GoldBar.cs" type="Script" id=1]
[ext_resource path="res://assets/CreatusPiratePack/Models/Items/gltf/Gold_Bar.glb" type="PackedScene" id=2] [ext_resource path="res://assets/CreatusPiratePack/Models/Items/gltf/Gold_Bar.glb" type="PackedScene" id=2]
[ext_resource path="res://components/CollectibleComponent.tscn" type="PackedScene" id=3]
[sub_resource type="BoxShape" id=21] [sub_resource type="BoxShape" id=21]
extents = Vector3( 0.354271, 0.0817164, 0.173406 ) extents = Vector3( 0.354271, 0.0817164, 0.173406 )
@ -17,10 +16,8 @@ script = ExtResource( 1 )
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.084728, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.084728, 0 )
shape = SubResource( 21 ) shape = SubResource( 21 )
[node name="Geometry" type="Spatial" parent="."] [node name="Spatial" type="Spatial" parent="."]
transform = Transform( 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0 ) transform = Transform( 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0 )
[node name="Gold_Bar" parent="Geometry" instance=ExtResource( 2 )] [node name="Gold_Bar" parent="Spatial" instance=ExtResource( 2 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 21.335, -0.022, -6.533 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 21.335, -0.022, -6.533 )
[node name="CollectibleComponent" parent="." instance=ExtResource( 3 )]

View File

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

View File

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

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=26 format=2] [gd_scene load_steps=27 format=2]
[ext_resource path="res://entities/Player.cs" type="Script" id=1] [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://components/NavigationComponent.cs" type="Script" id=2]
@ -28,6 +28,277 @@ radius = 0.1
flags_unshaded = true flags_unshaded = true
vertex_color_use_as_albedo = 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] [sub_resource type="AnimationNodeAnimation" id=1]
animation = "Hit-loop" animation = "Hit-loop"
@ -37,9 +308,6 @@ animation = "Idle-loop"
[sub_resource type="AnimationNodeAnimation" id=3] [sub_resource type="AnimationNodeAnimation" id=3]
animation = "Interaction-loop" animation = "Interaction-loop"
[sub_resource type="AnimationNodeAnimation" id=35]
animation = "Shrug-loop"
[sub_resource type="AnimationNodeStateMachineTransition" id=4] [sub_resource type="AnimationNodeStateMachineTransition" id=4]
[sub_resource type="AnimationNodeStateMachineTransition" id=5] [sub_resource type="AnimationNodeStateMachineTransition" id=5]
@ -53,26 +321,15 @@ xfade_time = 0.1
switch_mode = 2 switch_mode = 2
xfade_time = 0.3 xfade_time = 0.3
[sub_resource type="AnimationNodeStateMachineTransition" id=36]
xfade_time = 0.1
[sub_resource type="AnimationNodeStateMachineTransition" id=37]
switch_mode = 2
auto_advance = true
xfade_time = 0.1
[sub_resource type="AnimationNodeStateMachine" id=8] [sub_resource type="AnimationNodeStateMachine" id=8]
states/Hit/node = SubResource( 1 ) states/Hit/node = SubResource( 1 )
states/Hit/position = Vector2( 413, 37 ) states/Hit/position = Vector2( 415, 45 )
states/Idle/node = SubResource( 2 ) states/Idle/node = SubResource( 2 )
states/Idle/position = Vector2( 157, 37 ) states/Idle/position = Vector2( 149, 39 )
states/Interaction/node = SubResource( 3 ) states/Interaction/node = SubResource( 3 )
states/Interaction/position = Vector2( 157, 162 ) states/Interaction/position = Vector2( 176, 157 )
states/Shrug-loop/node = SubResource( 35 ) transitions = [ "Idle", "Hit", SubResource( 4 ), "Hit", "Idle", SubResource( 5 ), "Idle", "Interaction", SubResource( 6 ), "Interaction", "Idle", SubResource( 33 ) ]
states/Shrug-loop/position = Vector2( -53, 37 )
transitions = [ "Idle", "Hit", SubResource( 4 ), "Hit", "Idle", SubResource( 5 ), "Idle", "Interaction", SubResource( 6 ), "Interaction", "Idle", SubResource( 33 ), "Idle", "Shrug-loop", SubResource( 36 ), "Shrug-loop", "Idle", SubResource( 37 ) ]
start_node = "Idle" start_node = "Idle"
graph_offset = Vector2( -175, -42 )
[sub_resource type="AnimationNodeStateMachinePlayback" id=34] [sub_resource type="AnimationNodeStateMachinePlayback" id=34]
@ -140,17 +397,27 @@ generate_lightmap = false
[node name="PirateAsset" parent="Geometry" instance=ExtResource( 8 )] [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 ) 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"] [node name="ToolAttachement" type="BoneAttachment" parent="Geometry/PirateAsset/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 ) 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 visible = false
bone_name = "HandTip.R" bone_name = "HandTip.R"
[node name="toolAxe" parent="Geometry/PirateAsset/Armature/Skeleton/ToolAttachement" instance=ExtResource( 4 )] [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 ) 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"] [node name="AnimationTree" type="AnimationTree" parent="Geometry"]
tree_root = SubResource( 8 ) tree_root = SubResource( 8 )
anim_player = NodePath("../PirateAsset/AnimationPlayer") anim_player = NodePath("../AnimationPlayer")
active = true active = true
parameters/playback = SubResource( 34 ) parameters/playback = SubResource( 34 )

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Godot; using Godot;
using GodotComponentTest.components; using GodotComponentTest.components;
using GodotComponentTest.entities; using GodotComponentTest.entities;
public class Tree : StaticBody, IInteractionInterface { public class Tree : StaticBody, IInteractionInterface
private readonly PackedScene _woodScene = GD.Load<PackedScene>("res://entities/Wood.tscn"); {
[Export] public float ChopDuration = 2; [Export] public float ChopDuration = 2;
public bool IsMouseOver; public bool IsMouseOver;
public InteractionComponent InteractionComponent { get; set; } public InteractionComponent InteractionComponent { get; set; }
@ -16,16 +14,13 @@ public class Tree : StaticBody, IInteractionInterface {
private AnimationPlayer _animationPlayer; private AnimationPlayer _animationPlayer;
private float _health = 100; private float _health = 100;
private bool _isBeingChopped; private bool _isBeingChopped;
private bool _isDead;
[Signal] [Signal]
public delegate void EntityClicked(Entity entity); 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. // 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"); _geometry = GetNode<MeshInstance>("Geometry/tree");
Debug.Assert(_geometry != null); Debug.Assert(_geometry != null);
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer"); _animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
@ -38,61 +33,56 @@ public class Tree : StaticBody, IInteractionInterface {
Connect("mouse_exited", this, nameof(OnAreaMouseExited)); Connect("mouse_exited", this, nameof(OnAreaMouseExited));
} }
public override void _Process(float delta) {
public override void _Process(float delta)
{
base._Process(delta); base._Process(delta);
if (_isBeingChopped) { if (_isBeingChopped)
{
_health = Math.Max(0, _health - delta * 100 / ChopDuration); _health = Math.Max(0, _health - delta * 100 / ChopDuration);
} }
if (!_isDead && _health == 0) { if (_health == 0)
{
InteractionComponent.EndInteraction(); InteractionComponent.EndInteraction();
InteractionComponent.TargetEntity = null; InteractionComponent.TargetEntity = null;
GD.Print("Tree chopped!"); QueueFree();
int numWoodLogs = (int)(GD.Randi() % 2) + 1;
foreach (int i in Enumerable.Range(1, numWoodLogs)) {
Wood wood = (Wood)_woodScene.Instance();
wood.Transform = new Transform(Transform.basis.Rotated(Vector3.Up, GD.Randf() * 2 * Mathf.Pi),
Transform.origin + Vector3.Up * 0.8f);
wood.Velocity = new Vector3(
(GD.Randf() * 2f - 1f) * 1.2f,
5 + GD.Randf() * 0.3f,
(GD.Randf() * 2f - 1f) * 1.2f
);
GetParent().AddChild(wood);
}
_isDead = true;
EmitSignal("TreeChopped", this);
} }
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) { int shapeIndex)
if (IsMouseOver && inputEvent is InputEventMouseButton) { {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) { if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed)
{
EmitSignal("EntityClicked", this); EmitSignal("EntityClicked", this);
} }
} }
} }
public void OnAreaMouseEntered() {
public void OnAreaMouseEntered()
{
IsMouseOver = true; IsMouseOver = true;
SpatialMaterial overrideMaterial = new(); SpatialMaterial overrideMaterial = new SpatialMaterial();
overrideMaterial.AlbedoColor = new Color(1, 0, 0); overrideMaterial.AlbedoColor = new Color(1, 0, 0);
_geometry.MaterialOverride = overrideMaterial; _geometry.MaterialOverride = overrideMaterial;
} }
public void OnAreaMouseExited() {
public void OnAreaMouseExited()
{
IsMouseOver = false; IsMouseOver = false;
_geometry.MaterialOverride = null; _geometry.MaterialOverride = null;
} }
public void OnInteractionStart() {
public void OnInteractionStart()
{
GD.Print("Starting tree animationplayer"); GD.Print("Starting tree animationplayer");
_animationPlayer.CurrentAnimation = "TreeShake"; _animationPlayer.CurrentAnimation = "TreeShake";
_animationPlayer.Seek(0); _animationPlayer.Seek(0);
@ -100,7 +90,8 @@ public class Tree : StaticBody, IInteractionInterface {
_isBeingChopped = true; _isBeingChopped = true;
} }
public void OnInteractionEnd() { public void OnInteractionEnd()
{
_animationPlayer.CurrentAnimation = "Idle"; _animationPlayer.CurrentAnimation = "Idle";
_animationPlayer.Seek(0); _animationPlayer.Seek(0);
_animationPlayer.Stop(); _animationPlayer.Stop();

View File

@ -88,6 +88,7 @@ script = ExtResource( 3 )
[node name="MountPoint" type="Spatial" parent="."] [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 ) 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"] [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 ) transform = Transform( -1, 0, -8.74227e-08, 0, 1, 0, 8.74227e-08, 0, -1, 2.38419e-07, 0, 0 )

View File

@ -1 +0,0 @@
public class Wood : Entity { }

View File

@ -1,25 +0,0 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://entities/Wood.cs" type="Script" id=1]
[ext_resource path="res://assets/KenneySurvivalKit/Models/resourceWood.glb" type="PackedScene" id=2]
[ext_resource path="res://components/CollectibleComponent.tscn" type="PackedScene" id=3]
[sub_resource type="BoxShape" id=21]
extents = Vector3( 0.344943, 0.0817164, 0.173406 )
[node name="Wood" type="KinematicBody"]
collision_layer = 9
collision_mask = 0
input_ray_pickable = false
script = ExtResource( 1 )
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.084728, 0 )
shape = SubResource( 21 )
[node name="Geometry" type="Spatial" parent="."]
transform = Transform( 2.12132, 0, -2.12132, 0, 3, 0, 2.12132, 0, 2.12132, 0, 0, 0 )
[node name="resourceWood" parent="Geometry" instance=ExtResource( 2 )]
[node name="CollectibleComponent" parent="." instance=ExtResource( 3 )]

View File

@ -1,49 +0,0 @@
using System.Diagnostics;
using Godot;
public class Workbench : Entity {
public bool IsMouseOver;
private MeshInstance _geometry;
[Signal]
public delegate void EntityClicked(Entity entity);
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
_geometry = GetNode<MeshInstance>("Geometry/workbench");
Debug.Assert(_geometry != null);
Connect("input_event", this, nameof(OnAreaInputEvent));
Connect("mouse_entered", this, nameof(OnAreaMouseEntered));
Connect("mouse_exited", this, nameof(OnAreaMouseExited));
}
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) {
if (IsMouseOver && inputEvent is InputEventMouseButton) {
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
EmitSignal("EntityClicked", this);
}
}
}
public void OnAreaMouseEntered() {
IsMouseOver = true;
SpatialMaterial overrideMaterial = new();
overrideMaterial.AlbedoColor = new Color(1, 0, 0);
_geometry.MaterialOverride = overrideMaterial;
}
public void OnAreaMouseExited() {
IsMouseOver = false;
_geometry.MaterialOverride = null;
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
}

View File

@ -1,51 +0,0 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://assets/KenneySurvivalKit/Models/workbench.glb" type="PackedScene" id=1]
[ext_resource path="res://entities/Workbench.cs" type="Script" id=2]
[sub_resource type="BoxShape" id=1]
extents = Vector3( 0.19, 0.19, 0.33 )
[sub_resource type="CubeMesh" id=2]
size = Vector3( 0.38, 0.38, 0.66 )
[sub_resource type="SpatialMaterial" id=3]
flags_transparent = true
albedo_color = Color( 0.380392, 0.145098, 0.145098, 0.501961 )
[sub_resource type="CubeMesh" id=4]
[sub_resource type="PrismMesh" id=5]
[node name="Workbench" type="KinematicBody"]
script = ExtResource( 2 )
[node name="Geometry" parent="." instance=ExtResource( 1 )]
transform = Transform( 2.26249, 0, -1.06354, 0, 2.5, 0, 1.06354, 0, 2.26249, 0, 0, 0 )
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( -1.29904, 0, 0.5, 0, 1.5, 0, -0.749999, 0, -0.866026, 0, 0.240716, 0 )
shape = SubResource( 1 )
[node name="MeshInstance" type="MeshInstance" parent="CollisionShape" groups=["PhysicsGeometry"]]
visible = false
mesh = SubResource( 2 )
material/0 = SubResource( 3 )
[node name="MountPoint" type="Spatial" parent="."]
transform = Transform( -0.861434, 0, 0.507869, 0, 1, 0, -0.507869, 0, -0.861434, -0.429363, 0, 0.763898 )
[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 )
[node name="MeshInstance" type="MeshInstance" parent="MountPoint/Arrow"]
transform = Transform( -0.1, 0, -1.24676e-08, 0, 0.1, 0, 1.24676e-08, 0, -0.1, 0, 0, 0.0394838 )
mesh = SubResource( 4 )
skeleton = NodePath("../..")
[node name="MeshInstance2" type="MeshInstance" parent="MountPoint/Arrow"]
transform = Transform( -0.1, -1.24676e-08, 6.04182e-16, 0, -4.37114e-09, -0.1, 1.24676e-08, -0.1, 4.37114e-09, 0, 0, -0.151838 )
mesh = SubResource( 5 )
skeleton = NodePath("../..")
[editable path="Geometry"]

View File

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

View File

@ -1,11 +1,11 @@
[gd_resource type="ShaderMaterial" load_steps=2 format=2] [gd_resource type="ShaderMaterial" load_steps=2 format=2]
[ext_resource path="res://materials/shader/WorldTileTypeShader.gdshader" type="Shader" id=1] [ext_resource path="res://materials/shader/IslandColorRampShader.gdshader" type="Shader" id=1]
[resource] [resource]
shader = ExtResource( 1 ) shader = ExtResource( 1 )
shader_param/DeepWaterColor = Color( 0, 0, 0.6, 1 ) shader_param/DeepWaterColor = Color( 0, 0, 0.6, 1 )
shader_param/WaterColor = Color( 0, 0, 0.698039, 1 ) shader_param/WaterColor = Color( 0, 0, 0.7, 1 )
shader_param/LightWaterColor = Color( 0, 0, 1, 1 ) shader_param/LightWaterColor = Color( 0, 0, 1, 1 )
shader_param/SandColor = Color( 0.8, 0.8, 0.1, 1 ) shader_param/SandColor = Color( 0.8, 0.8, 0.1, 1 )
shader_param/GrassColor = Color( 0, 0.6, 0, 1 ) shader_param/GrassColor = Color( 0, 0.6, 0, 1 )

View File

@ -56,7 +56,7 @@ void fragment() {
// COLOR.rgb = color_ramp(texture_color.r * borderFalloffCircle(UV) * 1.8); // COLOR.rgb = color_ramp(texture_color.r * borderFalloffCircle(UV) * 1.8);
COLOR.rgb = color_ramp(texture_color.r * texture(TextureMask, UV).r); COLOR.rgb = color_ramp(texture_color.r * texture(TextureMask, UV).r);
COLOR.a = 1.0; COLOR.a = 0.8;
// COLOR.rgb = vec3(1, 0, 0); // COLOR.rgb = vec3(1, 0, 0);
} }

View File

@ -1,166 +0,0 @@
shader_type canvas_item;
uniform vec4 DeepWaterColor : hint_color = vec4(0, 0, 0.6, 1);
uniform vec4 WaterColor : hint_color = vec4(0, 0, 0.7, 1);
uniform vec4 LightWaterColor : hint_color = vec4(0, 0, 1, 1);
uniform vec4 SandColor : hint_color = vec4(0.8, 0.8, 0.1, 1);
uniform vec4 GrassColor : hint_color = vec4(0, 0.6, 0, 1);
uniform vec4 ForestColor : hint_color = vec4(0, 0.4, 0, 1);
uniform vec4 RockColor : hint_color = vec4(0.5, 0.5, 0.4, 1);
uniform vec4 SnowColor : hint_color = vec4(1);
vec2 rand2d(vec2 uv) {
return vec2(fract(sin(dot(uv.xy,
vec2(12.9898,78.233))) * 43758.5453123));
}
float rand1d(vec2 uv){
return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453);
}
vec2 voronoi_segment(vec2 uv, float columns, float rows) {
vec2 index_uv = floor(vec2(uv.x * columns, uv.y * rows));
vec2 fract_uv = fract(vec2(uv.x * columns, uv.y * rows));
float minimum_dist = 1.0;
vec2 minimum_point;
vec2 minimum_neighbour;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 neighbour = vec2(float(x), float(y));
vec2 point = rand2d(index_uv + neighbour);
vec2 diff = neighbour + point - fract_uv;
float dist = length(diff);
if (dist < minimum_dist) {
minimum_dist = dist;
minimum_point = point;
minimum_neighbour = neighbour;
}
}
}
return minimum_point;
}
ivec2 voronoiCellId(vec2 uv, vec2 grid_size) {
vec2 index_uv = floor(uv * grid_size);
vec2 fract_uv = fract(uv * grid_size);
float minimum_dist = 1.0;
vec2 minimum_point;
vec2 minimum_neighbour;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 neighbour = vec2(float(x), float(y));
vec2 point = rand2d(index_uv + neighbour);
vec2 diff = neighbour + point - fract_uv;
float dist = length(diff);
if (dist < minimum_dist) {
minimum_dist = dist;
minimum_point = point;
minimum_neighbour = index_uv + neighbour;
}
}
}
return ivec2(minimum_neighbour);
}
vec3 voronoi_cell_id_and_border_dist(vec2 uv, vec2 grid_size) {
vec2 index_uv = floor(uv * grid_size);
vec2 fract_uv = fract(uv * grid_size);
float minimum_dist = 1.0;
vec2 minimum_neighbour;
vec2 cell_point = rand2d(index_uv);
float border_dist = 1.0;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 neighbour = vec2(float(x), float(y));
vec2 point = rand2d(index_uv + neighbour);
vec2 diff = neighbour + point - fract_uv;
float dist = length(diff);
if (dist < minimum_dist) {
minimum_dist = dist;
minimum_neighbour = index_uv + neighbour;
float cell_point_dist = length(neighbour + point - cell_point);
border_dist = (cell_point_dist * 0.5 - dist);
border_dist = 1.0 - dist;
}
}
}
return vec3(minimum_neighbour, border_dist);
}
vec4 biomeValue (vec2 uv, vec2 grid_size) {
vec2 index_uv = floor(uv * grid_size);
vec2 fract_uv = fract(uv * grid_size);
float minimum_dist = 1.0;
vec2 minimum_point;
vec2 minimum_neighbour;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 neighbour = vec2(float(x), float(y));
vec2 point = rand2d(index_uv + neighbour);
vec2 diff = neighbour + point - fract_uv;
float dist = length(diff);
if (dist < minimum_dist) {
minimum_dist = dist;
minimum_point = point;
minimum_neighbour = index_uv + neighbour;
}
}
}
float biomeId = rand1d(minimum_neighbour);
if (biomeId < 0.2) {
return SandColor;
} else if (biomeId < 0.4) {
return GrassColor;
} else if (biomeId < 0.6) {
return RockColor;
} else if (biomeId < 0.8) {
return SnowColor;
} else {
return ForestColor;
}
}
void fragment() {
vec2 uv = UV / 0.01;
vec2 offset = vec2(sin(TIME * 0.6), cos(TIME * 0.6)) * 14.0;
ivec2 cellId = voronoiCellId(uv + offset, vec2(0.125, 0.125));
vec4 water_land_value = rand1d(vec2(cellId)) < 0.66 ? DeepWaterColor : GrassColor;
vec4 biome_id_value = biomeValue(uv + offset, vec2(0.14, 0.14));
// COLOR = vec4(water_land_value.xyz, 0.4) + vec4 (biome_id_value.xyz, 0.3);
if (water_land_value == DeepWaterColor) {
COLOR = water_land_value;
} else {
COLOR = biome_id_value;
}
vec3 cellIdAndBorderDist = voronoi_cell_id_and_border_dist(uv + offset, vec2(0.125, 0.125));
//COLOR = vec4(rand2d(vec2(cellIdAndBorderDist.xy)), 0.0, cellIdAndBorderDist.z);
COLOR = vec4(cellIdAndBorderDist.z, 0., 0., 1.0);
}

View File

@ -9,17 +9,17 @@
config_version=4 config_version=4
_global_script_classes=[ { _global_script_classes=[ {
"base": "Node", "base": "Reference",
"class": "ClickableComponent", "class": "ClickableComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/ClickableComponent.gd" "path": "res://components/ClickableComponent.gd"
}, { }, {
"base": "KinematicBody2D", "base": "Reference",
"class": "CollisionLine", "class": "CollisionLine",
"language": "GDScript", "language": "GDScript",
"path": "res://utils/CollisionLine.gd" "path": "res://utils/CollisionLine.gd"
}, { }, {
"base": "Node", "base": "Reference",
"class": "ColorComponent", "class": "ColorComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/ColorComponent.gd" "path": "res://components/ColorComponent.gd"
@ -54,7 +54,7 @@ _global_script_classes=[ {
"language": "GDScript", "language": "GDScript",
"path": "res://utils/SpringDamper.gd" "path": "res://utils/SpringDamper.gd"
}, { }, {
"base": "Sprite", "base": "Reference",
"class": "TintedSpriteComponent", "class": "TintedSpriteComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/TintedSpriteComponent.gd" "path": "res://components/TintedSpriteComponent.gd"
@ -114,11 +114,6 @@ Right={
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":68,"unicode":0,"echo":false,"script":null) "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":68,"unicode":0,"echo":false,"script":null)
] ]
} }
ToggleMainMenu={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":16777217,"unicode":0,"echo":false,"script":null)
]
}
[layer_names] [layer_names]
@ -139,9 +134,5 @@ common/enable_pause_aware_picking=true
quality/directional_shadow/size.mobile=512 quality/directional_shadow/size.mobile=512
quality/shadow_atlas/size.mobile=1024 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 quality/subsurface_scattering/quality=0
environment/default_environment="res://default_env.tres" environment/default_environment="res://default_env.tres"

View File

@ -1,14 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
public class Game : Spatial { public class Game : Spatial
private enum ActionMode { {
Default,
Building
}
private ActionMode _actionMode = ActionMode.Default;
private ImageTexture _blackWhitePatternTexture; private ImageTexture _blackWhitePatternTexture;
private Camera _camera; private Camera _camera;
private Vector3 _cameraOffset; private Vector3 _cameraOffset;
@ -19,58 +13,59 @@ public class Game : Spatial {
// ui elements // ui elements
private Label _framesPerSecondLabel; private Label _framesPerSecondLabel;
private Control _gameUi; private Control _gameUi;
private Label _woodCountLabel; private Button _generateWorldButton;
private Label _goldCountLabel; private Label _goldCountLabel;
private TextureRect _heightTextureRect; private TextureRect _heightTextureRect;
private Button _walkActionButton;
private Button _buildActionButton;
// other members // other members
private HexGrid _hexGrid; private HexGrid _hexGrid;
private InteractionSystem _interactionSystem; private InteractionSystem _interactionSystem;
private HexCell _lastTile;
private Label _mouseTileCubeLabel; private Label _mouseTileCubeLabel;
private Label _mouseTileAxialLabel; private Label _mouseTileAxialLabel;
private Spatial _mouseTileHighlight; private Spatial _mouseTileHighlight;
private Label _mouseTileOffsetLabel; private Label _mouseTileOffsetLabel;
private Label _mouseWorldLabel; private Label _mouseWorldLabel;
private Label _numCoordsAddedLabel;
private Label _numCoordsRemovedLabel;
private Label _numTilesLabel;
private Player _player; private Player _player;
// scene nodes // scene nodes
private Spatial _tileHighlight; private Spatial _tileHighlight;
// Resources // Resources
private PackedScene _tileHighlightScene;
private ShaderMaterial _tileMaterial; private ShaderMaterial _tileMaterial;
private Label _tileOffsetLabel; private Label _tileOffsetLabel;
private World _world; private World _world;
private TextureRect _worldTextureRect; private TextureRect _worldTextureRect;
private readonly PackedScene _workbenchScene = GD.Load<PackedScene>("res://entities/Workbench.tscn");
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() { public override void _Ready()
{
// debugStatsContainer // debugStatsContainer
Container debugStatsContainer = (Container)FindNode("DebugStatsContainer"); Container debugStatsContainer = (Container)FindNode("DebugStatsContainer");
_framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label"); _framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label");
_centerLabel = debugStatsContainer.GetNode<Label>("center_label"); _centerLabel = debugStatsContainer.GetNode<Label>("center_label");
_tileOffsetLabel = debugStatsContainer.GetNode<Label>("tile_offset_label"); _tileOffsetLabel = debugStatsContainer.GetNode<Label>("tile_offset_label");
_numTilesLabel = debugStatsContainer.GetNode<Label>("num_tiles_label");
_mouseWorldLabel = debugStatsContainer.GetNode<Label>("mouse_world_label"); _mouseWorldLabel = debugStatsContainer.GetNode<Label>("mouse_world_label");
_mouseTileOffsetLabel = debugStatsContainer.GetNode<Label>("mouse_tile_offset_label"); _mouseTileOffsetLabel = debugStatsContainer.GetNode<Label>("mouse_tile_offset_label");
_mouseTileCubeLabel = debugStatsContainer.GetNode<Label>("mouse_tile_cube_label"); _mouseTileCubeLabel = debugStatsContainer.GetNode<Label>("mouse_tile_cube_label");
_mouseTileAxialLabel = debugStatsContainer.GetNode<Label>("mouse_tile_axial_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 // UI elements
Container worldGeneratorWidget = (Container)FindNode("WorldGeneratorWidget"); Container worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer");
_worldTextureRect = worldGeneratorWidget.GetNode<TextureRect>("WorldTextureRect"); _worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect");
_heightTextureRect = worldGeneratorWidget.GetNode<TextureRect>("HeightTextureRect"); _heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect");
_generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton");
_gameUi = (Control)FindNode("GameUI"); _gameUi = (Control)FindNode("GameUI");
_woodCountLabel = _gameUi.GetNode<Label>("TopRight/WoodCount"); _goldCountLabel = _gameUi.GetNode<Label>("GoldCount");
Debug.Assert(_woodCountLabel != null);
_goldCountLabel = _gameUi.GetNode<Label>("TopRight/GoldCount");
Debug.Assert(_goldCountLabel != null); Debug.Assert(_goldCountLabel != null);
_walkActionButton = _gameUi.GetNode<Button>("Actions/WalkActionButton");
Debug.Assert(_walkActionButton != null);
_buildActionButton = _gameUi.GetNode<Button>("Actions/BuildActionButton");
Debug.Assert(_buildActionButton != null);
// scene nodes // scene nodes
_tileHighlight = GetNode<Spatial>("TileHighlight"); _tileHighlight = GetNode<Spatial>("TileHighlight");
@ -82,39 +77,40 @@ public class Game : Spatial {
_world = (World)FindNode("World"); _world = (World)FindNode("World");
// populate UI values
Slider generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
// resources // resources
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
Debug.Assert(_tileMaterial != null); Debug.Assert(_tileMaterial != null);
_blackWhitePatternTexture = new ImageTexture(); _blackWhitePatternTexture = new ImageTexture();
Image image = new(); Image image = new Image();
image.Load("assets/4x4checker.png"); image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
// other members // other members
_lastTile = new HexCell();
_currentTile = new HexCell(); _currentTile = new HexCell();
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
_interactionSystem = GetNode<InteractionSystem>("InteractionSystem"); _interactionSystem = GetNode<InteractionSystem>("InteractionSystem");
Debug.Assert(_interactionSystem != null); Debug.Assert(_interactionSystem != null);
// connect signals // connect signals
_generateWorldButton.Connect("pressed", this, nameof(OnGenerateButton));
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem, _player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
nameof(_interactionSystem.OnStartInteraction)); nameof(_interactionSystem.OnStartInteraction));
_player.Connect("WoodCountChanged", this, nameof(OnWoodCountChanged));
_player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged)); _player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged));
_world.Connect("TileClicked", this, nameof(OnTileClicked)); _world.Connect("TileClicked", this, nameof(OnTileClicked));
_world.Connect("TileHovered", this, nameof(OnTileHovered)); _world.Connect("TileHovered", this, nameof(OnTileHovered));
_world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged)); _world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged));
_world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged)); _world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged));
_walkActionButton.Connect("pressed", this, nameof(OnWalkActionPressed));
_buildActionButton.Connect("pressed", this, nameof(OnBuildActionPressed));
// register entity events // register entity events
foreach (Node node in GetNode("Entities").GetChildren()) { foreach (Node node in GetNode("Entities").GetChildren())
if (node.HasSignal("EntityClicked")) { if (node.HasSignal("EntityClicked"))
node.Connect("EntityClicked", this, nameof(OnEntityClicked)); node.Connect("EntityClicked", this, nameof(OnEntityClicked));
}
}
_world.Connect("EntityClicked", this, nameof(OnEntityClicked)); _world.Connect("EntityClicked", this, nameof(OnEntityClicked));
@ -123,39 +119,18 @@ public class Game : Spatial {
WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo"); WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
UpdateCurrentTile(); UpdateCurrentTile();
StartNewGame(2, 12);
} }
public override void _Input(InputEvent inputEvent) { public override void _Input(InputEvent inputEvent)
if (inputEvent.IsAction("Forward")) { {
GD.Print("Forward"); if (inputEvent.IsAction("Forward")) GD.Print("Forward");
}
if (inputEvent.IsAction("Back")) { if (inputEvent.IsAction("Back")) GD.Print("Back");
GD.Print("Back");
}
} }
public void StartNewGame(int seed, int chunkSize) { public void UpdateCurrentTile()
_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 // cast a ray from the camera to center
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f); Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f); Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
@ -163,9 +138,12 @@ public class Game : Spatial {
Vector3 centerCoord; Vector3 centerCoord;
if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition) { if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition)
{
centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y); centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y);
} else { }
else
{
centerCoord = _camera.GlobalTranslation; centerCoord = _camera.GlobalTranslation;
centerCoord.y = 0; centerCoord.y = 0;
} }
@ -179,8 +157,10 @@ public class Game : Spatial {
} }
public override void _Process(float delta) { public override void _Process(float delta)
{
_framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString(); _framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString();
_lastTile = _currentTile;
UpdateCurrentTile(); UpdateCurrentTile();
@ -195,8 +175,18 @@ public class Game : Spatial {
_camera.Transform = cameraTransform; _camera.Transform = cameraTransform;
} }
public void OnGenerateButton()
{
GD.Print("Generating");
Slider worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!");
}
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) { int shapeIndex)
{
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z)); HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
Transform highlightTransform = Transform.Identity; Transform highlightTransform = Transform.Identity;
@ -208,33 +198,19 @@ public class Game : Spatial {
} }
public void OnTileClicked(HexTile3D tile) { public void OnTileClicked(HexTile3D tile)
if (_player == null) { {
return; if (_player == null) return;
}
if (_actionMode == ActionMode.Building) { if (_player.InteractionComponent != null) _player.InteractionComponent.EmitSignal("InteractionEnd");
Workbench workbench = (Workbench)_workbenchScene.Instance();
workbench.Connect("EntityClicked", this, nameof(OnEntityClicked));
workbench.Transform = tile.GlobalTransform;
AddChild(workbench);
_world.MarkCellUnwalkable(tile.Cell);
return;
}
if (_player.InteractionComponent != null) {
_player.InteractionComponent.EmitSignal("InteractionEnd");
}
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(tile.GlobalTranslation))); new NavigationPoint(tile.GlobalTranslation)));
} }
public void OnTileHovered(HexTile3D tile) { public void OnTileHovered(HexTile3D tile)
{
Transform highlightTransform = tile.GlobalTransform; Transform highlightTransform = tile.GlobalTransform;
_mouseTileHighlight.Transform = highlightTransform; _mouseTileHighlight.Transform = highlightTransform;
_mouseWorldLabel.Text = highlightTransform.origin.ToString("F3"); _mouseWorldLabel.Text = highlightTransform.origin.ToString("F3");
@ -244,11 +220,13 @@ public class Game : Spatial {
_player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
public void OnEntityClicked(Entity entity) { public void OnEntityClicked(Entity entity)
{
GD.Print("Clicked on entity at " + entity.GlobalTranslation); GD.Print("Clicked on entity at " + entity.GlobalTranslation);
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint"); Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null) { if (mountPoint != null)
{
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(mountPoint.GlobalTransform))); new NavigationPoint(mountPoint.GlobalTransform)));
@ -257,7 +235,8 @@ public class Game : Spatial {
} }
public void ResetGameState() { public void ResetGameState()
{
Transform playerStartTransform = Transform.Identity; Transform playerStartTransform = Transform.Identity;
playerStartTransform.origin.y = 0; playerStartTransform.origin.y = 0;
_player.Transform = playerStartTransform; _player.Transform = playerStartTransform;
@ -265,29 +244,30 @@ public class Game : Spatial {
_player.NavigationComponent.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin, _player.NavigationComponent.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin,
playerStartTransform.basis.Quat()); playerStartTransform.basis.Quat());
_woodCountLabel.Text = "0";
_goldCountLabel.Text = "0"; _goldCountLabel.Text = "0";
_actionMode = ActionMode.Default;
foreach (Spatial entity in GetNode("Entities").GetChildren()) { foreach (Spatial entity in GetNode("Entities").GetChildren())
{
Transform entityTransform = entity.Transform; Transform entityTransform = entity.Transform;
Vector2 entityPlanePos = new(entityTransform.origin.x, entityTransform.origin.z); Vector2 entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords; Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
entityTransform.origin.y = 0; entityTransform.origin.y = 0;
entity.Transform = entityTransform; entity.Transform = entityTransform;
} }
} }
private void OnHeightmapImageChanged(Image heightmapImage) { private void OnHeightmapImageChanged(Image heightmapImage)
ImageTexture newHeightmapTexture = new(); {
ImageTexture newHeightmapTexture = new ImageTexture();
newHeightmapTexture.CreateFromImage(heightmapImage, newHeightmapTexture.CreateFromImage(heightmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_heightTextureRect.Texture = newHeightmapTexture; _heightTextureRect.Texture = newHeightmapTexture;
} }
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) { private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage)
ImageTexture newWorldTexture = new(); {
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(viewTileTypeImage, newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -299,27 +279,12 @@ public class Game : Spatial {
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y); _tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y);
} }
public void OnWoodCountChanged(int woodCount) { public void OnGoldCountChanged(int goldCount)
AnimationPlayer animationPlayer = _woodCountLabel.GetNode<AnimationPlayer>("AnimationPlayer"); {
_woodCountLabel.Text = woodCount.ToString(); AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
animationPlayer.CurrentAnimation = "FlashLabel";
animationPlayer.Seek(0);
animationPlayer.Play();
}
public void OnGoldCountChanged(int goldCount) {
AnimationPlayer animationPlayer = _goldCountLabel.GetNode<AnimationPlayer>("AnimationPlayer");
_goldCountLabel.Text = goldCount.ToString(); _goldCountLabel.Text = goldCount.ToString();
animationPlayer.CurrentAnimation = "FlashLabel"; animationPlayer.CurrentAnimation = "FlashLabel";
animationPlayer.Seek(0); animationPlayer.Seek(0);
animationPlayer.Play(); animationPlayer.Play();
} }
public void OnWalkActionPressed() {
_actionMode = ActionMode.Default;
}
public void OnBuildActionPressed() {
_actionMode = ActionMode.Building;
}
} }

View File

@ -1,51 +1,22 @@
[gd_scene load_steps=20 format=2] [gd_scene load_steps=21 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://entities/Player.tscn" type="PackedScene" id=2]
[ext_resource path="res://scenes/Camera.tscn" type="PackedScene" id=3] [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://ui/EditorUI.tscn" type="PackedScene" id=4]
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=5] [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://ui/DebugStatsContainer.gd" type="Script" id=6]
[ext_resource path="res://scenes/World.tscn" type="PackedScene" id=7] [ext_resource path="res://scenes/World.cs" type="Script" id=7]
[ext_resource path="res://ui/WorldGeneratorUI.tscn" type="PackedScene" id=8]
[ext_resource path="res://scenes/Game.cs" type="Script" id=9] [ext_resource path="res://scenes/Game.cs" type="Script" id=9]
[ext_resource path="res://entities/Wood.tscn" type="PackedScene" id=10]
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11] [ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
[ext_resource path="res://ui/game_theme.tres" type="Theme" id=12] [ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12]
[ext_resource path="res://ui/action_buttongroup.tres" type="ButtonGroup" id=13] [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://entities/Axe.tscn" type="PackedScene" id=14]
[ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15] [ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15]
[ext_resource path="res://entities/rockA.tscn" type="PackedScene" id=16]
[sub_resource type="Animation" id=27] [ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=17]
resource_name = "FlashLabel" [ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=18]
length = 0.3 [ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=19]
tracks/0/type = "value" [ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=20]
tracks/0/path = NodePath("WoodCount:rect_scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.1, 0.3 ),
"transitions": PoolRealArray( 1, 0.1701, 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ), Vector2( 2, 2 ), Vector2( 1, 1 ) ]
}
[sub_resource type="Animation" id=28]
length = 0.001
tracks/0/type = "value"
tracks/0/path = NodePath("WoodCount:rect_scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ) ]
}
[sub_resource type="Animation" id=25] [sub_resource type="Animation" id=25]
resource_name = "FlashLabel" resource_name = "FlashLabel"
@ -65,11 +36,15 @@ tracks/0/keys = {
[sub_resource type="AnimationNodeStateMachinePlayback" id=26] [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"] [node name="Game" type="Spatial"]
script = ExtResource( 9 ) script = ExtResource( 9 )
__meta__ = {
"_edit_horizontal_guides_": [ -333.0 ]
}
[node name="TileHighlight" parent="." instance=ExtResource( 5 )] [node name="TileHighlight" parent="." instance=ExtResource( 5 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0 )
@ -77,94 +52,42 @@ visible = false
[node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )] [node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )]
[node name="GameUI" type="Control" parent="."] [node name="GameUI" type="HBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
theme = ExtResource( 12 )
[node name="TopRight" type="HBoxContainer" parent="GameUI"]
anchor_left = 1.0 anchor_left = 1.0
anchor_right = 1.0 anchor_right = 1.0
margin_left = -206.0 margin_left = -40.0
margin_right = -10.0
margin_bottom = 40.0 margin_bottom = 40.0
grow_horizontal = 0 grow_horizontal = 0
mouse_filter = 2
alignment = 2 alignment = 2
[node name="WoodLabel" type="Label" parent="GameUI/TopRight"] [node name="GoldLabel" type="Label" parent="GameUI"]
margin_left = 4.0 margin_top = 13.0
margin_top = 10.0 margin_right = 30.0
margin_right = 53.0 margin_bottom = 27.0
margin_bottom = 29.0
text = "Wood"
[node name="WoodCount" type="Label" parent="GameUI/TopRight"]
margin_left = 57.0
margin_top = 10.0
margin_right = 107.0
margin_bottom = 29.0
rect_min_size = Vector2( 50, 0 )
rect_pivot_offset = Vector2( 25, 8 )
text = "0"
align = 1
[node name="AnimationPlayer" type="AnimationPlayer" parent="GameUI/TopRight/WoodCount"]
root_node = NodePath("../..")
anims/FlashLabel = SubResource( 27 )
anims/RESET = SubResource( 28 )
[node name="GoldLabel" type="Label" parent="GameUI/TopRight"]
margin_left = 111.0
margin_top = 10.0
margin_right = 152.0
margin_bottom = 29.0
text = "Gold" text = "Gold"
[node name="GoldCount" type="Label" parent="GameUI/TopRight"] [node name="GoldCount" type="Label" parent="GameUI"]
margin_left = 156.0 margin_left = 34.0
margin_top = 10.0 margin_top = 13.0
margin_right = 206.0 margin_right = 84.0
margin_bottom = 29.0 margin_bottom = 27.0
rect_min_size = Vector2( 50, 0 ) rect_min_size = Vector2( 50, 0 )
rect_pivot_offset = Vector2( 25, 8 ) rect_pivot_offset = Vector2( 45, 8 )
text = "0" text = "0"
align = 1 align = 2
[node name="AnimationPlayer" type="AnimationPlayer" parent="GameUI/TopRight/GoldCount"] [node name="AnimationPlayer" type="AnimationPlayer" parent="GameUI"]
root_node = NodePath("../..")
anims/FlashLabel = SubResource( 25 ) anims/FlashLabel = SubResource( 25 )
[node name="Actions" type="HBoxContainer" parent="GameUI"]
margin_left = 10.0
margin_top = 10.0
margin_bottom = 40.0
[node name="WalkActionButton" type="Button" parent="GameUI/Actions"]
margin_right = 58.0
margin_bottom = 40.0
rect_min_size = Vector2( 40, 40 )
toggle_mode = true
pressed = true
group = ExtResource( 13 )
text = "Walk"
[node name="BuildActionButton" type="Button" parent="GameUI/Actions"]
margin_left = 62.0
margin_right = 120.0
margin_bottom = 40.0
toggle_mode = true
group = ExtResource( 13 )
text = "Build"
[node name="DebugContainer" type="PanelContainer" parent="."] [node name="DebugContainer" type="PanelContainer" parent="."]
self_modulate = Color( 1, 1, 1, 0.443137 ) self_modulate = Color( 1, 1, 1, 0.443137 )
anchor_left = 1.0 anchor_left = 1.0
anchor_top = 1.0 anchor_top = 1.0
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
margin_left = -67.0 margin_left = -141.0
margin_top = -34.0 margin_top = -172.0
grow_horizontal = 0 grow_horizontal = 0
grow_vertical = 0 grow_vertical = 0
mouse_filter = 2 mouse_filter = 2
@ -174,8 +97,8 @@ size_flags_vertical = 3
[node name="DebugStatsContainer" type="GridContainer" parent="DebugContainer"] [node name="DebugStatsContainer" type="GridContainer" parent="DebugContainer"]
margin_left = 7.0 margin_left = 7.0
margin_top = 7.0 margin_top = 7.0
margin_right = 60.0 margin_right = 134.0
margin_bottom = 27.0 margin_bottom = 165.0
grow_horizontal = 0 grow_horizontal = 0
grow_vertical = 0 grow_vertical = 0
mouse_filter = 2 mouse_filter = 2
@ -368,24 +291,22 @@ margin_bottom = 200.0
text = "0" text = "0"
[node name="Generator Container" type="Control" parent="."] [node name="Generator Container" type="Control" parent="."]
visible = false
margin_right = 40.0 margin_right = 40.0
margin_bottom = 40.0 margin_bottom = 40.0
mouse_filter = 2 mouse_filter = 2
[node name="WorldGeneratorWidget" type="VBoxContainer" parent="Generator Container"] [node name="WorldGeneratorContainer" type="VBoxContainer" parent="Generator Container"]
margin_left = 10.0 margin_left = 10.0
margin_top = 10.0 margin_top = 10.0
margin_right = 145.0 margin_right = 145.0
margin_bottom = 94.0 margin_bottom = 94.0
script = ExtResource( 1 ) script = ExtResource( 12 )
[node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorWidget"] [node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorContainer"]
visible = false
margin_right = 135.0 margin_right = 135.0
margin_bottom = 16.0 margin_bottom = 16.0
[node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorWidget/HBoxContainer"] [node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorContainer/HBoxContainer"]
margin_right = 123.0 margin_right = 123.0
margin_bottom = 16.0 margin_bottom = 16.0
size_flags_horizontal = 3 size_flags_horizontal = 3
@ -393,26 +314,26 @@ min_value = 1.0
max_value = 256.0 max_value = 256.0
value = 1.0 value = 1.0
[node name="WorldSizeLabel" type="Label" parent="Generator Container/WorldGeneratorWidget/HBoxContainer"] [node name="WorldSizeLabel" type="Label" parent="Generator Container/WorldGeneratorContainer/HBoxContainer"]
margin_left = 127.0 margin_left = 127.0
margin_top = 1.0 margin_top = 1.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 15.0 margin_bottom = 15.0
text = "4" text = "4"
[node name="WorldGenerateButton" type="Button" parent="Generator Container/WorldGeneratorWidget"] [node name="WorldGenerateButton" type="Button" parent="Generator Container/WorldGeneratorContainer"]
visible = false
margin_top = 20.0 margin_top = 20.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 40.0 margin_bottom = 40.0
text = "Generate" text = "Generate"
[node name="ShowTexturesCheckButton" type="CheckButton" parent="Generator Container/WorldGeneratorWidget"] [node name="ShowTexturesCheckButton" type="CheckButton" parent="Generator Container/WorldGeneratorContainer"]
margin_top = 44.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 40.0 margin_bottom = 84.0
text = "Textures" text = "Textures"
[node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"] [node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"]
visible = false visible = false
margin_top = 88.0 margin_top = 88.0
margin_right = 135.0 margin_right = 135.0
@ -422,11 +343,11 @@ expand = true
stretch_mode = 5 stretch_mode = 5
flip_v = true flip_v = true
[node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"] [node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"]
visible = false visible = false
margin_top = 192.0 margin_top = 88.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 292.0 margin_bottom = 188.0
rect_min_size = Vector2( 100, 100 ) rect_min_size = Vector2( 100, 100 )
expand = true expand = true
stretch_mode = 5 stretch_mode = 5
@ -444,182 +365,63 @@ script = ExtResource( 15 )
[node name="NavigationSystem" type="Node" parent="."] [node name="NavigationSystem" type="Node" parent="."]
[node name="Player" parent="." instance=ExtResource( 2 )] [node name="Player" parent="." instance=ExtResource( 2 )]
EntityMask = 2
WorldNode = NodePath("../World") WorldNode = NodePath("../World")
[node name="WorldInfo" parent="Player" index="2"] [node name="WorldInfo" parent="Player" index="2"]
WorldPath = NodePath("../../World") WorldPath = NodePath("../../World")
[node name="Skeleton" parent="Player/Geometry/PirateAsset/Armature" index="0"]
bones/4/bound_children = [ NodePath("ToolAttachement") ]
[node name="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"] [node name="ToolAttachement" parent="Player/Geometry/PirateAsset/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 ) 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="1"] [node name="AnimationTree" parent="Player/Geometry" index="2"]
parameters/playback = SubResource( 26 ) parameters/playback = SubResource( 26 )
[node name="Entities" type="Spatial" parent="."] [node name="Entities" type="Spatial" parent="."]
[node name="Axe" parent="Entities" instance=ExtResource( 14 )] [node name="Axe" parent="Entities" instance=ExtResource( 14 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.03292, -2.38419e-07, -4.33215 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.79762, 0, 0 )
input_ray_pickable = false input_ray_pickable = false
[node name="Chest" parent="Entities" instance=ExtResource( 11 )] [node name="Chest" parent="Entities" instance=ExtResource( 11 )]
transform = Transform( -0.825665, 0, 0.56416, 0, 1, 0, -0.56416, 0, -0.825665, -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="Wood" parent="Entities" instance=ExtResource( 10 )] [node name="World" type="Spatial" parent="."]
transform = Transform( 0.791533, 0, 0.611126, 0, 1, 0, -0.611126, 0, 0.791533, -2.46279, 0, -0.631194 ) script = ExtResource( 7 )
[node name="Wood2" parent="Entities" instance=ExtResource( 10 )] [node name="Chunks" type="Spatial" parent="World"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.66947, 0, -2.19408 )
[node name="Wood3" parent="Entities" instance=ExtResource( 10 )] [node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.89011, 0, 1.70148 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 )
multimesh = SubResource( 27 )
[node name="Wood4" parent="Entities" instance=ExtResource( 10 )] [node name="Assets" type="Spatial" parent="World"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.85616, 0, -2.01499 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 )
visible = false
[node name="Wood5" parent="Entities" instance=ExtResource( 10 )] [node name="Rocks" type="Spatial" parent="World/Assets"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5591, 0, 1.55701 )
[node name="World" parent="." instance=ExtResource( 7 )] [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="DirectionalLight" type="DirectionalLight" parent="."] [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 ) transform = Transform( 0.328059, -0.878387, 0.347583, 0, 0.367946, 0.929847, -0.944657, -0.305045, 0.120708, 0, 6.59293, 1.20265 )
shadow_enabled = true shadow_enabled = true
directional_shadow_mode = 0 directional_shadow_mode = 0
[node name="GameMenu" type="Control" parent="."]
visible = false
anchor_left = 0.1
anchor_top = 0.1
anchor_right = 0.9
anchor_bottom = 0.9
[node name="MainMenu" type="Panel" parent="GameMenu"]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="MainMenu" type="VBoxContainer" parent="GameMenu/MainMenu"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -41.0
margin_top = -22.0
margin_right = 41.0
margin_bottom = 22.0
[node name="Label" type="Label" parent="GameMenu/MainMenu/MainMenu"]
margin_right = 82.0
margin_bottom = 14.0
text = "Pirate Game"
align = 1
[node name="NewGameButton" type="Button" parent="GameMenu/MainMenu/MainMenu"]
margin_top = 18.0
margin_right = 82.0
margin_bottom = 38.0
text = "New Game"
[node name="QuitButton" type="Button" parent="GameMenu/MainMenu/MainMenu"]
margin_top = 42.0
margin_right = 82.0
margin_bottom = 62.0
text = "Quit"
[node name="NewGameMenu" type="Panel" parent="GameMenu"]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
[node name="MainMenu" type="VBoxContainer" parent="GameMenu/NewGameMenu"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -41.0
margin_top = -10.0
margin_right = 41.0
margin_bottom = 10.0
[node name="NewGameButton" type="Button" parent="GameMenu/NewGameMenu/MainMenu"]
margin_right = 82.0
margin_bottom = 20.0
text = "New Game"
[node name="QuitButton" type="Button" parent="GameMenu/NewGameMenu/MainMenu"]
margin_top = 24.0
margin_right = 82.0
margin_bottom = 44.0
text = "Quit"
[node name="WorldGeneratorUI" parent="GameMenu/NewGameMenu" instance=ExtResource( 8 )]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -143.5
margin_top = -160.0
margin_right = 143.5
margin_bottom = 160.0
[node name="VBoxContainer" parent="GameMenu/NewGameMenu/WorldGeneratorUI" index="0"]
margin_left = 0.0
margin_top = 21.0
margin_right = 287.0
margin_bottom = 299.0
[node name="Label" parent="GameMenu/NewGameMenu/WorldGeneratorUI/VBoxContainer" index="0"]
text = "New World"
[node name="GameRunningMenu" type="Panel" parent="GameMenu"]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="GameMenu/GameRunningMenu"]
margin_left = 107.0
margin_top = 113.0
margin_right = 148.0
margin_bottom = 207.0
custom_constants/separation = 20
[node name="Label" type="Label" parent="GameMenu/GameRunningMenu/VBoxContainer"]
margin_right = 42.0
margin_bottom = 14.0
align = 1
[node name="SaveButton" type="Button" parent="GameMenu/GameRunningMenu/VBoxContainer"]
margin_top = 34.0
margin_right = 42.0
margin_bottom = 54.0
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Save"
[node name="LoadButton" type="Button" parent="GameMenu/GameRunningMenu/VBoxContainer"]
margin_top = 74.0
margin_right = 42.0
margin_bottom = 94.0
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Load"
[node name="QuitButton" type="Button" parent="GameMenu/GameRunningMenu/VBoxContainer"]
margin_top = 114.0
margin_right = 42.0
margin_bottom = 134.0
text = "Quit"
[connection signal="toggled" from="DebugContainer/DebugStatsContainer/DebugMenuButton" to="DebugContainer/DebugStatsContainer" method="_on_DebugMenuButton_toggled"] [connection signal="toggled" from="DebugContainer/DebugStatsContainer/DebugMenuButton" to="DebugContainer/DebugStatsContainer" method="_on_DebugMenuButton_toggled"]
[connection signal="value_changed" from="Generator Container/WorldGeneratorWidget/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorWidget" method="_on_HSlider_value_changed"] [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/WorldGeneratorWidget/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorWidget" method="_on_ShowTexturesCheckButton_toggled"] [connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"]
[editable path="Player"] [editable path="Player"]
[editable path="Player/Geometry/PirateAsset"] [editable path="Player/Geometry/PirateAsset"]
[editable path="World"]
[editable path="GameMenu/NewGameMenu/WorldGeneratorUI"]

View File

@ -1,43 +1,44 @@
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
public class HexTile3D : Spatial { public class HexTile3D : Spatial
public class TileTypeInfo { {
public readonly string Name; public enum TileType
public readonly Color Color; {
public readonly ushort TileTypeMask; Undefined,
Sand,
public TileTypeInfo(string name, Color color, ushort tileTypeMask) { Grass,
Name = name; DeepGrass
Color = color;
TileTypeMask = tileTypeMask;
}
} }
static public TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass };
// scene nodes // scene nodes
private MeshInstance _mesh; private MeshInstance _mesh;
private StaticBody _staticBody; private StaticBody _staticBody;
// signals // signals
[Signal] [Signal]
private delegate void TileClicked(HexTile3D tile3d); delegate void TileClicked(HexTile3D tile3d);
[Signal] [Signal]
private delegate void TileHovered(HexTile3D tile3d); delegate void TileHovered(HexTile3D tile3d);
// other member variables // other member variables
private SpatialMaterial _previousMaterial; private SpatialMaterial _previousMaterial;
private readonly HexGrid _hexGrid; private HexGrid _hexGrid;
public HexCell Cell = new(); public HexCell Cell = new HexCell();
public bool IsMouseOver; public bool IsMouseOver = false;
public MeshInstance Mesh; public MeshInstance Mesh;
public Vector2 OffsetCoords { public Vector2 OffsetCoords
get => Cell.OffsetCoords; {
get { return Cell.OffsetCoords; }
set { set
{
Cell.OffsetCoords = value; Cell.OffsetCoords = value;
Transform tile3dTransform = Transform; Transform tile3dTransform = Transform;
Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell); Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell);
@ -47,12 +48,16 @@ public class HexTile3D : Spatial {
} }
} }
private HexTile3D() { public TileType Type { get; set; }
HexTile3D()
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() { public override void _Ready()
{
_mesh = GetNode<MeshInstance>("Mesh"); _mesh = GetNode<MeshInstance>("Mesh");
_staticBody = GetNode<StaticBody>("StaticBody"); _staticBody = GetNode<StaticBody>("StaticBody");
@ -62,20 +67,26 @@ public class HexTile3D : Spatial {
Mesh = GetNode<MeshInstance>("Mesh"); Mesh = GetNode<MeshInstance>("Mesh");
Debug.Assert(Mesh != null); Debug.Assert(Mesh != null);
this.Type = TileType.Undefined;
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) { int shapeIndex)
if (IsMouseOver && inputEvent is InputEventMouseButton) { {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) { if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed)
{
EmitSignal("TileClicked", this); EmitSignal("TileClicked", this);
} }
} }
} }
public void OnAreaMouseEntered() { public void OnAreaMouseEntered()
{
IsMouseOver = true; IsMouseOver = true;
_previousMaterial = (SpatialMaterial)_mesh.MaterialOverride; _previousMaterial = (SpatialMaterial)_mesh.MaterialOverride;
@ -85,7 +96,8 @@ public class HexTile3D : Spatial {
// _mesh.MaterialOverride = clonedMaterial; // _mesh.MaterialOverride = clonedMaterial;
} }
public void OnAreaMouseExited() { public void OnAreaMouseExited()
{
IsMouseOver = false; IsMouseOver = false;
_mesh.MaterialOverride = _previousMaterial; _mesh.MaterialOverride = _previousMaterial;
} }

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=4 format=2] [gd_scene load_steps=4 format=2]
[ext_resource path="res://materials/WorldTileTypeMaterial.tres" type="Material" id=1] [ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=1]
[sub_resource type="OpenSimplexNoise" id=3] [sub_resource type="OpenSimplexNoise" id=3]
seed = 57 seed = 57

View File

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

View File

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

View File

@ -2,7 +2,7 @@
[ext_resource path="res://scenes/TileWorld.cs" type="Script" id=1] [ext_resource path="res://scenes/TileWorld.cs" type="Script" id=1]
[ext_resource path="res://icon.png" type="Texture" id=2] [ext_resource path="res://icon.png" type="Texture" id=2]
[ext_resource path="res://materials/WorldTileTypeMaterial.tres" type="Material" id=3] [ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=3]
[ext_resource path="res://materials/IslandHeightmapFalloffShader.tres" type="Material" id=4] [ext_resource path="res://materials/IslandHeightmapFalloffShader.tres" type="Material" id=4]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=5] [ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=5]
[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=6] [ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=6]

View File

@ -1,23 +0,0 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://icon.png" type="Texture" id=1]
[ext_resource path="res://materials/shader/VoronoiBasedBiomes2D.gdshader" type="Shader" id=2]
[sub_resource type="ShaderMaterial" id=2]
shader = ExtResource( 2 )
shader_param/DeepWaterColor = Color( 0, 0, 0.6, 1 )
shader_param/WaterColor = Color( 0, 0, 0.7, 1 )
shader_param/LightWaterColor = Color( 0, 0, 1, 1 )
shader_param/SandColor = Color( 0.8, 0.8, 0.1, 1 )
shader_param/GrassColor = Color( 0, 0.6, 0, 1 )
shader_param/ForestColor = Color( 0, 0.4, 0, 1 )
shader_param/RockColor = Color( 0.5, 0.5, 0.4, 1 )
shader_param/SnowColor = Color( 1, 1, 1, 1 )
[node name="Node2D" type="Node2D"]
[node name="Sprite" type="Sprite" parent="."]
material = SubResource( 2 )
position = Vector2( 195, 368 )
scale = Vector2( 5.89062, 5.29688 )
texture = ExtResource( 1 )

View File

@ -4,11 +4,12 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using Godot; using Godot;
using Godot.Collections; using Godot.Collections;
using Priority_Queue;
public class World : Spatial { public class World : Spatial
public enum GenerationState { {
Empty, public enum GenerationState
{
Undefined,
Heightmap, Heightmap,
TileType, TileType,
Objects, Objects,
@ -16,30 +17,32 @@ public class World : Spatial {
} }
// constants // constants
public int ChunkSize = 14; public const int ChunkSize = 12;
public const int NumChunkRows = 3; public const int NumChunkRows = 3;
public const int NumChunkColumns = NumChunkRows; public const int NumChunkColumns = NumChunkRows;
private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f);
private static readonly Color GrassColor = new(0, 0.4f, 0);
private readonly System.Collections.Generic.Dictionary<int, HexTile3D.TileTypeInfo> _tileTypeInfos = new(); 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 Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks; private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
private readonly List<Vector2> _addedChunkIndices = new(); private readonly List<Vector2> _addedChunkIndices = new();
private readonly List<WorldChunk> _deactivatedWorldChunks = new(); private readonly List<WorldChunk> _unusedWorldChunks = new();
private readonly Image _heightmapImage = new(); private readonly Image _heightmapImage = new();
private readonly List<Vector2> _removedChunkIndices = new(); private readonly List<Vector2> _removedChunkIndices = new();
private readonly Image _tileTypeMapImage = new(); private readonly Image _tileTypeMapImage = new();
private int FrameCounter;
// referenced scenes // referenced scenes
private readonly PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn"); private readonly PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn");
private List<Vector2> _activeChunkIndices = new(); private List<Vector2> _activeChunkIndices = new();
private Rect2 _centerChunkRect; private Rect2 _centerChunkRect;
private readonly List<Spatial> _removedSpatialNodes = new();
// delegate void OnCoordClicked(Vector2 world_pos); // delegate void OnCoordClicked(Vector2 world_pos);
// other members // other members
private Vector2 _centerPlaneCoord;
private Vector2 _chunkIndexNorthEast; private Vector2 _chunkIndexNorthEast;
private Vector2 _chunkIndexSouthWest; private Vector2 _chunkIndexSouthWest;
private Array<Spatial> _grassAssets; private Array<Spatial> _grassAssets;
@ -57,9 +60,10 @@ public class World : Spatial {
public HexGrid HexGrid = new(); public HexGrid HexGrid = new();
public int Seed = 0; public int Seed = 0;
public GenerationState State = GenerationState.Empty; public GenerationState State = GenerationState.Done;
public Vector2 WorldTextureCoordinateOffset; public Vector2 WorldTextureCoordinateOffset;
// ui elements // ui elements
// scene nodes // scene nodes
@ -89,60 +93,43 @@ public class World : Spatial {
[Signal] [Signal]
private delegate void TileHovered(HexTile3D tile3d); private delegate void TileHovered(HexTile3D tile3d);
public World() { public World()
{
Debug.Assert(ChunkSize % 2 == 0); Debug.Assert(ChunkSize % 2 == 0);
_cachedWorldChunks = new Godot.Collections.Dictionary<Vector2, WorldChunk>(); _cachedWorldChunks = new Godot.Collections.Dictionary<Vector2, WorldChunk>();
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() { public override void _Ready()
{
Chunks = (Spatial)FindNode("Chunks"); Chunks = (Spatial)FindNode("Chunks");
Debug.Assert(Chunks != null); Debug.Assert(Chunks != null);
_tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance"); _tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
Debug.Assert(_tileMultiMeshInstance != null); Debug.Assert(_tileMultiMeshInstance != null);
_tileMultiMeshInstance.Multimesh.InstanceCount =
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
_usedTileMeshInstances = 0;
InitNoiseGenerator(); InitNoiseGenerator();
GetNode<Spatial>("Assets").Visible = false; GetNode<Spatial>("Assets").Visible = false;
_rockAssets = new Array<Spatial>(); _rockAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) { foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) _rockAssets.Add(asset);
_rockAssets.Add(asset);
}
_grassAssets = new Array<Spatial>(); _grassAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) { foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) _grassAssets.Add(asset);
_grassAssets.Add(asset);
}
_treeAssets = new Array<Spatial>(); _treeAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) { foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) _treeAssets.Add(asset);
_treeAssets.Add(asset);
}
_tileTypeInfos[0] = new HexTile3D.TileTypeInfo("Undefined", Colors.Black, 0xffff); SetCenterPlaneCoord(Vector2.Zero);
LoadTileTypeInfo("DeepWater", (int)Entity.EntityMaskEnum.Water);
LoadTileTypeInfo("Water", (int)Entity.EntityMaskEnum.Water);
LoadTileTypeInfo("LightWater", (int)Entity.EntityMaskEnum.Water);
LoadTileTypeInfo("Sand", (int)Entity.EntityMaskEnum.Ground);
LoadTileTypeInfo("Grass", (int)Entity.EntityMaskEnum.Ground);
LoadTileTypeInfo("Forest", (int)Entity.EntityMaskEnum.Ground);
LoadTileTypeInfo("Rock", (int)Entity.EntityMaskEnum.Ground);
LoadTileTypeInfo("Sand", (int)Entity.EntityMaskEnum.Ground);
} }
private void LoadTileTypeInfo(string tileTypeName, ushort tileTypeMask) { public void InitNoiseGenerator()
ShaderMaterial worldTileTypeShaderMaterial = {
GD.Load<ShaderMaterial>("res://materials/WorldTileTypeMaterial.tres");
Color tileTypeColor = (Color)worldTileTypeShaderMaterial.GetShaderParam(tileTypeName + "Color");
_tileTypeInfos[tileTypeColor.ToRgba32()] =
new HexTile3D.TileTypeInfo(tileTypeName, tileTypeColor, tileTypeMask);
}
public void InitNoiseGenerator() {
_noiseGenerator = new OpenSimplexNoise(); _noiseGenerator = new OpenSimplexNoise();
_noiseGenerator.Seed = Seed; _noiseGenerator.Seed = Seed;
@ -152,36 +139,22 @@ public class World : Spatial {
_noiseGenerator.Lacunarity = 2; _noiseGenerator.Lacunarity = 2;
} }
public void Reset() { public WorldChunk GetOrCreateWorldChunk(Vector2 chunkIndex, Color debugColor)
foreach (Spatial chunkChild in Chunks.GetChildren()) { {
chunkChild.QueueFree();
}
// foreach (WorldChunk chunk in _cachedWorldChunks.Values) {
// chunk.QueueFree();
// }
_cachedWorldChunks.Clear();
_addedChunkIndices.Clear();
_tileMultiMeshInstance.Multimesh.InstanceCount =
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
_usedTileMeshInstances = 0;
State = GenerationState.Empty;
}
public WorldChunk GetOrCreateWorldChunk(Vector2 chunkIndex, Color debugColor) {
WorldChunk chunk; WorldChunk chunk;
if (IsChunkCached(chunkIndex)) { if (IsChunkCached(chunkIndex))
return _cachedWorldChunks[chunkIndex]; return _cachedWorldChunks[chunkIndex];
}
if (_deactivatedWorldChunks.Count > 0) { if (_unusedWorldChunks.Count > 0)
chunk = _deactivatedWorldChunks.First(); {
_deactivatedWorldChunks.RemoveAt(0); chunk = _unusedWorldChunks.First();
} else { _unusedWorldChunks.RemoveAt(0);
GD.Print("Reusing chunk from former index " + chunk.ChunkIndex + " at new index " + chunkIndex);
}
else
{
chunk = CreateWorldChunk(chunkIndex, debugColor); chunk = CreateWorldChunk(chunkIndex, debugColor);
} }
@ -190,14 +163,15 @@ public class World : Spatial {
return chunk; return chunk;
} }
private bool IsChunkCached(Vector2 chunkIndex) { private bool IsChunkCached(Vector2 chunkIndex)
{
return _cachedWorldChunks.ContainsKey(chunkIndex); return _cachedWorldChunks.ContainsKey(chunkIndex);
} }
private WorldChunk CreateWorldChunk(Vector2 chunkIndex, Color debugColor) { private WorldChunk CreateWorldChunk(Vector2 chunkIndex, Color debugColor)
{
WorldChunk result = (WorldChunk)_worldChunkScene.Instance(); WorldChunk result = (WorldChunk)_worldChunkScene.Instance();
result.SetSize(ChunkSize);
Chunks.AddChild(result); Chunks.AddChild(result);
result.Connect("TileClicked", this, nameof(OnTileClicked)); result.Connect("TileClicked", this, nameof(OnTileClicked));
result.Connect("TileHovered", this, nameof(OnTileHovered)); result.Connect("TileHovered", this, nameof(OnTileHovered));
@ -217,16 +191,15 @@ public class World : Spatial {
return result; return result;
} }
private bool IsColorEqualApprox(Color colorA, Color colorB) { private bool IsColorEqualApprox(Color colorA, Color colorB)
{
Vector3 colorDifference = new(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b); Vector3 colorDifference = new(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
return colorDifference.LengthSquared() < 0.1 * 0.1; return colorDifference.LengthSquared() < 0.1 * 0.1;
} }
private Spatial SelectAsset(Vector2 textureCoord, Array<Spatial> assets, Random randomGenerator, private Spatial SelectAsset(Vector2 textureCoord, Array<Spatial> assets, Random randomGenerator, double probability)
double probability) { {
if (randomGenerator.NextDouble() < 1.0 - probability) { if (randomGenerator.NextDouble() < 1.0 - probability) return null;
return null;
}
int assetIndex = randomGenerator.Next(assets.Count); int assetIndex = randomGenerator.Next(assets.Count);
Spatial assetInstance = (Spatial)assets[assetIndex].Duplicate(); Spatial assetInstance = (Spatial)assets[assetIndex].Duplicate();
@ -241,47 +214,41 @@ public class World : Spatial {
return assetInstance; return assetInstance;
} }
private void PopulateChunk(WorldChunk chunk) { private void PopulateChunk(WorldChunk chunk)
{
Random environmentRandom = new(Seed); Random environmentRandom = new(Seed);
chunk.CreateUnlockedTileTypeImage(); Image tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData();
tileTypeImage.Lock();
foreach (int textureCoordU in Enumerable.Range(0, chunk.Size)) { foreach (int textureCoordU in Enumerable.Range(0, chunk.Size))
foreach (int textureCoordV in Enumerable.Range(0, chunk.Size)) { foreach (int textureCoordV in Enumerable.Range(0, chunk.Size))
Vector2 textureCoord = new(textureCoordU, textureCoordV); {
Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord; Color colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV);
Vector2 textureCoord = new(textureCoordU, textureCoordV);
Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
HexTile3D.TileTypeInfo tileTypeInfo = GetTileTypeInfoAtOffset(offsetCoord); if (IsColorEqualApprox(colorValue, RockColor))
{
Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.15);
if (rockAsset != null) chunk.Entities.AddChild(rockAsset);
// TODO: MarkCellUnwalkable(cell);
}
else if (IsColorEqualApprox(colorValue, GrassColor) || IsColorEqualApprox(colorValue, DarkGrassColor))
{
Spatial grassAsset = SelectAsset(textureCoord, _grassAssets, environmentRandom, 0.15);
if (grassAsset != null) chunk.Entities.AddChild(grassAsset);
if (tileTypeInfo.Name == "Rock") { Tree treeAsset = SelectAsset(textureCoord, _treeAssets, environmentRandom, 0.05) as Tree;
Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.15); if (treeAsset != null)
if (rockAsset != null) { {
chunk.Entities.AddChild(rockAsset); chunk.Entities.AddChild(treeAsset);
MarkCellUnwalkable(HexGrid.GetHexAtOffset(offsetCoord)); treeAsset.Connect("EntityClicked", this, nameof(OnEntityClicked));
} }
} else if (tileTypeInfo.Name == "Grass" || tileTypeInfo.Name == "DarkGrass") {
Spatial grassAsset = SelectAsset(textureCoord, _grassAssets, environmentRandom, 0.15);
if (grassAsset != null) {
chunk.Entities.AddChild(grassAsset);
}
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); // TODO: MarkCellUnwalkable(cell);
// else if (environmentRandom.NextDouble() < 0.01) // else if (environmentRandom.NextDouble() < 0.01)
} else if (tileTypeInfo.Name == "Water" || tileTypeInfo.Name == "LightWater" ||
tileTypeInfo.Name == "DeepWater") {
Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.01);
if (rockAsset != null) {
chunk.Entities.AddChild(rockAsset);
MarkCellUnwalkable(HexGrid.GetHexAtOffset(offsetCoord));
}
// { // {
// var chestAsset = (Chest)_chestScene.Instance(); // var chestAsset = (Chest)_chestScene.Instance();
// var assetTransform = Transform.Identity; // var assetTransform = Transform.Identity;
@ -291,35 +258,52 @@ public class World : Spatial {
// Entities.AddChild(chestAsset); // Entities.AddChild(chestAsset);
// MarkCellUnwalkable(cell); // MarkCellUnwalkable(cell);
// } // }
} }
// else if (IsColorWater(colorValue)) // else if (IsColorWater(colorValue))
// { // {
// MarkCellUnwalkable(cell); // MarkCellUnwalkable(cell);
// } // }
}
} }
tileTypeImage.Unlock();
} }
public Vector2 WorldToOffsetCoords(Vector3 fromPositionWorld) { public Vector2 WorldToOffsetCoords(Vector3 fromPositionWorld)
{
return HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)).OffsetCoords; return HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)).OffsetCoords;
} }
public Vector3 GetHexCenterFromOffset(Vector2 fromPositionOffset) { public Vector3 GetHexCenterFromOffset(Vector2 fromPositionOffset)
{
return HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset); return HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset);
} }
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord) { public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
if (State != GenerationState.Done && State != GenerationState.Empty) { {
if (State != GenerationState.Done)
{
GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!"); GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!");
return; return;
} }
GD.Print("Update Chunks: " + FrameCounter);
// mark all chunks as retired // mark all chunks as retired
Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks); Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks);
// set new center chunk // set new center chunk
CenterChunkIndex = GetChunkTupleFromPlaneCoord(planeCoord); CenterChunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
WorldChunk currentChunk = GetOrCreateWorldChunk(CenterChunkIndex,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_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 // load or create adjacent chunks
_activeChunkIndices = new List<Vector2>(); _activeChunkIndices = new List<Vector2>();
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, -1)); _activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, -1));
@ -335,57 +319,50 @@ public class World : Spatial {
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1)); _activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1));
// clear unused chunks // clear unused chunks
_deactivatedWorldChunks.Clear(); _unusedWorldChunks.Clear();
_addedChunkIndices.Clear(); _addedChunkIndices.Clear();
foreach (Vector2 oldChunkIndex in oldCachedChunks.Keys) { foreach (Vector2 oldChunkIndex in oldCachedChunks.Keys)
if (!_activeChunkIndices.Contains(oldChunkIndex)) { if (!_activeChunkIndices.Contains(oldChunkIndex))
DeactivateChunk(oldCachedChunks[oldChunkIndex]); DeactivateChunk(oldCachedChunks[oldChunkIndex]);
}
}
foreach (Vector2 activeChunkIndex in _activeChunkIndices) { foreach (Vector2 activeChunkIndex in _activeChunkIndices)
{
WorldChunk chunk = GetOrCreateWorldChunk(activeChunkIndex, WorldChunk chunk = GetOrCreateWorldChunk(activeChunkIndex,
new Color(GD.Randf(), GD.Randf(), GD.Randf())); new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_cachedWorldChunks[activeChunkIndex] = chunk;
} }
Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns); Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns);
foreach (Vector2 chunkKey in _activeChunkIndices) { foreach (Vector2 chunkKey in _activeChunkIndices)
if (!oldCachedChunks.ContainsKey(chunkKey)) { if (!oldCachedChunks.ContainsKey(chunkKey))
{
ActivateChunk(_cachedWorldChunks[chunkKey], chunkKey); ActivateChunk(_cachedWorldChunks[chunkKey], chunkKey);
State = GenerationState.Heightmap; State = GenerationState.Heightmap;
} }
}
WorldChunk currentChunk = GetOrCreateWorldChunk(CenterChunkIndex,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_centerChunkRect = new Rect2(
new Vector2(currentChunk.Transform.origin.x, currentChunk.Transform.origin.z)
+ currentChunk.PlaneRect.Position,
currentChunk.PlaneRect.Size);
UpdateChunkBounds();
UpdateNavigationBounds();
} }
private void ActivateChunk(WorldChunk chunk, Vector2 chunkIndex) { private void ActivateChunk(WorldChunk chunk, Vector2 chunkIndex)
{
chunk.SetChunkIndex(chunkIndex, HexGrid); chunk.SetChunkIndex(chunkIndex, HexGrid);
chunk.UpdateTileTransforms(); chunk.UpdateTileTransforms();
_addedChunkIndices.Add(chunk.ChunkIndex); _addedChunkIndices.Add(chunk.ChunkIndex);
GD.Print("Generating noise for chunk " + chunk.ChunkIndex);
GenerateChunkNoiseMap(chunk); GenerateChunkNoiseMap(chunk);
} }
private void DeactivateChunk(WorldChunk chunk) { private void DeactivateChunk(WorldChunk chunk)
{
GD.Print("Clearing chunk index: " + chunk.ChunkIndex);
_cachedWorldChunks.Remove(chunk.ChunkIndex); _cachedWorldChunks.Remove(chunk.ChunkIndex);
chunk.ClearContent(); chunk.ClearContent();
_deactivatedWorldChunks.Add(chunk); _unusedWorldChunks.Add(chunk);
} }
private void GenerateChunkNoiseMap(WorldChunk chunk) { private void GenerateChunkNoiseMap(WorldChunk chunk)
{
Vector2 chunkIndex = chunk.ChunkIndex; Vector2 chunkIndex = chunk.ChunkIndex;
ImageTexture noiseImageTexture = new(); ImageTexture noiseImageTexture = new();
@ -395,38 +372,37 @@ public class World : Spatial {
chunk.SetNoisemap(noiseImageTexture); chunk.SetNoisemap(noiseImageTexture);
} }
private void RemoveChunk(Vector2 cachedChunkKey) { private void RemoveChunk(Vector2 cachedChunkKey)
{
_cachedWorldChunks.Remove(cachedChunkKey); _cachedWorldChunks.Remove(cachedChunkKey);
_removedChunkIndices.Add(cachedChunkKey); _removedChunkIndices.Add(cachedChunkKey);
foreach (WorldChunk chunk in Chunks.GetChildren()) { foreach (WorldChunk chunk in Chunks.GetChildren())
if (chunk.ChunkIndex == new Vector2(cachedChunkKey.x, cachedChunkKey.y)) { if (chunk.ChunkIndex == new Vector2(cachedChunkKey.x, cachedChunkKey.y))
chunk.QueueFree(); chunk.QueueFree();
}
}
} }
private Vector2 GetChunkTupleFromPlaneCoord(Vector2 planeCoord) { private Vector2 GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
HexCell hexCell = HexGrid.GetHexAt(planeCoord); {
return GetChunkIndexFromOffsetCoord(hexCell.OffsetCoords); HexCell centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
return (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
} }
private Vector2 GetChunkIndexFromOffsetCoord(Vector2 offsetCoord) { public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
return (offsetCoord / ChunkSize).Floor(); {
} if (!_centerChunkRect.HasPoint(centerPlaneCoord))
{
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord) {
if (State != GenerationState.Done) {
return;
}
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) {
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord); UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
UpdateChunkBounds();
UpdateNavigationBounds();
} }
} }
private void UpdateWorldViewTexture() { private void UpdateWorldViewTexture()
{
int worldChunkSize = ChunkSize; int worldChunkSize = ChunkSize;
int numWorldChunkRows = NumChunkRows; int numWorldChunkRows = NumChunkRows;
int numWorldChunkColumns = NumChunkColumns; int numWorldChunkColumns = NumChunkColumns;
@ -436,7 +412,8 @@ public class World : Spatial {
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false, _tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
Image.Format.Rgba8); Image.Format.Rgba8);
foreach (Vector2 chunkIndex in _activeChunkIndices) { foreach (Vector2 chunkIndex in _activeChunkIndices)
{
WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White); WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White);
_heightmapImage.BlendRect( _heightmapImage.BlendRect(
@ -454,7 +431,7 @@ public class World : Spatial {
_heightmapTexture.CreateFromImage(_heightmapImage); _heightmapTexture.CreateFromImage(_heightmapImage);
_viewTileTypeTexture = new ImageTexture(); _viewTileTypeTexture = new ImageTexture();
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage, (uint)Texture.FlagsEnum.ConvertToLinear); _viewTileTypeTexture.CreateFromImage(_tileTypeMapImage);
WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize; WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize;
@ -462,316 +439,113 @@ public class World : Spatial {
EmitSignal("OnHeightmapImageChanged", _heightmapImage); EmitSignal("OnHeightmapImageChanged", _heightmapImage);
} }
private void UpdateChunkBounds() { private void UpdateChunkBounds()
{
_chunkIndexSouthWest = Vector2.Inf; _chunkIndexSouthWest = Vector2.Inf;
_chunkIndexNorthEast = -Vector2.Inf; _chunkIndexNorthEast = -Vector2.Inf;
foreach (Vector2 chunkIndex in _activeChunkIndices) { foreach (Vector2 chunkIndex in _activeChunkIndices)
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y) { {
WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White);
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y)
_chunkIndexSouthWest = chunkIndex; _chunkIndexSouthWest = chunkIndex;
} else if (chunkIndex.x >= _chunkIndexNorthEast.x && chunkIndex.y >= _chunkIndexNorthEast.y) { else if (chunkIndex.x >= _chunkIndexNorthEast.x && chunkIndex.y >= _chunkIndexNorthEast.y)
_chunkIndexNorthEast = chunkIndex; _chunkIndexNorthEast = chunkIndex;
}
} }
} }
private void UpdateNavigationBounds() { private void UpdateNavigationBounds()
{
HexCell cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize); HexCell 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.
HexCell cellNorthEast =
HexGrid.GetHexAtOffset(_chunkIndexNorthEast * ChunkSize + Vector2.One * (ChunkSize - 1));
HexCell centerCell =
HexGrid.GetHexAtOffset(((cellNorthEast.OffsetCoords - cellSouthWest.OffsetCoords) / 2).Round());
int numCells = ChunkSize * Math.Max(NumChunkColumns, NumChunkRows);
HexGrid.SetBoundsOffset(cellSouthWest, ChunkSize * new Vector2(NumChunkColumns, NumChunkRows)); HexGrid.SetBoundsOffset(cellSouthWest, ChunkSize * new Vector2(NumChunkColumns, NumChunkRows));
} }
public void MarkCellUnwalkable(HexCell cell) { public override void _Process(float delta)
HexGrid.AddObstacle(cell); {
}
public float GetHexCost(Entity entity, HexCell cell) {
float nextHexCost = HexGrid.GetHexCost(cell);
if (nextHexCost != 0) {
Vector2 nextOffset = cell.OffsetCoords;
Color tileTypeColor = GetTileTypeColorAtOffset(nextOffset);
if (_tileTypeInfos.TryGetValue(tileTypeColor.ToRgba32(), out HexTile3D.TileTypeInfo info)) {
int tileTypeMask = info.TileTypeMask;
if ((entity.EntityMask ^ tileTypeMask) != 0) {
nextHexCost = 0;
}
} else {
GD.Print("Could not find tile type info for color " + tileTypeColor);
}
}
return nextHexCost;
}
private HexTile3D.TileTypeInfo GetTileTypeInfoAtOffset(Vector2 offsetCoord) {
Color tileTypeColor = GetTileTypeColorAtOffset(offsetCoord);
if (_tileTypeInfos.TryGetValue(tileTypeColor.ToRgba32(), out HexTile3D.TileTypeInfo info)) {
return info;
}
return _tileTypeInfos[0];
}
public Color GetTileTypeColorAtOffset(Vector2 offsetCoord) {
Vector2 chunkIndex = GetChunkIndexFromOffsetCoord(offsetCoord);
if (!_cachedWorldChunks.ContainsKey(chunkIndex)) {
return Colors.Black;
}
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
Vector2 textureCoordinate = offsetCoord - Vector2.One * ChunkSize * chunkIndex;
Color tileTypeColor = chunk.TileTypeImage.GetPixel((int)textureCoordinate.x, (int)textureCoordinate.y);
return tileTypeColor;
}
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; GenerationState oldState = State;
UpdateGenerationState(); UpdateGenerationState();
if (oldState != GenerationState.Done && State == GenerationState.Done) { if (oldState != GenerationState.Done && State == GenerationState.Done)
UpdateWorldViewTexture(); UpdateWorldViewTexture();
}
while (_removedSpatialNodes.Count > 0) {
GD.Print("Queueing deletion of " + _removedSpatialNodes[0]);
_removedSpatialNodes[0].QueueFree();
_removedSpatialNodes.RemoveAt(0);
}
} }
private void UpdateGenerationState() { private void UpdateGenerationState()
if (State == GenerationState.Heightmap) { {
FrameCounter++;
if (State == GenerationState.Heightmap)
{
int numChunksGeneratingHeightmap = 0; int numChunksGeneratingHeightmap = 0;
foreach (Vector2 chunkIndex in _addedChunkIndices) { foreach (Vector2 chunkIndex in _addedChunkIndices)
{
WorldChunk chunk = _cachedWorldChunks[chunkIndex]; WorldChunk chunk = _cachedWorldChunks[chunkIndex];
if (chunk.HeightMapFrameCount > 0) { if (chunk.HeightMapFrameCount > 0) numChunksGeneratingHeightmap++;
numChunksGeneratingHeightmap++;
}
} }
if (numChunksGeneratingHeightmap == 0) { if (numChunksGeneratingHeightmap == 0)
{
// assign height map images // assign height map images
foreach (Vector2 chunkIndex in _addedChunkIndices) { foreach (Vector2 chunkIndex in _addedChunkIndices)
{
WorldChunk chunk = _cachedWorldChunks[chunkIndex]; WorldChunk chunk = _cachedWorldChunks[chunkIndex];
chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture()); chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture());
} }
GD.Print("Switching to TileType Generation: " + FrameCounter);
State = GenerationState.TileType; State = GenerationState.TileType;
} }
} else if (State == GenerationState.TileType) { }
else if (State == GenerationState.TileType)
{
int numChunksGeneratingTileType = 0; int numChunksGeneratingTileType = 0;
foreach (Vector2 chunkIndex in _addedChunkIndices) { foreach (Vector2 chunkIndex in _addedChunkIndices)
{
WorldChunk chunk = _cachedWorldChunks[chunkIndex]; WorldChunk chunk = _cachedWorldChunks[chunkIndex];
if (chunk.TileTypeMapFrameCount > 0) { if (chunk.TileTypeMapFrameCount > 0) numChunksGeneratingTileType++;
numChunksGeneratingTileType++;
}
} }
if (numChunksGeneratingTileType == 0) { if (numChunksGeneratingTileType == 0)
{
GD.Print("Switching to Object Generation: " + FrameCounter);
State = GenerationState.Objects; State = GenerationState.Objects;
} }
} else if (State == GenerationState.Objects) { }
else if (State == GenerationState.Objects)
{
// generate objects // generate objects
foreach (Vector2 chunkIndex in _addedChunkIndices) { foreach (Vector2 chunkIndex in _addedChunkIndices)
PopulateChunk(_cachedWorldChunks[chunkIndex]); PopulateChunk(_cachedWorldChunks[chunkIndex]);
}
_addedChunkIndices.Clear(); _addedChunkIndices.Clear();
GD.Print("Generation done: " + FrameCounter);
State = GenerationState.Done; State = GenerationState.Done;
} }
} }
private void OnEntityClicked(Entity entity) { private void OnEntityClicked(Entity entity)
{
EmitSignal("EntityClicked", entity); EmitSignal("EntityClicked", entity);
} }
public void OnTileClicked(HexTile3D tile) { public void OnTileClicked(HexTile3D tile)
if (State != GenerationState.Done) { {
return;
}
EmitSignal("TileClicked", tile); EmitSignal("TileClicked", tile);
} }
public void OnTileHovered(HexTile3D tile) { public void OnTileHovered(HexTile3D tile)
if (State != GenerationState.Done) { {
return;
}
EmitSignal("TileHovered", tile); EmitSignal("TileHovered", tile);
HexTile3D.TileTypeInfo tileTypeInfo = GetTileTypeInfoAtOffset(tile.OffsetCoords);
}
public void OnBlockingSpatialRemoved(Spatial spatialNode) {
if (spatialNode.IsQueuedForDeletion()) {
return;
}
HexGrid.RemoveObstacle(HexGrid.GetHexAt(new Vector2(spatialNode.GlobalTranslation.x,
spatialNode.GlobalTranslation.z)));
_removedSpatialNodes.Add(spatialNode);
} }
} }

View File

@ -1,50 +0,0 @@
[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

@ -3,7 +3,8 @@ using System.Linq;
using Godot; using Godot;
using Godot.Collections; using Godot.Collections;
public class WorldChunk : Spatial { public class WorldChunk : Spatial
{
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn"); private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
private MultiMeshInstance _multiMeshInstance; private MultiMeshInstance _multiMeshInstance;
private readonly Array<int> _tileInstanceIndices = new(); private readonly Array<int> _tileInstanceIndices = new();
@ -27,11 +28,10 @@ public class WorldChunk : Spatial {
[Export] public Texture HeightMap; [Export] public Texture HeightMap;
public int HeightMapFrameCount; public int HeightMapFrameCount;
public Image TileTypeImage;
public Viewport HeightmapOffscreenViewport; public Viewport HeightmapOffscreenViewport;
[Export] public Texture NavigationMap; [Export] public Texture NavigationMap;
public bool NoiseTextureCheckerboardOverlay = false; public bool NoiseTextureCheckerboardOverlay = true;
// signals // signals
[Signal] [Signal]
@ -47,7 +47,7 @@ public class WorldChunk : Spatial {
// scene nodes // scene nodes
private MeshInstance PlaneRectMesh; private MeshInstance PlaneRectMesh;
public int Size = 32; [Export] public int Size = 32;
// resources // resources
@ -56,24 +56,32 @@ public class WorldChunk : Spatial {
public int TileTypeMapFrameCount; public int TileTypeMapFrameCount;
public Viewport TileTypeOffscreenViewport; public Viewport TileTypeOffscreenViewport;
public WorldChunk()
{
}
public WorldChunk(int size)
{
SetSize(size);
}
[Export] [Export]
public bool ShowTextureOverlay { public bool ShowTextureOverlay
{
get => _showTextureOverlay; get => _showTextureOverlay;
set { set
if (PlaneRectMesh != null) { {
PlaneRectMesh.Visible = value; if (PlaneRectMesh != null) PlaneRectMesh.Visible = value;
}
} }
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() { public override void _Ready()
{
PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh"); PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh");
Debug.Assert(PlaneRectMesh != null); Debug.Assert(PlaneRectMesh != null);
if (PlaneRectMesh.Visible) { if (PlaneRectMesh.Visible) _showTextureOverlay = true;
_showTextureOverlay = true;
}
Transform planeRectTransform = Transform.Identity; Transform planeRectTransform = Transform.Identity;
planeRectTransform = planeRectTransform =
@ -104,12 +112,16 @@ public class WorldChunk : Spatial {
Tiles = (Spatial)FindNode("Tiles"); Tiles = (Spatial)FindNode("Tiles");
Debug.Assert(Tiles != null); Debug.Assert(Tiles != null);
SetSize(World.ChunkSize);
} }
public void SetSize(int size) { public void SetSize(int size)
{
Size = size; Size = size;
if (TileTypeOffscreenViewport != null) { if (TileTypeOffscreenViewport != null)
{
TileTypeOffscreenViewport.Size = Vector2.One * size; TileTypeOffscreenViewport.Size = Vector2.One * size;
HeightmapOffscreenViewport.Size = Vector2.One * size; HeightmapOffscreenViewport.Size = Vector2.One * size;
_noiseMask.Transform = Transform2D.Identity.Scaled(Vector2.One * size / _noiseMask.Texture.GetSize().x); _noiseMask.Transform = Transform2D.Identity.Scaled(Vector2.One * size / _noiseMask.Texture.GetSize().x);
@ -119,9 +131,10 @@ public class WorldChunk : Spatial {
} }
} }
public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid) { public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid)
{
ChunkIndex = chunkIndex; ChunkIndex = chunkIndex;
float chunkSize = Size; float chunkSize = World.ChunkSize;
Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize); Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize);
@ -135,60 +148,62 @@ public class WorldChunk : Spatial {
new Vector2(localPlaneCoordSouthWest.x, localPlaneCoordNorthEast.y), new Vector2(localPlaneCoordSouthWest.x, localPlaneCoordNorthEast.y),
new Vector2(localPlaneCoordNorthEast.x - localPlaneCoordSouthWest.x, new Vector2(localPlaneCoordNorthEast.x - localPlaneCoordSouthWest.x,
localPlaneCoordSouthWest.y - localPlaneCoordNorthEast.y) localPlaneCoordSouthWest.y - localPlaneCoordNorthEast.y)
); );
} }
public void InitializeTileInstances(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance, public void InitializeTileInstances(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance,
int tileInstanceIndexStart) { int tileInstanceIndexStart)
{
_multiMeshInstance = multiMeshInstance; _multiMeshInstance = multiMeshInstance;
_tileInstanceIndices.Clear(); _tileInstanceIndices.Clear();
int chunkSize = Size; int chunkSize = World.ChunkSize;
foreach (Spatial node in Tiles.GetChildren()) { foreach (Spatial node in Tiles.GetChildren())
node.QueueFree(); node.QueueFree();
}
foreach (int i in Enumerable.Range(0, chunkSize)) { foreach (int i in Enumerable.Range(0, chunkSize))
foreach (int j in Enumerable.Range(0, chunkSize)) { foreach (int j in Enumerable.Range(0, chunkSize))
HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance(); {
tile3D.Connect("TileClicked", this, nameof(OnTileClicked)); HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance();
tile3D.Connect("TileHovered", this, nameof(OnTileHovered)); tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * chunkSize + new Vector2(i, j)); tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * World.ChunkSize + new Vector2(i, j));
_tileInstanceIndices.Add(tileInstanceIndexStart + _tileInstanceIndices.Count); _tileInstanceIndices.Add(tileInstanceIndexStart + _tileInstanceIndices.Count);
Transform tileTransform = Transform.Identity; Transform tileTransform = Transform.Identity;
Vector2 centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j)); Vector2 centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y); tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
tile3D.Transform = tileTransform; tile3D.Transform = tileTransform;
Tiles.AddChild(tile3D); Tiles.AddChild(tile3D);
}
} }
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount; _multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
GD.Print("Chunk: " + chunkIndex + " Last index: " + _tileInstanceIndices.Last());
} }
public void ClearContent() { public void ClearContent()
foreach (Spatial child in Entities.GetChildren()) { {
foreach (Spatial child in Entities.GetChildren())
child.QueueFree(); child.QueueFree();
}
} }
public void UpdateTileTransforms() { public void UpdateTileTransforms()
{
Transform chunkTransform = Transform.Identity; Transform chunkTransform = Transform.Identity;
Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * Size); Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * World.ChunkSize);
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y); chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
Transform = chunkTransform; Transform = chunkTransform;
Basis tileOrientation = new(Vector3.Up, 90f * Mathf.Pi / 180f); Basis tileOrientation = new(Vector3.Up, 90f * Mathf.Pi / 180f);
foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count)) { GD.Print("Updating transforms for instances of chunk " + ChunkIndex + " origin: " + chunkTransform.origin);
int column = i % Size;
int row = i / Size; foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count))
{
int column = i % World.ChunkSize;
int row = i / World.ChunkSize;
Vector2 tilePlaneCoord = Vector2 tilePlaneCoord =
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row)); _hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
@ -196,17 +211,17 @@ public class WorldChunk : Spatial {
Transform hexTransform = new(tileOrientation, Transform hexTransform = new(tileOrientation,
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y)); chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
if (_showHexTiles) { if (_showHexTiles)
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f), hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
hexTransform.origin); hexTransform.origin);
}
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform); _multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
} }
} }
// other members // other members
public void SaveToFile(string chunkName) { public void SaveToFile(string chunkName)
{
Image image = new(); Image image = new();
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData()); image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData());
@ -219,10 +234,13 @@ public class WorldChunk : Spatial {
image.SavePng(chunkName + "_heightMap.png"); 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.Texture = texture;
_noiseSprite.Transform = _noiseSprite.Transform =
Transform2D.Identity.Scaled(HeightmapOffscreenViewport.Size / _noiseSprite.Texture.GetSize().x); Transform2D.Identity.Scaled(HeightmapOffscreenViewport.Size / _noiseSprite.Texture.GetSize().x);
@ -230,7 +248,8 @@ public class WorldChunk : Spatial {
HeightMapFrameCount = 1; HeightMapFrameCount = 1;
} }
public void SetHeightmap(Texture texture) { public void SetHeightmap(Texture texture)
{
_heightmapSprite.Texture = texture; _heightmapSprite.Texture = texture;
_heightmapSprite.Transform = _heightmapSprite.Transform =
Transform2D.Identity.Scaled(TileTypeOffscreenViewport.Size / _heightmapSprite.Texture.GetSize()); Transform2D.Identity.Scaled(TileTypeOffscreenViewport.Size / _heightmapSprite.Texture.GetSize());
@ -239,29 +258,25 @@ public class WorldChunk : Spatial {
TileTypeMapFrameCount = 1; TileTypeMapFrameCount = 1;
} }
public void CreateUnlockedTileTypeImage() { public override void _Process(float delta)
TileTypeImage = TileTypeOffscreenViewport.GetTexture().GetData(); {
TileTypeImage.Lock();
}
public override void _Process(float delta) {
Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture(); Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture();
if (NoiseTextureCheckerboardOverlay) { if (NoiseTextureCheckerboardOverlay)
{
Image tileTypeImage = tileTypeTexture.GetData(); Image tileTypeImage = tileTypeTexture.GetData();
tileTypeImage.Lock(); tileTypeImage.Lock();
foreach (int i in Enumerable.Range(0, Size)) { foreach (int i in Enumerable.Range(0, Size))
foreach (int j in Enumerable.Range(0, Size)) { foreach (int j in Enumerable.Range(0, Size))
Vector2 textureCoord = new(i, j); {
Color baseColor = tileTypeImage.GetPixelv(textureCoord); Vector2 textureCoord = new(i, j);
Color baseColor = tileTypeImage.GetPixelv(textureCoord);
if ((i + j) % 2 == 0) { if ((i + j) % 2 == 0)
tileTypeImage.SetPixelv(textureCoord, baseColor); tileTypeImage.SetPixelv(textureCoord, baseColor);
} else { else
tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f); tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
}
}
} }
tileTypeImage.Unlock(); tileTypeImage.Unlock();
@ -280,26 +295,24 @@ public class WorldChunk : Spatial {
//RectMaterial.Uv1Triplanar = true; //RectMaterial.Uv1Triplanar = true;
PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial); PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial);
if (HeightMapFrameCount == 0) { if (HeightMapFrameCount == 0) HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0; HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0;
if (TileTypeMapFrameCount == 0) { if (TileTypeMapFrameCount == 0) TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
TileTypeMapFrameCount = TileTypeMapFrameCount > 0 ? TileTypeMapFrameCount - 1 : 0; TileTypeMapFrameCount = TileTypeMapFrameCount > 0 ? TileTypeMapFrameCount - 1 : 0;
PlaneRectMesh.MaterialOverride = null; PlaneRectMesh.MaterialOverride = null;
} }
public void OnTileClicked(HexTile3D tile) { public void OnTileClicked(HexTile3D tile)
{
EmitSignal("TileClicked", tile); EmitSignal("TileClicked", tile);
} }
public void OnTileHovered(HexTile3D tile) { public void OnTileHovered(HexTile3D tile)
{
EmitSignal("TileHovered", tile); EmitSignal("TileHovered", tile);
} }
} }

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=10 format=2] [gd_scene load_steps=10 format=2]
[ext_resource path="res://scenes/WorldChunk.cs" type="Script" id=1] [ext_resource path="res://scenes/WorldChunk.cs" type="Script" id=1]
[ext_resource path="res://materials/WorldTileTypeMaterial.tres" type="Material" id=2] [ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=2]
[ext_resource path="res://assets/TestHeightmap.tres" type="Texture" id=3] [ext_resource path="res://assets/TestHeightmap.tres" type="Texture" id=3]
[ext_resource path="res://assets/4x4checkerColor.png" type="Texture" id=4] [ext_resource path="res://assets/4x4checkerColor.png" type="Texture" id=4]
[ext_resource path="res://addons/gdhexgrid/icon.png" type="Texture" id=6] [ext_resource path="res://addons/gdhexgrid/icon.png" type="Texture" id=6]

View File

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

View File

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

View File

@ -1,8 +1,10 @@
using System.Diagnostics;
using Godot; using Godot;
using System;
using System.Diagnostics;
using Godot.Collections; using Godot.Collections;
public class NavigationTests : Spatial { public class NavigationTests : Spatial
{
private HexGrid _hexGrid; private HexGrid _hexGrid;
private HexCell _currentTile; private HexCell _currentTile;
private HexCell _lastTile; private HexCell _lastTile;
@ -11,11 +13,13 @@ public class NavigationTests : Spatial {
private ShaderMaterial _tileMaterial; private ShaderMaterial _tileMaterial;
private EditorUI _editorUi; private EditorUI _editorUi;
private World _world; private TileWorld _tileWorld;
private StreamContainer _streamContainer;
private Player _player; private Player _player;
private NavigationComponent _playerNavigationComponent; private NavigationComponent _playerNavigationComponent;
public override void _Ready() { public override void _Ready()
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
_currentTile = new HexCell(); _currentTile = new HexCell();
_lastTile = new HexCell(); _lastTile = new HexCell();
@ -25,32 +29,37 @@ public class NavigationTests : Spatial {
_mouseHighlight = GetNode<Spatial>("MouseHighlight"); _mouseHighlight = GetNode<Spatial>("MouseHighlight");
_editorUi = GetNode<EditorUI>("EditorUI"); _editorUi = GetNode<EditorUI>("EditorUI");
_world = GetNode<World>("World"); _tileWorld = GetNode<TileWorld>("TileWorld");
_tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated));
_streamContainer = GetNode<StreamContainer>("StreamContainer");
_streamContainer.SetCenterTile(_currentTile);
_player = GetNode<Player>("Player"); _player = GetNode<Player>("Player");
_playerNavigationComponent = _player.GetNode<NavigationComponent>("Navigation"); _playerNavigationComponent = _player.GetNode<NavigationComponent>("Navigation");
// connect signals // input handling
_world.Connect("TileClicked", this, nameof(OnTileClicked)); // _groundLayer.Connect("input_event", this, nameof(OnGroundLayerInputEvent));
_world.Connect("TileHovered", this, nameof(OnTileHovered)); _streamContainer.Connect("TileClicked", this, nameof(OnTileClicked));
_world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged)); _streamContainer.Connect("TileHovered", this, nameof(OnTileHovered));
_world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged));
CorrectEntityGridPositions(); CorrectEntityGridPositions();
} }
public void CorrectEntityGridPositions() { public void CorrectEntityGridPositions()
{
Spatial entitiesNode = GetNode<Spatial>("Entities"); Spatial entitiesNode = GetNode<Spatial>("Entities");
if (entitiesNode == null) { if (entitiesNode == null)
{
return; return;
} }
Array entities = entitiesNode.GetChildren(); var entities = entitiesNode.GetChildren();
foreach (Spatial entity in entities) { foreach (Spatial entity in entities)
Vector2 entityPlaneCoords = new(entity.GlobalTranslation.x, entity.GlobalTranslation.z); {
HexCell entityCell = _world.HexGrid.GetHexAt(entityPlaneCoords); Vector2 entityPlaneCoords = new Vector2(entity.GlobalTranslation.x, entity.GlobalTranslation.z);
_world.MarkCellUnwalkable(entityCell); HexCell entityCell = _tileWorld.HexGrid.GetHexAt(entityPlaneCoords);
_tileWorld.MarkCellUnwalkable(entityCell);
Vector2 cellPlaneCoords = _hexGrid.GetHexCenterFromOffset(entityCell.OffsetCoords); Vector2 cellPlaneCoords = _hexGrid.GetHexCenterFromOffset(entityCell.OffsetCoords);
Vector3 entityGlobalTranslation = entity.GlobalTranslation; Vector3 entityGlobalTranslation = entity.GlobalTranslation;
entityGlobalTranslation.x = cellPlaneCoords.x; entityGlobalTranslation.x = cellPlaneCoords.x;
@ -59,26 +68,32 @@ public class NavigationTests : Spatial {
} }
} }
private void OnHeightmapImageChanged(Image heightmapImage) { public void OnWorldGenerated()
ImageTexture newHeightmapTexture = new(); {
newHeightmapTexture.CreateFromImage(heightmapImage, _streamContainer.OnWorldGenerated();
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
}
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) { // Properly place the Player
ImageTexture newWorldTexture = new(); Vector2 centerTileCoord = (Vector2.One * _tileWorld.Size / 2).Round();
newWorldTexture.CreateFromImage(viewTileTypeImage, Vector3 worldCenterTileCoords = _tileWorld.GetTileWorldCenterFromOffset(centerTileCoord);
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); worldCenterTileCoords.y = _tileWorld.GetHeightAtOffset(centerTileCoord);
Transform playerTransform = Transform.Identity;
playerTransform.origin = worldCenterTileCoords;
_player.Transform = playerTransform;
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)newWorldTexture.GetSize().x); _tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
_tileMaterial.SetShaderParam("CoordinateOffsetU", (int)_world.WorldTextureCoordinateOffset.x);
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y); CorrectEntityGridPositions();
} }
public void UpdateCurrentTile(HexCell tile) { public void UpdateCurrentTile(HexCell tile)
if (_currentTile.AxialCoords == tile.AxialCoords) { {
if (_currentTile.AxialCoords == tile.AxialCoords)
{
return; return;
} }
@ -87,29 +102,41 @@ public class NavigationTests : Spatial {
GD.Print("Current tile: " + _currentTile.OffsetCoords); GD.Print("Current tile: " + _currentTile.OffsetCoords);
if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null) { if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null)
{
_editorUi.currentTileOffset = _currentTile.OffsetCoords; _editorUi.currentTileOffset = _currentTile.OffsetCoords;
} }
Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords); Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords);
Transform tileTransform = Transform.Identity;
tileTransform.origin.x = planeCoords.x;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(_currentTile.OffsetCoords) + 0.1f;
tileTransform.origin.z = planeCoords.y;
_mouseHighlight.Transform = tileTransform;
} }
public void OnGroundLayerInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnGroundLayerInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) { int shapeIndex)
{
UpdateCurrentTile(_hexGrid.GetHexAt(new Vector2(position.x, position.z))); UpdateCurrentTile(_hexGrid.GetHexAt(new Vector2(position.x, position.z)));
} }
public void OnTileClicked(HexTile3D tile) { public void OnTileClicked(HexTile3D tile)
if (_editorUi != null) { {
if (_editorUi != null)
{
_editorUi.OnTileClicked(tile.OffsetCoords); _editorUi.OnTileClicked(tile.OffsetCoords);
if (_editorUi.CurrentInputMode == EditorUI.InputMode.Navigate) { if (_editorUi.CurrentInputMode == EditorUI.InputMode.Navigate)
{
_playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
} }
} }
public void OnTileHovered(HexTile3D tile) { public void OnTileHovered(HexTile3D tile)
{
UpdateCurrentTile(tile.Cell); UpdateCurrentTile(tile.Cell);
Debug.Assert(_playerNavigationComponent != null); Debug.Assert(_playerNavigationComponent != null);

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=26 format=2] [gd_scene load_steps=25 format=2]
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=1] [ext_resource path="res://entities/Player.tscn" type="PackedScene" id=1]
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=2] [ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=2]
@ -11,7 +11,6 @@
[ext_resource path="res://scenes/HexTile3DPatch.tscn" type="PackedScene" id=9] [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/rockB.tscn" type="PackedScene" id=10]
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11] [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] [sub_resource type="AnimationNodeStateMachinePlayback" id=8]
@ -290,21 +289,18 @@ script = ExtResource( 4 )
[node name="Player" parent="." instance=ExtResource( 1 )] [node name="Player" parent="." instance=ExtResource( 1 )]
collision_mask = 1 collision_mask = 1
WorldNode = NodePath("../World") TileWorldNode = NodePath("../TileWorld")
[node name="WorldInfo" parent="Player" index="2"] [node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
WorldPath = NodePath("../../World") 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"] [node name="AnimationTree" parent="Player/Geometry" index="2"]
parameters/playback = SubResource( 8 ) parameters/playback = SubResource( 8 )
[node name="TileWorld" parent="." instance=ExtResource( 2 )] [node name="TileWorld" parent="." instance=ExtResource( 2 )]
DebugMap = true
GenerationMapType = 2 GenerationMapType = 2
Size = 20 Size = 20
DebugMap = true
[node name="MouseHighlight" parent="." instance=ExtResource( 3 )] [node name="MouseHighlight" parent="." instance=ExtResource( 3 )]
@ -378,10 +374,8 @@ anims/TreeShake = SubResource( 17 )
[node name="Geometry" parent="Entities/Tree5" index="2"] [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 ) 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"]
[editable path="Player/Geometry/PirateAsset"] [editable path="Player/Geometry"]
[editable path="TileWorld"] [editable path="TileWorld"]
[editable path="StreamContainer"] [editable path="StreamContainer"]
[editable path="Entities/Chest"] [editable path="Entities/Chest"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +1,21 @@
extends CenterContainer extends VBoxContainer
onready var SeedLineEdit: LineEdit var world_size_label = null
onready var ChunkSizeSpinBox: SpinBox var world_size_slider = null
#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(): func _ready():
SeedLineEdit = find_node("SeedLineEdit") world_size_label = find_node("WorldSizeLabel")
assert(SeedLineEdit) world_size_slider = find_node("WorldSizeSlider");
ChunkSizeSpinBox = find_node("ChunkSizeSpinBox") world_size_slider.value = 4
assert(ChunkSizeSpinBox)
func _on_HSlider_value_changed(value):
world_size_label.text = str(value)
func _on_RefreshSeedButton_pressed(): func _on_ShowTexturesCheckButton_toggled(button_pressed):
var rng = RandomNumberGenerator.new() WorldTextureRect.visible = button_pressed
rng.seed = Time.get_ticks_msec() HeightTextureRect.visible = button_pressed
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))

View File

@ -1,128 +0,0 @@
[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 = 61.0
margin_right = 303.0
margin_bottom = 339.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_top = 218.0
margin_right = 287.0
margin_bottom = 238.0
size_flags_horizontal = 3
text = "Generate"
[node name="Back" type="Button" parent="VBoxContainer"]
margin_top = 258.0
margin_right = 287.0
margin_bottom = 278.0
size_flags_horizontal = 3
text = "Back"
[connection signal="pressed" from="VBoxContainer/GridContainer/RefreshSeedButton" to="." method="_on_RefreshSeedButton_pressed"]
[connection signal="pressed" from="VBoxContainer/GenerateButton" to="." method="_on_GenerateButton_pressed"]
[connection signal="pressed" from="VBoxContainer/Back" to="." method="_on_GenerateButton_pressed"]

View File

@ -1,21 +0,0 @@
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,5 +0,0 @@
[gd_resource type="ButtonGroup" format=2]
[resource]
resource_local_to_scene = false
resource_name = "ActionButtonGroup"

View File

@ -1,11 +0,0 @@
[gd_resource type="Theme" load_steps=3 format=2]
[sub_resource type="DynamicFontData" id=3]
font_path = "res://assets/Fonts/LuckiestGuy/LuckiestGuy-Regular.ttf"
[sub_resource type="DynamicFont" id=2]
size = 18
font_data = SubResource( 3 )
[resource]
default_font = SubResource( 2 )

View File

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

View File

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

View File

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

View File

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