Tile instancing now working, also fixed navigation bounds.
parent
eaaa5219ed
commit
c59d92618b
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using Godot;
|
using Godot;
|
||||||
using Vector2 = Godot.Vector2;
|
|
||||||
using Vector3 = Godot.Vector3;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -10,23 +6,21 @@ 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 float DistanceToTarget;
|
||||||
public float MaxSpeed = 8;
|
public float MaxSpeed = 8;
|
||||||
|
|
||||||
public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180;
|
public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180;
|
||||||
public Vector3 DirectionToTarget = Vector3.Zero;
|
public float TargetAngle;
|
||||||
public float DistanceToTarget = 0;
|
public float TargetDeltaAngle;
|
||||||
public float TargetAngle = 0;
|
|
||||||
public float TargetDeltaAngle = 0;
|
|
||||||
|
|
||||||
|
|
||||||
private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation)
|
private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation)
|
||||||
{
|
{
|
||||||
float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle);
|
float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle);
|
||||||
if (deltaAngleAbsolute > 0.001)
|
if (deltaAngleAbsolute > 0.001)
|
||||||
{
|
{
|
||||||
if (RotationSpeedRadPerSecond * delta + 0.001 >= deltaAngleAbsolute)
|
if (RotationSpeedRadPerSecond * delta >= deltaAngleAbsolute)
|
||||||
{
|
{
|
||||||
GD.Print("Target Angle " + TargetAngle + " reached! Current Angle: " + entity.PlaneAngle + " TargetDeltaAngle = " + TargetDeltaAngle);
|
|
||||||
entity.PlaneAngle = TargetAngle;
|
entity.PlaneAngle = TargetAngle;
|
||||||
TargetDeltaAngle = 0;
|
TargetDeltaAngle = 0;
|
||||||
}
|
}
|
||||||
|
@ -53,11 +47,13 @@ public class GroundMotionComponent : Component
|
||||||
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);
|
var 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)
|
||||||
{
|
{
|
||||||
|
@ -66,11 +62,8 @@ public class GroundMotionComponent : Component
|
||||||
// GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep);
|
// GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
float planeSpeed = planeVelocity.Length();
|
var planeSpeed = planeVelocity.Length();
|
||||||
if (planeSpeed > MaxSpeed)
|
if (planeSpeed > MaxSpeed) planeVelocity *= MaxSpeed / planeSpeed;
|
||||||
{
|
|
||||||
planeVelocity *= MaxSpeed / planeSpeed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -111,10 +104,10 @@ public class GroundMotionComponent : Component
|
||||||
entity.Velocity = entityVelocity;
|
entity.Velocity = entityVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation,
|
||||||
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation, TileWorld tileWorld)
|
World world)
|
||||||
{
|
{
|
||||||
DirectionToTarget = (targetPosition - entity.GlobalTranslation);
|
DirectionToTarget = targetPosition - entity.GlobalTranslation;
|
||||||
DirectionToTarget.y = 0;
|
DirectionToTarget.y = 0;
|
||||||
DistanceToTarget = DirectionToTarget.Length();
|
DistanceToTarget = DirectionToTarget.Length();
|
||||||
|
|
||||||
|
@ -144,5 +137,4 @@ public class GroundMotionComponent : Component
|
||||||
//GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity);
|
//GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity);
|
||||||
//GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity);
|
//GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,92 +4,23 @@ using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Godot;
|
using Godot;
|
||||||
using GodotComponentTest.utils;
|
using GodotComponentTest.utils;
|
||||||
using Vector2 = Godot.Vector2;
|
|
||||||
using Vector3 = Godot.Vector3;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NavigationComponent : Spatial
|
public class NavigationComponent : Spatial
|
||||||
{
|
{
|
||||||
public class NavigationPoint
|
public World World { set; get; }
|
||||||
{
|
public Vector3 CurrentGoalPositionWorld { get; private set; } = Vector3.Zero;
|
||||||
[Flags]
|
public float CurrentGoalAngleWorld { get; } = 0;
|
||||||
public enum NavigationFlags
|
public Quat CurrentGoalOrientationWorld { get; private set; } = Quat.Identity;
|
||||||
{
|
|
||||||
Position = 1,
|
|
||||||
Orientation = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3 WorldPosition = Vector3.Zero;
|
|
||||||
public Quat WorldOrientation = Quat.Identity;
|
|
||||||
public readonly NavigationFlags Flags;
|
|
||||||
|
|
||||||
public NavigationPoint(Vector3 worldPosition)
|
|
||||||
{
|
|
||||||
WorldPosition = worldPosition;
|
|
||||||
Flags = NavigationFlags.Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NavigationPoint(Quat worldOrientation)
|
|
||||||
{
|
|
||||||
WorldOrientation = worldOrientation;
|
|
||||||
Flags = NavigationFlags.Orientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NavigationPoint(Transform worldTransform)
|
|
||||||
{
|
|
||||||
WorldPosition = worldTransform.origin;
|
|
||||||
WorldOrientation = worldTransform.basis.Quat();
|
|
||||||
Flags = NavigationFlags.Position | NavigationFlags.Orientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsReached(Transform worldTransform)
|
|
||||||
{
|
|
||||||
bool goalReached = false;
|
|
||||||
Vector2 positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
|
|
||||||
WorldPosition.z - worldTransform.origin.z);
|
|
||||||
float positionErrorSquared = positionError.LengthSquared();
|
|
||||||
worldTransform.basis.Quat();
|
|
||||||
float orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
|
|
||||||
|
|
||||||
if (Flags.HasFlag(NavigationFlags.Position)
|
|
||||||
&& Flags.HasFlag(NavigationFlags.Orientation)
|
|
||||||
&& positionErrorSquared < Globals.EpsPositionSquared
|
|
||||||
&& orientationError < Globals.EpsRadians)
|
|
||||||
{
|
|
||||||
goalReached = true;
|
|
||||||
}
|
|
||||||
else if (Flags == NavigationFlags.Position &&
|
|
||||||
positionErrorSquared < Globals.EpsPositionSquared)
|
|
||||||
{
|
|
||||||
goalReached = true;
|
|
||||||
}
|
|
||||||
else if (Flags == NavigationFlags.Orientation &&
|
|
||||||
orientationError < Globals.EpsRadians)
|
|
||||||
{
|
|
||||||
goalReached = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return goalReached;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TileWorld TileWorld { set; get; }
|
|
||||||
|
|
||||||
public Vector3 CurrentGoalPositionWorld => _currentGoalPositionWorld;
|
|
||||||
public Quat CurrentGoalOrientationWorld => _currentGoalOrientationWorld;
|
|
||||||
|
|
||||||
private NavigationPoint _currentGoal;
|
private NavigationPoint _currentGoal;
|
||||||
private Vector3 _currentGoalPositionWorld = Vector3.Zero;
|
|
||||||
private float _currentGoalAngleWorld = 0;
|
|
||||||
private Quat _currentGoalOrientationWorld = Quat.Identity;
|
|
||||||
|
|
||||||
private List<NavigationPoint> _planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
|
||||||
private List<NavigationPoint> _planningPathSmoothedWorldNavigationPoints = new List<NavigationPoint>();
|
|
||||||
private List<NavigationPoint> _pathWorldNavigationPoints = new List<NavigationPoint>();
|
|
||||||
private List<NavigationPoint> _smoothedPathWorldNavigationPoints = new List<NavigationPoint>();
|
|
||||||
|
|
||||||
private HexCell[] _path;
|
private HexCell[] _path;
|
||||||
|
private List<NavigationPoint> _pathWorldNavigationPoints = new();
|
||||||
|
private List<NavigationPoint> _planningPathSmoothedWorldNavigationPoints = new();
|
||||||
|
|
||||||
|
private List<NavigationPoint> _planningPathWorldNavigationPoints = new();
|
||||||
|
private List<NavigationPoint> _smoothedPathWorldNavigationPoints = new();
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
|
@ -99,7 +30,7 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
public override void _Process(float delta)
|
public override void _Process(float delta)
|
||||||
{
|
{
|
||||||
Debug.Assert(TileWorld != null);
|
Debug.Assert(World != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlanSmoothedPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
|
public void PlanSmoothedPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
|
||||||
|
@ -121,8 +52,8 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
||||||
{
|
{
|
||||||
HexCell fromCell = TileWorld.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
|
var fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
|
||||||
if (TileWorld.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>();
|
||||||
|
@ -131,10 +62,10 @@ public class NavigationComponent : Spatial
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HexCell toCell = TileWorld.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
|
var toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
|
||||||
toCell = TileWorld.HexGrid.GetClosestWalkableCell(fromCell, toCell);
|
toCell = World.HexGrid.GetClosestWalkableCell(fromCell, toCell);
|
||||||
|
|
||||||
if (TileWorld.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>();
|
||||||
|
@ -143,22 +74,23 @@ public class NavigationComponent : Spatial
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TileWorld.HexGrid.SetBounds(fromCell, 40);
|
var path = World.HexGrid.FindPath(fromCell, toCell);
|
||||||
List<HexCell> path = TileWorld.HexGrid.FindPath(fromCell, toCell);
|
|
||||||
|
|
||||||
// Generate grid navigation points
|
// Generate grid navigation points
|
||||||
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
||||||
foreach (int index in Enumerable.Range(0, path.Count))
|
foreach (var index in Enumerable.Range(0, path.Count))
|
||||||
{
|
{
|
||||||
_planningPathWorldNavigationPoints.Add(
|
_planningPathWorldNavigationPoints.Add(
|
||||||
new NavigationPoint(TileWorld.GetHexCenterFromOffset(path[index].OffsetCoords)));
|
new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(path[index].OffsetCoords)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the last point coincides with the target position
|
// Ensure the last point coincides with the target position
|
||||||
if (_planningPathWorldNavigationPoints.Count > 0 && (_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() <
|
if (_planningPathWorldNavigationPoints.Count > 0 &&
|
||||||
|
(_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() <
|
||||||
0.5f * 0.5f)
|
0.5f * 0.5f)
|
||||||
{
|
{
|
||||||
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition = toPositionWorld;
|
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition =
|
||||||
|
toPositionWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform smoothing
|
// Perform smoothing
|
||||||
|
@ -176,18 +108,19 @@ public class NavigationComponent : Spatial
|
||||||
FindPath(body, fromPositionWorld, navigationPoint.WorldPosition);
|
FindPath(body, fromPositionWorld, navigationPoint.WorldPosition);
|
||||||
|
|
||||||
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint;
|
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint;
|
||||||
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] = navigationPoint;
|
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] =
|
||||||
|
navigationPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
||||||
{
|
{
|
||||||
Vector2 fromPositionOffset = TileWorld.WorldToOffsetCoords(fromPositionWorld);
|
var fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld);
|
||||||
Vector2 toPositionOffset = TileWorld.WorldToOffsetCoords(toPositionWorld);
|
var toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
|
||||||
|
|
||||||
HexCell fromCell = new HexCell();
|
var fromCell = new HexCell();
|
||||||
fromCell.OffsetCoords = fromPositionOffset;
|
fromCell.OffsetCoords = fromPositionOffset;
|
||||||
|
|
||||||
HexCell toCell = new HexCell();
|
var toCell = new HexCell();
|
||||||
toCell.OffsetCoords = toPositionOffset;
|
toCell.OffsetCoords = toPositionOffset;
|
||||||
|
|
||||||
_path = fromCell.LineTo(toCell);
|
_path = fromCell.LineTo(toCell);
|
||||||
|
@ -195,15 +128,15 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
||||||
_pathWorldNavigationPoints.Add(
|
_pathWorldNavigationPoints.Add(
|
||||||
new NavigationPoint(TileWorld.GetHexCenterFromOffset(fromPositionOffset)));
|
new NavigationPoint(World.HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset)));
|
||||||
|
|
||||||
foreach (int index in Enumerable.Range(1, _path.Length - 1))
|
foreach (var index in Enumerable.Range(1, _path.Length - 1))
|
||||||
{
|
{
|
||||||
_pathWorldNavigationPoints.Add(
|
_pathWorldNavigationPoints.Add(
|
||||||
new NavigationPoint(TileWorld.GetHexCenterFromOffset(_path[index].OffsetCoords)));
|
new NavigationPoint(World.GetHexCenterFromOffset(_path[index].OffsetCoords)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((fromPositionWorld - TileWorld.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.
|
||||||
|
@ -295,28 +228,29 @@ public class NavigationComponent : Spatial
|
||||||
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 = TileWorld.HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius);
|
List<HexCell> cells =
|
||||||
|
World.HexGrid.GetCellsForLine(startPlane + directionPlane * radius, endPlane + directionPlane * radius);
|
||||||
foreach (HexCell cell in cells)
|
foreach (HexCell cell in cells)
|
||||||
{
|
{
|
||||||
if (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cells = TileWorld.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 (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cells = TileWorld.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 (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -407,7 +341,7 @@ public class NavigationComponent : Spatial
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentGoal = _pathWorldNavigationPoints[0];
|
_currentGoal = _pathWorldNavigationPoints[0];
|
||||||
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
||||||
//_currentGoalOrientationWorld = Vector3.Right.SignedAngleTo(_pathWorldNavigationPoints[0].WorldOrientation);
|
//_currentGoalOrientationWorld = Vector3.Right.SignedAngleTo(_pathWorldNavigationPoints[0].WorldOrientation);
|
||||||
|
|
||||||
// GD.Print("Navigation Goal: pos " + _currentGoal.WorldPosition + " " + " rot: " + _currentGoal.WorldOrientation +
|
// GD.Print("Navigation Goal: pos " + _currentGoal.WorldPosition + " " + " rot: " + _currentGoal.WorldOrientation +
|
||||||
|
@ -419,11 +353,11 @@ public class NavigationComponent : Spatial
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
_currentGoalOrientationWorld = worldTransform.basis.Quat();
|
CurrentGoalOrientationWorld = worldTransform.basis.Quat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,29 +371,33 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
if (_pathWorldNavigationPoints.Count == 0)
|
if (_pathWorldNavigationPoints.Count == 0)
|
||||||
{
|
{
|
||||||
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||||
_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
|
||||||
{
|
{
|
||||||
_currentGoalPositionWorld = currentTransformWorld.origin;
|
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
||||||
{
|
{
|
||||||
_currentGoalOrientationWorld = _currentGoal.WorldOrientation;
|
CurrentGoalOrientationWorld = _currentGoal.WorldOrientation;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vector3 currentWorldXAxis = currentTransformWorld.basis.x;
|
||||||
|
// Debug.Assert(Mathf.Abs(currentWorldXAxis.y) < 0.9);
|
||||||
|
// float angle = Mathf.Atan2(currentWorldXAxis.y, currentWorldXAxis.x);
|
||||||
|
// _currentGoalOrientationWorld = Basis.Identity.Rotated(Vector3.Up, angle).Quat();
|
||||||
if (_currentGoal.IsReached(currentTransformWorld))
|
if (_currentGoal.IsReached(currentTransformWorld))
|
||||||
{
|
{
|
||||||
_pathWorldNavigationPoints.RemoveAt(0);
|
_pathWorldNavigationPoints.RemoveAt(0);
|
||||||
|
@ -470,11 +408,16 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
if (_pathWorldNavigationPoints.Count == 0)
|
if (_pathWorldNavigationPoints.Count == 0)
|
||||||
{
|
{
|
||||||
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||||
_currentGoalPositionWorld = currentTransformWorld.origin;
|
CurrentGoalPositionWorld = currentTransformWorld.origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsGoalReached()
|
||||||
|
{
|
||||||
|
return _pathWorldNavigationPoints.Count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry)
|
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry)
|
||||||
{
|
{
|
||||||
Vector3 yOffset = Vector3.Up * 0.1f;
|
Vector3 yOffset = Vector3.Up * 0.1f;
|
||||||
|
@ -534,4 +477,62 @@ public class NavigationComponent : Spatial
|
||||||
|
|
||||||
debugGeometry.End();
|
debugGeometry.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class NavigationPoint
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum NavigationFlags
|
||||||
|
{
|
||||||
|
Position = 1,
|
||||||
|
Orientation = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly NavigationFlags Flags;
|
||||||
|
public Quat WorldOrientation = Quat.Identity;
|
||||||
|
|
||||||
|
public Vector3 WorldPosition = Vector3.Zero;
|
||||||
|
|
||||||
|
public NavigationPoint(Vector3 worldPosition)
|
||||||
|
{
|
||||||
|
WorldPosition = worldPosition;
|
||||||
|
Flags = NavigationFlags.Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NavigationPoint(Quat worldOrientation)
|
||||||
|
{
|
||||||
|
WorldOrientation = worldOrientation;
|
||||||
|
Flags = NavigationFlags.Orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NavigationPoint(Transform worldTransform)
|
||||||
|
{
|
||||||
|
WorldPosition = worldTransform.origin;
|
||||||
|
WorldOrientation = worldTransform.basis.Quat();
|
||||||
|
Flags = NavigationFlags.Position | NavigationFlags.Orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReached(Transform worldTransform)
|
||||||
|
{
|
||||||
|
var goalReached = false;
|
||||||
|
var positionError = new Vector2(WorldPosition.x - worldTransform.origin.x,
|
||||||
|
WorldPosition.z - worldTransform.origin.z);
|
||||||
|
var positionErrorSquared = positionError.LengthSquared();
|
||||||
|
worldTransform.basis.Quat();
|
||||||
|
var orientationError = Mathf.Abs(worldTransform.basis.Quat().AngleTo(WorldOrientation));
|
||||||
|
|
||||||
|
if (Flags.HasFlag(NavigationFlags.Position)
|
||||||
|
&& Flags.HasFlag(NavigationFlags.Orientation)
|
||||||
|
&& positionErrorSquared < Globals.EpsPositionSquared
|
||||||
|
&& orientationError < Globals.EpsRadians)
|
||||||
|
goalReached = true;
|
||||||
|
else if (Flags == NavigationFlags.Position &&
|
||||||
|
positionErrorSquared < Globals.EpsPositionSquared)
|
||||||
|
goalReached = true;
|
||||||
|
else if (Flags == NavigationFlags.Orientation &&
|
||||||
|
orientationError < Globals.EpsRadians)
|
||||||
|
goalReached = true;
|
||||||
|
|
||||||
|
return goalReached;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,41 @@
|
||||||
using Godot;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using Godot;
|
||||||
using GodotComponentTest.entities;
|
|
||||||
using Object = Godot.Object;
|
|
||||||
|
|
||||||
public class TaskQueueComponent : Component
|
public class TaskQueueComponent : Component
|
||||||
{
|
{
|
||||||
[Signal]
|
[Signal]
|
||||||
delegate void StartInteraction(Entity entity, Entity targetEntity);
|
private delegate void StartInteraction(Entity entity, Entity targetEntity);
|
||||||
|
|
||||||
|
public Queue<Task> Queue;
|
||||||
|
|
||||||
|
public TaskQueueComponent()
|
||||||
|
{
|
||||||
|
Queue = new Queue<Task>();
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Queue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process(Entity entity, float delta)
|
||||||
|
{
|
||||||
|
if (Queue.Count == 0) return;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var currentTask = Queue.Peek();
|
||||||
|
var interactionTask = currentTask as InteractionTask;
|
||||||
|
if (interactionTask != null) EmitSignal("StartInteraction", entity, interactionTask.TargetEntity);
|
||||||
|
|
||||||
|
if (currentTask.PerformTask(entity, delta))
|
||||||
|
Queue.Dequeue();
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
} while (Queue.Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class Task : Object
|
public abstract class Task : Object
|
||||||
{
|
{
|
||||||
|
@ -19,6 +46,9 @@ public class TaskQueueComponent : Component
|
||||||
public abstract bool PerformTask(Entity entity, float delta);
|
public abstract bool PerformTask(Entity entity, float delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes an entity move towards a target (translation and or orientation).
|
||||||
|
/// </summary>
|
||||||
public class NavigationTask : Task
|
public class NavigationTask : Task
|
||||||
{
|
{
|
||||||
public NavigationComponent.NavigationPoint NavigationPoint;
|
public NavigationComponent.NavigationPoint NavigationPoint;
|
||||||
|
@ -49,45 +79,4 @@ public class TaskQueueComponent : Component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Queue<Task> Queue;
|
|
||||||
|
|
||||||
public TaskQueueComponent()
|
|
||||||
{
|
|
||||||
Queue = new Queue<Task>();
|
|
||||||
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
Queue.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Process(Entity entity, float delta)
|
|
||||||
{
|
|
||||||
if (Queue.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
Task currentTask = Queue.Peek();
|
|
||||||
InteractionTask interactionTask = currentTask as InteractionTask;
|
|
||||||
if (interactionTask != null)
|
|
||||||
{
|
|
||||||
EmitSignal("StartInteraction", entity, interactionTask.TargetEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTask.PerformTask(entity, delta))
|
|
||||||
{
|
|
||||||
Queue.Dequeue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (Queue.Count > 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,20 +1,18 @@
|
||||||
using Godot;
|
using Godot;
|
||||||
using System;
|
|
||||||
|
|
||||||
public class WorldInfoComponent : Component
|
public class WorldInfoComponent : Component
|
||||||
{
|
{
|
||||||
[Export] public NodePath World;
|
[Export] public NodePath WorldPath;
|
||||||
|
public World World;
|
||||||
public 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()
|
||||||
{
|
{
|
||||||
TileWorld = GetNode<TileWorld>(World);
|
World = GetNode<World>(WorldPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetWorld(TileWorld tileWorld)
|
public void SetWorld(World world)
|
||||||
{
|
{
|
||||||
TileWorld = tileWorld;
|
World = world;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
using Godot;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using Godot;
|
||||||
using GodotComponentTest.components;
|
using GodotComponentTest.components;
|
||||||
using GodotComponentTest.entities;
|
using GodotComponentTest.entities;
|
||||||
using GodotComponentTest.utils;
|
using GodotComponentTest.utils;
|
||||||
|
@ -10,52 +8,40 @@ using GodotComponentTest.utils;
|
||||||
public class Player : Entity, IInteractionInterface
|
public class Player : Entity, IInteractionInterface
|
||||||
{
|
{
|
||||||
// public members
|
// public members
|
||||||
[Export] public NodePath TileWorldNode;
|
[Export] public NodePath WorldNode;
|
||||||
|
|
||||||
public int GoldCount = 0;
|
|
||||||
|
|
||||||
|
public int GoldCount;
|
||||||
public TaskQueueComponent TaskQueueComponent;
|
public TaskQueueComponent TaskQueueComponent;
|
||||||
|
public NavigationComponent NavigationComponent { get; private set; }
|
||||||
public NavigationComponent Navigation
|
public InteractionComponent InteractionComponent { get; set; }
|
||||||
{
|
|
||||||
get { return _navigationComponent; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Signal]
|
[Signal]
|
||||||
delegate void GoldCountChanged(int goldCount);
|
private delegate void GoldCountChanged(int goldCount);
|
||||||
|
|
||||||
// private members
|
// private members
|
||||||
private WorldInfoComponent _worldInfo;
|
private WorldInfoComponent _worldInfo;
|
||||||
private GroundMotionComponent _groundMotion;
|
private readonly List<Node> _attractedItemList = new();
|
||||||
private NavigationComponent _navigationComponent;
|
|
||||||
private Area _itemAttractorArea;
|
|
||||||
private Area _itemPickupArea;
|
|
||||||
private List<Node> _attractedItemList = new List<Node>();
|
|
||||||
private BoneAttachment _toolAttachement;
|
|
||||||
private AnimationPlayer _playerAnimationPlayer;
|
|
||||||
private AnimationTree _animationTree;
|
private AnimationTree _animationTree;
|
||||||
private DebugGeometry _debugGeometry;
|
private DebugGeometry _debugGeometry;
|
||||||
private InteractionComponent _interactionComponent;
|
private GroundMotionComponent _groundMotion;
|
||||||
|
private Area _itemAttractorArea;
|
||||||
public InteractionComponent InteractionComponent
|
private Area _itemPickupArea;
|
||||||
{
|
private AnimationPlayer _playerAnimationPlayer;
|
||||||
get => _interactionComponent;
|
private BoneAttachment _toolAttachement;
|
||||||
set => _interactionComponent = 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()
|
||||||
{
|
{
|
||||||
_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);
|
||||||
_navigationComponent.TileWorld = GetNode<TileWorld>(TileWorldNode);
|
NavigationComponent.World = GetNode<World>(WorldNode);
|
||||||
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 " + this.GetClass());
|
GD.PushWarning("No ItemAttractorArea node found for " + GetClass());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -65,27 +51,20 @@ public class Player : Entity, IInteractionInterface
|
||||||
|
|
||||||
_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 " + this.GetClass());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
_itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered));
|
_itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered));
|
||||||
}
|
|
||||||
|
|
||||||
_playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer");
|
_playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer");
|
||||||
Debug.Assert(_playerAnimationPlayer != null);
|
Debug.Assert(_playerAnimationPlayer != null);
|
||||||
_animationTree = GetNode<AnimationTree>("Geometry/AnimationTree");
|
_animationTree = GetNode<AnimationTree>("Geometry/AnimationTree");
|
||||||
Debug.Assert(_animationTree != null);
|
Debug.Assert(_animationTree != null);
|
||||||
AnimationNodeStateMachinePlayback stateMachine =
|
var stateMachine =
|
||||||
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
||||||
stateMachine.Start("Idle");
|
stateMachine.Start("Idle");
|
||||||
|
|
||||||
_toolAttachement = (BoneAttachment)FindNode("ToolAttachement");
|
_toolAttachement = (BoneAttachment)FindNode("ToolAttachement");
|
||||||
if (_toolAttachement == null)
|
if (_toolAttachement == null) GD.PushWarning("No ToolAttachement found!");
|
||||||
{
|
|
||||||
GD.PushWarning("No ToolAttachement found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
_debugGeometry = (DebugGeometry)FindNode("DebugGeometry");
|
_debugGeometry = (DebugGeometry)FindNode("DebugGeometry");
|
||||||
}
|
}
|
||||||
|
@ -95,7 +74,7 @@ public class Player : Entity, IInteractionInterface
|
||||||
{
|
{
|
||||||
base._PhysicsProcess(delta);
|
base._PhysicsProcess(delta);
|
||||||
|
|
||||||
if (_navigationComponent == null)
|
if (NavigationComponent == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -111,15 +90,18 @@ public class Player : Entity, IInteractionInterface
|
||||||
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);
|
||||||
|
|
||||||
_navigationComponent.ActivatePlannedPath();
|
NavigationComponent.ActivatePlannedPath();
|
||||||
navigationTask.PlanningComplete = true;
|
navigationTask.PlanningComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_navigationComponent.UpdateCurrentGoal(GlobalTransform);
|
NavigationComponent.UpdateCurrentGoal(GlobalTransform);
|
||||||
_groundMotion.PhysicsProcess(delta, this, _navigationComponent.CurrentGoalPositionWorld,
|
_groundMotion.PhysicsProcess(delta, this, NavigationComponent.CurrentGoalPositionWorld,
|
||||||
_navigationComponent.CurrentGoalOrientationWorld, _worldInfo.TileWorld);
|
NavigationComponent.CurrentGoalOrientationWorld, _worldInfo.World);
|
||||||
|
|
||||||
|
if (NavigationComponent.IsGoalReached())
|
||||||
|
navigationTask.NavigationPoint = new NavigationComponent.NavigationPoint(GlobalTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,9 +109,9 @@ public class Player : Entity, IInteractionInterface
|
||||||
|
|
||||||
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)
|
||||||
|
@ -154,9 +136,9 @@ public class Player : Entity, IInteractionInterface
|
||||||
_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);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +180,7 @@ public class Player : Entity, IInteractionInterface
|
||||||
body.QueueFree();
|
body.QueueFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetActiveTool(String toolName)
|
public void SetActiveTool(string toolName)
|
||||||
{
|
{
|
||||||
Debug.Assert(_toolAttachement != null);
|
Debug.Assert(_toolAttachement != null);
|
||||||
if (toolName == "Axe")
|
if (toolName == "Axe")
|
||||||
|
@ -217,12 +199,12 @@ public class Player : Entity, IInteractionInterface
|
||||||
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
||||||
Debug.Assert(stateMachine != null);
|
Debug.Assert(stateMachine != null);
|
||||||
|
|
||||||
if (_interactionComponent.TargetEntity is Chest)
|
if (InteractionComponent.TargetEntity is Chest)
|
||||||
{
|
{
|
||||||
GD.Print("Player Opening Box");
|
GD.Print("Player Opening Box");
|
||||||
stateMachine.Travel("Interaction");
|
stateMachine.Travel("Interaction");
|
||||||
}
|
}
|
||||||
else if (_interactionComponent.TargetEntity is Tree)
|
else if (InteractionComponent.TargetEntity is Tree)
|
||||||
{
|
{
|
||||||
GD.Print("Player Chopping Tree");
|
GD.Print("Player Chopping Tree");
|
||||||
stateMachine.Travel("Hit");
|
stateMachine.Travel("Hit");
|
||||||
|
|
|
@ -22,6 +22,7 @@ public class Game : Spatial
|
||||||
private InteractionSystem _interactionSystem;
|
private InteractionSystem _interactionSystem;
|
||||||
private HexCell _lastTile;
|
private HexCell _lastTile;
|
||||||
private Label _mouseTileCubeLabel;
|
private Label _mouseTileCubeLabel;
|
||||||
|
private Label _mouseTileAxialLabel;
|
||||||
private Spatial _mouseTileHighlight;
|
private Spatial _mouseTileHighlight;
|
||||||
private Label _mouseTileOffsetLabel;
|
private Label _mouseTileOffsetLabel;
|
||||||
private Label _mouseWorldLabel;
|
private Label _mouseWorldLabel;
|
||||||
|
@ -29,9 +30,6 @@ public class Game : Spatial
|
||||||
private Label _numCoordsRemovedLabel;
|
private Label _numCoordsRemovedLabel;
|
||||||
private Label _numTilesLabel;
|
private Label _numTilesLabel;
|
||||||
private Player _player;
|
private Player _player;
|
||||||
private StreamContainer _streamContainer;
|
|
||||||
private Spatial _streamContainerActiveTiles;
|
|
||||||
private Area _streamContainerArea;
|
|
||||||
|
|
||||||
// scene nodes
|
// scene nodes
|
||||||
private Spatial _tileHighlight;
|
private Spatial _tileHighlight;
|
||||||
|
@ -41,7 +39,6 @@ public class Game : Spatial
|
||||||
private TileInstanceManager _tileInstanceManager;
|
private TileInstanceManager _tileInstanceManager;
|
||||||
private ShaderMaterial _tileMaterial;
|
private ShaderMaterial _tileMaterial;
|
||||||
private Label _tileOffsetLabel;
|
private Label _tileOffsetLabel;
|
||||||
private TileWorld _tileWorld;
|
|
||||||
private World _world;
|
private World _world;
|
||||||
private TextureRect _worldTextureRect;
|
private TextureRect _worldTextureRect;
|
||||||
|
|
||||||
|
@ -58,6 +55,7 @@ public class Game : Spatial
|
||||||
_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");
|
||||||
_numCoordsAddedLabel = debugStatsContainer.GetNode<Label>("num_coords_added_label");
|
_numCoordsAddedLabel = debugStatsContainer.GetNode<Label>("num_coords_added_label");
|
||||||
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
|
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
|
||||||
|
|
||||||
|
@ -74,12 +72,7 @@ public class Game : Spatial
|
||||||
_tileHighlight = GetNode<Spatial>("TileHighlight");
|
_tileHighlight = GetNode<Spatial>("TileHighlight");
|
||||||
_mouseTileHighlight = GetNode<Spatial>("MouseTileHighlight");
|
_mouseTileHighlight = GetNode<Spatial>("MouseTileHighlight");
|
||||||
|
|
||||||
_streamContainer = (StreamContainer)FindNode("StreamContainer");
|
|
||||||
_streamContainerArea = _streamContainer.GetNode<Area>("Area");
|
|
||||||
_streamContainerActiveTiles = _streamContainer.GetNode<Spatial>("ActiveTiles");
|
|
||||||
|
|
||||||
_player = GetNode<Player>("Player");
|
_player = GetNode<Player>("Player");
|
||||||
_tileWorld = GetNode<TileWorld>("TileWorld");
|
|
||||||
_camera = (Camera)FindNode("Camera");
|
_camera = (Camera)FindNode("Camera");
|
||||||
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
|
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
|
||||||
|
|
||||||
|
@ -88,9 +81,6 @@ public class Game : Spatial
|
||||||
|
|
||||||
// populate UI values
|
// populate UI values
|
||||||
var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
|
var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
|
||||||
generatorWorldSizeSlider.Value = _tileWorld.Size;
|
|
||||||
|
|
||||||
Debug.Assert(_tileWorld != null);
|
|
||||||
|
|
||||||
// resources
|
// resources
|
||||||
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
|
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
|
||||||
|
@ -109,15 +99,7 @@ public class Game : Spatial
|
||||||
_interactionSystem = GetNode<InteractionSystem>("InteractionSystem");
|
_interactionSystem = GetNode<InteractionSystem>("InteractionSystem");
|
||||||
Debug.Assert(_interactionSystem != null);
|
Debug.Assert(_interactionSystem != null);
|
||||||
|
|
||||||
// update data
|
|
||||||
_worldTextureRect.RectSize = Vector2.One * _tileWorld.Size;
|
|
||||||
_heightTextureRect.RectSize = Vector2.One * _tileWorld.Size;
|
|
||||||
|
|
||||||
// connect signals
|
// connect signals
|
||||||
_streamContainerArea.Connect("input_event", this, nameof(OnAreaInputEvent));
|
|
||||||
_streamContainer.Connect("TileClicked", this, nameof(OnTileClicked));
|
|
||||||
_streamContainer.Connect("TileHovered", this, nameof(OnTileHovered));
|
|
||||||
_tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated));
|
|
||||||
_generateWorldButton.Connect("pressed", this, nameof(OnGenerateButton));
|
_generateWorldButton.Connect("pressed", this, nameof(OnGenerateButton));
|
||||||
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
|
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
|
||||||
nameof(_interactionSystem.OnStartInteraction));
|
nameof(_interactionSystem.OnStartInteraction));
|
||||||
|
@ -135,11 +117,8 @@ public class Game : Spatial
|
||||||
// perform dependency injection
|
// perform dependency injection
|
||||||
//_streamContainer.SetWorld(_tileWorld);Clicked
|
//_streamContainer.SetWorld(_tileWorld);Clicked
|
||||||
var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
|
var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
|
||||||
if (worldInfoComponent != null) worldInfoComponent.SetWorld(_tileWorld);
|
|
||||||
|
|
||||||
_tileWorld.Generate(_tileWorld.Size);
|
|
||||||
UpdateCurrentTile();
|
UpdateCurrentTile();
|
||||||
_streamContainer.SetCenterTile(_currentTile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,15 +170,6 @@ public class Game : Spatial
|
||||||
tileHighlightTransform.origin.z = currentTileCenter.y;
|
tileHighlightTransform.origin.z = currentTileCenter.y;
|
||||||
_tileHighlight.Transform = tileHighlightTransform;
|
_tileHighlight.Transform = tileHighlightTransform;
|
||||||
|
|
||||||
if (_currentTile.CubeCoords != _lastTile.CubeCoords)
|
|
||||||
{
|
|
||||||
_streamContainer.SetCenterTile(_currentTile);
|
|
||||||
|
|
||||||
_numTilesLabel.Text = _streamContainerActiveTiles.GetChildCount().ToString();
|
|
||||||
_numCoordsAddedLabel.Text = _streamContainer.AddedCoords.Count.ToString();
|
|
||||||
_numCoordsRemovedLabel.Text = _streamContainer.RemovedCoords.Count.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
var cameraTransform = _camera.Transform;
|
var cameraTransform = _camera.Transform;
|
||||||
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
|
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
|
||||||
_camera.Transform = cameraTransform;
|
_camera.Transform = cameraTransform;
|
||||||
|
@ -210,14 +180,7 @@ public class Game : Spatial
|
||||||
{
|
{
|
||||||
GD.Print("Generating");
|
GD.Print("Generating");
|
||||||
var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
|
var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
|
||||||
if (worldSizeSlider == null)
|
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!");
|
||||||
{
|
|
||||||
GD.PrintErr("Could not find WorldSizeSlider!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_tileWorld.Seed = _tileWorld.Seed + 1;
|
|
||||||
_tileWorld.Generate((int)worldSizeSlider.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,10 +193,8 @@ public class Game : Spatial
|
||||||
_mouseWorldLabel.Text = position.ToString("F3");
|
_mouseWorldLabel.Text = position.ToString("F3");
|
||||||
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
|
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
|
||||||
_mouseTileCubeLabel.Text = cellAtCursor.CubeCoords.ToString("N");
|
_mouseTileCubeLabel.Text = cellAtCursor.CubeCoords.ToString("N");
|
||||||
|
_mouseTileAxialLabel.Text = cellAtCursor.AxialCoords.ToString("N");
|
||||||
_mouseTileHighlight.Transform = highlightTransform;
|
_mouseTileHighlight.Transform = highlightTransform;
|
||||||
|
|
||||||
if (inputEvent is InputEventMouseButton && ((InputEventMouseButton)inputEvent).Pressed)
|
|
||||||
_streamContainer.EmitSignal("TileClicked", _streamContainer.GetTile3dAt(cellAtCursor.OffsetCoords));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,7 +217,7 @@ public class Game : Spatial
|
||||||
_mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N");
|
_mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N");
|
||||||
_mouseTileCubeLabel.Text = tile.Cell.CubeCoords.ToString("N");
|
_mouseTileCubeLabel.Text = tile.Cell.CubeCoords.ToString("N");
|
||||||
|
|
||||||
_player.Navigation.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
|
_player.NavigationComponent.FindPath(_player, _player.GlobalTranslation, tile.GlobalTranslation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnEntityClicked(Entity entity)
|
public void OnEntityClicked(Entity entity)
|
||||||
|
@ -277,11 +238,10 @@ public class Game : Spatial
|
||||||
public void ResetGameState()
|
public void ResetGameState()
|
||||||
{
|
{
|
||||||
var playerStartTransform = Transform.Identity;
|
var playerStartTransform = Transform.Identity;
|
||||||
var height = _tileWorld.GetHeightAtOffset(new Vector2(0, 0));
|
playerStartTransform.origin.y = 0;
|
||||||
playerStartTransform.origin.y = height;
|
|
||||||
_player.Transform = playerStartTransform;
|
_player.Transform = playerStartTransform;
|
||||||
_player.TaskQueueComponent.Reset();
|
_player.TaskQueueComponent.Reset();
|
||||||
_player.Navigation.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin,
|
_player.NavigationComponent.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin,
|
||||||
playerStartTransform.basis.Quat());
|
playerStartTransform.basis.Quat());
|
||||||
|
|
||||||
_goldCountLabel.Text = "0";
|
_goldCountLabel.Text = "0";
|
||||||
|
@ -291,48 +251,11 @@ public class Game : Spatial
|
||||||
var entityTransform = entity.Transform;
|
var entityTransform = entity.Transform;
|
||||||
var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
|
var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
|
||||||
var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
|
var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
|
||||||
var entityHeight = _tileWorld.GetHeightAtOffset(entityOffsetCoordinates);
|
entityTransform.origin.y = 0;
|
||||||
entityTransform.origin.y = entityHeight;
|
|
||||||
entity.Transform = entityTransform;
|
entity.Transform = entityTransform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnWorldGenerated()
|
|
||||||
{
|
|
||||||
GD.Print("Using new map. Size: " + (int)_tileWorld.ColormapImage.GetSize().x);
|
|
||||||
|
|
||||||
_goldCountLabel.Text = "0";
|
|
||||||
|
|
||||||
UpdateWorldTextures();
|
|
||||||
|
|
||||||
_streamContainer.OnWorldGenerated();
|
|
||||||
|
|
||||||
// Reset player transform to offset 0,0 and at current height
|
|
||||||
ResetGameState();
|
|
||||||
|
|
||||||
// Connect all signals of the generated world
|
|
||||||
foreach (Node node in _tileWorld.Entities.GetChildren())
|
|
||||||
if (node.HasSignal("EntityClicked"))
|
|
||||||
node.Connect("EntityClicked", this, nameof(OnEntityClicked));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateWorldTextures()
|
|
||||||
{
|
|
||||||
GD.Print("Updating World textures");
|
|
||||||
|
|
||||||
var newWorldTexture = new ImageTexture();
|
|
||||||
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
|
|
||||||
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
|
||||||
_worldTextureRect.Texture = newWorldTexture;
|
|
||||||
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
|
|
||||||
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
|
|
||||||
|
|
||||||
var newHeightTexture = new ImageTexture();
|
|
||||||
newHeightTexture.CreateFromImage(_tileWorld.HeightmapImage,
|
|
||||||
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
|
||||||
_heightTextureRect.Texture = newHeightTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHeightmapImageChanged(Image heightmapImage)
|
private void OnHeightmapImageChanged(Image heightmapImage)
|
||||||
{
|
{
|
||||||
var newHeightmapTexture = new ImageTexture();
|
var newHeightmapTexture = new ImageTexture();
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
[gd_scene load_steps=24 format=2]
|
[gd_scene load_steps=22 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://scenes/StreamContainer.tscn" type="PackedScene" 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.cs" type="Script" id=7]
|
||||||
[ext_resource path="res://scenes/TileWorld.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://scenes/TileInstanceManager.cs" type="Script" id=10]
|
[ext_resource path="res://scenes/TileInstanceManager.cs" type="Script" id=10]
|
||||||
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
|
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
|
||||||
|
@ -55,9 +53,6 @@ visible = false
|
||||||
|
|
||||||
[node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )]
|
[node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )]
|
||||||
|
|
||||||
[node name="TileWorld" parent="." instance=ExtResource( 8 )]
|
|
||||||
GenerationMapType = 2
|
|
||||||
|
|
||||||
[node name="GameUI" type="HBoxContainer" parent="."]
|
[node name="GameUI" type="HBoxContainer" parent="."]
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
@ -126,7 +121,7 @@ margin_bottom = 20.0
|
||||||
[node name="Label9" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
[node name="Label9" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||||
visible = false
|
visible = false
|
||||||
margin_top = 24.0
|
margin_top = 24.0
|
||||||
margin_right = 113.0
|
margin_right = 53.0
|
||||||
margin_bottom = 38.0
|
margin_bottom = 38.0
|
||||||
rect_pivot_offset = Vector2( -335, -33 )
|
rect_pivot_offset = Vector2( -335, -33 )
|
||||||
text = "FPS"
|
text = "FPS"
|
||||||
|
@ -188,9 +183,10 @@ text = "0,0"
|
||||||
|
|
||||||
[node name="Label6" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
[node name="Label6" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||||
visible = false
|
visible = false
|
||||||
margin_top = 96.0
|
margin_left = 57.0
|
||||||
margin_right = 113.0
|
margin_top = 3.0
|
||||||
margin_bottom = 110.0
|
margin_right = 170.0
|
||||||
|
margin_bottom = 17.0
|
||||||
rect_pivot_offset = Vector2( -335, -33 )
|
rect_pivot_offset = Vector2( -335, -33 )
|
||||||
text = "Mouse Tile Offset"
|
text = "Mouse Tile Offset"
|
||||||
|
|
||||||
|
@ -218,6 +214,23 @@ margin_right = 149.0
|
||||||
margin_bottom = 128.0
|
margin_bottom = 128.0
|
||||||
text = "0,0,0"
|
text = "0,0,0"
|
||||||
|
|
||||||
|
[node name="Label11" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||||
|
visible = false
|
||||||
|
margin_left = 57.0
|
||||||
|
margin_top = 3.0
|
||||||
|
margin_right = 162.0
|
||||||
|
margin_bottom = 17.0
|
||||||
|
rect_pivot_offset = Vector2( -335, -33 )
|
||||||
|
text = "Mouse Tile Axial"
|
||||||
|
|
||||||
|
[node name="mouse_tile_axial_label" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||||
|
visible = false
|
||||||
|
margin_left = 57.0
|
||||||
|
margin_top = 3.0
|
||||||
|
margin_right = 89.0
|
||||||
|
margin_bottom = 17.0
|
||||||
|
text = "0,0,0"
|
||||||
|
|
||||||
[node name="Label3" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
[node name="Label3" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||||
visible = false
|
visible = false
|
||||||
margin_top = 132.0
|
margin_top = 132.0
|
||||||
|
@ -344,10 +357,6 @@ flip_v = true
|
||||||
[node name="EditorUI" parent="." instance=ExtResource( 4 )]
|
[node name="EditorUI" parent="." instance=ExtResource( 4 )]
|
||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
[node name="StreamContainer" parent="." instance=ExtResource( 1 )]
|
|
||||||
visible = false
|
|
||||||
ShowHexTiles = true
|
|
||||||
|
|
||||||
[node name="Camera" parent="." instance=ExtResource( 3 )]
|
[node name="Camera" parent="." instance=ExtResource( 3 )]
|
||||||
|
|
||||||
[node name="InteractionSystem" type="Node" parent="."]
|
[node name="InteractionSystem" type="Node" parent="."]
|
||||||
|
@ -356,7 +365,10 @@ 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 )]
|
||||||
TileWorldNode = NodePath("../TileWorld")
|
WorldNode = NodePath("../World")
|
||||||
|
|
||||||
|
[node name="WorldInfo" parent="Player" index="2"]
|
||||||
|
WorldPath = NodePath("../../World")
|
||||||
|
|
||||||
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
|
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
|
||||||
transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 )
|
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 )
|
||||||
|
@ -365,6 +377,7 @@ transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-
|
||||||
parameters/playback = SubResource( 26 )
|
parameters/playback = SubResource( 26 )
|
||||||
|
|
||||||
[node name="Entities" type="Spatial" parent="."]
|
[node name="Entities" type="Spatial" parent="."]
|
||||||
|
visible = false
|
||||||
|
|
||||||
[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.79762, 0, 0 )
|
||||||
|
@ -380,6 +393,7 @@ script = ExtResource( 7 )
|
||||||
|
|
||||||
[node name="TileInstanceManager" type="Spatial" parent="World"]
|
[node name="TileInstanceManager" type="Spatial" parent="World"]
|
||||||
script = ExtResource( 10 )
|
script = ExtResource( 10 )
|
||||||
|
ShowHexTiles = true
|
||||||
World = NodePath("..")
|
World = NodePath("..")
|
||||||
|
|
||||||
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World/TileInstanceManager"]
|
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World/TileInstanceManager"]
|
||||||
|
@ -406,11 +420,14 @@ visible = false
|
||||||
|
|
||||||
[node name="tree" parent="World/Assets/Trees" instance=ExtResource( 19 )]
|
[node name="tree" parent="World/Assets/Trees" instance=ExtResource( 19 )]
|
||||||
|
|
||||||
|
[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 )
|
||||||
|
shadow_enabled = true
|
||||||
|
directional_shadow_mode = 0
|
||||||
|
|
||||||
[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/WorldGeneratorContainer/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorContainer" method="_on_HSlider_value_changed"]
|
||||||
[connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"]
|
[connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"]
|
||||||
|
|
||||||
[editable path="TileWorld"]
|
|
||||||
[editable path="StreamContainer"]
|
|
||||||
[editable path="Player"]
|
[editable path="Player"]
|
||||||
[editable path="Player/Geometry"]
|
[editable path="Player/Geometry"]
|
||||||
|
|
|
@ -5,24 +5,19 @@ using Godot.Collections;
|
||||||
|
|
||||||
public class TileInstanceManager : Spatial
|
public class TileInstanceManager : Spatial
|
||||||
{
|
{
|
||||||
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
|
// exports
|
||||||
private MultiMeshInstance _tileMultiMeshInstance;
|
[Export] public NodePath World;
|
||||||
private ImageTexture _viewTileTypeTexture;
|
|
||||||
|
|
||||||
private World _world;
|
|
||||||
|
|
||||||
// other members
|
|
||||||
[Export] public bool ShowHexTiles;
|
[Export] public bool ShowHexTiles;
|
||||||
[Export] public Vector2 ViewCenterPlaneCoord;
|
[Export] public Vector2 ViewCenterPlaneCoord;
|
||||||
|
|
||||||
// ui elements
|
|
||||||
|
|
||||||
// scene nodes
|
// scene nodes
|
||||||
|
public MultiMeshInstance TileMultiMeshInstance;
|
||||||
|
|
||||||
// resources
|
// other members
|
||||||
|
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
|
||||||
// exports
|
private int _usedTileInstanceIndices;
|
||||||
[Export] public NodePath World;
|
private ImageTexture _viewTileTypeTexture;
|
||||||
|
private 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()
|
||||||
|
@ -31,8 +26,8 @@ public class TileInstanceManager : Spatial
|
||||||
|
|
||||||
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
|
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
|
||||||
|
|
||||||
_tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
TileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
||||||
Debug.Assert(_tileMultiMeshInstance != null);
|
Debug.Assert(TileMultiMeshInstance != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +37,9 @@ public class TileInstanceManager : Spatial
|
||||||
|
|
||||||
private SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex)
|
private SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex)
|
||||||
{
|
{
|
||||||
var sceneTileChunk = new SceneTileChunk(chunkIndex, _tileMultiMeshInstance);
|
var sceneTileChunk =
|
||||||
|
new SceneTileChunk(chunkIndex, TileMultiMeshInstance, _usedTileInstanceIndices, ShowHexTiles);
|
||||||
|
_usedTileInstanceIndices += sceneTileChunk.TileNodes.Count;
|
||||||
|
|
||||||
foreach (var hexTile3D in sceneTileChunk.TileNodes)
|
foreach (var hexTile3D in sceneTileChunk.TileNodes)
|
||||||
{
|
{
|
||||||
|
@ -82,23 +79,15 @@ public class TileInstanceManager : Spatial
|
||||||
if (removedChunks.Count > 0)
|
if (removedChunks.Count > 0)
|
||||||
{
|
{
|
||||||
sceneTileChunk = removedChunks[^1];
|
sceneTileChunk = removedChunks[^1];
|
||||||
|
sceneTileChunk.ChunkIndex = chunkIndex;
|
||||||
removedChunks.RemoveAt(removedChunks.Count - 1);
|
removedChunks.RemoveAt(removedChunks.Count - 1);
|
||||||
GD.Print("Reused SceneTileChunk");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
|
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
|
||||||
AddChild(sceneTileChunk);
|
AddChild(sceneTileChunk);
|
||||||
GD.Print("Created SceneTileChunk");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sceneTileChunk.ChunkIndex = chunkIndex;
|
|
||||||
|
|
||||||
// if (ShowHexTiles)
|
|
||||||
// foreach (var tile3D in sceneTileChunk.TileNodes)
|
|
||||||
// tile3D.Transform = new Transform(Basis.Identity.Scaled(Vector3.One * 0.95f),
|
|
||||||
// tile3D.Transform.origin);
|
|
||||||
|
|
||||||
_sceneTileChunks.Add(sceneTileChunk);
|
_sceneTileChunks.Add(sceneTileChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +117,20 @@ public class TileInstanceManager : Spatial
|
||||||
{
|
{
|
||||||
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
||||||
private readonly MultiMeshInstance _multiMeshInstance;
|
private readonly MultiMeshInstance _multiMeshInstance;
|
||||||
private readonly HexGrid HexGrid = new();
|
private readonly Array<int> _tileInstanceIndices = new();
|
||||||
private readonly Array<int> TileInstanceIndices = new();
|
private readonly HexGrid _hexGrid = new();
|
||||||
|
private readonly bool _showHexTiles;
|
||||||
|
|
||||||
public readonly Array<HexTile3D> TileNodes = new();
|
public readonly Array<HexTile3D> TileNodes = new();
|
||||||
private Vector2 _chunkIndex = Vector2.Inf;
|
private Vector2 _chunkIndex = Vector2.Inf;
|
||||||
|
|
||||||
public SceneTileChunk(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance)
|
|
||||||
|
public SceneTileChunk(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance, int tileInstanceIndexStart,
|
||||||
|
bool showHexTiles)
|
||||||
{
|
{
|
||||||
|
_showHexTiles = showHexTiles;
|
||||||
|
|
||||||
|
var tileInstanceIndexStart1 = tileInstanceIndexStart;
|
||||||
var chunkSize = global::World.ChunkSize;
|
var chunkSize = global::World.ChunkSize;
|
||||||
|
|
||||||
foreach (var i in Enumerable.Range(0, chunkSize))
|
foreach (var i in Enumerable.Range(0, chunkSize))
|
||||||
|
@ -145,7 +140,7 @@ public class TileInstanceManager : Spatial
|
||||||
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * global::World.ChunkSize + new Vector2(i, j));
|
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * global::World.ChunkSize + new Vector2(i, j));
|
||||||
|
|
||||||
var tileTransform = Transform.Identity;
|
var tileTransform = Transform.Identity;
|
||||||
var centerPlaneCoord = HexGrid.GetHexCenterFromOffset(new Vector2(i, j));
|
var 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;
|
||||||
|
|
||||||
|
@ -157,14 +152,14 @@ public class TileInstanceManager : Spatial
|
||||||
|
|
||||||
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
|
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
|
||||||
|
|
||||||
|
Debug.Assert(tileInstanceIndexStart1 + chunkTileCount <= _multiMeshInstance.Multimesh.InstanceCount);
|
||||||
|
|
||||||
foreach (var i in Enumerable.Range(0, chunkTileCount))
|
foreach (var i in Enumerable.Range(0, chunkTileCount))
|
||||||
TileInstanceIndices.Add(_multiMeshInstance.Multimesh.InstanceCount + i);
|
_tileInstanceIndices.Add(tileInstanceIndexStart1 + i);
|
||||||
|
|
||||||
_multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
|
// _multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
|
||||||
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
||||||
|
|
||||||
GD.Print("VisibleInstanceCount = " + _multiMeshInstance.Multimesh.VisibleInstanceCount);
|
|
||||||
|
|
||||||
ChunkIndex = chunkIndex;
|
ChunkIndex = chunkIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +170,7 @@ public class TileInstanceManager : Spatial
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var chunkTransform = Transform.Identity;
|
var chunkTransform = Transform.Identity;
|
||||||
var chunkOriginPlaneCoord = HexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize);
|
var chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize);
|
||||||
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
|
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
|
||||||
Transform = chunkTransform;
|
Transform = chunkTransform;
|
||||||
_chunkIndex = value;
|
_chunkIndex = value;
|
||||||
|
@ -184,17 +179,22 @@ public class TileInstanceManager : Spatial
|
||||||
|
|
||||||
GD.Print("Updating transforms for instances of chunk " + value + " origin: " + chunkTransform.origin);
|
GD.Print("Updating transforms for instances of chunk " + value + " origin: " + chunkTransform.origin);
|
||||||
|
|
||||||
foreach (var i in Enumerable.Range(0, TileInstanceIndices.Count))
|
foreach (var i in Enumerable.Range(0, _tileInstanceIndices.Count))
|
||||||
{
|
{
|
||||||
var column = i % global::World.ChunkSize;
|
var column = i % global::World.ChunkSize;
|
||||||
var row = i / global::World.ChunkSize;
|
var row = i / global::World.ChunkSize;
|
||||||
|
|
||||||
var tilePlaneCoord =
|
var tilePlaneCoord =
|
||||||
HexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
||||||
|
|
||||||
_multiMeshInstance.Multimesh.SetInstanceTransform(TileInstanceIndices[i],
|
var hexTransform = new Transform(tileOrientation,
|
||||||
new Transform(tileOrientation,
|
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
|
||||||
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y)));
|
|
||||||
|
if (_showHexTiles)
|
||||||
|
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
|
||||||
|
hexTransform.origin);
|
||||||
|
|
||||||
|
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
using Godot;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Godot;
|
||||||
using Godot.Collections;
|
using Godot.Collections;
|
||||||
using Namespace;
|
using Namespace;
|
||||||
using Vector2 = Godot.Vector2;
|
|
||||||
using Vector3 = Godot.Vector3;
|
|
||||||
|
|
||||||
public class TileWorld : Spatial
|
public class TileWorld : Spatial
|
||||||
{
|
{
|
||||||
|
// exports
|
||||||
|
[Export] public bool DebugMap;
|
||||||
|
[ExportFlagsEnum(typeof(MapType))] public MapType GenerationMapType = MapType.Debug;
|
||||||
|
[Export] public int Size = 64;
|
||||||
|
|
||||||
|
// constants
|
||||||
|
private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f);
|
||||||
|
private static readonly Color GrassColor = new(0, 0.4f, 0);
|
||||||
|
private PackedScene _chestScene = GD.Load<PackedScene>("res://entities/Chest.tscn");
|
||||||
|
|
||||||
|
private GenerationState _currentGenerationState = GenerationState.Heightmap;
|
||||||
|
private Spatial _environmentNode;
|
||||||
|
private readonly Array<Spatial> _grassAssets = new();
|
||||||
|
|
||||||
private enum GenerationState
|
private enum GenerationState
|
||||||
{
|
{
|
||||||
Heightmap,
|
Heightmap,
|
||||||
|
@ -17,51 +29,37 @@ public class TileWorld : Spatial
|
||||||
Done
|
Done
|
||||||
}
|
}
|
||||||
|
|
||||||
// constants
|
|
||||||
private static readonly Color RockColor = new Color(0.5f, 0.5f, 0.4f);
|
|
||||||
private static readonly Color GrassColor = new Color(0, 0.4f, 0);
|
|
||||||
|
|
||||||
private GenerationState _currentGenerationState = GenerationState.Heightmap;
|
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
[Signal]
|
[Signal]
|
||||||
delegate void WorldGenerated();
|
private delegate void WorldGenerated();
|
||||||
|
|
||||||
// public members
|
// public members
|
||||||
public enum MapType
|
public enum MapType
|
||||||
{
|
{
|
||||||
Noise,
|
Noise,
|
||||||
Debug,
|
Debug,
|
||||||
Flat,
|
Flat
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExportFlagsEnum(typeof(MapType))] public MapType GenerationMapType = MapType.Debug;
|
|
||||||
|
|
||||||
[Export] public int Size = 64;
|
|
||||||
[Export] public bool DebugMap;
|
|
||||||
|
|
||||||
public float HeightScale = 1.0f;
|
|
||||||
public Image HeightmapImage;
|
|
||||||
public Image ColormapImage;
|
public Image ColormapImage;
|
||||||
|
public Spatial Entities;
|
||||||
|
public Image HeightmapImage;
|
||||||
|
public float HeightScale = 1.0f;
|
||||||
|
public HexGrid HexGrid;
|
||||||
public Image NavigationmapImage;
|
public Image NavigationmapImage;
|
||||||
public int Seed = 0;
|
public int Seed = 0;
|
||||||
public Spatial Entities;
|
|
||||||
public HexGrid HexGrid;
|
|
||||||
|
|
||||||
// private members
|
// private members
|
||||||
private int _halfSize;
|
private int _halfSize;
|
||||||
private Random _tileTypeRandom;
|
|
||||||
private Viewport _worldOffscreenViewport;
|
|
||||||
private TextureRect _worldOffscreenTextureRect;
|
|
||||||
private Viewport _heightmapOffscreenViewport;
|
|
||||||
private TextureRect _heightmapOffscreenTextureRect;
|
private TextureRect _heightmapOffscreenTextureRect;
|
||||||
private Array<Spatial> _rockAssets = new();
|
private Viewport _heightmapOffscreenViewport;
|
||||||
private Array<Spatial> _grassAssets = new();
|
private int _resizeExtraFrameCount;
|
||||||
private Array<Spatial> _treeAssets = new();
|
|
||||||
private PackedScene _chestScene = GD.Load<PackedScene>("res://entities/Chest.tscn");
|
|
||||||
private Spatial _environmentNode;
|
|
||||||
private bool _resizeTriggered;
|
private bool _resizeTriggered;
|
||||||
private int _resizeExtraFrameCount = 0;
|
private readonly Array<Spatial> _rockAssets = new();
|
||||||
|
private Random _tileTypeRandom;
|
||||||
|
private readonly Array<Spatial> _treeAssets = new();
|
||||||
|
private TextureRect _worldOffscreenTextureRect;
|
||||||
|
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()
|
||||||
|
@ -149,10 +147,6 @@ public class TileWorld : Spatial
|
||||||
_heightmapOffscreenViewport.Size = new Vector2(size, size);
|
_heightmapOffscreenViewport.Size = new Vector2(size, size);
|
||||||
|
|
||||||
_halfSize = Mathf.RoundToInt(size) / 2;
|
_halfSize = Mathf.RoundToInt(size) / 2;
|
||||||
HexGrid.SetBounds(
|
|
||||||
TextureCoordToCell(new Vector2(0, 0)),
|
|
||||||
TextureCoordToCell(new Vector2(size, size))
|
|
||||||
);
|
|
||||||
|
|
||||||
HexGrid.Obstacles.Clear();
|
HexGrid.Obstacles.Clear();
|
||||||
HexGrid.Barriers.Clear();
|
HexGrid.Barriers.Clear();
|
||||||
|
@ -282,7 +276,7 @@ public class TileWorld : Spatial
|
||||||
EmitSignal("WorldGenerated");
|
EmitSignal("WorldGenerated");
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -301,13 +295,13 @@ public class TileWorld : Spatial
|
||||||
return assetInstance;
|
return assetInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -461,16 +455,13 @@ public class TileWorld : Spatial
|
||||||
|
|
||||||
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
|
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
|
||||||
|
|
||||||
float heightmapHeight = (HeightmapImage.GetPixel((int)textureCoord.x, (int)(textureCoord.y)).r - 0.5f) * HeightScale;
|
float heightmapHeight = (HeightmapImage.GetPixel((int)textureCoord.x, (int)(textureCoord.y)).r - 0.5f) *
|
||||||
|
HeightScale;
|
||||||
|
|
||||||
if (heightmapHeight <= 0)
|
if (heightmapHeight <= 0)
|
||||||
{
|
|
||||||
heightmapHeight -= 0.03f;
|
heightmapHeight -= 0.03f;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
heightmapHeight = 0f;
|
heightmapHeight = 0f;
|
||||||
}
|
|
||||||
|
|
||||||
return heightmapHeight;
|
return heightmapHeight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,6 @@
|
||||||
[node name="TileWorld" type="Spatial"]
|
[node name="TileWorld" type="Spatial"]
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
|
|
||||||
[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 )
|
|
||||||
shadow_enabled = true
|
|
||||||
directional_shadow_mode = 0
|
|
||||||
|
|
||||||
[node name="WorldOffscreenViewport" type="Viewport" parent="."]
|
[node name="WorldOffscreenViewport" type="Viewport" parent="."]
|
||||||
size = Vector2( 100, 100 )
|
size = Vector2( 100, 100 )
|
||||||
own_world = true
|
own_world = true
|
||||||
|
|
104
scenes/World.cs
104
scenes/World.cs
|
@ -17,7 +17,7 @@ public class World : Spatial
|
||||||
}
|
}
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
public const int ChunkSize = 10;
|
public const int ChunkSize = 18;
|
||||||
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 RockColor = new(0.5f, 0.5f, 0.4f);
|
||||||
|
@ -41,6 +41,8 @@ public class World : Spatial
|
||||||
|
|
||||||
// other members
|
// other members
|
||||||
private Vector2 _centerPlaneCoord;
|
private Vector2 _centerPlaneCoord;
|
||||||
|
private Vector2 _chunkIndexNorthEast;
|
||||||
|
private Vector2 _chunkIndexSouthWest;
|
||||||
private Array<Spatial> _grassAssets;
|
private Array<Spatial> _grassAssets;
|
||||||
private ImageTexture _heightmapTexture;
|
private ImageTexture _heightmapTexture;
|
||||||
|
|
||||||
|
@ -58,6 +60,26 @@ public class World : Spatial
|
||||||
public GenerationState State = GenerationState.Done;
|
public GenerationState State = GenerationState.Done;
|
||||||
public Vector2 WorldTextureCoordinateOffset;
|
public Vector2 WorldTextureCoordinateOffset;
|
||||||
|
|
||||||
|
|
||||||
|
// ui elements
|
||||||
|
|
||||||
|
// scene nodes
|
||||||
|
|
||||||
|
// resources
|
||||||
|
|
||||||
|
// exports
|
||||||
|
// [Export] public Vector2 Size = new Vector2(1, 1);
|
||||||
|
|
||||||
|
// signals
|
||||||
|
[Signal]
|
||||||
|
private delegate void OnTilesChanged(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices);
|
||||||
|
|
||||||
|
[Signal]
|
||||||
|
private delegate void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage);
|
||||||
|
|
||||||
|
[Signal]
|
||||||
|
private delegate void OnHeightmapImageChanged(Image heightmapImage);
|
||||||
|
|
||||||
public World()
|
public World()
|
||||||
{
|
{
|
||||||
Debug.Assert(ChunkSize % 2 == 0);
|
Debug.Assert(ChunkSize % 2 == 0);
|
||||||
|
@ -73,6 +95,8 @@ public class World : Spatial
|
||||||
|
|
||||||
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
|
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
|
||||||
Debug.Assert(_tileInstanceManager != null);
|
Debug.Assert(_tileInstanceManager != null);
|
||||||
|
_tileInstanceManager.TileMultiMeshInstance.Multimesh.InstanceCount =
|
||||||
|
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
|
||||||
|
|
||||||
InitNoiseGenerator();
|
InitNoiseGenerator();
|
||||||
|
|
||||||
|
@ -217,6 +241,16 @@ public class World : Spatial
|
||||||
tileTypeImage.Unlock();
|
tileTypeImage.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2 WorldToOffsetCoords(Vector3 fromPositionWorld)
|
||||||
|
{
|
||||||
|
return HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z)).OffsetCoords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 GetHexCenterFromOffset(Vector2 fromPositionOffset)
|
||||||
|
{
|
||||||
|
return HexGrid.GetHexCenterVec3FromOffset(fromPositionOffset);
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
|
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
|
||||||
{
|
{
|
||||||
if (State != GenerationState.Done)
|
if (State != GenerationState.Done)
|
||||||
|
@ -326,7 +360,14 @@ public class World : Spatial
|
||||||
|
|
||||||
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
|
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
|
||||||
{
|
{
|
||||||
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
|
if (!_centerChunkRect.HasPoint(centerPlaneCoord))
|
||||||
|
{
|
||||||
|
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
|
||||||
|
|
||||||
|
UpdateChunkBounds();
|
||||||
|
|
||||||
|
UpdateNavigationBounds();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateWorldViewTexture()
|
private void UpdateWorldViewTexture()
|
||||||
|
@ -340,18 +381,10 @@ public class World : Spatial
|
||||||
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
|
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
|
||||||
Image.Format.Rgba8);
|
Image.Format.Rgba8);
|
||||||
|
|
||||||
var chunkIndexSouthWest = Vector2.Inf;
|
|
||||||
var chunkIndexNorthEast = -Vector2.Inf;
|
|
||||||
|
|
||||||
foreach (var chunkIndex in _activeChunkIndices)
|
foreach (var chunkIndex in _activeChunkIndices)
|
||||||
{
|
{
|
||||||
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
|
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
|
||||||
|
|
||||||
if (chunkIndex.x <= chunkIndexSouthWest.x && chunkIndex.y <= chunkIndexSouthWest.y)
|
|
||||||
chunkIndexSouthWest = chunkIndex;
|
|
||||||
else if (chunkIndex.x >= chunkIndexNorthEast.x && chunkIndex.y >= chunkIndexNorthEast.y)
|
|
||||||
chunkIndexNorthEast = chunkIndex;
|
|
||||||
|
|
||||||
_heightmapImage.BlendRect(
|
_heightmapImage.BlendRect(
|
||||||
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
|
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
|
||||||
new Rect2(Vector2.Zero, Vector2.One * worldChunkSize),
|
new Rect2(Vector2.Zero, Vector2.One * worldChunkSize),
|
||||||
|
@ -369,12 +402,42 @@ public class World : Spatial
|
||||||
_viewTileTypeTexture = new ImageTexture();
|
_viewTileTypeTexture = new ImageTexture();
|
||||||
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage);
|
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage);
|
||||||
|
|
||||||
WorldTextureCoordinateOffset = chunkIndexSouthWest * worldChunkSize;
|
WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize;
|
||||||
|
|
||||||
EmitSignal("OnWorldViewTileTypeImageChanged", _tileTypeMapImage);
|
EmitSignal("OnWorldViewTileTypeImageChanged", _tileTypeMapImage);
|
||||||
EmitSignal("OnHeightmapImageChanged", _heightmapImage);
|
EmitSignal("OnHeightmapImageChanged", _heightmapImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateChunkBounds()
|
||||||
|
{
|
||||||
|
_chunkIndexSouthWest = Vector2.Inf;
|
||||||
|
_chunkIndexNorthEast = -Vector2.Inf;
|
||||||
|
|
||||||
|
foreach (var chunkIndex in _activeChunkIndices)
|
||||||
|
{
|
||||||
|
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
|
||||||
|
|
||||||
|
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y)
|
||||||
|
_chunkIndexSouthWest = chunkIndex;
|
||||||
|
else if (chunkIndex.x >= _chunkIndexNorthEast.x && chunkIndex.y >= _chunkIndexNorthEast.y)
|
||||||
|
_chunkIndexNorthEast = chunkIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateNavigationBounds()
|
||||||
|
{
|
||||||
|
var cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize);
|
||||||
|
// Chunks have their cells ordered from south west (0,0) to north east (ChunkSize, ChunkSize). For the
|
||||||
|
// north east cell we have to add the chunk size to get to the actual corner cell.
|
||||||
|
var cellNorthEast = HexGrid.GetHexAtOffset(_chunkIndexNorthEast * ChunkSize + Vector2.One * (ChunkSize - 1));
|
||||||
|
|
||||||
|
var centerCell =
|
||||||
|
HexGrid.GetHexAtOffset(((cellNorthEast.OffsetCoords - cellSouthWest.OffsetCoords) / 2).Round());
|
||||||
|
var numCells = ChunkSize * Math.Max(NumChunkColumns, NumChunkRows);
|
||||||
|
|
||||||
|
HexGrid.SetBoundsOffset(cellSouthWest, ChunkSize * new Vector2(NumChunkColumns, NumChunkRows));
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Process(float delta)
|
public override void _Process(float delta)
|
||||||
{
|
{
|
||||||
var oldState = State;
|
var oldState = State;
|
||||||
|
@ -431,23 +494,4 @@ public class World : Spatial
|
||||||
State = GenerationState.Done;
|
State = GenerationState.Done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ui elements
|
|
||||||
|
|
||||||
// scene nodes
|
|
||||||
|
|
||||||
// resources
|
|
||||||
|
|
||||||
// exports
|
|
||||||
// [Export] public Vector2 Size = new Vector2(1, 1);
|
|
||||||
|
|
||||||
// signals
|
|
||||||
[Signal]
|
|
||||||
private delegate void OnTilesChanged(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices);
|
|
||||||
|
|
||||||
[Signal]
|
|
||||||
private delegate void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage);
|
|
||||||
|
|
||||||
[Signal]
|
|
||||||
private delegate void OnHeightmapImageChanged(Image heightmapImage);
|
|
||||||
}
|
}
|
|
@ -1,32 +1,7 @@
|
||||||
using Godot;
|
using Godot;
|
||||||
using System;
|
|
||||||
using Array = Godot.Collections.Array;
|
|
||||||
|
|
||||||
public class EditorUI : Control
|
public class EditorUI : Control
|
||||||
{
|
{
|
||||||
// exported members
|
|
||||||
[Export] public NodePath World;
|
|
||||||
[Export] public NodePath StreamContainer;
|
|
||||||
|
|
||||||
// public members
|
|
||||||
public Vector2 currentTileOffset = Vector2.Zero;
|
|
||||||
|
|
||||||
// private members
|
|
||||||
private Button _resetButton;
|
|
||||||
private Button _grassButton;
|
|
||||||
private Button _sandButton;
|
|
||||||
private Button _waterButton;
|
|
||||||
private Button _obstacleButton;
|
|
||||||
private Button _navigateButton;
|
|
||||||
private ShaderMaterial _tileMaterial;
|
|
||||||
|
|
||||||
private CheckBox _gameGeometryCheckBox;
|
|
||||||
private CheckBox _physicsGeometryCheckBox;
|
|
||||||
private CheckBox _navigationGeometryCheckBox;
|
|
||||||
|
|
||||||
private TileWorld _tileWorld;
|
|
||||||
private StreamContainer _streamContainer;
|
|
||||||
|
|
||||||
public enum InputMode
|
public enum InputMode
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@ -36,13 +11,32 @@ public class EditorUI : Control
|
||||||
Obstacle,
|
Obstacle,
|
||||||
Navigate
|
Navigate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CheckBox _gameGeometryCheckBox;
|
||||||
|
private Button _grassButton;
|
||||||
|
private Button _navigateButton;
|
||||||
|
private CheckBox _navigationGeometryCheckBox;
|
||||||
|
private Button _obstacleButton;
|
||||||
|
private CheckBox _physicsGeometryCheckBox;
|
||||||
|
|
||||||
|
// private members
|
||||||
|
private Button _resetButton;
|
||||||
|
private Button _sandButton;
|
||||||
|
private StreamContainer _streamContainer;
|
||||||
|
private ShaderMaterial _tileMaterial;
|
||||||
|
|
||||||
|
private TileWorld _tileWorld;
|
||||||
|
private Button _waterButton;
|
||||||
|
|
||||||
public InputMode CurrentInputMode = InputMode.None;
|
public InputMode CurrentInputMode = InputMode.None;
|
||||||
|
// exported members
|
||||||
|
|
||||||
|
// public members
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
_tileWorld = (TileWorld) GetNode(World);
|
|
||||||
_streamContainer = (StreamContainer)GetNode(StreamContainer);
|
|
||||||
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
|
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
|
@ -90,7 +84,6 @@ public class EditorUI : Control
|
||||||
public void OnSandButton()
|
public void OnSandButton()
|
||||||
{
|
{
|
||||||
CurrentInputMode = InputMode.Sand;
|
CurrentInputMode = InputMode.Sand;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnWaterButton()
|
public void OnWaterButton()
|
||||||
|
@ -98,43 +91,32 @@ public class EditorUI : Control
|
||||||
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)
|
||||||
|
@ -167,11 +149,14 @@ public class EditorUI : Control
|
||||||
{
|
{
|
||||||
switch (CurrentInputMode)
|
switch (CurrentInputMode)
|
||||||
{
|
{
|
||||||
case InputMode.Grass:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
|
case InputMode.Grass:
|
||||||
|
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
|
||||||
break;
|
break;
|
||||||
case InputMode.Water:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Blue);
|
case InputMode.Water:
|
||||||
|
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Blue);
|
||||||
break;
|
break;
|
||||||
case InputMode.Sand:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Yellow);
|
case InputMode.Sand:
|
||||||
|
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Yellow);
|
||||||
break;
|
break;
|
||||||
case InputMode.Obstacle:
|
case InputMode.Obstacle:
|
||||||
_tileWorld.MarkCellUnwalkable(HexCell.FromOffsetCoords(offsetCoord));
|
_tileWorld.MarkCellUnwalkable(HexCell.FromOffsetCoords(offsetCoord));
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
using Godot;
|
using Godot;
|
||||||
using GoDotTest;
|
using GoDotTest;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
public class NavigationComponentTests : TestClass
|
public class NavigationComponentTests : TestClass
|
||||||
{
|
{
|
||||||
private Node _testScene = null;
|
private readonly Node _testScene;
|
||||||
private TileWorld _tileWorld;
|
|
||||||
private NavigationComponent _navigationComponent;
|
|
||||||
private KinematicBody _kinematicBody;
|
|
||||||
|
|
||||||
private PackedScene _tileWorldScene = GD.Load<PackedScene>("res://scenes/TileWorld.tscn");
|
private readonly PackedScene _WorldScene = GD.Load<PackedScene>("res://scenes/World.tscn");
|
||||||
|
private KinematicBody _kinematicBody;
|
||||||
|
private NavigationComponent _navigationComponent;
|
||||||
|
private World _world;
|
||||||
|
|
||||||
public NavigationComponentTests(Node testScene) : base(testScene)
|
public NavigationComponentTests(Node testScene) : base(testScene)
|
||||||
{
|
{
|
||||||
|
@ -21,11 +18,11 @@ public class NavigationComponentTests : TestClass
|
||||||
[Setup]
|
[Setup]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_tileWorld = (TileWorld)_tileWorldScene.Instance();
|
_world = (World)_WorldScene.Instance();
|
||||||
_tileWorld.HexGrid = new HexGrid();
|
_world.HexGrid = new HexGrid();
|
||||||
_testScene.AddChild(_tileWorld);
|
_testScene.AddChild(_world);
|
||||||
_kinematicBody = new KinematicBody();
|
_kinematicBody = new KinematicBody();
|
||||||
_navigationComponent = new NavigationComponent();
|
_navigationComponent = new NavigationComponent();
|
||||||
_navigationComponent.TileWorld = _tileWorld;
|
_navigationComponent.World = _world;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue