GodotComponentTest/scenes/TileInstanceManager.cs

202 lines
6.9 KiB
C#

using System.Diagnostics;
using System.Linq;
using Godot;
using Godot.Collections;
public class TileInstanceManager : Spatial
{
// exports
[Export] public NodePath World;
[Export] public bool ShowHexTiles;
[Export] public Vector2 ViewCenterPlaneCoord;
// scene nodes
public MultiMeshInstance TileMultiMeshInstance;
// other members
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
private int _usedTileInstanceIndices;
private ImageTexture _viewTileTypeTexture;
private World _world;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_world = GetNode<World>(World);
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
TileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
Debug.Assert(TileMultiMeshInstance != null);
}
public override void _Process(float delta)
{
}
private SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex)
{
var sceneTileChunk =
new SceneTileChunk(chunkIndex, TileMultiMeshInstance, _usedTileInstanceIndices, ShowHexTiles);
_usedTileInstanceIndices += sceneTileChunk.TileNodes.Count;
foreach (var hexTile3D in sceneTileChunk.TileNodes)
{
hexTile3D.Connect("TileClicked", this, nameof(OnTileClicked));
hexTile3D.Connect("TileHovered", this, nameof(OnTileHovered));
}
return sceneTileChunk;
}
private SceneTileChunk FindSceneTileChunkAtIndex(Vector2 chunkIndex)
{
foreach (Spatial child in GetChildren())
{
var sceneTileChunk = child as SceneTileChunk;
if (sceneTileChunk == null) continue;
if (sceneTileChunk.ChunkIndex == chunkIndex) return sceneTileChunk;
}
return null;
}
private void HandleWorldTileChange(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices)
{
Array<SceneTileChunk> removedChunks = new();
foreach (var chunkIndex in removedChunkIndices)
{
var chunk = FindSceneTileChunkAtIndex(chunkIndex);
if (chunk != null) removedChunks.Add(chunk);
}
foreach (var chunkIndex in addedChunkIndices)
{
SceneTileChunk sceneTileChunk = null;
if (removedChunks.Count > 0)
{
sceneTileChunk = removedChunks[^1];
sceneTileChunk.ChunkIndex = chunkIndex;
removedChunks.RemoveAt(removedChunks.Count - 1);
}
else
{
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
AddChild(sceneTileChunk);
}
_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);
}
// signals
[Signal]
private delegate void TileClicked(HexTile3D tile3d);
[Signal]
private delegate void TileHovered(HexTile3D tile3d);
private class SceneTileChunk : Spatial
{
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
private readonly MultiMeshInstance _multiMeshInstance;
private readonly Array<int> _tileInstanceIndices = new();
private readonly HexGrid _hexGrid = new();
private readonly bool _showHexTiles;
public readonly Array<HexTile3D> TileNodes = new();
private Vector2 _chunkIndex = Vector2.Inf;
public SceneTileChunk(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance, int tileInstanceIndexStart,
bool showHexTiles)
{
_showHexTiles = showHexTiles;
var tileInstanceIndexStart1 = tileInstanceIndexStart;
var chunkSize = global::World.ChunkSize;
foreach (var i in Enumerable.Range(0, chunkSize))
foreach (var j in Enumerable.Range(0, chunkSize))
{
var tile3D = (HexTile3D)_hexTile3DScene.Instance();
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * global::World.ChunkSize + new Vector2(i, j));
var tileTransform = Transform.Identity;
var centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
tile3D.Transform = tileTransform;
TileNodes.Add(tile3D);
AddChild(tile3D);
}
_multiMeshInstance = multiMeshInstance;
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
Debug.Assert(tileInstanceIndexStart1 + chunkTileCount <= _multiMeshInstance.Multimesh.InstanceCount);
foreach (var i in Enumerable.Range(0, chunkTileCount))
_tileInstanceIndices.Add(tileInstanceIndexStart1 + i);
// _multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
ChunkIndex = chunkIndex;
}
public Vector2 ChunkIndex
{
get => _chunkIndex;
set
{
var chunkTransform = Transform.Identity;
var chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize);
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
Transform = chunkTransform;
_chunkIndex = value;
var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f);
GD.Print("Updating transforms for instances of chunk " + value + " origin: " + chunkTransform.origin);
foreach (var i in Enumerable.Range(0, _tileInstanceIndices.Count))
{
var column = i % global::World.ChunkSize;
var row = i / global::World.ChunkSize;
var tilePlaneCoord =
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
var hexTransform = new Transform(tileOrientation,
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
if (_showHexTiles)
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
hexTransform.origin);
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
}
}
}
}
}