Compare commits

...

21 Commits

Author SHA1 Message Date
Martin Felis 3e251d2843 Intermediate commit 2024-05-07 18:49:04 +02:00
Martin Felis a74145fc57 Moved Action UI 2024-02-28 21:47:40 +01:00
Martin Felis 0a46a5fc10 Added voronoi based biome experiments. 2024-01-22 21:10:00 +01:00
Martin Felis 7bdda54112 Extremely simple building (placement of workbenches). 2024-01-04 16:41:26 +01:00
Martin Felis 60000a8570 Using a more game-y font. 2024-01-04 16:05:28 +01:00
Martin Felis 29153c2d35 Minor project cleanup. 2024-01-04 15:48:05 +01:00
Martin Felis ee859886f0 Added CollectibleComponent and refactored GoldBar, Wood and Axe. 2024-01-04 15:45:26 +01:00
Martin Felis c34c704b38 Stuff changed to References instead of node types. 2024-01-02 17:07:56 +01:00
Martin Felis 4c9f6e5815 Added shrug animation when interaction is not possible. 2024-01-01 17:01:00 +01:00
Martin Felis ce53cdba6a Tree chopping only possible with axe. 2024-01-01 16:07:27 +01:00
Martin Felis b6b11228a5 Added Wood entity, trees drop 1-2 Wood logs when chopped. 2024-01-01 15:56:56 +01:00
Martin Felis c2cce1ae0f Tested navigation on water and rocks in water. 2023-12-29 17:55:43 +01:00
Martin Felis 06b808c10d Fixed tile masks, made path smoothing optional. 2023-12-29 17:43:04 +01:00
Martin Felis a5a94d1b89 Added missing pirate model. 2023-12-29 09:28:25 +01:00
Martin Felis 7d0e0b23bd Added TileTypeInfo to store tile masks. 2023-12-29 09:26:43 +01:00
Martin Felis 883e256101 Renamed world tile coloring shader. 2023-12-28 20:52:58 +01:00
Martin Felis 593c2f556b Fixed wrong tile type texture when generating new level. 2023-12-28 18:14:07 +01:00
Martin Felis 453cfcde00 Fixed build NavigationTests.cs, still defunct though. 2023-12-28 10:25:01 +01:00
Martin Felis cfb731c27e Intermediate commit 2023-12-14 17:27:07 +01:00
Martin Felis bfd5eef2f5 Mass reformatting. 2023-11-18 22:32:57 +01:00
Martin Felis 2109c6b6ec Moved path planning to world class.
- Tree entities now are obstacles in navigation planning.
 - Once chopped obstacles are removed.
2023-11-18 22:11:34 +01:00
92 changed files with 5040 additions and 2398 deletions

24
.gitignore vendored
View File

