From da09c66cb368de6d4037b5079a5007b77e8784eb Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Mon, 23 Oct 2023 21:22:53 +0200 Subject: [PATCH] Added SceneTileChunk that is used in WorldView to manage state needed for the visible part of a chunk. --- assets/TestHeightmap.png | Bin 0 -> 6154 bytes assets/TestHeightmap.png.import | 37 +++++++++ assets/TestHeightmap.tres | 5 ++ icon.png.import | 2 +- scenes/Game.cs | 2 + scenes/Game.tscn | 4 + scenes/HexTile3D.tscn | 1 - scenes/HexTile3DPatch.tscn | 1 - scenes/World.cs | 114 ++++++++++++++++++++++++--- scenes/WorldChunk.cs | 124 +++++++++++++++++++++++------- scenes/WorldChunk.tscn | 54 ++++++++++++- scenes/WorldView.cs | 131 +++++++++++++++++++++++++++++++- 12 files changed, 431 insertions(+), 44 deletions(-) create mode 100644 assets/TestHeightmap.png create mode 100644 assets/TestHeightmap.png.import create mode 100644 assets/TestHeightmap.tres diff --git a/assets/TestHeightmap.png b/assets/TestHeightmap.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac81195ada657d8bf06e2817ea679fb863f09f6 GIT binary patch literal 6154 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 literal 0 HcmV?d00001 diff --git a/assets/TestHeightmap.png.import b/assets/TestHeightmap.png.import new file mode 100644 index 0000000..3c5ee79 --- /dev/null +++ b/assets/TestHeightmap.png.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="StreamTexture" +path.s3tc="res://.import/TestHeightmap.png-6cbcc94e211273dff5857931034e368e.s3tc.stex" +path.etc2="res://.import/TestHeightmap.png-6cbcc94e211273dff5857931034e368e.etc2.stex" +metadata={ +"imported_formats": [ "s3tc", "etc2" ], +"vram_texture": true +} + +[deps] + +source_file="res://assets/TestHeightmap.png" +dest_files=[ "res://.import/TestHeightmap.png-6cbcc94e211273dff5857931034e368e.s3tc.stex", "res://.import/TestHeightmap.png-6cbcc94e211273dff5857931034e368e.etc2.stex" ] + +[params] + +compress/mode=2 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=true +flags/filter=true +flags/mipmaps=true +flags/anisotropic=false +flags/srgb=1 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/assets/TestHeightmap.tres b/assets/TestHeightmap.tres new file mode 100644 index 0000000..dd38c23 --- /dev/null +++ b/assets/TestHeightmap.tres @@ -0,0 +1,5 @@ +[gd_resource type="StreamTexture" format=2] + +[resource] +flags = 20 +load_path = "res://.import/TestHeightmap.png-6cbcc94e211273dff5857931034e368e.stex" diff --git a/icon.png.import b/icon.png.import index f6a79f2..fb1e23a 100644 --- a/icon.png.import +++ b/icon.png.import @@ -25,7 +25,7 @@ flags/repeat=true flags/filter=true flags/mipmaps=true flags/anisotropic=false -flags/srgb=2 +flags/srgb=1 process/fix_alpha_border=true process/premult_alpha=false process/HDR_as_SRGB=false diff --git a/scenes/Game.cs b/scenes/Game.cs index 9dd27e6..5d048c4 100644 --- a/scenes/Game.cs +++ b/scenes/Game.cs @@ -122,6 +122,8 @@ public class Game : Spatial _player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem, nameof(_interactionSystem.OnStartInteraction)); _player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged)); + _worldView.Connect("TileClicked", this, nameof(OnTileClicked)); + _worldView.Connect("TileHovered", this, nameof(OnTileHovered)); // register entity events foreach (Node node in GetNode("Entities").GetChildren()) diff --git a/scenes/Game.tscn b/scenes/Game.tscn index a7eb109..01d5dad 100644 --- a/scenes/Game.tscn +++ b/scenes/Game.tscn @@ -329,6 +329,7 @@ flip_v = true visible = false [node name="StreamContainer" parent="." instance=ExtResource( 1 )] +visible = false ShowHexTiles = true [node name="Camera" parent="." instance=ExtResource( 3 )] @@ -339,6 +340,9 @@ script = ExtResource( 15 ) [node name="Player" parent="." instance=ExtResource( 2 )] 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"] 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 ) diff --git a/scenes/HexTile3D.tscn b/scenes/HexTile3D.tscn index 811fbe8..b05e91a 100644 --- a/scenes/HexTile3D.tscn +++ b/scenes/HexTile3D.tscn @@ -23,7 +23,6 @@ script = ExtResource( 1 ) [node name="Mesh" type="MeshInstance" parent="." groups=["GameGeometry"]] transform = Transform( -4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0, -2.5, 0 ) -visible = false mesh = ExtResource( 3 ) material/0 = ExtResource( 2 ) diff --git a/scenes/HexTile3DPatch.tscn b/scenes/HexTile3DPatch.tscn index 57f2235..59766d6 100644 --- a/scenes/HexTile3DPatch.tscn +++ b/scenes/HexTile3DPatch.tscn @@ -5,7 +5,6 @@ [node name="Spatial" type="Spatial"] [node name="HexTile3D1" parent="." instance=ExtResource( 1 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) [node name="HexTile3D2" parent="." instance=ExtResource( 1 )] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.866 ) diff --git a/scenes/World.cs b/scenes/World.cs index 97a7beb..5e5b32a 100644 --- a/scenes/World.cs +++ b/scenes/World.cs @@ -2,15 +2,27 @@ using Godot; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Godot.Collections; public class World : Spatial { + public enum GenerationState + { + Undefined, + Heightmap, + TileType, + Objects, + Done + } + public GenerationState State = GenerationState.Done; + // referenced scenes private PackedScene _worldChunkScene = GD.Load("res://scenes/WorldChunk.tscn"); // constants public const int ChunkSize = 16; + public int Seed = 0; public HexGrid HexGrid = new HexGrid(); public Spatial Chunks; public Color DebugColor; @@ -34,13 +46,15 @@ public class World : Spatial private Vector2 _centerPlaneCoord; private int[] _centerChunkCoord = { 0, 0 }; private int[] _previousCenterChunkCoord = { 0, 0 }; - private Rect2 _centerChunkRect2 = new Rect2(); + private Rect2 _centerChunkRect = new Rect2(); private Random _debugColorRandom = new Random(); private Godot.Collections.Dictionary _cachedWorldChunks; private List _activeChunkIndices = new(); private List _addedChunkIndices = new(); private List _removedChunkIndices = new(); + private OpenSimplexNoise noiseGenerator = new(); + public World() { Debug.Assert(ChunkSize % 2 == 0); @@ -53,10 +67,23 @@ public class World : Spatial { Chunks = (Spatial)FindNode("Chunks"); Debug.Assert(Chunks != null); + + InitNoiseGenerator(); SetCenterPlaneCoord(Vector2.Zero); } + public void InitNoiseGenerator() + { + noiseGenerator = new OpenSimplexNoise(); + + noiseGenerator.Seed = Seed; + noiseGenerator.Octaves = 1; + noiseGenerator.Period = 10; + noiseGenerator.Persistence = 0.5f; + noiseGenerator.Lacunarity = 2; + } + public WorldChunk GetOrCreateWorldChunk(int xIndex, int yIndex, Color debugColor) { if (IsTileCached(xIndex, yIndex)) @@ -76,6 +103,7 @@ public class World : Spatial private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor) { WorldChunk result = (WorldChunk)_worldChunkScene.Instance(); + result.SetSize(ChunkSize); Vector2 offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize; Vector2 offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1); @@ -102,6 +130,12 @@ public class World : Spatial public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord) { + if (State != GenerationState.Done) + { + GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!"); + return; + } + // mark all chunks as retired Godot.Collections.Dictionary oldCachedChunks = new(_cachedWorldChunks); @@ -109,7 +143,7 @@ public class World : Spatial var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord); WorldChunk currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2, new Color(GD.Randf(), GD.Randf(), GD.Randf())); - _centerChunkRect2 = currentChunk.PlaneRect; + _centerChunkRect = currentChunk.PlaneRect; // load or create adjacent chunks _activeChunkIndices = new List(); @@ -149,8 +183,11 @@ public class World : Spatial _addedChunkIndices.Add(chunkKey); } } - - EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray()); + + if (_addedChunkIndices.Count > 0) + { + State = GenerationState.Heightmap; + } } private void RemoveChunk(Vector2 cachedChunkKey) @@ -178,15 +215,72 @@ public class World : Spatial public void SetCenterPlaneCoord(Vector2 centerPlaneCoord) { - if (!_centerChunkRect2.HasPoint(centerPlaneCoord)) + if (!_centerChunkRect.HasPoint(centerPlaneCoord)) { UpdateCenterChunkFromPlaneCoord(centerPlaneCoord); } } -// // Called every frame. 'delta' is the elapsed time since the previous frame. -// public override void _Process(float delta) -// { -// -// } + public override void _Process(float delta) + { + GenerationState oldState = State; + + if (State == GenerationState.Heightmap) + { + // generate heightmap for all new chunks + foreach (Vector2 chunkIndex in _addedChunkIndices) + { + WorldChunk chunk = _cachedWorldChunks[chunkIndex]; + Color debugChunkColor = new Color(Mathf.Abs(chunkIndex.x) / 5, Mathf.Abs(chunkIndex.y) / 5, Mathf.RoundToInt(Mathf.Abs(chunkIndex.x + chunkIndex.y)) % 2); + + GD.Print("Generating for offset " + chunkIndex + " chunk: " + chunk + " debugChunkColor: " + debugChunkColor); + + ImageTexture noiseImageTexture = new ImageTexture(); + noiseImageTexture.CreateFromImage(noiseGenerator.GetImage(ChunkSize, ChunkSize, chunkIndex * ChunkSize), (uint) 0); + + // Debug Texture + Image simpleImage = new Image(); + simpleImage.Create(ChunkSize, ChunkSize, false, Image.Format.Rgb8); + simpleImage.Lock(); + + foreach (int i in Enumerable.Range(0, ChunkSize)) + { + foreach (int j in Enumerable.Range(0, ChunkSize)) + { + if ((i + j) % 2 == 0) + { + simpleImage.SetPixelv(new Vector2(i, j), Colors.Aqua); + } + else + { + simpleImage.SetPixelv(new Vector2(i, j), debugChunkColor); + } + } + } + + simpleImage.Unlock(); + // noiseImageTexture.CreateFromImage(simpleImage, 0); + chunk.SetHeightmap(noiseImageTexture); + } + + // assign height map images + + State = GenerationState.TileType; + } else if (State == GenerationState.TileType) + { + // assign tile type images + + State = GenerationState.Objects; + } else if (State == GenerationState.Objects) + { + // generate objects + + State = GenerationState.Done; + } + + if (oldState != GenerationState.Done && State == GenerationState.Done) + { + EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray()); + } + } } \ No newline at end of file diff --git a/scenes/WorldChunk.cs b/scenes/WorldChunk.cs index a288694..33e0c96 100644 --- a/scenes/WorldChunk.cs +++ b/scenes/WorldChunk.cs @@ -3,21 +3,22 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; public class WorldChunk : Spatial { // ui elements - + // scene nodes private MeshInstance PlaneRectMesh; // resources - + // exports [Export] public Texture TileTypeMap; [Export] public Texture NavigationMap; [Export] public Texture HeightMap; - [Export] public readonly int size = 32; + [Export] public int Size = 32; [Export] public Vector2 ChunkAddress; // [Export] public Vector2 Size = new Vector2(1, 1); @@ -28,58 +29,127 @@ public class WorldChunk : Spatial // other members public Rect2 PlaneRect; public Color DebugColor = Colors.White; + public Viewport TileTypeOffscreenViewport; + public bool NoiseTextureCheckerboardOverlay = true; + private TextureRect _heightmapRect; + private SpatialMaterial _rectMaterial = new SpatialMaterial(); + public WorldChunk() { - } - + public WorldChunk(int size) { - + SetSize(size); } - + + public void SetSize(int size) + { + Size = size; + + if (TileTypeOffscreenViewport != null) + { + TileTypeOffscreenViewport.Size = Vector2.One * size; + } + } + // other members public void SaveToFile(String chunkName) { Image image = new Image(); - - image.CreateFromData(size, size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData()); + + image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData()); image.SavePng(chunkName + "_tileType.png"); - - image.CreateFromData(size, size, false, Image.Format.Rgba8, NavigationMap.GetData().GetData()); + + image.CreateFromData(Size, Size, false, Image.Format.Rgba8, NavigationMap.GetData().GetData()); image.SavePng(chunkName + "_navigationMap.png"); - image.CreateFromData(size, size, false, Image.Format.Rgba8, HeightMap.GetData().GetData()); + image.CreateFromData(Size, Size, false, Image.Format.Rgba8, HeightMap.GetData().GetData()); image.SavePng(chunkName + "_heightMap.png"); } public void LoadFromFile(String chunkName) { - } - + // Called when the node enters the scene tree for the first time. public override void _Ready() { - PlaneRectMesh = (MeshInstance) FindNode("PlaneRectMesh"); + PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh"); Debug.Assert(PlaneRectMesh != null); - + Transform planeRectTransform = Transform.Identity; - planeRectTransform = planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x * 0.5f, 1, PlaneRect.Size.y * 0.5f)); + planeRectTransform = + planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x, 0.125f, PlaneRect.Size.y)); planeRectTransform.origin.x = PlaneRect.GetCenter().x; - planeRectTransform.origin.z = PlaneRect.GetCenter().y; + planeRectTransform.origin.z = PlaneRect.GetCenter().y; + PlaneRectMesh.Transform = planeRectTransform; - PlaneRectMesh.MaterialOverride = new SpatialMaterial(); - ((SpatialMaterial)PlaneRectMesh.MaterialOverride).AlbedoColor = DebugColor; - ((SpatialMaterial)PlaneRectMesh.MaterialOverride).FlagsTransparent = true; +// PlaneRectMesh.MaterialOverride = new SpatialMaterial(); +// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).AlbedoColor = DebugColor; +// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).FlagsTransparent = true; + + TileTypeOffscreenViewport = (Viewport)FindNode("TileTypeOffscreenViewport"); + TileTypeOffscreenViewport.Size = Vector2.One * Size; + Debug.Assert(TileTypeOffscreenViewport != null); + + _heightmapRect = (TextureRect)FindNode("HeightmapTexture"); + } + + public void SetHeightmap(Texture texture) + { + GD.Print("Setting HeightmapRect Texture: " + _heightmapRect + " with size " + _heightmapRect.GetSize()); + _heightmapRect.Texture = texture; } -// // Called every frame. 'delta' is the elapsed time since the previous frame. -// public override void _Process(float delta) -// { -// -// } -} +// Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(float delta) + { + Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture(); + + if (NoiseTextureCheckerboardOverlay) + { + Image tileTypeImage = tileTypeTexture.GetData(); + tileTypeImage.Lock(); + + foreach (int i in Enumerable.Range(0, Size)) + { + foreach (int j in Enumerable.Range(0, Size)) + { + Vector2 textureCoord = new Vector2(i, j); + Color baseColor = tileTypeImage.GetPixelv(textureCoord); + + if ((i + j) % 2 == 0) + { + tileTypeImage.SetPixelv(textureCoord, baseColor); + } + else + { + tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f); + } + } + } + + tileTypeImage.Unlock(); + ImageTexture imageTexture = new ImageTexture(); + imageTexture.CreateFromImage(tileTypeImage, (uint) 0); + tileTypeTexture = imageTexture; + } + + _rectMaterial.AlbedoTexture = tileTypeTexture; + + _rectMaterial.FlagsTransparent = true; +// _rectMaterial.AlbedoTexture = _heightmapRect.Texture; + _rectMaterial.Uv1Scale = new Vector3(-3, -2, 1); + _rectMaterial.Uv1Offset = Vector3.One * 2; + + //RectMaterial.Uv1Triplanar = true; + PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial); + + TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once; + PlaneRectMesh.MaterialOverride = null; + } +} \ No newline at end of file diff --git a/scenes/WorldChunk.tscn b/scenes/WorldChunk.tscn index 79210b3..933054d 100644 --- a/scenes/WorldChunk.tscn +++ b/scenes/WorldChunk.tscn @@ -1,11 +1,29 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=9 format=2] [ext_resource path="res://scenes/WorldChunk.cs" type="Script" id=1] +[ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=2] +[ext_resource path="res://assets/TestHeightmap.tres" type="Texture" id=3] +[ext_resource path="res://assets/4x4checkerColor.png" type="Texture" id=4] [sub_resource type="CubeMesh" id=27] -size = Vector3( 2, 0.5, 2 ) +size = Vector3( 1, 1, 1 ) [sub_resource type="SpatialMaterial" id=28] +flags_transparent = true +albedo_color = Color( 1, 1, 1, 0.501961 ) +albedo_texture = ExtResource( 4 ) +uv1_scale = Vector3( -3, -2, 0 ) +uv1_offset = Vector3( 2, 2, 0 ) + +[sub_resource type="OpenSimplexNoise" id=29] +octaves = 1 +period = 9.0 +persistence = 0.0 + +[sub_resource type="NoiseTexture" id=30] +width = 8 +height = 8 +noise = SubResource( 29 ) [node name="WorldChunk" type="Spatial"] script = ExtResource( 1 ) @@ -13,5 +31,37 @@ script = ExtResource( 1 ) [node name="Entities" type="Spatial" parent="."] [node name="PlaneRectMesh" type="MeshInstance" parent="."] +transform = Transform( 2, 0, 0, 0, 0.125, 0, 0, 0, 2, 0, -0.1, 0 ) mesh = SubResource( 27 ) material/0 = SubResource( 28 ) + +[node name="TileTypeOffscreenViewport" type="Viewport" parent="."] +size = Vector2( 8, 8 ) +transparent_bg = true +handle_input_locally = false +hdr = false +disable_3d = true +usage = 0 +render_target_v_flip = true +render_target_update_mode = 3 +shadow_atlas_quad_0 = 0 +shadow_atlas_quad_1 = 0 +shadow_atlas_quad_2 = 0 +shadow_atlas_quad_3 = 0 + +[node name="NoiseTexture" type="TextureRect" parent="TileTypeOffscreenViewport"] +visible = false +margin_right = 40.0 +margin_bottom = 40.0 +texture = SubResource( 30 ) + +[node name="IslandShader" type="TextureRect" parent="TileTypeOffscreenViewport"] +material = ExtResource( 2 ) +margin_right = 100.0 +margin_bottom = 100.0 + +[node name="HeightmapTexture" type="TextureRect" parent="TileTypeOffscreenViewport/IslandShader"] +use_parent_material = true +margin_right = 100.0 +margin_bottom = 100.0 +texture = ExtResource( 3 ) diff --git a/scenes/WorldView.cs b/scenes/WorldView.cs index 1e99237..4902b03 100644 --- a/scenes/WorldView.cs +++ b/scenes/WorldView.cs @@ -1,3 +1,4 @@ +using System.Linq; using Godot; using Godot.Collections; @@ -14,10 +15,62 @@ public class WorldView : Spatial [Export] public Vector2 ViewCenterPlaneCoord; // signals - // delegate void OnCoordClicked(Vector2 world_pos); + [Signal] + delegate void TileClicked(HexTile3D tile3d); + [Signal] + delegate void TileHovered(HexTile3D tile3d); // other members private World _world; + + private class SceneTileChunk : Spatial + { + private Vector2 _chunkIndex = Vector2.Inf; + public Vector2 ChunkIndex + { + get + { + return _chunkIndex; + } + + set + { + Transform chunkTransform = Transform.Identity; + Vector2 chunkOriginPlaneCoord = HexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize); + chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y); + Transform = chunkTransform; + _chunkIndex = value; + } + } + + public Array TileNodes = new(); + + private PackedScene _hexTile3DScene = GD.Load("res://scenes/HexTile3D.tscn"); + private HexGrid HexGrid = new(); + + public SceneTileChunk(Vector2 chunkIndex, int size) + { + foreach (int i in Enumerable.Range(0, size)) + { + foreach (int j in Enumerable.Range(0, size)) + { + HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance(); + + Transform tileTransform = Transform.Identity; + Vector2 centerPlaneCoord = HexGrid.GetHexCenterFromOffset(new Vector2(i, j)); + tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y); + tile3D.Transform = tileTransform; + + TileNodes.Add(tile3D); + AddChild(tile3D); + } + } + + ChunkIndex = chunkIndex; + } + } + + private Array _sceneTileChunks = new Array(); // Called when the node enters the scene tree for the first time. public override void _Ready() @@ -27,14 +80,88 @@ public class WorldView : Spatial _world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange)); } + public override void _Process(float delta) { } + SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex) + { + SceneTileChunk sceneTileChunk = new SceneTileChunk(chunkIndex, global::World.ChunkSize); + + foreach (HexTile3D hexTile3D in sceneTileChunk.TileNodes) + { + hexTile3D.Connect("TileClicked", this, nameof(OnTileClicked)); + hexTile3D.Connect("TileHovered", this, nameof(OnTileHovered)); + } + + return sceneTileChunk; + } + + SceneTileChunk RemoveChunkFromScene(Vector2 chunkIndex) + { + foreach (Spatial child in GetChildren()) + { + SceneTileChunk sceneTileChunk = child as SceneTileChunk; + if (sceneTileChunk == null) + { + RemoveChild(child); + continue; + } + + if (sceneTileChunk.ChunkIndex == chunkIndex) + { + return sceneTileChunk; + } + } + + return null; + } + private void HandleWorldTileChange(Array removedChunkIndices, Array addedChunkIndices) { - + Array removedChunks = new(); + foreach (Vector2 chunkIndex in removedChunkIndices) + { + SceneTileChunk chunk = RemoveChunkFromScene(chunkIndex); + if (chunk != null) + { + removedChunks.Add(chunk); + } + } + + foreach (Vector2 chunkIndex in addedChunkIndices) + { + SceneTileChunk sceneTileChunk = null; + if (removedChunks.Count > 0) + { + sceneTileChunk = removedChunks[^1]; + removedChunks.RemoveAt(removedChunks.Count - 1); + GD.Print("Reused SceneTileChunk"); + } + else + { + sceneTileChunk = CreateSceneTileChunk(chunkIndex); + AddChild(sceneTileChunk); + GD.Print("Created SceneTileChunk"); + } + + sceneTileChunk.ChunkIndex = chunkIndex; + _sceneTileChunks.Add(sceneTileChunk); + } + GD.Print("Removed Chunks " + removedChunkIndices.Count); GD.Print("Added Chunks " + addedChunkIndices.Count); + GD.Print("Removed chunk count: " + removedChunks.Count); + } + + public void OnTileClicked(HexTile3D tile) + { + EmitSignal("TileClicked", tile); + } + + public void OnTileHovered(HexTile3D tile) + { + EmitSignal("TileHovered", tile); } } \ No newline at end of file