Tile instancing now working, also fixed navigation bounds.
parent
eaaa5219ed
commit
c59d92618b
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Godot;
|
||||
using Vector2 = Godot.Vector2;
|
||||
using Vector3 = Godot.Vector3;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
|
@ -10,23 +6,21 @@ public class GroundMotionComponent : Component
|
|||
{
|
||||
public float Accel = 50;
|
||||
public float Damping = 0.2f;
|
||||
public float MaxSpeed = 8;
|
||||
|
||||
public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180;
|
||||
public Vector3 DirectionToTarget = Vector3.Zero;
|
||||
public float DistanceToTarget = 0;
|
||||
public float TargetAngle = 0;
|
||||
public float TargetDeltaAngle = 0;
|
||||
|
||||
|
||||
public float DistanceToTarget;
|
||||
public float MaxSpeed = 8;
|
||||
|
||||
public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180;
|
||||
public float TargetAngle;
|
||||
public float TargetDeltaAngle;
|
||||
|
||||
private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation)
|
||||
{
|
||||
float deltaAngleAbsolute = Mathf.Abs(TargetDeltaAngle);
|
||||
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;
|
||||
TargetDeltaAngle = 0;
|
||||
}
|
||||
|
@ -40,24 +34,26 @@ public class GroundMotionComponent : Component
|
|||
TargetDeltaAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition)
|
||||
{
|
||||
Vector2 planeTargetDirection = new Vector2(DirectionToTarget.x, DirectionToTarget.z);
|
||||
|
||||
|
||||
Vector2 planeVelocity = new Vector2(entity.Velocity.x, entity.Velocity.z);
|
||||
// GD.Print("-- Step: distance: " + targetDistance + " dir: " + planeTargetDirection + " speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
|
||||
planeVelocity -= planeVelocity * Damping;
|
||||
// GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
|
||||
|
||||
|
||||
if (DistanceToTarget < 0.01)
|
||||
{
|
||||
planeVelocity = Vector2.Zero;
|
||||
} else if (TargetDeltaAngle == 0.0) {
|
||||
}
|
||||
else if (TargetDeltaAngle == 0.0)
|
||||
{
|
||||
planeVelocity = planeVelocity.Length() * planeTargetDirection + planeTargetDirection * Accel * delta;
|
||||
// GD.Print(" accel: speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
|
||||
|
||||
float projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
|
||||
var projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
|
||||
// GD.Print(" Projected step: " + projectedStep + " Speed: " + planeVelocity.Length() + " delta: " + delta);
|
||||
if (projectedStep > DistanceToTarget)
|
||||
{
|
||||
|
@ -65,12 +61,9 @@ public class GroundMotionComponent : Component
|
|||
projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
|
||||
// GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep);
|
||||
}
|
||||
|
||||
float planeSpeed = planeVelocity.Length();
|
||||
if (planeSpeed > MaxSpeed)
|
||||
{
|
||||
planeVelocity *= MaxSpeed / planeSpeed;
|
||||
}
|
||||
|
||||
var planeSpeed = planeVelocity.Length();
|
||||
if (planeSpeed > MaxSpeed) planeVelocity *= MaxSpeed / planeSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -94,7 +87,7 @@ public class GroundMotionComponent : Component
|
|||
float nextHeight = tileWorld.GetHeightAtOffset(nextTile);
|
||||
|
||||
bool isOnFloor = entity.IsOnFloor();
|
||||
|
||||
|
||||
if (nextHeight - entity.GlobalTransform.origin.y > 0.1)
|
||||
{
|
||||
GD.Print("Jump!");
|
||||
|
@ -107,14 +100,14 @@ public class GroundMotionComponent : Component
|
|||
}
|
||||
|
||||
entityVelocity.y -= 9.81f * delta;
|
||||
|
||||
|
||||
entity.Velocity = entityVelocity;
|
||||
}
|
||||
|
||||
|
||||
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation, TileWorld tileWorld)
|
||||
|
||||
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation,
|
||||
World world)
|
||||
{
|
||||
DirectionToTarget = (targetPosition - entity.GlobalTranslation);
|
||||
DirectionToTarget = targetPosition - entity.GlobalTranslation;
|
||||
DirectionToTarget.y = 0;
|
||||
DistanceToTarget = DirectionToTarget.Length();
|
||||
|
||||
|
@ -122,7 +115,7 @@ public class GroundMotionComponent : Component
|
|||
{
|
||||
DirectionToTarget = DirectionToTarget.Normalized();
|
||||
TargetAngle = Vector3.Right.SignedAngleTo(DirectionToTarget, Vector3.Up);
|
||||
TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget);
|
||||
TargetDeltaAngle = entity.CalcShortestPlaneRotationToTargetDirection(DirectionToTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -134,15 +127,14 @@ public class GroundMotionComponent : Component
|
|||
entity.Velocity = new Vector3(0, entity.Velocity.y, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CalcAndApplyOrientation(delta, entity, targetPosition, targetOrientation);
|
||||
CalcPlaneVelocity(delta, entity, targetPosition);
|
||||
// CalcVerticalVelocity(delta, entity, tileWorld);
|
||||
|
||||
entity.Velocity = entity.MoveAndSlide(entity.Velocity);
|
||||
|
||||
|
||||
//GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity);
|
||||
//GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,92 +4,23 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using Godot;
|
||||
using GodotComponentTest.utils;
|
||||
using Vector2 = Godot.Vector2;
|
||||
using Vector3 = Godot.Vector3;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class NavigationComponent : Spatial
|
||||
{
|
||||
public class NavigationPoint
|
||||
{
|
||||
[Flags]
|
||||
public enum NavigationFlags
|
||||
{
|
||||
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;
|
||||
public World World { set; get; }
|
||||
public Vector3 CurrentGoalPositionWorld { get; private set; } = Vector3.Zero;
|
||||
public float CurrentGoalAngleWorld { get; } = 0;
|
||||
public Quat CurrentGoalOrientationWorld { get; private set; } = Quat.Identity;
|
||||
|
||||
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 List<NavigationPoint> _pathWorldNavigationPoints = new();
|
||||
private List<NavigationPoint> _planningPathSmoothedWorldNavigationPoints = new();
|
||||
|
||||
private List<NavigationPoint> _planningPathWorldNavigationPoints = new();
|
||||
private List<NavigationPoint> _smoothedPathWorldNavigationPoints = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
@ -99,9 +30,9 @@ public class NavigationComponent : Spatial
|
|||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
Debug.Assert(TileWorld != null);
|
||||
Debug.Assert(World != null);
|
||||
}
|
||||
|
||||
|
||||
public void PlanSmoothedPath(KinematicBody body, Transform fromTransformWorld, NavigationPoint navigationPoint)
|
||||
{
|
||||
if (navigationPoint.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
|
||||
|
@ -121,8 +52,8 @@ public class NavigationComponent : Spatial
|
|||
|
||||
public void FindPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
||||
{
|
||||
HexCell fromCell = TileWorld.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
|
||||
if (TileWorld.HexGrid.GetHexCost(fromCell) == 0)
|
||||
var fromCell = World.HexGrid.GetHexAt(new Vector2(fromPositionWorld.x, fromPositionWorld.z));
|
||||
if (World.HexGrid.GetHexCost(fromCell) == 0)
|
||||
{
|
||||
GD.Print("Invalid starting point for FindPath(): returning empty path.");
|
||||
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
||||
|
@ -130,11 +61,11 @@ public class NavigationComponent : Spatial
|
|||
_planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints;
|
||||
return;
|
||||
}
|
||||
|
||||
HexCell toCell = TileWorld.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
|
||||
toCell = TileWorld.HexGrid.GetClosestWalkableCell(fromCell, toCell);
|
||||
|
||||
if (TileWorld.HexGrid.GetHexCost(toCell) == 0)
|
||||
|
||||
var toCell = World.HexGrid.GetHexAt(new Vector2(toPositionWorld.x, toPositionWorld.z));
|
||||
toCell = World.HexGrid.GetClosestWalkableCell(fromCell, toCell);
|
||||
|
||||
if (World.HexGrid.GetHexCost(toCell) == 0)
|
||||
{
|
||||
GD.Print("Invalid target point for FindPath(): returning empty path.");
|
||||
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
||||
|
@ -142,25 +73,26 @@ public class NavigationComponent : Spatial
|
|||
_planningPathSmoothedWorldNavigationPoints = _planningPathWorldNavigationPoints;
|
||||
return;
|
||||
}
|
||||
|
||||
TileWorld.HexGrid.SetBounds(fromCell, 40);
|
||||
List<HexCell> path = TileWorld.HexGrid.FindPath(fromCell, toCell);
|
||||
|
||||
var path = World.HexGrid.FindPath(fromCell, toCell);
|
||||
|
||||
// Generate grid navigation points
|
||||
_planningPathWorldNavigationPoints = new List<NavigationPoint>();
|
||||
foreach (int index in Enumerable.Range(0, path.Count))
|
||||
foreach (var index in Enumerable.Range(0, path.Count))
|
||||
{
|
||||
_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
|
||||
if (_planningPathWorldNavigationPoints.Count > 0 && (_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() <
|
||||
if (_planningPathWorldNavigationPoints.Count > 0 &&
|
||||
(_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() <
|
||||
0.5f * 0.5f)
|
||||
{
|
||||
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition = toPositionWorld;
|
||||
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition =
|
||||
toPositionWorld;
|
||||
}
|
||||
|
||||
|
||||
// Perform smoothing
|
||||
_planningPathSmoothedWorldNavigationPoints = SmoothPath(body, _planningPathWorldNavigationPoints);
|
||||
|
||||
|
@ -176,18 +108,19 @@ public class NavigationComponent : Spatial
|
|||
FindPath(body, fromPositionWorld, navigationPoint.WorldPosition);
|
||||
|
||||
_planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1] = navigationPoint;
|
||||
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] = navigationPoint;
|
||||
_planningPathSmoothedWorldNavigationPoints[_planningPathSmoothedWorldNavigationPoints.Count - 1] =
|
||||
navigationPoint;
|
||||
}
|
||||
|
||||
|
||||
public void PlanGridPath(KinematicBody body, Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
||||
{
|
||||
Vector2 fromPositionOffset = TileWorld.WorldToOffsetCoords(fromPositionWorld);
|
||||
Vector2 toPositionOffset = TileWorld.WorldToOffsetCoords(toPositionWorld);
|
||||
var fromPositionOffset = World.WorldToOffsetCoords(fromPositionWorld);
|
||||
var toPositionOffset = World.WorldToOffsetCoords(toPositionWorld);
|
||||
|
||||
HexCell fromCell = new HexCell();
|
||||
var fromCell = new HexCell();
|
||||
fromCell.OffsetCoords = fromPositionOffset;
|
||||
|
||||
HexCell toCell = new HexCell();
|
||||
var toCell = new HexCell();
|
||||
toCell.OffsetCoords = toPositionOffset;
|
||||
|
||||
_path = fromCell.LineTo(toCell);
|
||||
|
@ -195,15 +128,15 @@ public class NavigationComponent : Spatial
|
|||
|
||||
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
||||
_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(
|
||||
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)
|
||||
{
|
||||
// Remove the last one, because it is only the position rounded to HexGrid coordinates.
|
||||
|
@ -287,7 +220,7 @@ public class NavigationComponent : Spatial
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool CheckSweptTriangleCellCollision(Vector3 startWorld, Vector3 endWorld, float radius)
|
||||
{
|
||||
Vector2 startPlane = new Vector2(startWorld.x, startWorld.z);
|
||||
|
@ -295,36 +228,37 @@ public class NavigationComponent : Spatial
|
|||
Vector2 directionPlane = (endPlane - startPlane).Normalized();
|
||||
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)
|
||||
{
|
||||
if (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
||||
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
||||
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (TileWorld.HexGrid.GetHexCost(cell) == 0)
|
||||
if (World.HexGrid.GetHexCost(cell) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public List<NavigationPoint> SmoothPath(KinematicBody body, List<NavigationPoint> navigationPoints)
|
||||
{
|
||||
if (navigationPoints.Count <= 2)
|
||||
|
@ -360,7 +294,7 @@ public class NavigationComponent : Spatial
|
|||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (endIndex == navigationPoints.Count - 1)
|
||||
{
|
||||
break;
|
||||
|
@ -407,7 +341,7 @@ public class NavigationComponent : Spatial
|
|||
}
|
||||
|
||||
_currentGoal = _pathWorldNavigationPoints[0];
|
||||
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
||||
CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
||||
//_currentGoalOrientationWorld = Vector3.Right.SignedAngleTo(_pathWorldNavigationPoints[0].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)
|
||||
{
|
||||
_currentGoalPositionWorld = worldTransform.origin;
|
||||
CurrentGoalPositionWorld = worldTransform.origin;
|
||||
}
|
||||
else if (_currentGoal.Flags == NavigationPoint.NavigationFlags.Position)
|
||||
{
|
||||
_currentGoalOrientationWorld = worldTransform.basis.Quat();
|
||||
CurrentGoalOrientationWorld = worldTransform.basis.Quat();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,32 +368,36 @@ public class NavigationComponent : Spatial
|
|||
{
|
||||
_currentGoal = new NavigationPoint(currentTransformWorld);
|
||||
}
|
||||
|
||||
|
||||
if (_pathWorldNavigationPoints.Count == 0)
|
||||
{
|
||||
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||
_currentGoalPositionWorld = currentTransformWorld.origin;
|
||||
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||
CurrentGoalPositionWorld = currentTransformWorld.origin;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
|
||||
{
|
||||
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
||||
CurrentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentGoalPositionWorld = currentTransformWorld.origin;
|
||||
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||
}
|
||||
|
||||
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
|
||||
{
|
||||
_currentGoalOrientationWorld = _currentGoal.WorldOrientation;
|
||||
CurrentGoalOrientationWorld = _currentGoal.WorldOrientation;
|
||||
}
|
||||
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))
|
||||
{
|
||||
_pathWorldNavigationPoints.RemoveAt(0);
|
||||
|
@ -470,11 +408,16 @@ public class NavigationComponent : Spatial
|
|||
|
||||
if (_pathWorldNavigationPoints.Count == 0)
|
||||
{
|
||||
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||
_currentGoalPositionWorld = currentTransformWorld.origin;
|
||||
CurrentGoalOrientationWorld = currentTransformWorld.basis.Quat();
|
||||
CurrentGoalPositionWorld = currentTransformWorld.origin;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGoalReached()
|
||||
{
|
||||
return _pathWorldNavigationPoints.Count == 0;
|
||||
}
|
||||
|
||||
public void DebugDraw(Spatial parentNode, DebugGeometry debugGeometry)
|
||||
{
|
||||
Vector3 yOffset = Vector3.Up * 0.1f;
|
||||
|
@ -511,7 +454,7 @@ public class NavigationComponent : Spatial
|
|||
|
||||
previousPoint = point.WorldPosition;
|
||||
}
|
||||
|
||||
|
||||
previousPoint = parentNode.GlobalTranslation;
|
||||
foreach (NavigationPoint point in _planningPathWorldNavigationPoints)
|
||||
{
|
||||
|
@ -521,7 +464,7 @@ public class NavigationComponent : Spatial
|
|||
|
||||
previousPoint = point.WorldPosition;
|
||||
}
|
||||
|
||||
|
||||
previousPoint = parentNode.GlobalTranslation;
|
||||
foreach (NavigationPoint point in _planningPathSmoothedWorldNavigationPoints)
|
||||
{
|
||||
|
@ -534,4 +477,62 @@ public class NavigationComponent : Spatial
|
|||
|
||||
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,24 +1,54 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using GodotComponentTest.entities;
|
||||
using Object = Godot.Object;
|
||||
using Godot;
|
||||
|
||||
public class TaskQueueComponent : Component
|
||||
{
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes a Task on the specified entity.
|
||||
/// Executes a Task on the specified entity.
|
||||
/// </summary>
|
||||
/// <returns>true when the Task is complete, false otherwise</returns>
|
||||
public abstract bool PerformTask(Entity entity, float delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes an entity move towards a target (translation and or orientation).
|
||||
/// </summary>
|
||||
public class NavigationTask : Task
|
||||
{
|
||||
public NavigationComponent.NavigationPoint NavigationPoint;
|
||||
|
@ -49,45 +79,4 @@ public class TaskQueueComponent : Component
|
|||
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 System;
|
||||
|
||||
public class WorldInfoComponent : Component
|
||||
{
|
||||
[Export] public NodePath World;
|
||||
|
||||
public TileWorld TileWorld;
|
||||
[Export] public NodePath WorldPath;
|
||||
public World World;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
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.Diagnostics;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using GodotComponentTest.components;
|
||||
using GodotComponentTest.entities;
|
||||
using GodotComponentTest.utils;
|
||||
|
@ -10,52 +8,40 @@ using GodotComponentTest.utils;
|
|||
public class Player : Entity, IInteractionInterface
|
||||
{
|
||||
// public members
|
||||
[Export] public NodePath TileWorldNode;
|
||||
|
||||
public int GoldCount = 0;
|
||||
[Export] public NodePath WorldNode;
|
||||
|
||||
public int GoldCount;
|
||||
public TaskQueueComponent TaskQueueComponent;
|
||||
|
||||
public NavigationComponent Navigation
|
||||
{
|
||||
get { return _navigationComponent; }
|
||||
}
|
||||
public NavigationComponent NavigationComponent { get; private set; }
|
||||
public InteractionComponent InteractionComponent { get; set; }
|
||||
|
||||
[Signal]
|
||||
delegate void GoldCountChanged(int goldCount);
|
||||
private delegate void GoldCountChanged(int goldCount);
|
||||
|
||||
// private members
|
||||
private WorldInfoComponent _worldInfo;
|
||||
private GroundMotionComponent _groundMotion;
|
||||
private NavigationComponent _navigationComponent;
|
||||
private Area _itemAttractorArea;
|
||||
private Area _itemPickupArea;
|
||||
private List<Node> _attractedItemList = new List<Node>();
|
||||
private BoneAttachment _toolAttachement;
|
||||
private AnimationPlayer _playerAnimationPlayer;
|
||||
private readonly List<Node> _attractedItemList = new();
|
||||
private AnimationTree _animationTree;
|
||||
private DebugGeometry _debugGeometry;
|
||||
private InteractionComponent _interactionComponent;
|
||||
|
||||
public InteractionComponent InteractionComponent
|
||||
{
|
||||
get => _interactionComponent;
|
||||
set => _interactionComponent = value;
|
||||
}
|
||||
private GroundMotionComponent _groundMotion;
|
||||
private Area _itemAttractorArea;
|
||||
private Area _itemPickupArea;
|
||||
private AnimationPlayer _playerAnimationPlayer;
|
||||
private BoneAttachment _toolAttachement;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
_groundMotion = new GroundMotionComponent();
|
||||
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
|
||||
_navigationComponent = (NavigationComponent)FindNode("Navigation", false);
|
||||
_navigationComponent.TileWorld = GetNode<TileWorld>(TileWorldNode);
|
||||
NavigationComponent = (NavigationComponent)FindNode("Navigation", false);
|
||||
NavigationComponent.World = GetNode<World>(WorldNode);
|
||||
TaskQueueComponent = new TaskQueueComponent();
|
||||
|
||||
_itemAttractorArea = (Area)FindNode("ItemAttractorArea", false);
|
||||
if (_itemAttractorArea == null)
|
||||
{
|
||||
GD.PushWarning("No ItemAttractorArea node found for " + this.GetClass());
|
||||
GD.PushWarning("No ItemAttractorArea node found for " + GetClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -65,27 +51,20 @@ public class Player : Entity, IInteractionInterface
|
|||
|
||||
_itemPickupArea = (Area)FindNode("ItemPickupArea", false);
|
||||
if (_itemPickupArea == null)
|
||||
{
|
||||
GD.PushWarning("No ItemPickupArea node found for " + this.GetClass());
|
||||
}
|
||||
GD.PushWarning("No ItemPickupArea node found for " + GetClass());
|
||||
else
|
||||
{
|
||||
_itemPickupArea.Connect("body_entered", this, nameof(OnItemPickupAreaBodyEntered));
|
||||
}
|
||||
|
||||
_playerAnimationPlayer = GetNode<AnimationPlayer>("Geometry/AnimationPlayer");
|
||||
Debug.Assert(_playerAnimationPlayer != null);
|
||||
_animationTree = GetNode<AnimationTree>("Geometry/AnimationTree");
|
||||
Debug.Assert(_animationTree != null);
|
||||
AnimationNodeStateMachinePlayback stateMachine =
|
||||
var stateMachine =
|
||||
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
||||
stateMachine.Start("Idle");
|
||||
|
||||
_toolAttachement = (BoneAttachment)FindNode("ToolAttachement");
|
||||
if (_toolAttachement == null)
|
||||
{
|
||||
GD.PushWarning("No ToolAttachement found!");
|
||||
}
|
||||
if (_toolAttachement == null) GD.PushWarning("No ToolAttachement found!");
|
||||
|
||||
_debugGeometry = (DebugGeometry)FindNode("DebugGeometry");
|
||||
}
|
||||
|
@ -95,7 +74,7 @@ public class Player : Entity, IInteractionInterface
|
|||
{
|
||||
base._PhysicsProcess(delta);
|
||||
|
||||
if (_navigationComponent == null)
|
||||
if (NavigationComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -111,15 +90,18 @@ public class Player : Entity, IInteractionInterface
|
|||
if (navigationTask != null && navigationTask.PlanningComplete == false)
|
||||
{
|
||||
// _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;
|
||||
}
|
||||
|
||||
_navigationComponent.UpdateCurrentGoal(GlobalTransform);
|
||||
_groundMotion.PhysicsProcess(delta, this, _navigationComponent.CurrentGoalPositionWorld,
|
||||
_navigationComponent.CurrentGoalOrientationWorld, _worldInfo.TileWorld);
|
||||
NavigationComponent.UpdateCurrentGoal(GlobalTransform);
|
||||
_groundMotion.PhysicsProcess(delta, this, NavigationComponent.CurrentGoalPositionWorld,
|
||||
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)
|
||||
{
|
||||
if (_navigationComponent != null)
|
||||
if (NavigationComponent != null)
|
||||
{
|
||||
_navigationComponent.UpdateCurrentGoal(GlobalTransform);
|
||||
NavigationComponent.UpdateCurrentGoal(GlobalTransform);
|
||||
}
|
||||
|
||||
foreach (Node node in _attractedItemList)
|
||||
|
@ -154,9 +136,9 @@ public class Player : Entity, IInteractionInterface
|
|||
_debugGeometry.Clear();
|
||||
_debugGeometry.GlobalTransform = Transform.Identity;
|
||||
|
||||
if (_navigationComponent != null)
|
||||
if (NavigationComponent != null)
|
||||
{
|
||||
_navigationComponent.DebugDraw(this, _debugGeometry);
|
||||
NavigationComponent.DebugDraw(this, _debugGeometry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +180,7 @@ public class Player : Entity, IInteractionInterface
|
|||
body.QueueFree();
|
||||
}
|
||||
|
||||
public void SetActiveTool(String toolName)
|
||||
public void SetActiveTool(string toolName)
|
||||
{
|
||||
Debug.Assert(_toolAttachement != null);
|
||||
if (toolName == "Axe")
|
||||
|
@ -217,12 +199,12 @@ public class Player : Entity, IInteractionInterface
|
|||
(AnimationNodeStateMachinePlayback)_animationTree.Get("parameters/playback");
|
||||
Debug.Assert(stateMachine != null);
|
||||
|
||||
if (_interactionComponent.TargetEntity is Chest)
|
||||
if (InteractionComponent.TargetEntity is Chest)
|
||||
{
|
||||
GD.Print("Player Opening Box");
|
||||
stateMachine.Travel("Interaction");
|
||||
}
|
||||
else if (_interactionComponent.TargetEntity is Tree)
|
||||
else if (InteractionComponent.TargetEntity is Tree)
|
||||
{
|
||||
GD.Print("Player Chopping Tree");
|
||||
stateMachine.Travel("Hit");
|
||||
|
|
|
@ -22,6 +22,7 @@ public class Game : Spatial
|
|||
private InteractionSystem _interactionSystem;
|
||||
private HexCell _lastTile;
|
||||
private Label _mouseTileCubeLabel;
|
||||
private Label _mouseTileAxialLabel;
|
||||
private Spatial _mouseTileHighlight;
|
||||
private Label _mouseTileOffsetLabel;
|
||||
private Label _mouseWorldLabel;
|
||||
|
@ -29,9 +30,6 @@ public class Game : Spatial
|
|||
private Label _numCoordsRemovedLabel;
|
||||
private Label _numTilesLabel;
|
||||
private Player _player;
|
||||
private StreamContainer _streamContainer;
|
||||
private Spatial _streamContainerActiveTiles;
|
||||
private Area _streamContainerArea;
|
||||
|
||||
// scene nodes
|
||||
private Spatial _tileHighlight;
|
||||
|
@ -41,7 +39,6 @@ public class Game : Spatial
|
|||
private TileInstanceManager _tileInstanceManager;
|
||||
private ShaderMaterial _tileMaterial;
|
||||
private Label _tileOffsetLabel;
|
||||
private TileWorld _tileWorld;
|
||||
private World _world;
|
||||
private TextureRect _worldTextureRect;
|
||||
|
||||
|
@ -58,6 +55,7 @@ public class Game : Spatial
|
|||
_mouseWorldLabel = debugStatsContainer.GetNode<Label>("mouse_world_label");
|
||||
_mouseTileOffsetLabel = debugStatsContainer.GetNode<Label>("mouse_tile_offset_label");
|
||||
_mouseTileCubeLabel = debugStatsContainer.GetNode<Label>("mouse_tile_cube_label");
|
||||
_mouseTileAxialLabel = debugStatsContainer.GetNode<Label>("mouse_tile_axial_label");
|
||||
_numCoordsAddedLabel = debugStatsContainer.GetNode<Label>("num_coords_added_label");
|
||||
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
|
||||
|
||||
|
@ -74,12 +72,7 @@ public class Game : Spatial
|
|||
_tileHighlight = GetNode<Spatial>("TileHighlight");
|
||||
_mouseTileHighlight = GetNode<Spatial>("MouseTileHighlight");
|
||||
|
||||
_streamContainer = (StreamContainer)FindNode("StreamContainer");
|
||||
_streamContainerArea = _streamContainer.GetNode<Area>("Area");
|
||||
_streamContainerActiveTiles = _streamContainer.GetNode<Spatial>("ActiveTiles");
|
||||
|
||||
_player = GetNode<Player>("Player");
|
||||
_tileWorld = GetNode<TileWorld>("TileWorld");
|
||||
_camera = (Camera)FindNode("Camera");
|
||||
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
|
||||
|
||||
|
@ -88,9 +81,6 @@ public class Game : Spatial
|
|||
|
||||
// populate UI values
|
||||
var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
|
||||
generatorWorldSizeSlider.Value = _tileWorld.Size;
|
||||
|
||||
Debug.Assert(_tileWorld != null);
|
||||
|
||||
// resources
|
||||
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
|
||||
|
@ -109,15 +99,7 @@ public class Game : Spatial
|
|||
_interactionSystem = GetNode<InteractionSystem>("InteractionSystem");
|
||||
Debug.Assert(_interactionSystem != null);
|
||||
|
||||
// update data
|
||||
_worldTextureRect.RectSize = Vector2.One * _tileWorld.Size;
|
||||
_heightTextureRect.RectSize = Vector2.One * _tileWorld.Size;
|
||||
|
||||
// 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));
|
||||
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
|
||||
nameof(_interactionSystem.OnStartInteraction));
|
||||
|
@ -135,11 +117,8 @@ public class Game : Spatial
|
|||
// perform dependency injection
|
||||
//_streamContainer.SetWorld(_tileWorld);Clicked
|
||||
var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
|
||||
if (worldInfoComponent != null) worldInfoComponent.SetWorld(_tileWorld);
|
||||
|
||||
_tileWorld.Generate(_tileWorld.Size);
|
||||
UpdateCurrentTile();
|
||||
_streamContainer.SetCenterTile(_currentTile);
|
||||
}
|
||||
|
||||
|
||||
|
@ -191,15 +170,6 @@ public class Game : Spatial
|
|||
tileHighlightTransform.origin.z = currentTileCenter.y;
|
||||
_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;
|
||||
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
|
||||
_camera.Transform = cameraTransform;
|
||||
|
@ -210,14 +180,7 @@ public class Game : Spatial
|
|||
{
|
||||
GD.Print("Generating");
|
||||
var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
|
||||
if (worldSizeSlider == null)
|
||||
{
|
||||
GD.PrintErr("Could not find WorldSizeSlider!");
|
||||
return;
|
||||
}
|
||||
|
||||
_tileWorld.Seed = _tileWorld.Seed + 1;
|
||||
_tileWorld.Generate((int)worldSizeSlider.Value);
|
||||
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!");
|
||||
}
|
||||
|
||||
|
||||
|
@ -230,10 +193,8 @@ public class Game : Spatial
|
|||
_mouseWorldLabel.Text = position.ToString("F3");
|
||||
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
|
||||
_mouseTileCubeLabel.Text = cellAtCursor.CubeCoords.ToString("N");
|
||||
_mouseTileAxialLabel.Text = cellAtCursor.AxialCoords.ToString("N");
|
||||
_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");
|
||||
_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)
|
||||
|
@ -277,11 +238,10 @@ public class Game : Spatial
|
|||
public void ResetGameState()
|
||||
{
|
||||
var playerStartTransform = Transform.Identity;
|
||||
var height = _tileWorld.GetHeightAtOffset(new Vector2(0, 0));
|
||||
playerStartTransform.origin.y = height;
|
||||
playerStartTransform.origin.y = 0;
|
||||
_player.Transform = playerStartTransform;
|
||||
_player.TaskQueueComponent.Reset();
|
||||
_player.Navigation.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin,
|
||||
_player.NavigationComponent.PlanDirectPath(_player, playerStartTransform.origin, playerStartTransform.origin,
|
||||
playerStartTransform.basis.Quat());
|
||||
|
||||
_goldCountLabel.Text = "0";
|
||||
|
@ -291,48 +251,11 @@ public class Game : Spatial
|
|||
var entityTransform = entity.Transform;
|
||||
var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
|
||||
var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
|
||||
var entityHeight = _tileWorld.GetHeightAtOffset(entityOffsetCoordinates);
|
||||
entityTransform.origin.y = entityHeight;
|
||||
entityTransform.origin.y = 0;
|
||||
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)
|
||||
{
|
||||
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://scenes/Camera.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://ui/EditorUI.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://ui/DebugStatsContainer.gd" type="Script" id=6]
|
||||
[ext_resource path="res://scenes/World.cs" type="Script" id=7]
|
||||
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=8]
|
||||
[ext_resource path="res://scenes/Game.cs" type="Script" id=9]
|
||||
[ext_resource path="res://scenes/TileInstanceManager.cs" type="Script" id=10]
|
||||
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
|
||||
|
@ -55,9 +53,6 @@ visible = false
|
|||
|
||||
[node name="MouseTileHighlight" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="TileWorld" parent="." instance=ExtResource( 8 )]
|
||||
GenerationMapType = 2
|
||||
|
||||
[node name="GameUI" type="HBoxContainer" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
|
@ -126,7 +121,7 @@ margin_bottom = 20.0
|
|||
[node name="Label9" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||
visible = false
|
||||
margin_top = 24.0
|
||||
margin_right = 113.0
|
||||
margin_right = 53.0
|
||||
margin_bottom = 38.0
|
||||
rect_pivot_offset = Vector2( -335, -33 )
|
||||
text = "FPS"
|
||||
|
@ -188,9 +183,10 @@ text = "0,0"
|
|||
|
||||
[node name="Label6" type="Label" parent="DebugContainer/DebugStatsContainer"]
|
||||
visible = false
|
||||
margin_top = 96.0
|
||||
margin_right = 113.0
|
||||
margin_bottom = 110.0
|
||||
margin_left = 57.0
|
||||
margin_top = 3.0
|
||||
margin_right = 170.0
|
||||
margin_bottom = 17.0
|
||||
rect_pivot_offset = Vector2( -335, -33 )
|
||||
text = "Mouse Tile Offset"
|
||||
|
||||
|
@ -218,6 +214,23 @@ margin_right = 149.0
|
|||
margin_bottom = 128.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"]
|
||||
visible = false
|
||||
margin_top = 132.0
|
||||
|
@ -344,10 +357,6 @@ flip_v = true
|
|||
[node name="EditorUI" parent="." instance=ExtResource( 4 )]
|
||||
visible = false
|
||||
|
||||
[node name="StreamContainer" parent="." instance=ExtResource( 1 )]
|
||||
visible = false
|
||||
ShowHexTiles = true
|
||||
|
||||
[node name="Camera" parent="." instance=ExtResource( 3 )]
|
||||
|
||||
[node name="InteractionSystem" type="Node" parent="."]
|
||||
|
@ -356,7 +365,10 @@ script = ExtResource( 15 )
|
|||
[node name="NavigationSystem" type="Node" parent="."]
|
||||
|
||||
[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"]
|
||||
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 )
|
||||
|
||||
[node name="Entities" type="Spatial" parent="."]
|
||||
visible = false
|
||||
|
||||
[node name="Axe" parent="Entities" instance=ExtResource( 14 )]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.79762, 0, 0 )
|
||||
|
@ -380,6 +393,7 @@ script = ExtResource( 7 )
|
|||
|
||||
[node name="TileInstanceManager" type="Spatial" parent="World"]
|
||||
script = ExtResource( 10 )
|
||||
ShowHexTiles = true
|
||||
World = NodePath("..")
|
||||
|
||||
[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="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="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"]
|
||||
|
||||
[editable path="TileWorld"]
|
||||
[editable path="StreamContainer"]
|
||||
[editable path="Player"]
|
||||
[editable path="Player/Geometry"]
|
||||
|
|
|
@ -5,24 +5,19 @@ using Godot.Collections;
|
|||
|
||||
public class TileInstanceManager : Spatial
|
||||
{
|
||||
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
|
||||
private MultiMeshInstance _tileMultiMeshInstance;
|
||||
private ImageTexture _viewTileTypeTexture;
|
||||
|
||||
private World _world;
|
||||
|
||||
// other members
|
||||
// exports
|
||||
[Export] public NodePath World;
|
||||
[Export] public bool ShowHexTiles;
|
||||
[Export] public Vector2 ViewCenterPlaneCoord;
|
||||
|
||||
// ui elements
|
||||
|
||||
// scene nodes
|
||||
public MultiMeshInstance TileMultiMeshInstance;
|
||||
|
||||
// resources
|
||||
|
||||
// exports
|
||||
[Export] public NodePath World;
|
||||
// other members
|
||||
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
|
||||
private int _usedTileInstanceIndices;
|
||||
private ImageTexture _viewTileTypeTexture;
|
||||
private World _world;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
|
@ -31,8 +26,8 @@ public class TileInstanceManager : Spatial
|
|||
|
||||
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
|
||||
|
||||
_tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
||||
Debug.Assert(_tileMultiMeshInstance != null);
|
||||
TileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
||||
Debug.Assert(TileMultiMeshInstance != null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,7 +37,9 @@ public class TileInstanceManager : Spatial
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -82,23 +79,15 @@ public class TileInstanceManager : Spatial
|
|||
if (removedChunks.Count > 0)
|
||||
{
|
||||
sceneTileChunk = removedChunks[^1];
|
||||
sceneTileChunk.ChunkIndex = chunkIndex;
|
||||
removedChunks.RemoveAt(removedChunks.Count - 1);
|
||||
GD.Print("Reused SceneTileChunk");
|
||||
}
|
||||
else
|
||||
{
|
||||
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -128,14 +117,20 @@ public class TileInstanceManager : Spatial
|
|||
{
|
||||
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
||||
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();
|
||||
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;
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
tile3D.Transform = tileTransform;
|
||||
|
||||
|
@ -157,14 +152,14 @@ public class TileInstanceManager : Spatial
|
|||
|
||||
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
|
||||
|
||||
Debug.Assert(tileInstanceIndexStart1 + chunkTileCount <= _multiMeshInstance.Multimesh.InstanceCount);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, chunkTileCount))
|
||||
TileInstanceIndices.Add(_multiMeshInstance.Multimesh.InstanceCount + i);
|
||||
_tileInstanceIndices.Add(tileInstanceIndexStart1 + i);
|
||||
|
||||
_multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
|
||||
// _multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
|
||||
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
||||
|
||||
GD.Print("VisibleInstanceCount = " + _multiMeshInstance.Multimesh.VisibleInstanceCount);
|
||||
|
||||
ChunkIndex = chunkIndex;
|
||||
}
|
||||
|
||||
|
@ -175,7 +170,7 @@ public class TileInstanceManager : Spatial
|
|||
set
|
||||
{
|
||||
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);
|
||||
Transform = chunkTransform;
|
||||
_chunkIndex = value;
|
||||
|
@ -184,17 +179,22 @@ public class TileInstanceManager : Spatial
|
|||
|
||||
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 row = i / global::World.ChunkSize;
|
||||
|
||||
var tilePlaneCoord =
|
||||
HexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
||||
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
||||
|
||||
_multiMeshInstance.Multimesh.SetInstanceTransform(TileInstanceIndices[i],
|
||||
new Transform(tileOrientation,
|
||||
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y)));
|
||||
var hexTransform = new Transform(tileOrientation,
|
||||
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
|
||||
|
||||
if (_showHexTiles)
|
||||
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
|
||||
hexTransform.origin);
|
||||
|
||||
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Namespace;
|
||||
using Vector2 = Godot.Vector2;
|
||||
using Vector3 = Godot.Vector3;
|
||||
|
||||
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
|
||||
{
|
||||
Heightmap,
|
||||
|
@ -17,51 +29,37 @@ public class TileWorld : Spatial
|
|||
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
|
||||
[Signal]
|
||||
delegate void WorldGenerated();
|
||||
private delegate void WorldGenerated();
|
||||
|
||||
// public members
|
||||
public enum MapType
|
||||
{
|
||||
Noise,
|
||||
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 Spatial Entities;
|
||||
public Image HeightmapImage;
|
||||
public float HeightScale = 1.0f;
|
||||
public HexGrid HexGrid;
|
||||
public Image NavigationmapImage;
|
||||
public int Seed = 0;
|
||||
public Spatial Entities;
|
||||
public HexGrid HexGrid;
|
||||
|
||||
// private members
|
||||
private int _halfSize;
|
||||
private Random _tileTypeRandom;
|
||||
private Viewport _worldOffscreenViewport;
|
||||
private TextureRect _worldOffscreenTextureRect;
|
||||
private Viewport _heightmapOffscreenViewport;
|
||||
private TextureRect _heightmapOffscreenTextureRect;
|
||||
private Array<Spatial> _rockAssets = new();
|
||||
private Array<Spatial> _grassAssets = new();
|
||||
private Array<Spatial> _treeAssets = new();
|
||||
private PackedScene _chestScene = GD.Load<PackedScene>("res://entities/Chest.tscn");
|
||||
private Spatial _environmentNode;
|
||||
private Viewport _heightmapOffscreenViewport;
|
||||
private int _resizeExtraFrameCount;
|
||||
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.
|
||||
public override void _Ready()
|
||||
|
@ -149,10 +147,6 @@ public class TileWorld : Spatial
|
|||
_heightmapOffscreenViewport.Size = new Vector2(size, size);
|
||||
|
||||
_halfSize = Mathf.RoundToInt(size) / 2;
|
||||
HexGrid.SetBounds(
|
||||
TextureCoordToCell(new Vector2(0, 0)),
|
||||
TextureCoordToCell(new Vector2(size, size))
|
||||
);
|
||||
|
||||
HexGrid.Obstacles.Clear();
|
||||
HexGrid.Barriers.Clear();
|
||||
|
@ -282,7 +276,7 @@ public class TileWorld : Spatial
|
|||
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)
|
||||
{
|
||||
|
@ -301,13 +295,13 @@ public class TileWorld : Spatial
|
|||
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);
|
||||
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);
|
||||
}
|
||||
|
@ -319,7 +313,7 @@ public class TileWorld : Spatial
|
|||
NavigationmapImage.SetPixelv(OffsetToTextureCoord(cell.OffsetCoords), Colors.Red);
|
||||
NavigationmapImage.Unlock();
|
||||
}
|
||||
|
||||
|
||||
private void PopulateEnvironment()
|
||||
{
|
||||
Random environmentRandom = new Random(Seed);
|
||||
|
@ -461,16 +455,13 @@ public class TileWorld : Spatial
|
|||
|
||||
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)
|
||||
{
|
||||
heightmapHeight -= 0.03f;
|
||||
}
|
||||
else
|
||||
{
|
||||
heightmapHeight = 0f;
|
||||
}
|
||||
|
||||
return heightmapHeight;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,6 @@
|
|||
[node name="TileWorld" type="Spatial"]
|
||||
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="."]
|
||||
size = Vector2( 100, 100 )
|
||||
own_world = true
|
||||
|
|
104
scenes/World.cs
104
scenes/World.cs
|
@ -17,7 +17,7 @@ public class World : Spatial
|
|||
}
|
||||
|
||||
// constants
|
||||
public const int ChunkSize = 10;
|
||||
public const int ChunkSize = 18;
|
||||
public const int NumChunkRows = 3;
|
||||
public const int NumChunkColumns = NumChunkRows;
|
||||
private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f);
|
||||
|
@ -41,6 +41,8 @@ public class World : Spatial
|
|||
|
||||
// other members
|
||||
private Vector2 _centerPlaneCoord;
|
||||
private Vector2 _chunkIndexNorthEast;
|
||||
private Vector2 _chunkIndexSouthWest;
|
||||
private Array<Spatial> _grassAssets;
|
||||
private ImageTexture _heightmapTexture;
|
||||
|
||||
|
@ -58,6 +60,26 @@ public class World : Spatial
|
|||
public GenerationState State = GenerationState.Done;
|
||||
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()
|
||||
{
|
||||
Debug.Assert(ChunkSize % 2 == 0);
|
||||
|
@ -73,6 +95,8 @@ public class World : Spatial
|
|||
|
||||
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
|
||||
Debug.Assert(_tileInstanceManager != null);
|
||||
_tileInstanceManager.TileMultiMeshInstance.Multimesh.InstanceCount =
|
||||
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
|
||||
|
||||
InitNoiseGenerator();
|
||||
|
||||
|
@ -217,6 +241,16 @@ public class World : Spatial
|
|||
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)
|
||||
{
|
||||
if (State != GenerationState.Done)
|
||||
|
@ -326,7 +360,14 @@ public class World : Spatial
|
|||
|
||||
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
|
||||
{
|
||||
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
|
||||
if (!_centerChunkRect.HasPoint(centerPlaneCoord))
|
||||
{
|
||||
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
|
||||
|
||||
UpdateChunkBounds();
|
||||
|
||||
UpdateNavigationBounds();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateWorldViewTexture()
|
||||
|
@ -340,18 +381,10 @@ public class World : Spatial
|
|||
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
|
||||
Image.Format.Rgba8);
|
||||
|
||||
var chunkIndexSouthWest = Vector2.Inf;
|
||||
var 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;
|
||||
|
||||
_heightmapImage.BlendRect(
|
||||
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
|
||||
new Rect2(Vector2.Zero, Vector2.One * worldChunkSize),
|
||||
|
@ -369,12 +402,42 @@ public class World : Spatial
|
|||
_viewTileTypeTexture = new ImageTexture();
|
||||
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage);
|
||||
|
||||
WorldTextureCoordinateOffset = chunkIndexSouthWest * worldChunkSize;
|
||||
WorldTextureCoordinateOffset = _chunkIndexSouthWest * worldChunkSize;
|
||||
|
||||
EmitSignal("OnWorldViewTileTypeImageChanged", _tileTypeMapImage);
|
||||
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)
|
||||
{
|
||||
var oldState = State;
|
||||
|
@ -431,23 +494,4 @@ public class World : Spatial
|
|||
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 System;
|
||||
using Array = Godot.Collections.Array;
|
||||
|
||||
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
|
||||
{
|
||||
None,
|
||||
|
@ -36,40 +11,59 @@ public class EditorUI : Control
|
|||
Obstacle,
|
||||
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;
|
||||
|
||||
// exported members
|
||||
|
||||
// public members
|
||||
public Vector2 currentTileOffset = Vector2.Zero;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
_tileWorld = (TileWorld) GetNode(World);
|
||||
_streamContainer = (StreamContainer)GetNode(StreamContainer);
|
||||
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
|
||||
|
||||
|
||||
// signals
|
||||
_resetButton = (Button) FindNode("ResetButton");
|
||||
_resetButton = (Button)FindNode("ResetButton");
|
||||
_resetButton.Connect("pressed", this, nameof(OnResetButton));
|
||||
|
||||
_grassButton = (Button) FindNode("GrassButton");
|
||||
|
||||
_grassButton = (Button)FindNode("GrassButton");
|
||||
_grassButton.Connect("pressed", this, nameof(OnGrassButton));
|
||||
|
||||
_sandButton = (Button) FindNode("SandButton");
|
||||
|
||||
_sandButton = (Button)FindNode("SandButton");
|
||||
_sandButton.Connect("pressed", this, nameof(OnSandButton));
|
||||
|
||||
_waterButton = (Button) FindNode("WaterButton");
|
||||
|
||||
_waterButton = (Button)FindNode("WaterButton");
|
||||
_waterButton.Connect("pressed", this, nameof(OnWaterButton));
|
||||
|
||||
_obstacleButton = (Button) FindNode("ObstacleButton");
|
||||
_obstacleButton = (Button)FindNode("ObstacleButton");
|
||||
_obstacleButton.Connect("pressed", this, nameof(OnObstacleButton));
|
||||
|
||||
_navigateButton = (Button) FindNode("NavigateButton");
|
||||
_navigateButton = (Button)FindNode("NavigateButton");
|
||||
_navigateButton.Connect("pressed", this, nameof(OnNavigateButton));
|
||||
|
||||
|
||||
_gameGeometryCheckBox = (CheckBox)FindNode("GameGeometryCheckBox");
|
||||
_gameGeometryCheckBox.Connect("toggled", this, nameof(OnGameGeometryCheckBoxToggled));
|
||||
|
||||
|
||||
_physicsGeometryCheckBox = (CheckBox)FindNode("PhysicsGeometryCheckBox");
|
||||
_physicsGeometryCheckBox.Connect("toggled", this, nameof(OnPhysicsGeometryCheckBoxToggled));
|
||||
|
||||
|
||||
_navigationGeometryCheckBox = (CheckBox)FindNode("NavigationGeometryCheckBox");
|
||||
_navigationGeometryCheckBox.Connect("toggled", this, nameof(OnNavigationGeometryCheckBoxToggled));
|
||||
}
|
||||
|
@ -90,53 +84,41 @@ public class EditorUI : Control
|
|||
public void OnSandButton()
|
||||
{
|
||||
CurrentInputMode = InputMode.Sand;
|
||||
|
||||
}
|
||||
|
||||
public void OnWaterButton()
|
||||
{
|
||||
CurrentInputMode = InputMode.Water;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void OnObstacleButton()
|
||||
{
|
||||
CurrentInputMode = InputMode.Obstacle;
|
||||
}
|
||||
|
||||
|
||||
public void OnNavigateButton()
|
||||
{
|
||||
CurrentInputMode = InputMode.Navigate;
|
||||
}
|
||||
|
||||
|
||||
public void OnGameGeometryCheckBoxToggled(bool pressed)
|
||||
{
|
||||
Array gameGeometries = GetTree().GetNodesInGroup("GameGeometry");
|
||||
var gameGeometries = GetTree().GetNodesInGroup("GameGeometry");
|
||||
foreach (Spatial mesh in gameGeometries)
|
||||
{
|
||||
if (mesh != null)
|
||||
{
|
||||
mesh.Visible = pressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void OnPhysicsGeometryCheckBoxToggled(bool pressed)
|
||||
{
|
||||
Array physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry");
|
||||
var physicsGeometries = GetTree().GetNodesInGroup("PhysicsGeometry");
|
||||
foreach (Spatial mesh in physicsGeometries)
|
||||
{
|
||||
if (mesh != null)
|
||||
{
|
||||
mesh.Visible = pressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void OnNavigationGeometryCheckBoxToggled(bool pressed)
|
||||
{
|
||||
UpdateTileMaterial();
|
||||
|
@ -161,23 +143,26 @@ public class EditorUI : Control
|
|||
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void OnTileClicked(Vector2 offsetCoord)
|
||||
{
|
||||
switch (CurrentInputMode)
|
||||
{
|
||||
case InputMode.Grass:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
|
||||
break;
|
||||
case InputMode.Water:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Blue);
|
||||
break;
|
||||
case InputMode.Sand:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Yellow);
|
||||
break;
|
||||
case InputMode.Grass:
|
||||
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
|
||||
break;
|
||||
case InputMode.Water:
|
||||
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Blue);
|
||||
break;
|
||||
case InputMode.Sand:
|
||||
_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Yellow);
|
||||
break;
|
||||
case InputMode.Obstacle:
|
||||
_tileWorld.MarkCellUnwalkable(HexCell.FromOffsetCoords(offsetCoord));
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
UpdateTileMaterial();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,14 @@
|
|||
using Godot;
|
||||
using GoDotTest;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
public class NavigationComponentTests : TestClass
|
||||
{
|
||||
private Node _testScene = null;
|
||||
private TileWorld _tileWorld;
|
||||
private NavigationComponent _navigationComponent;
|
||||
private KinematicBody _kinematicBody;
|
||||
private readonly Node _testScene;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -21,11 +18,11 @@ public class NavigationComponentTests : TestClass
|
|||
[Setup]
|
||||
public void Setup()
|
||||
{
|
||||
_tileWorld = (TileWorld)_tileWorldScene.Instance();
|
||||
_tileWorld.HexGrid = new HexGrid();
|
||||
_testScene.AddChild(_tileWorld);
|
||||
_world = (World)_WorldScene.Instance();
|
||||
_world.HexGrid = new HexGrid();
|
||||
_testScene.AddChild(_world);
|
||||
_kinematicBody = new KinematicBody();
|
||||
_navigationComponent = new NavigationComponent();
|
||||
_navigationComponent.TileWorld = _tileWorld;
|
||||
_navigationComponent.World = _world;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue