WIP added navigation component, worked on ground motion component, added hexcell line to and distance functions.

WorldChunkRefactoring
Martin Felis 2023-01-03 17:46:55 +01:00
parent 3ed113dd41
commit c7349f4cee
8 changed files with 301 additions and 47 deletions

View File

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

View File

@ -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();
Vector2 planeTargetVector = new Vector2(targetPosition.x - entity.GlobalTranslation.x,
targetPosition.z - entity.GlobalTranslation.z);
float targetDistance = planeTargetVector.Length();
Vector2 planeTargetDirection = planeTargetVector / targetDistance;
if (distance_error < 0.01)
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);
}
}

View File

@ -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;
/// <summary>
/// </summary>
public class NavigationComponent
{
public Vector3 currentGoalWorld;
public Vector2 currentGoalOffset;
public List<Vector2> 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<Vector2>();
foreach (HexCell cell in path)
{
pathOffsetCoords.Append(cell.OffsetCoords);
}
currentGoalOffset = pathOffsetCoords[1];
}
}

View File

@ -3,18 +3,10 @@ using System;
public class Entity : KinematicBody
{
public Vector3 Position = Vector3.Zero;
public Quat Orientation = Quat.Identity;
public Vector3 Velocity = Vector3.Zero;
public float RotationalVelocity = 0;
public Vector3 Velocity { get; set; } = Vector3.Zero;
public float RotationalVelocity { get; set; } = 0;
public override void _Ready()
{
}
public override void _Process(float _delta)
{
Velocity = MoveAndSlide(Velocity);
}
}

View File

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

View File

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

View File

@ -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)
// {

View File

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