using System.Linq; using Godot; using Godot.Collections; using Vector2 = Godot.Vector2; using Vector3 = Godot.Vector3; public class TileInstanceManager : Spatial { // ui elements // scene nodes // resources // exports [Export] public NodePath World; [Export] public Vector2 ViewCenterPlaneCoord; [Export] public bool ShowHexTiles = false; // signals [Signal] delegate void TileClicked(HexTile3D tile3d); [Signal] delegate void TileHovered(HexTile3D tile3d); // other members public Vector2 WorldTextureCoordinateOffset = Vector2.Zero; private World _world; private ImageTexture _viewTileTypeTexture; private Image _viewTileTypeImage = new(); 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 chunkSize = global::World.ChunkSize; foreach (int i in Enumerable.Range(0, chunkSize)) { foreach (int j in Enumerable.Range(0, chunkSize)) { 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() { _world = GetNode(World); _world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange)); } public override void _Process(float delta) { } SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex) { SceneTileChunk sceneTileChunk = new SceneTileChunk(chunkIndex); foreach (HexTile3D hexTile3D in sceneTileChunk.TileNodes) { hexTile3D.Connect("TileClicked", this, nameof(OnTileClicked)); hexTile3D.Connect("TileHovered", this, nameof(OnTileHovered)); } return sceneTileChunk; } SceneTileChunk FindSceneTileChunkAtIndex(Vector2 chunkIndex) { foreach (Spatial child in GetChildren()) { SceneTileChunk sceneTileChunk = child as SceneTileChunk; if (sceneTileChunk == null) { 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 = FindSceneTileChunkAtIndex(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; if (ShowHexTiles) { foreach (HexTile3D tile3D in sceneTileChunk.TileNodes) { tile3D.Transform = new Transform(Basis.Identity.Scaled(Vector3.One * 0.95f), tile3D.Transform.origin); } } _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); } }