Tile instancing now working, also fixed navigation bounds.

WorldChunkRefactoring
Martin Felis 2023-11-10 11:11:08 +01:00
parent eaaa5219ed
commit c59d92618b
13 changed files with 490 additions and 576 deletions

View File

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

View File

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

View File

@ -1,24 +1,54 @@
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
{ {
/// <summary> /// <summary>
/// Executes a Task on the specified entity. /// Executes a Task on the specified entity.
/// </summary> /// </summary>
/// <returns>true when the Task is complete, false otherwise</returns> /// <returns>true when the Task is complete, false otherwise</returns>
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);
}
} }

View File

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

View File

@ -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");

View File

@ -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();

View File

@ -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"]

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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,32 +11,51 @@ 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
_resetButton = (Button) FindNode("ResetButton"); _resetButton = (Button)FindNode("ResetButton");
_resetButton.Connect("pressed", this, nameof(OnResetButton)); _resetButton.Connect("pressed", this, nameof(OnResetButton));
_grassButton = (Button) FindNode("GrassButton"); _grassButton = (Button)FindNode("GrassButton");
_grassButton.Connect("pressed", this, nameof(OnGrassButton)); _grassButton.Connect("pressed", this, nameof(OnGrassButton));
_sandButton = (Button) FindNode("SandButton"); _sandButton = (Button)FindNode("SandButton");
_sandButton.Connect("pressed", this, nameof(OnSandButton)); _sandButton.Connect("pressed", this, nameof(OnSandButton));
_waterButton = (Button) FindNode("WaterButton"); _waterButton = (Button)FindNode("WaterButton");
_waterButton.Connect("pressed", this, nameof(OnWaterButton)); _waterButton.Connect("pressed", this, nameof(OnWaterButton));
_obstacleButton = (Button) FindNode("ObstacleButton"); _obstacleButton = (Button)FindNode("ObstacleButton");
_obstacleButton.Connect("pressed", this, nameof(OnObstacleButton)); _obstacleButton.Connect("pressed", this, nameof(OnObstacleButton));
_navigateButton = (Button) FindNode("NavigateButton"); _navigateButton = (Button)FindNode("NavigateButton");
_navigateButton.Connect("pressed", this, nameof(OnNavigateButton)); _navigateButton.Connect("pressed", this, nameof(OnNavigateButton));
_gameGeometryCheckBox = (CheckBox)FindNode("GameGeometryCheckBox"); _gameGeometryCheckBox = (CheckBox)FindNode("GameGeometryCheckBox");
@ -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,42 +91,31 @@ 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;
}
}
} }
@ -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));

View File

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