Also populate chunks with entities when chunks are being generated.

WorldChunkRefactoring
Martin Felis 2023-11-05 21:46:28 +01:00
parent 928ddd3937
commit 4c0a2e7714
7 changed files with 197 additions and 100 deletions

View File

@ -6,31 +6,33 @@ using AxialCoordDirectionPair = System.Tuple<Godot.Vector2, Godot.Vector3>;
public class HexGrid : Resource public class HexGrid : Resource
{ {
private Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2); private readonly Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2); private Rect2 _boundsAxialCoords;
private Vector2 _hexScale = new(1, 1); private Vector2 _hexScale = new(1, 1);
private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2);
private Transform2D _hexTransform; private Transform2D _hexTransform;
private Transform2D _hexTransformInv; private Transform2D _hexTransformInv;
private HexCell _minCoords = new();
private HexCell _maxCoords = new(); private HexCell _maxCoords = new();
private Rect2 _boundsAxialCoords; private HexCell _minCoords = new();
public Dictionary<(Vector2, Vector3), float> Barriers = new();
public int FindPathCheckedCellCount;
public Dictionary<Vector2, float> Obstacles = new(); public Dictionary<Vector2, float> Obstacles = new();
public Dictionary<(Vector2, Vector3), float> Barriers = new();
public float PathCostDefault = 1; public float PathCostDefault = 1;
public int FindPathCheckedCellCount;
public Vector2 HexSize public HexGrid()
{ {
get { return _hexSize; } HexScale = new Vector2(1, 1);
} }
public Vector2 HexSize => _hexSize;
public Vector2 HexScale public Vector2 HexScale
{ {
get { return _hexScale; } get => _hexScale;
set set
{ {
_hexScale = value; _hexScale = value;
@ -45,11 +47,6 @@ public class HexGrid : Resource
} }
} }
public HexGrid()
{
HexScale = new Vector2(1, 1);
}
public Vector2 GetHexCenter(HexCell cell) public Vector2 GetHexCenter(HexCell cell)
{ {
return _hexTransform * cell.AxialCoords; return _hexTransform * cell.AxialCoords;
@ -57,21 +54,29 @@ public class HexGrid : Resource
public Vector2 GetHexCenterFromOffset(Vector2 offsetCoord) public Vector2 GetHexCenterFromOffset(Vector2 offsetCoord)
{ {
HexCell cell = new HexCell(); var cell = new HexCell();
cell.OffsetCoords = offsetCoord; cell.OffsetCoords = offsetCoord;
return GetHexCenter(cell); return GetHexCenter(cell);
} }
public Vector3 GetHexCenterVec3FromOffset(Vector2 offsetCoord)
{
var cell = new HexCell();
cell.OffsetCoords = offsetCoord;
var hexCenter = GetHexCenter(cell);
return new Vector3(hexCenter.x, 0, hexCenter.y);
}
public HexCell GetHexAtOffset(Vector2 offsetCoord) public HexCell GetHexAtOffset(Vector2 offsetCoord)
{ {
HexCell cell = new HexCell(); var cell = new HexCell();
cell.OffsetCoords = offsetCoord; cell.OffsetCoords = offsetCoord;
return cell; return cell;
} }
public HexCell GetHexAt(Vector2 planeCoord) public HexCell GetHexAt(Vector2 planeCoord)
{ {
HexCell result = new HexCell(_hexTransformInv * planeCoord); var result = new HexCell(_hexTransformInv * planeCoord);
return result; return result;
} }
@ -85,12 +90,12 @@ public class HexGrid : Resource
_minCoords = minCell; _minCoords = minCell;
_maxCoords = maxCell; _maxCoords = maxCell;
_boundsAxialCoords = new Rect2(_minCoords.AxialCoords, _boundsAxialCoords = new Rect2(_minCoords.AxialCoords,
(_maxCoords.AxialCoords - _minCoords.AxialCoords) + Vector2.One); _maxCoords.AxialCoords - _minCoords.AxialCoords + Vector2.One);
} }
public void SetBounds(HexCell center, int size) public void SetBounds(HexCell center, int size)
{ {
Vector2 centerOffset = center.OffsetCoords; var centerOffset = center.OffsetCoords;
SetBounds(GetHexAtOffset(centerOffset - Vector2.One * size / 2), SetBounds(GetHexAtOffset(centerOffset - Vector2.One * size / 2),
GetHexAtOffset(centerOffset + Vector2.One * size / 2)); GetHexAtOffset(centerOffset + Vector2.One * size / 2));
} }
@ -123,10 +128,7 @@ public class HexGrid : Resource
public void RemoveBarrier(HexCell cell, Vector3 directionCube) public void RemoveBarrier(HexCell cell, Vector3 directionCube)
{ {
if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) Barriers.Remove((cell.AxialCoords, directionCube));
{
Barriers.Remove((cell.AxialCoords, directionCube));
}
} }
public float GetHexCost(HexCell cell) public float GetHexCost(HexCell cell)
@ -136,10 +138,7 @@ public class HexGrid : Resource
public float GetHexCost(Vector2 axialCoords) public float GetHexCost(Vector2 axialCoords)
{ {
if (!_boundsAxialCoords.HasPoint(axialCoords)) if (!_boundsAxialCoords.HasPoint(axialCoords)) return 0;
{
return 0;
}
float value; float value;
return Obstacles.TryGetValue(axialCoords, out value) ? value : PathCostDefault; return Obstacles.TryGetValue(axialCoords, out value) ? value : PathCostDefault;
@ -147,29 +146,20 @@ public class HexGrid : Resource
public float GetMoveCost(Vector2 axialCoords, Vector3 directionCube) public float GetMoveCost(Vector2 axialCoords, Vector3 directionCube)
{ {
HexCell startCell = new HexCell(axialCoords); var startCell = new HexCell(axialCoords);
HexCell targetCell = new HexCell(startCell.CubeCoords + directionCube); var targetCell = new HexCell(startCell.CubeCoords + directionCube);
float cost = GetHexCost(axialCoords); var cost = GetHexCost(axialCoords);
if (cost == 0) if (cost == 0) return 0;
{
return 0;
}
cost = GetHexCost(targetCell.AxialCoords); cost = GetHexCost(targetCell.AxialCoords);
if (cost == 0) if (cost == 0) return 0;
{
return 0;
}
float barrierCost; float barrierCost;
if (Barriers.ContainsKey((axialCoords, directionCube))) if (Barriers.ContainsKey((axialCoords, directionCube)))
{ {
barrierCost = Barriers[(axialCoords, directionCube)]; barrierCost = Barriers[(axialCoords, directionCube)];
if (barrierCost == 0) if (barrierCost == 0) return 0;
{
return 0;
}
cost += barrierCost; cost += barrierCost;
} }
@ -177,10 +167,7 @@ public class HexGrid : Resource
if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube))) if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube)))
{ {
barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)]; barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)];
if (barrierCost == 0) if (barrierCost == 0) return 0;
{
return 0;
}
cost += barrierCost; cost += barrierCost;
} }
@ -193,16 +180,14 @@ public class HexGrid : Resource
{ {
if (GetHexCost(toCell) == 0) if (GetHexCost(toCell) == 0)
{ {
HexCell[] line = fromCell.LineTo(toCell); var line = fromCell.LineTo(toCell);
foreach (int i in Enumerable.Range(1, line.Length)) foreach (var i in Enumerable.Range(1, line.Length))
{
if (GetHexCost(line[i]) == 0) if (GetHexCost(line[i]) == 0)
{ {
toCell = line[i - 1]; toCell = line[i - 1];
break; break;
} }
}
} }
return toCell; return toCell;
@ -210,13 +195,13 @@ public class HexGrid : Resource
public List<HexCell> FindPath(HexCell startHex, HexCell goalHex) public List<HexCell> FindPath(HexCell startHex, HexCell goalHex)
{ {
Vector2 goalAxialCoords = goalHex.AxialCoords; var goalAxialCoords = goalHex.AxialCoords;
SimplePriorityQueue<Vector2, float> frontier = new SimplePriorityQueue<Vector2, float>(); var frontier = new SimplePriorityQueue<Vector2, float>();
frontier.Enqueue(startHex.AxialCoords, 0); frontier.Enqueue(startHex.AxialCoords, 0);
Dictionary<Vector2, Vector2> cameFrom = var cameFrom =
new Dictionary<Vector2, Vector2>(); new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, float> costSoFar = var costSoFar =
new Dictionary<Vector2, float>(); new Dictionary<Vector2, float>();
cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords); cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords);
@ -227,20 +212,17 @@ public class HexGrid : Resource
while (frontier.Any()) while (frontier.Any())
{ {
FindPathCheckedCellCount++; FindPathCheckedCellCount++;
HexCell currentHex = new HexCell(frontier.Dequeue()); var currentHex = new HexCell(frontier.Dequeue());
Vector2 currentAxial = currentHex.AxialCoords; var currentAxial = currentHex.AxialCoords;
if (currentHex == goalHex) if (currentHex == goalHex) break;
foreach (var nextHex in currentHex.GetAllAdjacent())
{ {
break; var nextAxial = nextHex.AxialCoords;
} var nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords);
foreach (HexCell nextHex in currentHex.GetAllAdjacent()) if (nextHex == goalHex && GetHexCost(nextAxial) == 0)
{
Vector2 nextAxial = nextHex.AxialCoords;
float nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords);
if ((nextHex == goalHex) && (GetHexCost(nextAxial) == 0))
{ {
// Goal ist an obstacle // Goal ist an obstacle
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
@ -248,16 +230,13 @@ public class HexGrid : Resource
break; break;
} }
if (nextCost == 0) if (nextCost == 0) continue;
{
continue;
}
nextCost += costSoFar[currentHex.AxialCoords]; nextCost += costSoFar[currentHex.AxialCoords];
if (!costSoFar.ContainsKey(nextHex.AxialCoords) || nextCost < costSoFar[nextHex.AxialCoords]) if (!costSoFar.ContainsKey(nextHex.AxialCoords) || nextCost < costSoFar[nextHex.AxialCoords])
{ {
costSoFar[nextHex.AxialCoords] = nextCost; costSoFar[nextHex.AxialCoords] = nextCost;
float priority = nextCost + nextHex.DistanceTo(goalHex); var priority = nextCost + nextHex.DistanceTo(goalHex);
frontier.Enqueue(nextHex.AxialCoords, priority); frontier.Enqueue(nextHex.AxialCoords, priority);
cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords;
@ -267,19 +246,16 @@ public class HexGrid : Resource
// GD.Print("Checked Cell Count: " + FindPathCheckedCellCount); // GD.Print("Checked Cell Count: " + FindPathCheckedCellCount);
List<HexCell> result = new List<HexCell>(); var result = new List<HexCell>();
if (!cameFrom.ContainsKey(goalHex.AxialCoords)) if (!cameFrom.ContainsKey(goalHex.AxialCoords))
{ {
GD.Print("Failed to find path from " + startHex + " to " + goalHex); GD.Print("Failed to find path from " + startHex + " to " + goalHex);
return result; return result;
} }
if (GetHexCost(goalAxialCoords) != 0) if (GetHexCost(goalAxialCoords) != 0) result.Add(goalHex);
{
result.Add(goalHex);
}
HexCell pathHex = goalHex; var pathHex = goalHex;
while (pathHex != startHex) while (pathHex != startHex)
{ {
pathHex = new HexCell(cameFrom[pathHex.AxialCoords]); pathHex = new HexCell(cameFrom[pathHex.AxialCoords]);
@ -292,26 +268,26 @@ public class HexGrid : Resource
public List<HexCell> GetCellsForLine(Vector2 fromPlane, Vector2 toPlane) public List<HexCell> GetCellsForLine(Vector2 fromPlane, Vector2 toPlane)
{ {
List<HexCell> result = new List<HexCell>(); var result = new List<HexCell>();
float distance = (toPlane - fromPlane).Length(); var distance = (toPlane - fromPlane).Length();
Vector2 direction = (toPlane - fromPlane) / distance; var direction = (toPlane - fromPlane) / distance;
Vector2 currentPointPlane = fromPlane; var currentPointPlane = fromPlane;
HexCell currentCell = GetHexAt(currentPointPlane); var currentCell = GetHexAt(currentPointPlane);
float currentDistance = 0; float currentDistance = 0;
do do
{ {
result.Add(currentCell); result.Add(currentCell);
GetHexCenter(currentCell); GetHexCenter(currentCell);
Vector2 currentPointLocal = currentPointPlane - GetHexCenter(currentCell); var currentPointLocal = currentPointPlane - GetHexCenter(currentCell);
int neighbourIndex; int neighbourIndex;
float boundaryPlaneDistance; float boundaryPlaneDistance;
currentCell.QueryClosestCellBoundary(currentPointLocal, direction, out neighbourIndex, currentCell.QueryClosestCellBoundary(currentPointLocal, direction, out neighbourIndex,
out boundaryPlaneDistance); out boundaryPlaneDistance);
currentCell = currentCell.GetAdjacent(HexCell.NeighborDirections[neighbourIndex]); currentCell = currentCell.GetAdjacent(HexCell.NeighborDirections[neighbourIndex]);
currentDistance += boundaryPlaneDistance * 1.001f; currentDistance += boundaryPlaneDistance * 1.001f;
currentPointPlane = fromPlane + direction * boundaryPlaneDistance; currentPointPlane = fromPlane + direction * boundaryPlaneDistance;

View File

@ -2,7 +2,7 @@
name="PirateGame3D" name="PirateGame3D"
platform="Android" platform="Android"
runnable=true runnable=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
@ -15,7 +15,7 @@ script_encryption_key=""
custom_template/debug="" custom_template/debug=""
custom_template/release="" custom_template/release=""
custom_build/use_custom_build=true custom_build/use_custom_build=false
custom_build/export_format=0 custom_build/export_format=0
custom_build/min_sdk="" custom_build/min_sdk=""
custom_build/target_sdk="" custom_build/target_sdk=""

View File

@ -132,5 +132,7 @@ common/enable_pause_aware_picking=true
[rendering] [rendering]
quality/shadows/filter_mode.mobile=1 quality/directional_shadow/size.mobile=512
quality/shadow_atlas/size.mobile=1024
quality/subsurface_scattering/quality=0
environment/default_environment="res://default_env.tres" environment/default_environment="res://default_env.tres"

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=19 format=2] [gd_scene load_steps=24 format=2]
[ext_resource path="res://scenes/StreamContainer.tscn" type="PackedScene" id=1] [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]
@ -15,6 +15,11 @@
[ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13] [ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13]
[ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14] [ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14]
[ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15] [ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15]
[ext_resource path="res://entities/rockA.tscn" type="PackedScene" id=16]
[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=17]
[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=18]
[ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=19]
[ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=20]
[sub_resource type="Animation" id=25] [sub_resource type="Animation" id=25]
resource_name = "FlashLabel" resource_name = "FlashLabel"
@ -35,7 +40,9 @@ tracks/0/keys = {
[sub_resource type="AnimationNodeStateMachinePlayback" id=26] [sub_resource type="AnimationNodeStateMachinePlayback" id=26]
[sub_resource type="MultiMesh" id=27] [sub_resource type="MultiMesh" id=27]
color_format = 1
transform_format = 1 transform_format = 1
custom_data_format = 1
visible_instance_count = 0 visible_instance_count = 0
mesh = ExtResource( 13 ) mesh = ExtResource( 13 )
@ -351,9 +358,6 @@ script = ExtResource( 15 )
[node name="Player" parent="." instance=ExtResource( 2 )] [node name="Player" parent="." instance=ExtResource( 2 )]
TileWorldNode = NodePath("../TileWorld") TileWorldNode = NodePath("../TileWorld")
[node name="Skeleton" parent="Player/Geometry/Armature" index="0"]
bones/4/bound_children = [ ]
[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 )
@ -382,6 +386,26 @@ World = NodePath("..")
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 )
multimesh = SubResource( 27 ) multimesh = SubResource( 27 )
[node name="Assets" type="Spatial" parent="World"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 )
visible = false
[node name="Rocks" type="Spatial" parent="World/Assets"]
[node name="rockA" parent="World/Assets/Rocks" instance=ExtResource( 16 )]
[node name="rockB" parent="World/Assets/Rocks" instance=ExtResource( 18 )]
[node name="rockC" parent="World/Assets/Rocks" instance=ExtResource( 17 )]
[node name="Grass" type="Spatial" parent="World/Assets"]
[node name="grassLarge" parent="World/Assets/Grass" instance=ExtResource( 20 )]
[node name="Trees" type="Spatial" parent="World/Assets"]
[node name="tree" parent="World/Assets/Trees" instance=ExtResource( 19 )]
[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"]

View File

@ -142,6 +142,7 @@ public class TileInstanceManager : Spatial
foreach (var j in Enumerable.Range(0, chunkSize)) foreach (var j in Enumerable.Range(0, chunkSize))
{ {
var tile3D = (HexTile3D)_hexTile3DScene.Instance(); var tile3D = (HexTile3D)_hexTile3DScene.Instance();
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));
@ -163,7 +164,7 @@ public class TileInstanceManager : Spatial
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount; _multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
GD.Print("VisibleInstanceCount = " + _multiMeshInstance.Multimesh.VisibleInstanceCount); GD.Print("VisibleInstanceCount = " + _multiMeshInstance.Multimesh.VisibleInstanceCount);
ChunkIndex = chunkIndex; ChunkIndex = chunkIndex;
} }
@ -181,8 +182,8 @@ public class TileInstanceManager : Spatial
var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f); var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f);
GD.Print("Updating transforms for instances of chunk " + value); 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;
@ -190,6 +191,7 @@ public class TileInstanceManager : Spatial
var tilePlaneCoord = var tilePlaneCoord =
HexGrid.GetHexCenterFromOffset(new Vector2(column, row)); HexGrid.GetHexCenterFromOffset(new Vector2(column, row));
_multiMeshInstance.Multimesh.SetInstanceTransform(TileInstanceIndices[i], _multiMeshInstance.Multimesh.SetInstanceTransform(TileInstanceIndices[i],
new Transform(tileOrientation, new Transform(tileOrientation,
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y))); chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y)));

View File

@ -17,16 +17,18 @@ public class World : Spatial
} }
// constants // constants
public const int ChunkSize = 16; public const int ChunkSize = 10;
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 GrassColor = new(0, 0.4f, 0);
private static readonly Color DarkGrassColor = new(0.05882353f, 0.5411765f, 0.05882353f);
private static readonly Color LightWaterColor = new(0.05882353f, 0.05882353f, 0.8627451f);
private readonly List<Vector2> _addedChunkIndices = new(); private readonly List<Vector2> _addedChunkIndices = new();
private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks; private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
private readonly Image _heightmapImage = new(); private readonly Image _heightmapImage = new();
private readonly List<Vector2> _removedChunkIndices = new(); private readonly List<Vector2> _removedChunkIndices = new();
private readonly Image _tileTypeMapImage = new(); private readonly Image _tileTypeMapImage = new();
// referenced scenes // referenced scenes
@ -39,10 +41,13 @@ public class World : Spatial
// other members // other members
private Vector2 _centerPlaneCoord; private Vector2 _centerPlaneCoord;
private Array<Spatial> _grassAssets;
private ImageTexture _heightmapTexture; private ImageTexture _heightmapTexture;
private OpenSimplexNoise _noiseGenerator = new(); private OpenSimplexNoise _noiseGenerator = new();
private Array<Spatial> _rockAssets;
private TileInstanceManager _tileInstanceManager; private TileInstanceManager _tileInstanceManager;
private Array<Spatial> _treeAssets;
private ImageTexture _viewTileTypeTexture; private ImageTexture _viewTileTypeTexture;
public Vector2 CenterChunkIndex = Vector2.Zero; public Vector2 CenterChunkIndex = Vector2.Zero;
public Spatial Chunks; public Spatial Chunks;
@ -71,6 +76,17 @@ public class World : Spatial
InitNoiseGenerator(); InitNoiseGenerator();
GetNode<Spatial>("Assets").Visible = false;
_rockAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Rocks").GetChildren()) _rockAssets.Add(asset);
_grassAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Grass").GetChildren()) _grassAssets.Add(asset);
_treeAssets = new Array<Spatial>();
foreach (Spatial asset in GetNode<Node>("Assets/Trees").GetChildren()) _treeAssets.Add(asset);
SetCenterPlaneCoord(Vector2.Zero); SetCenterPlaneCoord(Vector2.Zero);
} }
@ -129,6 +145,78 @@ public class World : Spatial
return result; return result;
} }
private bool IsColorEqualApprox(Color colorA, Color colorB)
{
var colorDifference = new Vector3(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
return colorDifference.LengthSquared() < 0.1 * 0.1;
}
private Spatial SelectAsset(Vector2 offsetCoord, Array<Spatial> assets, Random randomGenerator, double probability)
{
if (randomGenerator.NextDouble() < 1.0 - probability) return null;
var assetIndex = randomGenerator.Next(assets.Count);
var assetInstance = (Spatial)assets[assetIndex].Duplicate();
var assetTransform = Transform.Identity;
assetTransform.origin = HexGrid.GetHexCenterVec3FromOffset(offsetCoord);
// TODO: assetTransform.origin.y = GetHeightAtOffset(offsetCoord);
assetTransform.origin.y = 0;
assetTransform.basis =
assetTransform.basis.Rotated(Vector3.Up, (float)(randomGenerator.NextDouble() * Mathf.Pi * 2));
assetInstance.Transform = assetTransform;
return assetInstance;
}
private void PopulateChunk(WorldChunk chunk)
{
var environmentRandom = new Random(Seed);
var tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData();
tileTypeImage.Lock();
foreach (var textureCoordU in Enumerable.Range(0, chunk.Size))
foreach (var textureCoordV in Enumerable.Range(0, chunk.Size))
{
var colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV);
var textureCoord = new Vector2(textureCoordU, textureCoordV);
var offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
if (IsColorEqualApprox(colorValue, RockColor))
{
var rockAsset = SelectAsset(offsetCoord, _rockAssets, environmentRandom, 0.15);
if (rockAsset != null) chunk.Entities.AddChild(rockAsset);
// TODO: MarkCellUnwalkable(cell);
}
else if (IsColorEqualApprox(colorValue, GrassColor) || IsColorEqualApprox(colorValue, DarkGrassColor))
{
var grassAsset = SelectAsset(offsetCoord, _grassAssets, environmentRandom, 0.15);
if (grassAsset != null) chunk.Entities.AddChild(grassAsset);
var treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.05);
if (treeAsset != null) chunk.Entities.AddChild(treeAsset);
// TODO: MarkCellUnwalkable(cell);
// else if (environmentRandom.NextDouble() < 0.01)
// {
// var chestAsset = (Chest)_chestScene.Instance();
// var assetTransform = Transform.Identity;
// assetTransform.origin = GetHexCenterFromOffset(offsetCoord);
// assetTransform.origin.y = GetHeightAtOffset(offsetCoord);
// chestAsset.Transform = assetTransform;
// Entities.AddChild(chestAsset);
// MarkCellUnwalkable(cell);
// }
}
// else if (IsColorWater(colorValue))
// {
// MarkCellUnwalkable(cell);
// }
}
tileTypeImage.Unlock();
}
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord) public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
{ {
if (State != GenerationState.Done) if (State != GenerationState.Done)
@ -338,6 +426,8 @@ public class World : Spatial
else if (State == GenerationState.Objects) else if (State == GenerationState.Objects)
{ {
// generate objects // generate objects
foreach (var chunkIndex in _addedChunkIndices) PopulateChunk(_cachedWorldChunks[chunkIndex]);
State = GenerationState.Done; State = GenerationState.Done;
} }
} }

