337 lines
11 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
} |