@ -1,8 +1,28 @@
.import/* # Godot 4+ specific ignores
.mono/* .godot/
# 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,14 +1,12 @@
using Godot; using Godot;
public static class Globals public static class Globals {
{
public const float EpsPosition = 0.01f; public const float EpsPosition = 0.01f;
public const float EpsPositionSquared = EpsPosition * EpsPosition; public const float EpsPositionSquared = EpsPosition * EpsPosition;
public const float EpsRadians = 0.1f * Mathf.Pi / 180f; public const float EpsRadians = 0.1f * Mathf.Pi / 180f;
public const float EpsRadiansSquared = EpsRadians * EpsRadians; public const float EpsRadiansSquared = EpsRadians * EpsRadians;
public static float CalcPlaneAngle(Transform worldTransform) public static float CalcPlaneAngle(Transform worldTransform) {
{
return worldTransform.basis.x.SignedAngleTo(Vector3.Right.Rotated(Vector3.Up, Mathf.Pi * 0.5f), -Vector3.Up); return worldTransform.basis.x.SignedAngleTo(Vector3.Right.Rotated(Vector3.Up, Mathf.Pi * 0.5f), -Vector3.Up);
} }
} }

View File

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

View File

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

View File

@ -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=true flags/repeat=1
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=true flags/repeat=0
flags/filter=false flags/filter=false
flags/mipmaps=true flags/mipmaps=true
flags/anisotropic=false flags/anisotropic=false

View File

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

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,202 @@
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.

Binary file not shown.

View File

@ -2,28 +2,30 @@
importer="texture" importer="texture"
type="StreamTexture" type="StreamTexture"
path="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.stex" path.s3tc="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.s3tc.stex"
path.etc2="res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.etc2.stex"
metadata={ metadata={
"vram_texture": false "imported_formats": [ "s3tc", "etc2" ],
"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.stex" ] dest_files=[ "res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.s3tc.stex", "res://.import/IslandMask2.png-b09deb1a0f8633d5ff352ae53948f6ed.etc2.stex" ]
[params] [params]
compress/mode=0 compress/mode=2
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=0 flags/repeat=true
flags/filter=true flags/filter=true
flags/mipmaps=false flags/mipmaps=true
flags/anisotropic=false flags/anisotropic=false
flags/srgb=2 flags/srgb=1
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
@ -31,5 +33,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=true detect_3d=false
svg/scale=1.0 svg/scale=1.0

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

@ -0,0 +1,6 @@
[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,6 +1,3 @@
using Godot; using Godot;
public class Component : Node public class Component : Node { }
{
}

View File

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

View File

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

View File

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

View File

@ -7,8 +7,9 @@ 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; }
@ -16,69 +17,57 @@ 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> _planningPathSmoothedWorldNavigationPoints = new(); private List<NavigationPoint> _activePathWorldNavigationPoints = 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(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint) public void PlanSmoothedPath(Entity 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(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) public void FindPath(Entity entity, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
{ HexCell fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
var fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)); if (World.HexGrid.GetHexCost(fromCell) == 0) {
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));
_planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints; _activePathWorldNavigationPoints = _planningPathWorldNavigationPoints;
return; return;
} }
var toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z)); HexCell toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
toCell = World.HexGrid.GetClosestWalkableCell(fromCell, toCell); 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));
_planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints; _activePathWorldNavigationPoints = _planningPathWorldNavigationPoints;
return; return;
} }
var path = World.HexGrid.FindPath(fromCell, toCell); List<HexCell> path = World.FindPath(entity, fromCell, toCell);
// Generate grid navigation points // Generate grid navigation points
_planningPathWorldNavigationPoints = new List<NavigationPoint>(); _planningPathWorldNavigationPoints = new List<NavigationPoint>();
foreach (var index in Enumerable.Range(0, path.Count)) foreach (int 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)));
} }
@ -86,40 +75,44 @@ 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
_planningPathSmoothedWorldNavigationPoints = SmoothPath(body, _planningPathWorldNavigationPoints); if (PathSmoothing)
// Ensure starting point is the current position
if (_planningPathSmoothedWorldNavigationPoints.Count > 0)
{ {
_planningPathSmoothedWorldNavigationPoints[0] = new NavigationPoint(fromPositionWorld); _activePathWorldNavigationPoints = World.SmoothPath(entity, _planningPathWorldNavigationPoints);
}
else
{
_activePathWorldNavigationPoints = _planningPathWorldNavigationPoints;
}
// Ensure starting point is the current position
if (_activePathWorldNavigationPoints.Count > 0) {
_activePathWorldNavigationPoints[0] = new NavigationPoint(fromPositionWorld);
} }
} }
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, NavigationPoint navigationPoint) public void FindPath(Entity entity, Vector3 fromPositionWorld, NavigationPoint navigationPoint) {
{ FindPath(entity, fromPositionWorld, navigationPoint.WorldPosition);
FindPath(body, fromPositionWorld, navigationPoint.WorldPosition);
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint; _planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint;
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] = _activePathWorldNavigationPoints[_activePathWorldNavigationPoints.Count - 1] =
navigationPoint; navigationPoint;
} }
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld) public void PlanGridPath(Entity entity, Vector3 fromPositionWorld, Vector3 toPositionWorld) {
{ Vector2 fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld);
var fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld); Vector2 toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
var toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
var fromCell = new HexCell(); HexCell fromCell = new();
fromCell.OffsetCoords = fromPositionOffset; fromCell.OffsetCoords = fromPositionOffset;
var toCell = new HexCell(); HexCell toCell = new();
toCell.OffsetCoords = toPositionOffset; toCell.OffsetCoords = toPositionOffset;
_path = fromCell.LineTo(toCell); _path = fromCell.LineTo(toCell);
@ -129,26 +122,23 @@ public class NavigationComponent : Spatial
_pathWorldNavigationPoints.Add( _pathWorldNavigationPoints.Add(
new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset))); new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset)));
foreach (var index in Enumerable.Range(1, _path.Length - 1)) foreach (int index in Enumerable.Range(1, _path.Length - 1)) {
{
_pathWorldNavigationPoints.Add( _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. // Remove the last one, because it is only the position rounded to HexGrid coordinates.
if (_pathWorldNavigationPoints.Count > 0) {
{ if (_pathWorldNavigationPoints.Count > 0) {
_pathWorldNavigationPoints.RemoveAt(_pathWorldNavigationPoints.Count - 1); _pathWorldNavigationPoints.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;
} }
@ -156,36 +146,28 @@ public class NavigationComponent : Spatial
} }
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld, public void PlanGridPath(Entity entity, 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(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint) public void PlanGridPath(Entity entity, 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)) {
else if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)) PlanGridPath(entity, fromTransformWorld.origin, navigationPoint.WorldPosition);
{ } 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));
@ -194,23 +176,20 @@ 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;
@ -220,37 +199,30 @@ 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 startPlane = new Vector2(startWorld.x, startWorld.z); Vector2 endPlane = new(endWorld.x, endWorld.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;
} }
} }
@ -258,29 +230,24 @@ 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>(); List<NavigationPoint> smoothedPath = new();
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;
@ -294,8 +261,7 @@ public class NavigationComponent : Spatial
continue; continue;
} }
if (endIndex == navigationPoints.Count - 1) if (endIndex == navigationPoints.Count - 1) {
{
break; break;
} }
@ -308,34 +274,25 @@ 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;
} }
@ -344,73 +301,56 @@ 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;
@ -428,8 +368,7 @@ 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);
@ -437,8 +376,7 @@ 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);
@ -447,8 +385,7 @@ 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);
@ -457,8 +394,7 @@ public class NavigationComponent : Spatial
} }
previousPoint = parentNode.GlobalTranslation; previousPoint = parentNode.GlobalTranslation;
foreach (NavigationPoint point in _planningPathSmoothedWorldNavigationPoints) foreach (NavigationPoint point in _activePathWorldNavigationPoints) {
{
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,42 +1,39 @@
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
public class TaskQueueComponent : Component public class TaskQueueComponent : Component {
{
[Signal] [Signal]
private delegate void StartInteraction(Entity entity, Entity targetEntity); private delegate void StartInteraction(Entity entity, Entity targetEntity);
public Queue<Task> Queue; public Queue<Task> Queue;
public TaskQueueComponent() public TaskQueueComponent() {
{
Queue = new Queue<Task>(); Queue = new Queue<Task>();
Reset(); Reset();
} }
public void Reset() public void Reset() {
{
Queue.Clear(); Queue.Clear();
} }
public void Process(Entity entity, float delta) public void Process(Entity entity, float delta) {
{ if (Queue.Count == 0) {
if (Queue.Count == 0) return; return;
}
do do {
{ Task currentTask = Queue.Peek();
var currentTask = Queue.Peek();
if (currentTask.PerformTask(entity, this, delta)) if (currentTask.PerformTask(entity, this, delta)) {
Queue.Dequeue(); Queue.Dequeue();
else } else {
break; break;
}
} while (Queue.Count > 0); } while (Queue.Count > 0);
} }
public abstract class Task : Object public abstract class Task : Object {
{
/// <summary> /// <summary>
/// Executes a Task on the specified entity. /// Executes a Task on the specified entity.
/// </summary> /// </summary>
@ -47,33 +44,27 @@ public class TaskQueueComponent : Component
/// <summary> /// <summary>
/// Makes an entity move towards a target (translation and or orientation). /// Makes an entity move towards a target (translation and or orientation).
/// </summary> /// </summary>
public class NavigationTask : Task public class NavigationTask : Task {
{
public NavigationPoint NavigationPoint; public NavigationPoint NavigationPoint;
public bool PlanningComplete = false; public bool PlanningComplete = false;
public NavigationTask(NavigationPoint navigationPoint) public NavigationTask(NavigationPoint navigationPoint) {
{
NavigationPoint = navigationPoint; NavigationPoint = navigationPoint;
} }
public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) {
{
return NavigationPoint.IsReached(entity.GlobalTransform); return NavigationPoint.IsReached(entity.GlobalTransform);
} }
} }
public class InteractionTask : Task public class InteractionTask : Task {
{
public Entity TargetEntity; public Entity TargetEntity;
public InteractionTask(Entity entity) public InteractionTask(Entity entity) {
{
TargetEntity = entity; TargetEntity = entity;
} }
public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) public override bool PerformTask(Entity entity, TaskQueueComponent taskQueueComponent, float delta) {
{
taskQueueComponent.EmitSignal("StartInteraction", entity, TargetEntity); taskQueueComponent.EmitSignal("StartInteraction", entity, TargetEntity);
return true; return true;

View File

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

View File

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

View File

@ -79,8 +79,8 @@ tracks/2/keys = {
[sub_resource type="PrismMesh" id=15] [sub_resource type="PrismMesh" id=15]
[sub_resource type="BoxShape" id=16] [sub_resource type="SphereShape" id=19]
extents = Vector3( 0.19, 0.19, 0.33 ) radius = 0.359562
[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.240716, 0 ) transform = Transform( -0.866026, 0, 0.5, 0, 1, 0, -0.5, 0, -0.866026, 0, 0.110576, 0 )
shape = SubResource( 16 ) shape = SubResource( 19 )
[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,30 +1,61 @@
using Godot;
using System; using System;
using System.Diagnostics;
using Godot;
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 class Entity : KinematicBody
{
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;
/** Defines the angle in plane coordinates, 0 => pointing to the right/east, pi/2 pointing up/north, range [-pi,pi]. */ private CollectibleComponent _collectibleComponent;
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,61 +1 @@
using Godot; public class GoldBar : Entity { }
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,7 +1,8 @@
[gd_scene load_steps=4 format=2] [gd_scene load_steps=5 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 )
@ -16,8 +17,10 @@ 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="Spatial" type="Spatial" parent="."] [node name="Geometry" 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="Spatial" instance=ExtResource( 2 )] [node name="Gold_Bar" parent="Geometry" 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,14 +2,9 @@ using GodotComponentTest.components;
namespace GodotComponentTest.entities; namespace GodotComponentTest.entities;
public interface IInteractionInterface public interface IInteractionInterface {
{
void OnInteractionStart(); void OnInteractionStart();
void OnInteractionEnd(); void OnInteractionEnd();
InteractionComponent InteractionComponent InteractionComponent InteractionComponent { get; set; }
{
get;
set;
}
} }

View File

@ -5,16 +5,21 @@ 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);
@ -30,8 +35,7 @@ public class Player : Entity, IInteractionInterface
private BoneAttachment _toolAttachement; private BoneAttachment _toolAttachement;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_groundMotion = new GroundMotionComponent(); _groundMotion = new GroundMotionComponent();
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false); _worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
NavigationComponent = (NavigationComponent)FindNode("Navigation", false); NavigationComponent = (NavigationComponent)FindNode("Navigation", false);
@ -39,33 +43,28 @@ 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);
var stateMachine = AnimationNodeStateMachinePlayback stateMachine =
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback"); (AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
stateMachine.Start("Idle"); stateMachine.Start("Idle");
_toolAttachement = (BoneAttachment)FindNode("ToolAttachement"); _toolAttachement = (BoneAttachment)FindNode("ToolAttachement");
if (_toolAttachement == null) if (_toolAttachement == null) {
{
GD.PushWarning("No ToolAttachement found!"); GD.PushWarning("No ToolAttachement found!");
} }
@ -73,25 +72,20 @@ public class Player : Entity, IInteractionInterface
} }
public override void _PhysicsProcess(float delta) public override void _PhysicsProcess(float delta) {
{
base._PhysicsProcess(delta); base._PhysicsProcess(delta);
if (NavigationComponent == null) if (NavigationComponent == null) {
{
return; return;
} }
if (TaskQueueComponent != null && TaskQueueComponent.Queue.Count > 0) if (TaskQueueComponent != null && TaskQueueComponent.Queue.Count > 0) {
{
TaskQueueComponent.Process(this, delta); TaskQueueComponent.Process(this, delta);
if (TaskQueueComponent.Queue.Count > 0) if (TaskQueueComponent.Queue.Count > 0) {
{ TaskQueueComponent.Task currentTask = TaskQueueComponent.Queue.Peek();
var currentTask = TaskQueueComponent.Queue.Peek();
TaskQueueComponent.NavigationTask navigationTask = currentTask as TaskQueueComponent.NavigationTask; TaskQueueComponent.NavigationTask navigationTask = currentTask as TaskQueueComponent.NavigationTask;
if (navigationTask != null && navigationTask.PlanningComplete == false) if (navigationTask != null && navigationTask.PlanningComplete == false) {
{
// _navigationComponent.PlanGridPath(this, GlobalTransform, navigationTask.NavigationPoint); // _navigationComponent.PlanGridPath(this, GlobalTransform, navigationTask.NavigationPoint);
NavigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint); NavigationComponent.PlanSmoothedPath(this, GlobalTransform, navigationTask.NavigationPoint);
@ -103,115 +97,112 @@ 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 (node is GoldBar) if (entity != null) {
{ CollectibleComponent collectibleComponent = entity.GetCollectibleComponent();
GoldBar bar = (GoldBar)node; if (collectibleComponent != null) {
bar.SetTarget(GlobalTransform.origin); collectibleComponent.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 (node is GoldBar) if (entity != null) {
{ CollectibleComponent collectibleComponent = entity.GetCollectibleComponent();
GoldBar bar = (GoldBar)node; if (collectibleComponent != null) {
bar.UnsetTarget(); collectibleComponent.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) {
else if (InteractionComponent.TargetEntity is Tree) if (!HasAxe) {
{ 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=27 format=2] [gd_scene load_steps=26 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,277 +28,6 @@ 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"
@ -308,6 +37,9 @@ 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]
@ -321,15 +53,26 @@ 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( 415, 45 ) states/Hit/position = Vector2( 413, 37 )
states/Idle/node = SubResource( 2 ) states/Idle/node = SubResource( 2 )
states/Idle/position = Vector2( 149, 39 ) states/Idle/position = Vector2( 157, 37 )
states/Interaction/node = SubResource( 3 ) states/Interaction/node = SubResource( 3 )
states/Interaction/position = Vector2( 176, 157 ) states/Interaction/position = Vector2( 157, 162 )
transitions = [ "Idle", "Hit", SubResource( 4 ), "Hit", "Idle", SubResource( 5 ), "Idle", "Interaction", SubResource( 6 ), "Interaction", "Idle", SubResource( 33 ) ] states/Shrug-loop/node = SubResource( 35 )
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]
@ -397,27 +140,17 @@ 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, 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 ) 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 )
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("../AnimationPlayer") anim_player = NodePath("../PirateAsset/AnimationPlayer")
active = true active = true
parameters/playback = SubResource( 34 ) parameters/playback = SubResource( 34 )

View File

@ -1,11 +1,13 @@
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; }
@ -14,13 +16,16 @@ 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");
@ -33,56 +38,61 @@ 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 (_health == 0) if (!_isDead && _health == 0) {
{
InteractionComponent.EndInteraction(); InteractionComponent.EndInteraction();
InteractionComponent.TargetEntity = null; InteractionComponent.TargetEntity = null;
QueueFree(); GD.Print("Tree chopped!");
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(); SpatialMaterial overrideMaterial = new();
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);
@ -90,11 +100,10 @@ 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();
_isBeingChopped = false; _isBeingChopped = false;
} }
} }

View File

@ -88,7 +88,6 @@ 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 )

1
entities/Wood.cs Normal file
View File

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

25
entities/Wood.tscn Normal file
View File

@ -0,0 +1,25 @@
[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 )]

49
entities/Workbench.cs Normal file
View File

@ -0,0 +1,49 @@
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)
// {
//
// }
}

51
entities/Workbench.tscn Normal file
View File

@ -0,0 +1,51 @@
[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=1 version/code=5
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/IslandColorRampShader.gdshader" type="Shader" id=1] [ext_resource path="res://materials/shader/WorldTileTypeShader.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.7, 1 ) shader_param/WaterColor = Color( 0, 0, 0.698039, 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

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

@ -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 = 0.8; COLOR.a = 1.0;
// COLOR.rgb = vec3(1, 0, 0); // COLOR.rgb = vec3(1, 0, 0);
} }

View File

@ -9,17 +9,17 @@
config_version=4 config_version=4
_global_script_classes=[ { _global_script_classes=[ {
"base": "Reference", "base": "Node",
"class": "ClickableComponent", "class": "ClickableComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/ClickableComponent.gd" "path": "res://components/ClickableComponent.gd"
}, { }, {
"base": "Reference", "base": "KinematicBody2D",
"class": "CollisionLine", "class": "CollisionLine",
"language": "GDScript", "language": "GDScript",
"path": "res://utils/CollisionLine.gd" "path": "res://utils/CollisionLine.gd"
}, { }, {
"base": "Reference", "base": "Node",
"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": "Reference", "base": "Sprite",
"class": "TintedSpriteComponent", "class": "TintedSpriteComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/TintedSpriteComponent.gd" "path": "res://components/TintedSpriteComponent.gd"
@ -114,6 +114,11 @@ 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]
@ -134,5 +139,9 @@ 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,8 +1,14 @@
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;
@ -13,59 +19,58 @@ public class Game : Spatial
// ui elements // ui elements
private Label _framesPerSecondLabel; private Label _framesPerSecondLabel;
private Control _gameUi; private Control _gameUi;
private Button _generateWorldButton; private Label _woodCountLabel;
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 worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer"); Container worldGeneratorWidget = (Container)FindNode("WorldGeneratorWidget");
_worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect"); _worldTextureRect = worldGeneratorWidget.GetNode<TextureRect>("WorldTextureRect");
_heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect"); _heightTextureRect = worldGeneratorWidget.GetNode<TextureRect>("HeightTextureRect");
_generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton");
_gameUi = (Control)FindNode("GameUI"); _gameUi = (Control)FindNode("GameUI");
_goldCountLabel = _gameUi.GetNode<Label>("GoldCount"); _woodCountLabel = _gameUi.GetNode<Label>("TopRight/WoodCount");
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");
@ -77,40 +82,39 @@ 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 image = new();
image.Load("assets/4x4checker.png"); image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
// 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));
@ -119,18 +123,39 @@ 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")) {
if (inputEvent.IsAction("Forward")) GD.Print("Forward"); GD.Print("Forward");
}
if (inputEvent.IsAction("Back")) GD.Print("Back"); if (inputEvent.IsAction("Back")) {
GD.Print("Back");
}
} }
public void UpdateCurrentTile() public void StartNewGame(int seed, int chunkSize) {
{ _world.Seed = seed;
_world.ChunkSize = chunkSize;
_world.InitNoiseGenerator();
ResetGame();
_world.UpdateCenterChunkFromPlaneCoord(Vector2.Zero);
}
public void ResetGame() {
_player.GlobalTranslation = Vector3.Zero;
_player.PlaneAngle = -Mathf.Pi * 0.5f;
_world.Reset();
}
public void UpdateCurrentTile() {
// cast a ray from the camera to center // cast a ray from the camera to center
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f); Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f); Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
@ -138,12 +163,9 @@ public class Game : Spatial
Vector3 centerCoord; Vector3 centerCoord;
if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition) if (Mathf.Abs(cameraDir.y) > Globals.EpsPosition) {
{
centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y); centerCoord = cameraPosition + cameraNormal * (-cameraPosition.y / cameraNormal.y);
} } else {
else
{
centerCoord = _camera.GlobalTranslation; centerCoord = _camera.GlobalTranslation;
centerCoord.y = 0; centerCoord.y = 0;
} }
@ -157,10 +179,8 @@ 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();
@ -175,18 +195,8 @@ 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;
@ -198,19 +208,33 @@ public class Game : Spatial
} }
public void OnTileClicked(HexTile3D tile) public void OnTileClicked(HexTile3D tile) {
{ if (_player == null) {
if (_player == null) return; return;
}
if (_player.InteractionComponent != null) _player.InteractionComponent.EmitSignal("InteractionEnd"); if (_actionMode == ActionMode.Building) {
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");
@ -220,13 +244,11 @@ public class Game : Spatial
_player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
public void OnEntityClicked(Entity entity) public void OnEntityClicked(Entity entity) {
{
GD.Print("Clicked on entity at " + entity.GlobalTranslation); GD.Print("Clicked on entity at " + entity.GlobalTranslation);
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint"); Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null) if (mountPoint != null) {
{
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
new NavigationPoint(mountPoint.GlobalTransform))); new NavigationPoint(mountPoint.GlobalTransform)));
@ -235,8 +257,7 @@ public class Game : Spatial
} }
public void ResetGameState() public void ResetGameState() {
{
Transform playerStartTransform = Transform.Identity; Transform playerStartTransform = Transform.Identity;
playerStartTransform.origin.y = 0; playerStartTransform.origin.y = 0;
_player.Transform = playerStartTransform; _player.Transform = playerStartTransform;
@ -244,30 +265,29 @@ 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 Vector2(entityTransform.origin.x, entityTransform.origin.z); Vector2 entityPlanePos = new(entityTransform.origin.x, entityTransform.origin.z);
Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords; Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
entityTransform.origin.y = 0; entityTransform.origin.y = 0;
entity.Transform = entityTransform; entity.Transform = entityTransform;
} }
} }
private void OnHeightmapImageChanged(Image heightmapImage) private void OnHeightmapImageChanged(Image heightmapImage) {
{ ImageTexture newHeightmapTexture = new();
ImageTexture newHeightmapTexture = new ImageTexture();
newHeightmapTexture.CreateFromImage(heightmapImage, newHeightmapTexture.CreateFromImage(heightmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_heightTextureRect.Texture = newHeightmapTexture; _heightTextureRect.Texture = newHeightmapTexture;
} }
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) {
{ ImageTexture newWorldTexture = new();
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(viewTileTypeImage, newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -279,12 +299,27 @@ public class Game : Spatial
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y); _tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y);
} }
public void OnGoldCountChanged(int goldCount) public void OnWoodCountChanged(int woodCount) {
{ AnimationPlayer animationPlayer = _woodCountLabel.GetNode<AnimationPlayer>("AnimationPlayer");
AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer"); _woodCountLabel.Text = woodCount.ToString();
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,22 +1,51 @@
[gd_scene load_steps=21 format=2] [gd_scene load_steps=20 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.cs" type="Script" id=7] [ext_resource path="res://scenes/World.tscn" type="PackedScene" 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/WorldGeneratorUI.gd" type="Script" id=12] [ext_resource path="res://ui/game_theme.tres" type="Theme" id=12]
[ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13] [ext_resource path="res://ui/action_buttongroup.tres" type="ButtonGroup" 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]
[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=17] [sub_resource type="Animation" id=27]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=18] resource_name = "FlashLabel"
[ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=19] length = 0.3
[ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=20] 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, 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"
@ -36,15 +65,11 @@ 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 )
@ -52,42 +77,94 @@ visible = false
[node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )] [node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )]
[node name="GameUI" type="HBoxContainer" parent="."] [node name="GameUI" type="Control" 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 = -40.0 margin_left = -206.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="GoldLabel" type="Label" parent="GameUI"] [node name="WoodLabel" type="Label" parent="GameUI/TopRight"]
margin_top = 13.0 margin_left = 4.0
margin_right = 30.0 margin_top = 10.0
margin_bottom = 27.0 margin_right = 53.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"] [node name="GoldCount" type="Label" parent="GameUI/TopRight"]
margin_left = 34.0 margin_left = 156.0
margin_top = 13.0 margin_top = 10.0
margin_right = 84.0 margin_right = 206.0
margin_bottom = 27.0 margin_bottom = 29.0
rect_min_size = Vector2( 50, 0 ) rect_min_size = Vector2( 50, 0 )
rect_pivot_offset = Vector2( 45, 8 ) rect_pivot_offset = Vector2( 25, 8 )
text = "0" text = "0"
align = 2 align = 1
[node name="AnimationPlayer" type="AnimationPlayer" parent="GameUI"] [node name="AnimationPlayer" type="AnimationPlayer" parent="GameUI/TopRight/GoldCount"]
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 = -141.0 margin_left = -67.0
margin_top = -172.0 margin_top = -34.0
grow_horizontal = 0 grow_horizontal = 0
grow_vertical = 0 grow_vertical = 0
mouse_filter = 2 mouse_filter = 2
@ -97,8 +174,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 = 134.0 margin_right = 60.0
margin_bottom = 165.0 margin_bottom = 27.0
grow_horizontal = 0 grow_horizontal = 0
grow_vertical = 0 grow_vertical = 0
mouse_filter = 2 mouse_filter = 2
@ -291,22 +368,24 @@ 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="WorldGeneratorContainer" type="VBoxContainer" parent="Generator Container"] [node name="WorldGeneratorWidget" 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( 12 ) script = ExtResource( 1 )
[node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="Generator Container/WorldGeneratorWidget"]
visible = false
margin_right = 135.0 margin_right = 135.0
margin_bottom = 16.0 margin_bottom = 16.0
[node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorContainer/HBoxContainer"] [node name="WorldSizeSlider" type="HSlider" parent="Generator Container/WorldGeneratorWidget/HBoxContainer"]
margin_right = 123.0 margin_right = 123.0
margin_bottom = 16.0 margin_bottom = 16.0
size_flags_horizontal = 3 size_flags_horizontal = 3
@ -314,26 +393,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/WorldGeneratorContainer/HBoxContainer"] [node name="WorldSizeLabel" type="Label" parent="Generator Container/WorldGeneratorWidget/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/WorldGeneratorContainer"] [node name="WorldGenerateButton" type="Button" parent="Generator Container/WorldGeneratorWidget"]
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/WorldGeneratorContainer"] [node name="ShowTexturesCheckButton" type="CheckButton" parent="Generator Container/WorldGeneratorWidget"]
margin_top = 44.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 84.0 margin_bottom = 40.0
text = "Textures" text = "Textures"
[node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"] [node name="WorldTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"]
visible = false visible = false
margin_top = 88.0 margin_top = 88.0
margin_right = 135.0 margin_right = 135.0
@ -343,11 +422,11 @@ expand = true
stretch_mode = 5 stretch_mode = 5
flip_v = true flip_v = true
[node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorContainer"] [node name="HeightTextureRect" type="TextureRect" parent="Generator Container/WorldGeneratorWidget"]
visible = false visible = false
margin_top = 88.0 margin_top = 192.0
margin_right = 135.0 margin_right = 135.0
margin_bottom = 188.0 margin_bottom = 292.0
rect_min_size = Vector2( 100, 100 ) rect_min_size = Vector2( 100, 100 )
expand = true expand = true
stretch_mode = 5 stretch_mode = 5
@ -365,63 +444,182 @@ 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="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"] [node name="Skeleton" parent="Player/Geometry/PirateAsset/Armature" index="0"]
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 ) bones/4/bound_children = [ NodePath("ToolAttachement") ]
[node name="AnimationTree" parent="Player/Geometry" index="2"] [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 )
[node name="AnimationTree" parent="Player/Geometry" index="1"]
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.79762, 0, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.03292, -2.38419e-07, -4.33215 )
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="World" type="Spatial" parent="."] [node name="Wood" parent="Entities" instance=ExtResource( 10 )]
script = ExtResource( 7 ) transform = Transform( 0.791533, 0, 0.611126, 0, 1, 0, -0.611126, 0, 0.791533, -2.46279, 0, -0.631194 )
[node name="Chunks" type="Spatial" parent="World"] [node name="Wood2" parent="Entities" instance=ExtResource( 10 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.66947, 0, -2.19408 )
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World"] [node name="Wood3" parent="Entities" instance=ExtResource( 10 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -1.89011, 0, 1.70148 )
multimesh = SubResource( 27 )
[node name="Assets" type="Spatial" parent="World"] [node name="Wood4" parent="Entities" instance=ExtResource( 10 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.85616, 0, -2.01499 )
visible = false
[node name="Rocks" type="Spatial" parent="World/Assets"] [node name="Wood5" parent="Entities" instance=ExtResource( 10 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5591, 0, 1.55701 )
[node name="rockA" parent="World/Assets/Rocks" instance=ExtResource( 16 )] [node name="World" parent="." instance=ExtResource( 7 )]
[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/WorldGeneratorContainer/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorContainer" method="_on_HSlider_value_changed"] [connection signal="value_changed" from="Generator Container/WorldGeneratorWidget/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorWidget" method="_on_HSlider_value_changed"]
[connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"] [connection signal="toggled" from="Generator Container/WorldGeneratorWidget/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorWidget" 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,17 +1,18 @@
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
public class HexTile3D : Spatial public class HexTile3D : Spatial {
{ public class TileTypeInfo {
public enum TileType public readonly string Name;
{ public readonly Color Color;
Undefined, public readonly ushort TileTypeMask;
Sand,
Grass,
DeepGrass
}
static public TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass }; public TileTypeInfo(string name, Color color, ushort tileTypeMask) {
Name = name;
Color = color;
TileTypeMask = tileTypeMask;
}
}
// scene nodes // scene nodes
private MeshInstance _mesh; private MeshInstance _mesh;
@ -19,26 +20,24 @@ public class HexTile3D : Spatial
// signals // signals
[Signal] [Signal]
delegate void TileClicked(HexTile3D tile3d); private delegate void TileClicked(HexTile3D tile3d);
[Signal] [Signal]
delegate void TileHovered(HexTile3D tile3d); private delegate void TileHovered(HexTile3D tile3d);
// other member variables // other member variables
private SpatialMaterial _previousMaterial; private SpatialMaterial _previousMaterial;
private HexGrid _hexGrid; private readonly HexGrid _hexGrid;
public HexCell Cell = new HexCell(); public HexCell Cell = new();
public bool IsMouseOver = false; public bool IsMouseOver;
public MeshInstance Mesh; public MeshInstance Mesh;
public Vector2 OffsetCoords public Vector2 OffsetCoords {
{ get => Cell.OffsetCoords;
get { return Cell.OffsetCoords; }
set set {
{
Cell.OffsetCoords = value; Cell.OffsetCoords = value;
Transform tile3dTransform = Transform; Transform tile3dTransform = Transform;
Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell); Vector2 cellPlaneCoords = _hexGrid.GetHexCenter(Cell);
@ -48,16 +47,12 @@ public class HexTile3D : Spatial
} }
} }
public TileType Type { get; set; } private HexTile3D() {
HexTile3D()
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
} }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_mesh = GetNode<MeshInstance>("Mesh"); _mesh = GetNode<MeshInstance>("Mesh");
_staticBody = GetNode<StaticBody>("StaticBody"); _staticBody = GetNode<StaticBody>("StaticBody");
@ -65,28 +60,22 @@ public class HexTile3D : Spatial
_staticBody.Connect("mouse_entered", this, nameof(OnAreaMouseEntered)); _staticBody.Connect("mouse_entered", this, nameof(OnAreaMouseEntered));
_staticBody.Connect("mouse_exited", this, nameof(OnAreaMouseExited)); _staticBody.Connect("mouse_exited", this, nameof(OnAreaMouseExited));
Mesh = GetNode<MeshInstance>("Mesh"); Mesh = GetNode<MeshInstance>("Mesh");
Debug.Assert(Mesh != null); Debug.Assert(Mesh != null);
this.Type = TileType.Undefined;
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex) {
{ if (IsMouseOver && inputEvent is InputEventMouseButton) {
if (IsMouseOver && inputEvent is InputEventMouseButton)
{
InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent; InputEventMouseButton mouseButtonEvent = (InputEventMouseButton)inputEvent;
if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) if (mouseButtonEvent.ButtonIndex == 1 && mouseButtonEvent.Pressed) {
{
EmitSignal("TileClicked", this); EmitSignal("TileClicked", this);
} }
} }
} }
public void OnAreaMouseEntered() public void OnAreaMouseEntered() {
{
IsMouseOver = true; IsMouseOver = true;
_previousMaterial = (SpatialMaterial)_mesh.MaterialOverride; _previousMaterial = (SpatialMaterial)_mesh.MaterialOverride;
@ -96,8 +85,7 @@ public class HexTile3D : Spatial
// _mesh.MaterialOverride = clonedMaterial; // _mesh.MaterialOverride = clonedMaterial;
} }
public void OnAreaMouseExited() public void OnAreaMouseExited() {
{
IsMouseOver = false; IsMouseOver = false;
_mesh.MaterialOverride = _previousMaterial; _mesh.MaterialOverride = _previousMaterial;
} }

View File

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

View File

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

View File

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

View File

@ -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/IslandColorRampShader.tres" type="Material" id=3] [ext_resource path="res://materials/WorldTileTypeMaterial.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

@ -0,0 +1,23 @@
[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,12 +4,11 @@ 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 {
public enum GenerationState Empty,
{
Undefined,
Heightmap, Heightmap,
TileType, TileType,
Objects, Objects,
@ -17,32 +16,30 @@ public class World : Spatial
} }
// constants // constants
public const int ChunkSize = 12; public int ChunkSize = 14;
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 static readonly Color DarkGrassColor = new(0.05882353f, 0.5411765f, 0.05882353f); private readonly System.Collections.Generic.Dictionary<int, HexTile3D.TileTypeInfo> _tileTypeInfos = new();
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> _unusedWorldChunks = new(); private readonly List<WorldChunk> _deactivatedWorldChunks = 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;
@ -60,10 +57,9 @@ public class World : Spatial
public HexGrid HexGrid = new(); public HexGrid HexGrid = new();
public int Seed = 0; public int Seed = 0;
public GenerationState State = GenerationState.Done; public GenerationState State = GenerationState.Empty;
public Vector2 WorldTextureCoordinateOffset; public Vector2 WorldTextureCoordinateOffset;
// ui elements // ui elements
// scene nodes // scene nodes
@ -93,43 +89,60 @@ 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()) _rockAssets.Add(asset); foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) {
_rockAssets.Add(asset);
}
_grassAssets = new Array<Spatial>(); _grassAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) _grassAssets.Add(asset); foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) {
_grassAssets.Add(asset);
}
_treeAssets = new Array<Spatial>(); _treeAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) _treeAssets.Add(asset); foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) {
_treeAssets.Add(asset);
}
SetCenterPlaneCoord(Vector2.Zero); _tileTypeInfos[0] = new HexTile3D.TileTypeInfo("Undefined", Colors.Black, 0xffff);
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);
} }
public void InitNoiseGenerator() private void LoadTileTypeInfo(string tileTypeName, ushort tileTypeMask) {
{ 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;
@ -139,22 +152,36 @@ public class World : Spatial
_noiseGenerator.Lacunarity = 2; _noiseGenerator.Lacunarity = 2;
} }
public WorldChunk GetOrCreateWorldChunk(Vector2 chunkIndex, Color debugColor) public void Reset() {
{ 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 (_unusedWorldChunks.Count > 0)
{
chunk = _unusedWorldChunks.First();
_unusedWorldChunks.RemoveAt(0);
GD.Print("Reusing chunk from former index " + chunk.ChunkIndex + " at new index " + chunkIndex);
} }
else
{ if (_deactivatedWorldChunks.Count > 0) {
chunk = _deactivatedWorldChunks.First();
_deactivatedWorldChunks.RemoveAt(0);
} else {
chunk = CreateWorldChunk(chunkIndex, debugColor); chunk = CreateWorldChunk(chunkIndex, debugColor);
} }
@ -163,15 +190,14 @@ 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));
@ -191,15 +217,16 @@ 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, double probability) private Spatial SelectAsset(Vector2 textureCoord, Array<Spatial> assets, Random randomGenerator,
{ double probability) {
if (randomGenerator.NextDouble() < 1.0 - probability) return null; if (randomGenerator.NextDouble() < 1.0 - probability) {
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();
@ -214,41 +241,47 @@ 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);
Image tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData(); chunk.CreateUnlockedTileTypeImage();
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);
Color colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV); Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
Vector2 textureCoord = new(textureCoordU, textureCoordV);
Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
if (IsColorEqualApprox(colorValue, RockColor)) HexTile3D.TileTypeInfo tileTypeInfo = GetTileTypeInfoAtOffset(offsetCoord);
{
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);
Tree treeAsset = SelectAsset(textureCoord, _treeAssets, environmentRandom, 0.05) as Tree; if (tileTypeInfo.Name == "Rock") {
if (treeAsset != null) Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.15);
{ if (rockAsset != null) {
chunk.Entities.AddChild(treeAsset); chunk.Entities.AddChild(rockAsset);
treeAsset.Connect("EntityClicked", this, nameof(OnEntityClicked)); MarkCellUnwalkable(HexGrid.GetHexAtOffset(offsetCoord));
} }
} 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;
@ -258,52 +291,35 @@ 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));
@ -319,50 +335,57 @@ public class World : Spatial
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1)); _activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1));
// clear unused chunks // clear unused chunks
_unusedWorldChunks.Clear(); _deactivatedWorldChunks.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();
_unusedWorldChunks.Add(chunk); _deactivatedWorldChunks.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();
@ -372,37 +395,38 @@ 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)
{
HexCell centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
return (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
}
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
{
if (!_centerChunkRect.HasPoint(centerPlaneCoord))
{
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
UpdateChunkBounds();
UpdateNavigationBounds();
} }
} }
private void UpdateWorldViewTexture()
{ private Vector2 GetChunkTupleFromPlaneCoord(Vector2 planeCoord) {
HexCell hexCell = HexGrid.GetHexAt(planeCoord);
return GetChunkIndexFromOffsetCoord(hexCell.OffsetCoords);
}
private Vector2 GetChunkIndexFromOffsetCoord(Vector2 offsetCoord) {
return (offsetCoord / ChunkSize).Floor();
}
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord) {
if (State != GenerationState.Done) {
return;
}
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) {
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
}
}
private void UpdateWorldViewTexture() {
int worldChunkSize = ChunkSize; int worldChunkSize = ChunkSize;
int numWorldChunkRows = NumChunkRows; int numWorldChunkRows = NumChunkRows;
int numWorldChunkColumns = NumChunkColumns; int numWorldChunkColumns = NumChunkColumns;
@ -412,8 +436,7 @@ 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(
@ -431,7 +454,7 @@ public class World : Spatial
_heightmapTexture.CreateFromImage(_heightmapImage); _heightmapTexture.CreateFromImage(_heightmapImage);
_viewTileTypeTexture = new ImageTexture(); _viewTileTypeTexture = new ImageTexture();
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage); _viewTileTypeTexture.CreateFromImage(_tileTypeMapImage, (uint)Texture.FlagsEnum.ConvertToLinear);
WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize; WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize;
@ -439,113 +462,316 @@ 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 override void _Process(float delta) public void MarkCellUnwalkable(HexCell cell) {
{ HexGrid.AddObstacle(cell);
}
public float GetHexCost(Entity entity, HexCell cell) {
float nextHexCost = HexGrid.GetHexCost(cell);
if (nextHexCost != 0) {
Vector2 nextOffset = cell.OffsetCoords;
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) numChunksGeneratingHeightmap++; if (chunk.HeightMapFrameCount > 0) {
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) numChunksGeneratingTileType++; if (chunk.TileTypeMapFrameCount > 0) {
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);
} }
} }

