diff --git a/HexCell.cs b/HexCell.cs
index eb7d7e9..50cb04d 100644
--- a/HexCell.cs
+++ b/HexCell.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Linq;
using Godot;
using Array = Godot.Collections.Array;
@@ -45,6 +46,14 @@ public class HexCell : Resource
CubeCoords = other.CubeCoords;
}
+ public static HexCell FromOffsetCoords(Vector2 offsetCoords)
+ {
+ HexCell result = new HexCell();
+ result.OffsetCoords = offsetCoords;
+
+ return result;
+ }
+
private Vector3 _cubeCoords;
public Vector3 CubeCoords
@@ -123,8 +132,36 @@ public class HexCell : Resource
return rounded;
}
- public HexCell getAdjacent(Vector3 dir)
+ public HexCell GetAdjacent(Vector3 dir)
{
return new HexCell(this.CubeCoords + dir);
}
+
+ public int DistanceTo(HexCell target)
+ {
+ return (int)(
+ Mathf.Abs(_cubeCoords.x - target.CubeCoords.x)
+ + Mathf.Abs(_cubeCoords.y - target.CubeCoords.y)
+ + Mathf.Abs(_cubeCoords.z - target.CubeCoords.z)
+ ) / 2;
+ }
+
+ public HexCell[] LineTo(HexCell target)
+ {
+ HexCell nudgedTarget = new HexCell();
+ nudgedTarget.CubeCoords = target.CubeCoords + new Vector3(1.0e-6f, 2.0e-6f, -3.0e-6f);
+ int steps = DistanceTo(target);
+
+ HexCell[] path = new HexCell[steps + 1];
+
+ foreach (int dist in Enumerable.Range(0, steps))
+ {
+ path[dist] = new HexCell();
+ path[dist].CubeCoords = CubeCoords.LinearInterpolate(nudgedTarget.CubeCoords, (float)dist / steps);
+ }
+
+ path[steps] = target;
+
+ return path;
+ }
}
\ No newline at end of file
diff --git a/components/GroundMotionComponent.cs b/components/GroundMotionComponent.cs
index 5cb9108..32a38af 100644
--- a/components/GroundMotionComponent.cs
+++ b/components/GroundMotionComponent.cs
@@ -10,40 +10,84 @@ public class GroundMotionComponent
{
public float Accel = 50;
public float Damping = 0.2f;
- public float MaxSpeed = 2;
+ public float MaxSpeed = 8;
- public void PhysicsProcess(float delta, Entity entity, Vector3 target_position)
+
+ private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition)
{
- Vector2 plane_target_vector = new Vector2(target_position.x - entity.GlobalTranslation.x,
- target_position.z - entity.GlobalTranslation.z);
- float distance_error = plane_target_vector.Length();
-
- if (distance_error < 0.01)
+ Vector2 planeTargetVector = new Vector2(targetPosition.x - entity.GlobalTranslation.x,
+ targetPosition.z - entity.GlobalTranslation.z);
+ float targetDistance = planeTargetVector.Length();
+ Vector2 planeTargetDirection = planeTargetVector / targetDistance;
+
+ 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 (targetDistance < 0.01)
{
- entity.Velocity = Vector3.Zero;
+ planeVelocity = Vector2.Zero;
} else {
- Vector2 plane_velocity = new Vector2(entity.Velocity.x, entity.Velocity.z);
- plane_velocity = plane_velocity + plane_target_vector / distance_error * Accel * delta;
+ planeVelocity = planeVelocity.Length() * planeTargetDirection + planeTargetDirection * Accel * delta;
+ // GD.Print(" accel: speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
- float projected_step = plane_target_vector.Dot(plane_velocity * delta);
- GD.Print("Projected step: " + projected_step);
- if (projected_step > 1)
+ float projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
+ // GD.Print(" Projected step: " + projectedStep + " Speed: " + planeVelocity.Length() + " delta: " + delta);
+ if (projectedStep > targetDistance)
{
- plane_velocity /= projected_step;
+ planeVelocity *= targetDistance / projectedStep;
+ projectedStep = planeTargetDirection.Dot(planeVelocity * delta);
+ // GD.Print(" corr speed: " + planeVelocity.Length() + " step: " + projectedStep);
}
-
- float plane_speed = plane_velocity.Length();
- if (plane_speed > MaxSpeed)
+
+ float planeSpeed = planeVelocity.Length();
+ if (planeSpeed > MaxSpeed)
{
- plane_velocity *= MaxSpeed / plane_speed;
+ planeVelocity *= MaxSpeed / planeSpeed;
}
-
- entity.Velocity = new Vector3(
- plane_velocity.x, 0, plane_velocity.y);
}
- entity.Velocity.x -= entity.Velocity.x * Damping;
- entity.Velocity.z -= entity.Velocity.z * Damping;
+ entity.Velocity = new Vector3(planeVelocity.x, entity.Velocity.y, planeVelocity.y);
+ }
+
+
+ private void CalcVerticalVelocity(float delta, Entity entity, TileWorld tileWorld)
+ {
+ Vector3 entityVelocity = entity.Velocity;
+
+ Vector2 currentTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin);
+ Vector2 nextTile = tileWorld.WorldToOffsetCoords(entity.GlobalTransform.origin + entityVelocity * delta);
+
+ if (nextTile != currentTile)
+ {
+ float currentHeight = tileWorld.GetHeightAtOffset(currentTile);
+ float nextHeight = tileWorld.GetHeightAtOffset(nextTile);
+
+ bool isOnFloor = entity.IsOnFloor();
+
+ if (nextHeight - entity.GlobalTransform.origin.y > 0.1)
+ {
+ GD.Print("Jump!");
+ entityVelocity.y = 10;
+ }
+ }
+
+ entityVelocity.y -= 9.81f * delta;
+
+ entity.Velocity = entityVelocity;
+ }
+
+
+ public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, TileWorld tileWorld)
+ {
+ CalcPlaneVelocity(delta, entity, targetPosition);
+
+ CalcVerticalVelocity(delta, entity, tileWorld);
+
+ Vector3 prePhysicsVelocity = entity.Velocity;
entity.Velocity = entity.MoveAndSlide(entity.Velocity);
+ GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity);
+ GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity);
}
}
\ No newline at end of file
diff --git a/components/NavigationComponent.cs b/components/NavigationComponent.cs
new file mode 100644
index 0000000..e97bbd3
--- /dev/null
+++ b/components/NavigationComponent.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using Godot;
+using Vector2 = Godot.Vector2;
+using Vector3 = Godot.Vector3;
+
+///
+///
+public class NavigationComponent
+{
+ public Vector3 currentGoalWorld;
+ public Vector2 currentGoalOffset;
+ public List pathOffsetCoords;
+ public HexCell[] path;
+
+ private HexGrid _hexGrid;
+
+ public void Plan(Vector3 currentPositionWorld, Vector3 targetPositionWorld, TileWorld tileWorld)
+ {
+ if ((targetPositionWorld - currentGoalWorld).LengthSquared() < 0.01)
+ {
+ return;
+ }
+
+ currentGoalWorld = targetPositionWorld;
+ Vector2 currentPositionOffset = tileWorld.WorldToOffsetCoords(currentPositionWorld);
+ Vector2 currentGoalOffset = tileWorld.WorldToOffsetCoords(targetPositionWorld);
+
+ HexCell currentCell = new HexCell();
+ currentCell.OffsetCoords = currentPositionOffset;
+
+ HexCell targetCell = new HexCell();
+ targetCell.OffsetCoords = currentGoalOffset;
+
+ path = currentCell.LineTo(targetCell);
+
+ pathOffsetCoords = new List();
+ foreach (HexCell cell in path)
+ {
+ pathOffsetCoords.Append(cell.OffsetCoords);
+ }
+
+ currentGoalOffset = pathOffsetCoords[1];
+ }
+}
\ No newline at end of file
diff --git a/entities/Entity.cs b/entities/Entity.cs
index 14871f2..69ea556 100644
--- a/entities/Entity.cs
+++ b/entities/Entity.cs
@@ -3,18 +3,10 @@ using System;
public class Entity : KinematicBody
{
- public Vector3 Position = Vector3.Zero;
- public Quat Orientation = Quat.Identity;
+ public Vector3 Velocity { get; set; } = Vector3.Zero;
+ public float RotationalVelocity { get; set; } = 0;
- public Vector3 Velocity = Vector3.Zero;
- public float RotationalVelocity = 0;
-
public override void _Ready()
{
}
-
- public override void _Process(float _delta)
- {
- Velocity = MoveAndSlide(Velocity);
- }
}
\ No newline at end of file
diff --git a/entities/Player.cs b/entities/Player.cs
index 2cd39d3..d256e30 100644
--- a/entities/Player.cs
+++ b/entities/Player.cs
@@ -12,11 +12,13 @@ public class Player : Entity
private WorldInfoComponent _worldInfo;
private Vector2 _offsetCoord = Vector2.Zero;
private GroundMotionComponent _groundMotion;
+ private NavigationComponent _navigationComponent;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_groundMotion = new GroundMotionComponent();
+ _navigationComponent = new NavigationComponent();
_movable = (MovableComponent)FindNode("Movable", false);
if (_movable != null)
@@ -33,9 +35,27 @@ public class Player : Entity
public override void _PhysicsProcess(float delta)
{
base._PhysicsProcess(delta);
- _groundMotion.PhysicsProcess(delta, this, TargetPosition);
+
+ // update navigation target here
+
+ // update physics target
+ Vector3 targetPosition =
+ _worldInfo.TileWorld.GetTileWorldCenterFromOffset(_navigationComponent.currentGoalOffset);
+
+ // compute physics movement
+ _groundMotion.PhysicsProcess(delta, this, targetPosition, _worldInfo.TileWorld);
}
+
+ public override void _Process(float delta)
+ {
+ if ((_navigationComponent.currentGoalWorld - TargetPosition).LengthSquared() > 0.01)
+ {
+ _navigationComponent.Plan(GlobalTransform.origin, TargetPosition, _worldInfo.TileWorld);
+ }
+ }
+
+
public void OnPositionUpdated(Vector3 newPosition)
{
if (_worldInfo != null)
diff --git a/scenes/AdaptiveWorldStream.tscn b/scenes/AdaptiveWorldStream.tscn
index 7520505..4c9780d 100644
--- a/scenes/AdaptiveWorldStream.tscn
+++ b/scenes/AdaptiveWorldStream.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=15 format=2]
+[gd_scene load_steps=17 format=2]
[ext_resource path="res://scenes/AdaptiveWorldStream.cs" type="Script" id=1]
[ext_resource path="res://assets/CreatusPiratePack/characters/Pirate1final_0.01.glb" type="PackedScene" id=2]
@@ -10,6 +10,14 @@
[ext_resource path="res://components/MovableComponent.cs" type="Script" id=8]
[ext_resource path="res://scenes/TileWorld.cs" type="Script" id=9]
+[sub_resource type="OpenSimplexNoise" id=10]
+period = 39.6
+
+[sub_resource type="NoiseTexture" id=11]
+width = 100
+height = 100
+noise = SubResource( 10 )
+
[sub_resource type="CubeMesh" id=1]
size = Vector3( 1, 1, 1 )
@@ -183,8 +191,9 @@ text = "0"
[node name="WorldTextureRect" type="TextureRect" parent="Control"]
margin_left = 1.0
margin_top = 300.0
-margin_right = 101.0
-margin_bottom = 400.0
+margin_right = 513.0
+margin_bottom = 812.0
+texture = SubResource( 11 )
[node name="StreamContainer" type="Spatial" parent="."]
script = ExtResource( 4 )
diff --git a/scenes/TileWorld.cs b/scenes/TileWorld.cs
index 3a4231c..631c090 100644
--- a/scenes/TileWorld.cs
+++ b/scenes/TileWorld.cs
@@ -60,8 +60,8 @@ public class TileWorld : Spatial
noise_generator.Seed = -1626828106;
noise_generator.Octaves = 3;
- noise_generator.Period = 5;
- noise_generator.Persistence = 0.5f;
+ noise_generator.Period = 20;
+ noise_generator.Persistence = 0.1f;
noise_generator.Lacunarity = 2;
Heightmap.CopyFrom(noise_generator.GetImage((int)Size.x, (int)Size.y, null));
@@ -117,6 +117,17 @@ public class TileWorld : Spatial
return _hexGrid.GetHexAt(new Vector2(world_coord.x, world_coord.z)).OffsetCoords;
}
+
+ public Vector3 GetTileWorldCenterFromOffset(Vector2 offset_coord)
+ {
+ Vector2 tileCenter = _hexGrid.GetHexCenterFromOffset(offset_coord);
+
+ return new Vector3(
+ tileCenter.x,
+ GetHeightAtOffset(offset_coord),
+ tileCenter.y);
+ }
+
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
diff --git a/tests/HexCellTests.cs b/tests/HexCellTests.cs
index 54b0f66..edf1dfa 100644
--- a/tests/HexCellTests.cs
+++ b/tests/HexCellTests.cs
@@ -1,6 +1,8 @@
+using System;
using Godot;
using GoDotTest;
using System.Diagnostics;
+using System.Linq;
public class HexCellTests : TestClass
{
@@ -88,21 +90,113 @@ public class HexCellTests : TestClass
HexCell cell = new HexCell(new Vector2(1, 2));
// adjacent
- HexCell otherCell = cell.getAdjacent(HexCell.DIR_N);
+ HexCell otherCell = cell.GetAdjacent(HexCell.DIR_N);
Debug.Assert(otherCell.AxialCoords == new Vector2(1, 3));
- otherCell = cell.getAdjacent(HexCell.DIR_NE);
+ otherCell = cell.GetAdjacent(HexCell.DIR_NE);
Debug.Assert(otherCell.AxialCoords == new Vector2(2, 2));
- otherCell = cell.getAdjacent(HexCell.DIR_SE);
+ otherCell = cell.GetAdjacent(HexCell.DIR_SE);
Debug.Assert(otherCell.AxialCoords == new Vector2(2, 1));
- otherCell = cell.getAdjacent(HexCell.DIR_S);
+ otherCell = cell.GetAdjacent(HexCell.DIR_S);
Debug.Assert(otherCell.AxialCoords == new Vector2(1, 1));
- otherCell = cell.getAdjacent(HexCell.DIR_SW);
+ otherCell = cell.GetAdjacent(HexCell.DIR_SW);
Debug.Assert(otherCell.AxialCoords == new Vector2(0, 2));
- otherCell = cell.getAdjacent(HexCell.DIR_NW);
+ otherCell = cell.GetAdjacent(HexCell.DIR_NW);
Debug.Assert(otherCell.AxialCoords == new Vector2(0, 3));
// not really adjacent
- otherCell = cell.getAdjacent(new Vector3(-3, -3, 6));
+ otherCell = cell.GetAdjacent(new Vector3(-3, -3, 6));
Debug.Assert(otherCell.AxialCoords == new Vector2(-2, -1));
}
+
+ [Test]
+ public void TestDistance()
+ {
+ HexCell cell = new HexCell();
+ cell.OffsetCoords = new Vector2(1, 2);
+
+ Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(0, 0))) == 3);
+ Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(3, 4))) == 4);
+ Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(-1, -1))) == 5);
+ }
+
+
+ [Test]
+ public void TestLineTo()
+ {
+ HexCell cell = new HexCell();
+ cell.OffsetCoords = new Vector2(1, 2);
+
+ HexCell[] path = cell.LineTo(new HexCell(5, 2));
+
+ HexCell[] pathExpected =
+ {
+ new HexCell(1, 2),
+ new HexCell(2, 2),
+ new HexCell(3, 2),
+ new HexCell(4, 2),
+ new HexCell(5, 2)
+ };
+
+ Debug.Assert(path.Length == pathExpected.Length);
+
+ foreach (int index in Enumerable.Range(0, path.Length))
+ {
+ Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
+ }
+ }
+
+
+ [Test]
+ public void TestLineToAngled()
+ {
+ HexCell cell = new HexCell();
+ cell.OffsetCoords = new Vector2(1, 2);
+
+ HexCell[] path = cell.LineTo(new HexCell(5, 4));
+
+ HexCell[] pathExpected =
+ {
+ new HexCell(1, 2),
+ new HexCell(2, 2),
+ new HexCell(2, 3),
+ new HexCell(3, 3),
+ new HexCell(4, 3),
+ new HexCell(4, 4),
+ new HexCell(5, 4)
+ };
+
+ Debug.Assert(path.Length == pathExpected.Length);
+
+ foreach (int index in Enumerable.Range(0, path.Length))
+ {
+ Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
+ }
+ }
+
+
+ [Test]
+ public void TestLineEdge()
+ {
+ HexCell cell = new HexCell();
+ cell.OffsetCoords = new Vector2(1, 2);
+
+ HexCell[] path = cell.LineTo(new HexCell(3, 4));
+
+ HexCell[] pathExpected =
+ {
+ new HexCell(1, 2),
+ new HexCell(2, 2),
+ new HexCell(2, 3),
+ new HexCell(2, 4),
+ new HexCell(3, 4)
+ };
+
+ Debug.Assert(path.Length == pathExpected.Length);
+
+ foreach (int index in Enumerable.Range(0, path.Length))
+ {
+ Debug.Print("index: " + index + " path: " + path[index].AxialCoords + " expected: " + pathExpected[index].AxialCoords);
+ Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
+ }
+ }
}
\ No newline at end of file