GodotComponentTest/scenes/StreamContainer.cs

254 lines
8.5 KiB
C#

using System;
using Godot;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using GoDotLog;
public class StreamContainer : Spatial
{
// scene nodes
private MeshInstance _bounds;
private Spatial _activeTiles;
private Queue<HexTile3D> _unusedTiles;
private TileWorld _tileWorld;
// resources
private PackedScene _hexTileScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
// exports
[Export] public Vector2 Dimensions = new Vector2(8, 4);
[Export] public NodePath World;
[Signal]
delegate void TileClicked(HexTile3D tile3d);
[Signal]
delegate void TileHovered(HexTile3D tile3d);
// other members
private Rect2 _worldRect;
private Rect2 _currentOffsetCoordRect;
private Rect2 _oldOffsetCoordRect;
private HexGrid _hexGrid;
private Dictionary<Vector2, HexTile3D> _coordToTile = new Dictionary<Vector2, HexTile3D>();
public List<Vector2> RemovedCoords = new List<Vector2>();
public List<Vector2> AddedCoords = new List<Vector2>();
public Rect2 CurrentOffsetCoordRect
{
get { return _currentOffsetCoordRect; }
}
public void SetWorld(TileWorld tileWorld)
{
_tileWorld = tileWorld;
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_unusedTiles = new Queue<HexTile3D>();
_bounds = GetNode<MeshInstance>("Bounds");
_activeTiles = GetNode<Spatial>("ActiveTiles");
_hexGrid = new HexGrid();
Transform boundsTransform = Transform.Identity;
boundsTransform = boundsTransform.Scaled(new Vector3(Dimensions.x, 1, Dimensions.y));
_bounds.Transform = boundsTransform;
_tileWorld = GetNode<TileWorld>(World);
}
public void UpdateRects(Vector2 centerPlane)
{
_oldOffsetCoordRect = _currentOffsetCoordRect;
Vector2 bottomLeftCoord = centerPlane - new Vector2(Dimensions.x / 2, Dimensions.y / 2);
Vector2 topRightCoord = centerPlane + new Vector2(Dimensions.x / 2, Dimensions.y / 2);
_worldRect = new Rect2(bottomLeftCoord, Dimensions);
// GD.Print("World rect now: " + _worldRect.ToString() + " center: " + _worldRect.GetCenter());
// y axis needs to be inverted as HexGrid's offset coordinates use the opposite axis direction
HexCell bottomLeftCell = _hexGrid.GetHexAt(new Vector2(bottomLeftCoord.x, topRightCoord.y));
HexCell topRightCell = _hexGrid.GetHexAt(new Vector2(topRightCoord.x, bottomLeftCoord.y));
_currentOffsetCoordRect = new Rect2(bottomLeftCell.OffsetCoords,
topRightCell.OffsetCoords - bottomLeftCell.OffsetCoords + Vector2.One);
// GD.Print("Offset rect now: " + _currentOffsetCoordRect.ToString() + " center: " +
// _currentOffsetCoordRect.GetCenter());
Transform boundsTransform = _bounds.Transform;
boundsTransform.origin.x = centerPlane.x;
boundsTransform.origin.z = centerPlane.y;
_bounds.Transform = boundsTransform;
// GD.Print("Bounds Transform: " + boundsTransform.ToString());
// GD.Print("Bounds Global : " + _bounds.GlobalTransform.ToString());
if (!_currentOffsetCoordRect.Equals(_oldOffsetCoordRect))
{
UpdateTileCache();
}
}
public void UpdateTileCache()
{
RemovedCoords.Clear();
AddedCoords.Clear();
_unusedTiles.Clear();
Rect2 expandedRect = _currentOffsetCoordRect.Merge(_oldOffsetCoordRect).Grow(2);
Rect2 clippedRect = _currentOffsetCoordRect.Clip(_oldOffsetCoordRect);
MarkUnusedTiles(expandedRect, clippedRect);
AddNewTiles(expandedRect, clippedRect);
foreach (HexTile3D tile3D in _unusedTiles.ToArray())
{
RemovedCoords.Add(tile3D.OffsetCoords);
_activeTiles.RemoveChild(tile3D);
tile3D.Disconnect("TileClicked", this, nameof(OnTileClicked));
tile3D.Disconnect("TileHovered", this, nameof(OnTileHovered));
tile3D.QueueFree();
}
}
public void MarkUnusedTiles(Rect2 expandedRect, Rect2 clippedRect)
{
foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.x) - 5,
Mathf.CeilToInt(expandedRect.Size.x) + 20))
{
foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(expandedRect.Position.y) - 5,
Mathf.CeilToInt(expandedRect.Size.y) + 20))
{
Vector2 coord = new Vector2(coord_x, coord_y);
if (clippedRect.HasPoint(coord))
{
continue;
}
bool isInCurrent = _currentOffsetCoordRect.HasPoint(coord);
bool isInOld = _oldOffsetCoordRect.HasPoint(coord);
if (isInOld && !isInCurrent && _coordToTile.Keys.Contains(coord))
{
HexTile3D tile3D = _coordToTile[coord];
tile3D.Type = HexTile3D.TileType.Undefined;
_unusedTiles.Enqueue(tile3D);
_coordToTile.Remove(coord);
RemovedCoords.Add(coord);
}
}
}
}
public void AddNewTiles(Rect2 expandedRect, Rect2 clippedRect)
{
foreach (int coord_x in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.x),
Mathf.CeilToInt(_currentOffsetCoordRect.Size.x)))
{
foreach (int coord_y in Enumerable.Range(Mathf.FloorToInt(_currentOffsetCoordRect.Position.y),
Mathf.CeilToInt(_currentOffsetCoordRect.Size.y)))
{
Vector2 coord = new Vector2(coord_x, coord_y);
if (clippedRect.HasPoint(coord))
{
continue;
}
if (_unusedTiles.Count == 0)
{
AddedCoords.Add(coord);
HexTile3D tile3D = GetTile3dAt(coord);
}
else
{
HexTile3D tile3D = _unusedTiles.Dequeue();
tile3D.OffsetCoords = coord;
tile3D.Type = _tileWorld.GetTileTypeAtOffset(coord);
Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(coord);
tile3D.Transform = tileTransform;
_coordToTile[coord] = tile3D;
}
}
}
}
public HexTile3D GetTile3dAt(Vector2 offsetCoords)
{
if (!_coordToTile.Keys.Contains(offsetCoords))
{
HexTile3D tile3D = (HexTile3D)_hexTileScene.Instance();
tile3D.OffsetCoords = offsetCoords;
_activeTiles.AddChild(tile3D);
tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(offsetCoords);
tile3D.Transform = tileTransform;
tile3D.Type = _tileWorld.GetTileTypeAtOffset(offsetCoords);
_coordToTile[offsetCoords] = tile3D;
}
return _coordToTile[offsetCoords];
}
public void SetCenterTile(HexCell cell)
{
UpdateRects(_hexGrid.GetHexCenter(cell));
}
public void SetTileMaterial(ShaderMaterial material)
{
foreach (Spatial node in _activeTiles.GetChildren())
{
HexTile3D tile = (HexTile3D)node;
if (tile == null)
{
continue;
}
tile.Mesh.SetSurfaceMaterial(0, material);
}
}
public void OnTileClicked(HexTile3D tile)
{
GD.Print("Clicked on Tile at " + tile.OffsetCoords);
EmitSignal("TileClicked", tile);
}
public void OnTileHovered(HexTile3D tile)
{
GD.Print("Hovered on Tile at " + tile.OffsetCoords);
EmitSignal("TileHovered", tile);
}
public void OnWorldGenerated()
{
foreach (Spatial node in _activeTiles.GetChildren())
{
HexTile3D tile = (HexTile3D)node;
if (tile == null)
{
continue;
}
Transform tileTransform = tile.GlobalTransform;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(tile.OffsetCoords);
tile.GlobalTransform = tileTransform;
}
}
}