Files
Ipseity-Demo/Packages/OctoCore/Runtime/Scripts/Level/OctoLevelBuilderMap.cs
T
2026-02-09 23:53:14 +01:00

337 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace OctoCore.LevelBuilder
{
[ExecuteAlways]
public class OctoLevelBuilderMap : MonoBehaviour
{
[System.Serializable]
private class MapRow
{
public OctoLevelBuilderTile[] Rows;
}
[System.Serializable]
private class MapColumn
{
public MapRow[] Columns;
}
[System.Serializable]
private class Map
{
private bool _isMapUpToDate;
[SerializeField] private Vector3Int _minBounds;
[SerializeField] private Vector3Int _maxBounds;
[SerializeField] private Transform _tilesRoot;
[SerializeField] private List<OctoLevelBuilderTile> _tiles = new List<OctoLevelBuilderTile>();
private List<OctoLevelBuilderTile> _tilesToRefresh = new List<OctoLevelBuilderTile>();
[SerializeField] private MapColumn[] _floors = new MapColumn[0];
public Transform TilesRoot => _tilesRoot;
public OctoLevelBuilderTile[] Tiles => _tiles.ToArray();
public void SetTilesRoot(Transform tilesRoot)
{
_tilesRoot = tilesRoot;
}
public void RequireMapRefresh()
{
_isMapUpToDate = false;
}
public void RequireTileRefresh(OctoLevelBuilderTile tile)
{
if (tile && !_tilesToRefresh.Contains(tile))
_tilesToRefresh.Add(tile);
}
public void AddTile(OctoLevelBuilderTile tile)
{
if (!_tiles.Contains(tile))
{
_tiles.Add(tile);
RequireMapRefresh();
}
}
public void RemoveTile(OctoLevelBuilderTile tile)
{
if (_tiles.Contains(tile))
{
_tiles.Remove(tile);
RequireMapRefresh();
}
}
public OctoLevelBuilderTile GetTile(Vector3Int position)
{
Vector3Int p = position - _minBounds;
if (p.y < 0 || p.y >= _floors.Length)
return null;
if (p.z < 0 || p.z >= _floors[p.y].Columns.Length)
return null;
if (p.x < 0 || p.x >= _floors[p.y].Columns[p.z].Rows.Length)
return null;
return _floors[p.y].Columns[p.z].Rows[p.x];
}
public void RefreshMap(string name)
{
if (_isMapUpToDate)
return;
_minBounds = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue);
_maxBounds = new Vector3Int(int.MinValue, int.MinValue, int.MinValue);
// Get map bounds
foreach (OctoLevelBuilderTile tile in _tiles)
{
Vector3Int p = tile.GetCoordinates(true);
if (p.x < _minBounds.x)
_minBounds.x = p.x;
if (p.x > _maxBounds.x)
_maxBounds.x = p.x;
if (p.y < _minBounds.y)
_minBounds.y = p.y;
if (p.y > _maxBounds.y)
_maxBounds.y = p.y;
if (p.z < _minBounds.z)
_minBounds.z = p.z;
if (p.z > _maxBounds.z)
_maxBounds.z = p.z;
}
// Copy previous map
MapColumn[] previousFloors = new MapColumn[_floors.Length];
for (int y = 0; y < previousFloors.Length; y++)
{
previousFloors[y] = new MapColumn();
previousFloors[y].Columns = new MapRow[_floors[y].Columns.Length];
for (int z = 0; z < previousFloors[y].Columns.Length; z++)
{
previousFloors[y].Columns[z] = new MapRow();
previousFloors[y].Columns[z].Rows = new OctoLevelBuilderTile[_floors[y].Columns[z].Rows.Length];
for (int x = 0; x < previousFloors[y].Columns[z].Rows.Length; x++)
previousFloors[y].Columns[z].Rows[x] = _floors[y].Columns[z].Rows[x];
}
}
// Init map
Vector3Int size = _maxBounds - _minBounds + Vector3Int.one;
_floors = new MapColumn[size.y];
for (int y = 0; y < size.y; y++)
{
_floors[y] = new MapColumn();
_floors[y].Columns = new MapRow[size.z];
for (int z = 0; z < size.z; z++)
{
_floors[y].Columns[z] = new MapRow();
_floors[y].Columns[z].Rows = new OctoLevelBuilderTile[size.x];
}
}
// Assing tiles to the map
foreach (OctoLevelBuilderTile tile in _tiles)
{
Vector3Int p = tile.GetCoordinates(true) - _minBounds;
_floors[p.y].Columns[p.z].Rows[p.x] = tile;
}
// Refresh tiles
for (int y = 0; y < size.y; y++)
{
for (int z = 0; z < size.z; z++)
{
for (int x = 0; x < size.x; x++)
{
OctoLevelBuilderTile tile = _floors[y].Columns[z].Rows[x];
if (tile)
{
bool refreshTile = !tile.IsUpToDate;
if (!refreshTile)
{
OctoLevelBuilderTile previousTile = null;
if (previousFloors.Length > y && previousFloors[y].Columns.Length > z && previousFloors[y].Columns[z].Rows.Length > x)
previousTile = previousFloors[y].Columns[z].Rows[x];
refreshTile = tile != previousTile;
}
if (refreshTile)
{
RequireTileRefresh(tile);
}
}
}
}
}
_isMapUpToDate = true;
Debug.Log($"[OctoLevelGrid] {name}: Updated");
}
public void RefreshTiles()
{
if (_tilesToRefresh.Count == 0)
return;
OctoLevelBuilderTile[] _tmpTilesToRefresh = _tilesToRefresh.ToArray();
_tilesToRefresh.Clear();
foreach (OctoLevelBuilderTile tile in _tmpTilesToRefresh)
if (tile)
tile.Refresh();
}
}
public static float DEFAULT_TILE_SIZE = 2f;
public static float DEFAULT_FLOOR_HEIGHT = 3.5f;
[SerializeField] private OctoLevelBuilderStyleData _defaultStyle;
[SerializeField, OctoReadOnly] private Map _map;
public OctoLevelBuilderStyleData DefaultStyle => _defaultStyle;
public OctoLevelBuilderGridData Grid => DefaultStyle ? DefaultStyle.Grid : null;
public float TileSize => Grid ? Grid.TileSize : DEFAULT_TILE_SIZE;
public float FloorHeight => Grid ? Grid.FloorHeight : DEFAULT_FLOOR_HEIGHT;
void LateUpdate()
{
if (Application.isPlaying)
return;
_map.RefreshTiles();
_map.RefreshMap(name);
}
public void SetLayer(string layer)
{
LayerMask layerMask = LayerMask.NameToLayer(layer);
SetLayerRecursively(gameObject, layerMask);
}
public void RegisterTile(OctoLevelBuilderTile tile)
{
if (tile == null)
return;
_map.AddTile(tile);
}
public void UnregisterTile(OctoLevelBuilderTile tile)
{
if (tile == null)
return;
_map.RemoveTile(tile);
}
public void RequireMapRefresh()
{
_map.RequireMapRefresh();
}
public void RequireTileRefresh(OctoLevelBuilderTile tile)
{
if (tile)
_map.RequireTileRefresh(tile);
}
public bool GetTile(Vector3Int gridPosition, out OctoLevelBuilderTile tile)
{
tile = _map.GetTile(gridPosition);
return tile;
}
public Vector3 GridToWorld(Vector3Int gridPosition)
{
return new Vector3(
gridPosition.x * TileSize,
gridPosition.y * FloorHeight,
gridPosition.z * TileSize
);
}
public Vector3Int WorldToGrid(Vector3 worldPosition, int floorLevel)
{
return new Vector3Int(
Mathf.RoundToInt(worldPosition.x / TileSize),
floorLevel,
Mathf.RoundToInt(worldPosition.z / TileSize)
);
}
[ContextMenu("GenerateMap (5x5)")] public void GenerateMap5x5() { GenerateMap(new Vector3Int(5, 1, 5)); }
[ContextMenu("GenerateMap (7x7)")] public void GenerateMap7x7() { GenerateMap(new Vector3Int(7, 1, 7)); }
[ContextMenu("GenerateMap (9x9)")] public void GenerateMap9x9() { GenerateMap(new Vector3Int(9, 1, 9)); }
public void GenerateMap(Vector3Int size)
{
if (!_map.TilesRoot)
{
GameObject tilesRoot = new GameObject("Tiles");
tilesRoot.transform.parent = transform;
_map.SetTilesRoot(tilesRoot.transform);
}
int xMin = size.x / -2;
int yMin = size.y / -2;
int zMin = size.z / -2;
int xMax = xMin + size.x;
int yMax = yMin + size.y;
int zMax = zMin + size.z;
for (int y = yMin; y < yMax; y++)
{
for (int z = zMin; z < zMax; z++)
{
for (int x = xMin; x < xMax; x++)
{
GameObject tileObj = new GameObject();
tileObj.transform.parent = _map.TilesRoot;
tileObj.transform.localPosition = new Vector3(x * TileSize, y * FloorHeight, z * TileSize);
OctoLevelBuilderTile tile = tileObj.AddComponent<OctoLevelBuilderTile>();
tile.Init(x == xMin || x == xMax - 1 || z == zMin || z == zMax - 1);
}
}
}
Debug.Log("[OctoLevelBluiderMap] Map generated");
}
private void SetLayerRecursively(GameObject obj, int newLayer)
{
obj.layer = newLayer;
foreach (Transform child in obj.transform)
{
SetLayerRecursively(child.gameObject, newLayer);
}
}
}
}