GodotComponentTest/scenes/World.cs

192 lines
6.7 KiB
C#

using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Godot.Collections;
public class World : Spatial
{
// referenced scenes
private PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn");
// constants
public const int ChunkSize = 16;
public HexGrid HexGrid = new HexGrid();
public Spatial Chunks;
public Color DebugColor;
// ui elements
// scene nodes
// resources
// exports
// [Export] public Vector2 Size = new Vector2(1, 1);
// signals
[Signal]
delegate void OnTilesChanged(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices);
// delegate void OnCoordClicked(Vector2 world_pos);
// other members
private Vector2 _centerPlaneCoord;
private int[] _centerChunkCoord = { 0, 0 };
private int[] _previousCenterChunkCoord = { 0, 0 };
private Rect2 _centerChunkRect2 = new Rect2();
private Random _debugColorRandom = new Random();
private Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
private List<Vector2> _activeChunkIndices = new();
private List<Vector2> _addedChunkIndices = new();
private List<Vector2> _removedChunkIndices = new();
public World()
{
Debug.Assert(ChunkSize % 2 == 0);
_cachedWorldChunks = new Godot.Collections.Dictionary<Vector2, WorldChunk>();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
Chunks = (Spatial)FindNode("Chunks");
Debug.Assert(Chunks != null);
SetCenterPlaneCoord(Vector2.Zero);
}
public WorldChunk GetOrCreateWorldChunk(int xIndex, int yIndex, Color debugColor)
{
if (IsTileCached(xIndex, yIndex))
{
WorldChunk cachedChunk = _cachedWorldChunks[new Vector2(xIndex, yIndex)];
return cachedChunk;
}
return CreateWorldChunk(xIndex, yIndex, debugColor);
}
private bool IsTileCached(int xIndex, int yIndex)
{
return _cachedWorldChunks.ContainsKey(new Vector2(xIndex, yIndex));
}
private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor)
{
WorldChunk result = (WorldChunk)_worldChunkScene.Instance();
Vector2 offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize;
Vector2 offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1);
Vector2 planeCoordSouthWest = HexGrid.GetHexCenterFromOffset(offsetCoordSouthWest) +
new Vector2(-HexGrid.HexSize.x, HexGrid.HexSize.y) * 0.5f;
Vector2 planeCoordNorthEast = HexGrid.GetHexCenterFromOffset(offsetCoordNorthEast) +
new Vector2(HexGrid.HexSize.x, -HexGrid.HexSize.y) * 0.5f;
result.ChunkAddress = new Vector2(xIndex, yIndex);
result.PlaneRect = new Rect2(
new Vector2(planeCoordSouthWest.x, planeCoordNorthEast.y),
new Vector2(planeCoordNorthEast.x - planeCoordSouthWest.x, planeCoordSouthWest.y - planeCoordNorthEast.y));
result.DebugColor = debugColor;
result.DebugColor.a = 0.6f;
Chunks.AddChild(result);
Vector2 chunkIndex = new Vector2(xIndex, yIndex);
_cachedWorldChunks.Add(chunkIndex, result);
return result;
}
public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord)
{
// mark all chunks as retired
Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks);
// set new center chunk
var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
WorldChunk currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2,
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_centerChunkRect2 = currentChunk.PlaneRect;
// load or create adjacent chunks
_activeChunkIndices = new List<Vector2>();
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1));
foreach(Vector2 activeChunkIndex in _activeChunkIndices)
{
GetOrCreateWorldChunk((int) activeChunkIndex.x, (int) activeChunkIndex.y, new Color(GD.Randf(), GD.Randf(), GD.Randf()));
}
// unload retired chunks
_removedChunkIndices.Clear();
_addedChunkIndices.Clear();
foreach (var cachedChunkKey in oldCachedChunks.Keys)
{
if (!_activeChunkIndices.Contains(cachedChunkKey))
{
RemoveChunk(cachedChunkKey);
}
}
foreach (var chunkKey in _activeChunkIndices)
{
if (!oldCachedChunks.ContainsKey(chunkKey))
{
_addedChunkIndices.Add(chunkKey);
}
}
EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray());
}
private void RemoveChunk(Vector2 cachedChunkKey)
{
_cachedWorldChunks.Remove(cachedChunkKey);
_removedChunkIndices.Add(cachedChunkKey);
foreach (WorldChunk chunk in Chunks.GetChildren())
{
if (chunk.ChunkAddress == new Vector2(cachedChunkKey.x, cachedChunkKey.y))
{
chunk.QueueFree();
}
}
}
private Tuple<int, int> GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
{
HexCell centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
Vector2 chunkIndexFloat = (centerOffsetCoord.OffsetCoords / (float)ChunkSize).Floor();
Tuple<int, int> chunkIndex = new Tuple<int, int>((int)chunkIndexFloat.x, (int)chunkIndexFloat.y);
return chunkIndex;
}
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
{
if (!_centerChunkRect2.HasPoint(centerPlaneCoord))
{
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
}
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
}