View File

@ -4,16 +4,16 @@ using Godot;
public class WorldChunk : Spatial public class WorldChunk : Spatial
{ {
private readonly SpatialMaterial _rectMaterial = new();
private Sprite _heightmapSprite; private Sprite _heightmapSprite;
private TextureRect _heightmapTextureRect; private TextureRect _heightmapTextureRect;
private Sprite _noiseMask; private Sprite _noiseMask;
private Sprite _noiseSprite; private Sprite _noiseSprite;
private readonly SpatialMaterial _rectMaterial = new();
private bool _showTextureOverlay; private bool _showTextureOverlay;
[Export] public Vector2 ChunkIndex; [Export] public Vector2 ChunkIndex;
public Color DebugColor = Colors.White; public Color DebugColor = Colors.White;
[Export] public Spatial Entities;
[Export] public Texture HeightMap; [Export] public Texture HeightMap;
public int HeightMapFrameCount; public int HeightMapFrameCount;
@ -91,6 +91,9 @@ public class WorldChunk : Spatial
TileTypeOffscreenViewport.Size = Vector2.One * Size; TileTypeOffscreenViewport.Size = Vector2.One * Size;
Debug.Assert(TileTypeOffscreenViewport != null); Debug.Assert(TileTypeOffscreenViewport != null);
Entities = (Spatial)FindNode("Entities");
Debug.Assert(Entities != null);
SetSize(World.ChunkSize); SetSize(World.ChunkSize);
} }