50
scenes/World.tscn Normal file
View File

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

View File

@ -3,8 +3,7 @@ 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();
@ -28,10 +27,11 @@ 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 = true; public bool NoiseTextureCheckerboardOverlay = false;
// signals // signals
[Signal] [Signal]
@ -47,7 +47,7 @@ public class WorldChunk : Spatial
// scene nodes // scene nodes
private MeshInstance PlaneRectMesh; private MeshInstance PlaneRectMesh;
[Export] public int Size = 32; public int Size = 32;
// resources // resources
@ -56,32 +56,24 @@ 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) {
if (PlaneRectMesh != null) PlaneRectMesh.Visible = value; 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) _showTextureOverlay = true; if (PlaneRectMesh.Visible) {
_showTextureOverlay = true;
}
Transform planeRectTransform = Transform.Identity; Transform planeRectTransform = Transform.Identity;
planeRectTransform = planeRectTransform =
@ -112,16 +104,12 @@ 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);
@ -131,10 +119,9 @@ public class WorldChunk : Spatial
} }
} }
public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid) public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid) {
{
ChunkIndex = chunkIndex; ChunkIndex = chunkIndex;
float chunkSize = World.ChunkSize; float chunkSize = Size;
Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize); Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize);
@ -148,62 +135,60 @@ 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 = World.ChunkSize; int chunkSize = Size;
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();
HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance(); tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
tile3D.Connect("TileClicked", this, nameof(OnTileClicked)); tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * World.ChunkSize + new Vector2(i, j)); tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * 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 * World.ChunkSize); Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * Size);
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);
GD.Print("Updating transforms for instances of chunk " + ChunkIndex + " origin: " + chunkTransform.origin); foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count)) {
int column = i % Size;
foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count)) int row = i / Size;
{
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));
@ -211,17 +196,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());
@ -234,13 +219,10 @@ 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);
@ -248,8 +230,7 @@ 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());
@ -258,25 +239,29 @@ public class WorldChunk : Spatial
TileTypeMapFrameCount = 1; TileTypeMapFrameCount = 1;
} }
public override void _Process(float delta) public void CreateUnlockedTileTypeImage() {
{ TileTypeImage = TileTypeOffscreenViewport.GetTexture().GetData();
TileTypeImage.Lock();
}
public override void _Process(float delta) {
Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture(); 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);
Vector2 textureCoord = new(i, j); Color baseColor = tileTypeImage.GetPixelv(textureCoord);
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();
@ -295,24 +280,26 @@ public class WorldChunk : Spatial
//RectMaterial.Uv1Triplanar = true; //RectMaterial.Uv1Triplanar = true;
PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial); PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial);
if (HeightMapFrameCount == 0) HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled; if (HeightMapFrameCount == 0) {
HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0; HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0;
if (TileTypeMapFrameCount == 0) TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled; if (TileTypeMapFrameCount == 0) {
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
}
TileTypeMapFrameCount = TileTypeMapFrameCount > 0 ? TileTypeMapFrameCount - 1 : 0; 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/IslandColorRampShader.tres" type="Material" id=2] [ext_resource path="res://materials/WorldTileTypeMaterial.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,9 +1,8 @@
using Godot; using Godot;
using Godot.Collections;
public class EditorUI : Control public class EditorUI : Control {
{ public enum InputMode {
public enum InputMode
{
None, None,
Grass, Grass,
Sand, Sand,
@ -35,8 +34,7 @@ public class EditorUI : Control
public Vector2 currentTileOffset = Vector2.Zero; public Vector2 currentTileOffset = Vector2.Zero;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready() {
{
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
// signals // signals
@ -69,74 +67,65 @@ public class EditorUI : Control
} }
public void OnResetButton() public void OnResetButton() {
{
GD.Print("Resetting Map"); GD.Print("Resetting Map");
_tileWorld.Seed = _tileWorld.Seed + 1; _tileWorld.Seed = _tileWorld.Seed + 1;
_tileWorld.Generate(24); _tileWorld.Generate(24);
} }
public void OnGrassButton() public void OnGrassButton() {
{
CurrentInputMode = InputMode.Grass; CurrentInputMode = InputMode.Grass;
} }
public void OnSandButton() public void OnSandButton() {
{
CurrentInputMode = InputMode.Sand; CurrentInputMode = InputMode.Sand;
} }
public void OnWaterButton() public void OnWaterButton() {
{
CurrentInputMode = InputMode.Water; CurrentInputMode = InputMode.Water;
} }
public void OnObstacleButton() public void OnObstacleButton() {
{
CurrentInputMode = InputMode.Obstacle; CurrentInputMode = InputMode.Obstacle;
} }
public void OnNavigateButton() public void OnNavigateButton() {
{
CurrentInputMode = InputMode.Navigate; CurrentInputMode = InputMode.Navigate;
} }
public void OnGameGeometryCheckBoxToggled(bool pressed) public void OnGameGeometryCheckBoxToggled(bool pressed) {
{ Array gameGeometries = GetTree().GetNodesInGroup("GameGeometry");
var gameGeometries = GetTree().GetNodesInGroup("GameGeometry"); foreach (Spatial mesh in gameGeometries) {
foreach (Spatial mesh in gameGeometries) if (mesh != null) {
if (mesh != null)
mesh.Visible = pressed; mesh.Visible = pressed;
}
}
} }
public void OnPhysicsGeometryCheckBoxToggled(bool pressed) public void OnPhysicsGeometryCheckBoxToggled(bool pressed) {
{ Array physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry");
var physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry"); foreach (Spatial mesh in physicsGeometries) {
foreach (Spatial mesh in physicsGeometries) if (mesh != null) {
if (mesh != null)
mesh.Visible = pressed; mesh.Visible = pressed;
}
}
} }
public void OnNavigationGeometryCheckBoxToggled(bool pressed) public void OnNavigationGeometryCheckBoxToggled(bool pressed) {
{
UpdateTileMaterial(); UpdateTileMaterial();
} }
public void UpdateTileMaterial() public void UpdateTileMaterial() {
{ if (_navigationGeometryCheckBox.Pressed) {
if (_navigationGeometryCheckBox.Pressed) ImageTexture newWorldTexture = new();
{
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.NavigationmapImage, newWorldTexture.CreateFromImage(_tileWorld.NavigationmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.NavigationmapImage.GetSize().x); _tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.NavigationmapImage.GetSize().x);
} } else {
else ImageTexture newWorldTexture = new();
{
ImageTexture newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage, newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
@ -145,10 +134,8 @@ public class EditorUI : Control
} }
public void OnTileClicked(Vector2 offsetCoord) public void OnTileClicked(Vector2 offsetCoord) {
{ switch (CurrentInputMode) {
switch (CurrentInputMode)
{
case InputMode.Grass: case InputMode.Grass:
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green); _tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
break; break;

View File

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

View File

@ -1,65 +1,56 @@
using Godot;
using System;
using System.Diagnostics; using System.Diagnostics;
using Godot;
using Godot.Collections; using Godot.Collections;
public class NavigationTests : Spatial public class NavigationTests : Spatial {
{
private HexGrid _hexGrid; private HexGrid _hexGrid;
private HexCell _currentTile; private HexCell _currentTile;
private HexCell _lastTile; private HexCell _lastTile;
private Spatial _mouseHighlight; private Spatial _mouseHighlight;
private ShaderMaterial _tileMaterial; private ShaderMaterial _tileMaterial;
private EditorUI _editorUi; private EditorUI _editorUi;
private TileWorld _tileWorld; private World _world;
private StreamContainer _streamContainer;
private Player _player; private Player _player;
private NavigationComponent _playerNavigationComponent; private NavigationComponent _playerNavigationComponent;
public override void _Ready() public override void _Ready() {
{
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
_currentTile = new HexCell(); _currentTile = new HexCell();
_lastTile = new HexCell(); _lastTile = new HexCell();
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres"); _tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
_mouseHighlight = GetNode<Spatial>("MouseHighlight"); _mouseHighlight = GetNode<Spatial>("MouseHighlight");
_editorUi = GetNode<EditorUI>("EditorUI"); _editorUi = GetNode<EditorUI>("EditorUI");
_tileWorld = GetNode<TileWorld>("TileWorld"); _world = GetNode<World>("World");
_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");
// input handling // connect signals
// _groundLayer.Connect("input_event", this, nameof(OnGroundLayerInputEvent)); _world.Connect("TileClicked", this, nameof(OnTileClicked));
_streamContainer.Connect("TileClicked", this, nameof(OnTileClicked)); _world.Connect("TileHovered", this, nameof(OnTileHovered));
_streamContainer.Connect("TileHovered", this, nameof(OnTileHovered)); _world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged));
_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;
} }
var entities = entitiesNode.GetChildren(); Array entities = entitiesNode.GetChildren();
foreach (Spatial entity in entities) foreach (Spatial entity in entities) {
{ Vector2 entityPlaneCoords = new(entity.GlobalTranslation.x, entity.GlobalTranslation.z);
Vector2 entityPlaneCoords = new Vector2(entity.GlobalTranslation.x, entity.GlobalTranslation.z); HexCell entityCell = _world.HexGrid.GetHexAt(entityPlaneCoords);
HexCell entityCell = _tileWorld.HexGrid.GetHexAt(entityPlaneCoords); _world.MarkCellUnwalkable(entityCell);
_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;
@ -68,79 +59,61 @@ public class NavigationTests : Spatial
} }
} }
public void OnWorldGenerated() private void OnHeightmapImageChanged(Image heightmapImage) {
{ ImageTexture newHeightmapTexture = new();
_streamContainer.OnWorldGenerated(); newHeightmapTexture.CreateFromImage(heightmapImage,
// Properly place the Player
Vector2 centerTileCoord = (Vector2.One * _tileWorld.Size / 2).Round();
Vector3 worldCenterTileCoords = _tileWorld.GetTileWorldCenterFromOffset(centerTileCoord);
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)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); }
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
CorrectEntityGridPositions(); private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) {
ImageTexture newWorldTexture = new();
newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)newWorldTexture.GetSize().x);
_tileMaterial.SetShaderParam("CoordinateOffsetU", (int)_world.WorldTextureCoordinateOffset.x);
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_world.WorldTextureCoordinateOffset.y);
} }
public void UpdateCurrentTile(HexCell tile) public void UpdateCurrentTile(HexCell tile) {
{ if (_currentTile.AxialCoords == tile.AxialCoords) {
if (_currentTile.AxialCoords == tile.AxialCoords)
{
return; return;
} }
_lastTile = _currentTile; _lastTile = _currentTile;
_currentTile = tile; _currentTile = tile;
GD.Print("Current tile: " + _currentTile.OffsetCoords); GD.Print("Current tile: " + _currentTile.OffsetCoords);
if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null) if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null) {
{
_editorUi.currentTileOffset = _currentTile.OffsetCoords; _editorUi.currentTileOffset = _currentTile.OffsetCoords;
} }
Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords); Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords);
Transform tileTransform = Transform.Identity;
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);
_playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation); _playerNavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
} }
} }

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=25 format=2] [gd_scene load_steps=26 format=2]
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=1] [ext_resource path="res://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,6 +11,7 @@
[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]
@ -289,18 +290,21 @@ script = ExtResource( 4 )
[node name="Player" parent="." instance=ExtResource( 1 )] [node name="Player" parent="." instance=ExtResource( 1 )]
collision_mask = 1 collision_mask = 1
TileWorldNode = NodePath("../TileWorld") WorldNode = NodePath("../World")
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"] [node name="WorldInfo" parent="Player" index="2"]
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 ) WorldPath = NodePath("../../World")
[node name="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"]
transform = Transform( 1, 7.13626e-08, -4.47035e-08, 1.64262e-07, -1, -1.00583e-07, 1.19209e-07, 1.18278e-07, -1, -0.72, 0.45, 1.78362e-08 )
[node name="AnimationTree" parent="Player/Geometry" index="2"] [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 )]
@ -374,8 +378,10 @@ 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"] [editable path="Player/Geometry/PirateAsset"]
[editable path="TileWorld"] [editable path="TileWorld"]
[editable path="StreamContainer"] [editable path="StreamContainer"]
[editable path="Entities/Chest"] [editable path="Entities/Chest"]

View File

@ -1,84 +1,68 @@
using Godot;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Godot.Collections; using Godot;
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<NodePair> invalidInteractionPairs = new List<NodePair>(); List<InteractionComponent> endedInteractions = new();
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 || targetEntity.IsQueuedForDeletion()) if (owningEntity == null || owningEntity.IsQueuedForDeletion() || targetEntity == null ||
{ 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;
} }
endedInteractions.Add(interaction); endedInteractions.Add(interaction);
} }
} }
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;
ConnectInteractionSignals(owningEntity, interactionComponent); ConnectInteractionSignals(owningEntity, interactionComponent);
ConnectInteractionSignals(targetEntity, interactionComponent); ConnectInteractionSignals(targetEntity, interactionComponent);
interactionComponent.EmitSignal("InteractionStart"); interactionComponent.EmitSignal("InteractionStart");
_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,12 +1,11 @@
public class LookupWorldSystem : IWorldSystemInterface using System;
{
public void RegisterEntityComponent(Entity entity, Component component) public class LookupWorldSystem : IWorldSystemInterface {
{ public void RegisterEntityComponent(Entity entity, Component component) {
throw new System.NotImplementedException(); throw new NotImplementedException();
} }
public void Update(float delta) public void Update(float delta) {
{ throw new NotImplementedException();
throw new System.NotImplementedException();
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

128
ui/WorldGeneratorUI.tscn Normal file
View File

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

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

View File

@ -0,0 +1,5 @@
[gd_resource type="ButtonGroup" format=2]
[resource]
resource_local_to_scene = false
resource_name = "ActionButtonGroup"

11
ui/game_theme.tres Normal file
View File

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

View File

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

View File

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

View File

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

View File

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