614 lines
20 KiB
C#
614 lines
20 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace OctoCore.LevelBuilder
|
|
{
|
|
[ExecuteAlways]
|
|
public class OctoLevelBuilderTile : MonoBehaviour
|
|
{
|
|
[System.Serializable]
|
|
private class Configuration
|
|
{
|
|
public OctoLevelBuilderStyleData _overrideStyle;
|
|
public bool Ceiling;
|
|
public bool Wall;
|
|
public bool Floor;
|
|
public bool Stairs;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class References
|
|
{
|
|
[OctoReadOnly] public OctoLevelBuilderMap Map;
|
|
[OctoReadOnly] public GameObject CeilingInstance;
|
|
[OctoReadOnly] public GameObject WallInstance;
|
|
[OctoReadOnly] public GameObject FloorInstance;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class WallConnections
|
|
{
|
|
public OctoLevelBuilderTile North;
|
|
public OctoLevelBuilderTile Est;
|
|
public OctoLevelBuilderTile South;
|
|
public OctoLevelBuilderTile West;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class WallConfiguration
|
|
{
|
|
public bool AutoConnect = true;
|
|
[Range(0, 3)] public int Orientation = 0;
|
|
public WallType Type = WallType.Wall;
|
|
public WallShape Shape = WallShape.Straight;
|
|
[OctoReadOnly] public WallConnections Connections = new WallConnections();
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class Infos
|
|
{
|
|
public Vector3Int Coordinates;
|
|
public Vector3 Position;
|
|
}
|
|
|
|
[SerializeField] private Configuration _configuration = new Configuration();
|
|
[SerializeField] private WallConfiguration _wall = new WallConfiguration();
|
|
[SerializeField] private References _references = new References();
|
|
[SerializeField, OctoReadOnly] private Infos _infos = new Infos();
|
|
|
|
private bool _isMapUpToDate;
|
|
|
|
public bool IsUpToDate => _isMapUpToDate;
|
|
public bool HasWall => _configuration.Wall;
|
|
public bool HasFloor => _configuration.Floor;
|
|
public bool HasCeiling => _configuration.Ceiling;
|
|
public bool HasStairs => _configuration.Stairs;
|
|
public float TileSize => Map ? Map.TileSize : OctoLevelBuilderMap.DEFAULT_TILE_SIZE;
|
|
public float FloorHeight => Map ? Map.FloorHeight : OctoLevelBuilderMap.DEFAULT_FLOOR_HEIGHT;
|
|
public OctoLevelBuilderMap Map => _references.Map;
|
|
public OctoLevelBuilderStyleData Style => _configuration._overrideStyle ? _configuration._overrideStyle : _references.Map ? _references.Map.DefaultStyle : null;
|
|
|
|
#if UNITY_EDITOR
|
|
void OnValidate()
|
|
{
|
|
if (!Application.isPlaying)
|
|
{
|
|
UnityEditor.EditorApplication.delayCall += () =>
|
|
{
|
|
if (this != null)
|
|
RequireMapRefresh();
|
|
};
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void OnEnable()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
RegisterToMap();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
UnregisterFromMap();
|
|
//DestroyInstance(_wallInstance);
|
|
//DestroyInstance(_floorInstance);
|
|
//DestroyInstance(_ceilingInstance);
|
|
|
|
_references.Map.RequireTileRefresh(_wall.Connections.North);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.Est);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.South);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.West);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnDrawGizmos()
|
|
{
|
|
DrawTileGizmo(new Color(0, 1, 0, 0.2f));
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
DrawTileGizmo(new Color(0, 1, 0, 0.5f));
|
|
}
|
|
|
|
private void DrawTileGizmo(Color color)
|
|
{
|
|
float tileSize = Map ? Map.TileSize : 2;
|
|
|
|
Gizmos.matrix = transform.localToWorldMatrix;
|
|
Gizmos.color = color;
|
|
Gizmos.DrawCube(Vector3.up * tileSize * .025f, new Vector3(tileSize * .2f, tileSize * .05f, tileSize * .2f));
|
|
|
|
/*if (!HasWall())
|
|
{
|
|
Gizmos.color = color;
|
|
Gizmos.DrawCube(transform.position + Vector3.up * 0.5f, new Vector3(1.8f, 1f, 1.8f));
|
|
return;
|
|
}
|
|
|
|
float rotation = Placement.Angle * -90f;
|
|
if (Wall.Shape == WallShape.Straight)
|
|
rotation += 90f;
|
|
|
|
Matrix4x4 oldMatrix = Gizmos.matrix;
|
|
Gizmos.matrix = Matrix4x4.TRS(transform.position + Vector3.up * 1.5f, Quaternion.Euler(0, rotation, 0), Vector3.one);
|
|
Gizmos.color = color;
|
|
|
|
float size = 2f;
|
|
float thickness = 0.3f;
|
|
float height = 3f;
|
|
|
|
switch (Wall.Shape)
|
|
{
|
|
case WallShape.Straight:
|
|
Gizmos.DrawCube(Vector3.zero, new Vector3(size, height, thickness));
|
|
break;
|
|
|
|
case WallShape.Corner:
|
|
Vector3 arm1Pos = new Vector3(size / 4f, 0, 0);
|
|
Vector3 arm2Pos = new Vector3(0, 0, size / 4f);
|
|
Gizmos.DrawCube(arm1Pos, new Vector3(size / 2f, height, thickness));
|
|
Gizmos.DrawCube(arm2Pos, new Vector3(thickness, height, size / 2f));
|
|
break;
|
|
|
|
case WallShape.T:
|
|
Vector3 basePos = new Vector3(0, 0, 0);
|
|
Vector3 crossPos = new Vector3(0, 0, size / 4f);
|
|
Gizmos.DrawCube(basePos, new Vector3(size, height, thickness));
|
|
Gizmos.DrawCube(crossPos, new Vector3(thickness, height, size / 2f));
|
|
break;
|
|
|
|
case WallShape.Cross:
|
|
Gizmos.DrawCube(Vector3.zero, new Vector3(size, height, thickness));
|
|
Gizmos.DrawCube(Vector3.zero, new Vector3(thickness, height, size));
|
|
break;
|
|
|
|
case WallShape.DeadEnd:
|
|
Vector3 deadEndPos = new Vector3(0, 0, size / 4f);
|
|
Gizmos.DrawCube(deadEndPos, new Vector3(thickness, height, size / 2f));
|
|
break;
|
|
|
|
case WallShape.Door:
|
|
case WallShape.Window:
|
|
Gizmos.DrawCube(Vector3.zero, new Vector3(size, height, thickness));
|
|
break;
|
|
|
|
default:
|
|
Gizmos.DrawCube(Vector3.zero, new Vector3(size * 0.5f, height, size * 0.5f));
|
|
break;
|
|
}
|
|
|
|
Gizmos.matrix = oldMatrix;*/
|
|
}
|
|
#endif
|
|
|
|
void Update()
|
|
{
|
|
if (_infos.Position != transform.localPosition)
|
|
{
|
|
_infos.Position = transform.localPosition;
|
|
|
|
SetName();
|
|
RequireMapRefresh();
|
|
}
|
|
}
|
|
|
|
public void Init(bool wall)
|
|
{
|
|
Debug.Log(wall);
|
|
SetName();
|
|
|
|
_configuration.Ceiling = true;
|
|
_configuration.Wall = wall;
|
|
_configuration.Floor = true;
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
SetConnections();
|
|
ClearChildren();
|
|
RefreshWall();
|
|
RefreshFloor();
|
|
RefreshCeiling();
|
|
|
|
_isMapUpToDate = true;
|
|
//Debug.Log($"[OctoTile] {name}: Updated");
|
|
}
|
|
|
|
public Vector3Int GetCoordinates(bool update = false)
|
|
{
|
|
if (update)
|
|
SetCoordinates();
|
|
|
|
return _infos.Coordinates;
|
|
}
|
|
|
|
private void SetCoordinates()
|
|
{
|
|
Vector3 pos = transform.position;
|
|
|
|
_infos.Coordinates = new Vector3Int(
|
|
Mathf.RoundToInt(pos.x / TileSize),
|
|
Mathf.RoundToInt(pos.y / FloorHeight),
|
|
Mathf.RoundToInt(pos.z / TileSize)
|
|
);
|
|
}
|
|
|
|
public void SetConnections()
|
|
{
|
|
GetMap();
|
|
if (!_references.Map)
|
|
return;
|
|
|
|
// Require update for previous connections
|
|
|
|
OctoLevelBuilderTile previousNorth = _wall.Connections.North;
|
|
OctoLevelBuilderTile previousEst = _wall.Connections.Est;
|
|
OctoLevelBuilderTile previousSouth = _wall.Connections.South;
|
|
OctoLevelBuilderTile previousWest = _wall.Connections.West;
|
|
|
|
// Get new connections
|
|
|
|
OctoLevelBuilderTile newNorth = GetNextTile(Vector3Int.forward);
|
|
OctoLevelBuilderTile newEst = GetNextTile(Vector3Int.right );
|
|
OctoLevelBuilderTile newSouth = GetNextTile(Vector3Int.back );
|
|
OctoLevelBuilderTile newWest = GetNextTile(Vector3Int.left );
|
|
|
|
_wall.Connections.North = newNorth && newNorth.HasWall ? newNorth : null;
|
|
_wall.Connections.Est = newEst && newEst .HasWall ? newEst : null;
|
|
_wall.Connections.South = newSouth && newSouth.HasWall ? newSouth : null;
|
|
_wall.Connections.West = newWest && newWest .HasWall ? newWest : null;
|
|
|
|
bool wallChanged = HasWall != (_references.WallInstance ? true : false);
|
|
|
|
// Require update for new connections
|
|
|
|
if (_wall.Connections.North != previousNorth || wallChanged)
|
|
{
|
|
Debug.Log(_wall.Connections.North);
|
|
Debug.Log(previousNorth);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.North);
|
|
_references.Map.RequireTileRefresh(previousNorth);
|
|
}
|
|
if (_wall.Connections.Est != previousEst || wallChanged)
|
|
{
|
|
Debug.Log(_wall.Connections.Est);
|
|
Debug.Log(previousEst);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.Est);
|
|
_references.Map.RequireTileRefresh(previousEst);
|
|
}
|
|
if (_wall.Connections.South != previousSouth || wallChanged)
|
|
{
|
|
Debug.Log(_wall.Connections.South);
|
|
Debug.Log(previousSouth);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.South);
|
|
_references.Map.RequireTileRefresh(previousSouth);
|
|
}
|
|
if (_wall.Connections.West != previousWest || wallChanged)
|
|
{
|
|
Debug.Log(_wall.Connections.West);
|
|
Debug.Log(previousWest);
|
|
_references.Map.RequireTileRefresh(_wall.Connections.West);
|
|
_references.Map.RequireTileRefresh(previousWest);
|
|
}
|
|
}
|
|
|
|
private void RefreshShape()
|
|
{
|
|
if (!HasWall || !_wall.AutoConnect)
|
|
return;
|
|
|
|
int connectionHash = (_wall.Connections.North ? 1000 : 0) + (_wall.Connections.Est ? 100 : 0) + (_wall.Connections.South ? 10 : 0) + (_wall.Connections.West ? 1 : 0);
|
|
int connectionCount = (_wall.Connections.North ? 1 : 0) + (_wall.Connections.Est ? 1 : 0) + (_wall.Connections.South ? 1 : 0) + (_wall.Connections.West ? 1 : 0);
|
|
|
|
switch (connectionCount)
|
|
{
|
|
default:
|
|
_wall.Shape = WallShape.DeadEnd;
|
|
switch(connectionHash)
|
|
{
|
|
case 0010:
|
|
_wall.Orientation = 0;
|
|
break;
|
|
case 0001:
|
|
_wall.Orientation = 1;
|
|
break;
|
|
case 1000:
|
|
_wall.Orientation = 2;
|
|
break;
|
|
case 0100:
|
|
_wall.Orientation = 3;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if ((_wall.Connections.North && _wall.Connections.South) || (_wall.Connections.Est && _wall.Connections.West))
|
|
{
|
|
_wall.Shape = WallShape.Straight;
|
|
switch (connectionHash)
|
|
{
|
|
case 0101:
|
|
_wall.Orientation = 0;
|
|
break;
|
|
case 1010:
|
|
_wall.Orientation = 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_wall.Shape = WallShape.Corner;
|
|
switch (connectionHash)
|
|
{
|
|
case 1001:
|
|
_wall.Orientation = 0;
|
|
break;
|
|
case 1100:
|
|
_wall.Orientation = 1;
|
|
break;
|
|
case 0110:
|
|
_wall.Orientation = 2;
|
|
break;
|
|
case 0011:
|
|
_wall.Orientation = 3;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
_wall.Shape = WallShape.T;
|
|
switch (connectionHash)
|
|
{
|
|
case 1101:
|
|
_wall.Orientation = 0;
|
|
break;
|
|
case 1110:
|
|
_wall.Orientation = 1;
|
|
break;
|
|
case 0111:
|
|
_wall.Orientation = 2;
|
|
break;
|
|
case 1011:
|
|
_wall.Orientation = 3;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
_wall.Shape = WallShape.Cross;
|
|
_wall.Orientation = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private OctoLevelBuilderTile GetNextTile(Vector3Int direction)
|
|
{
|
|
Vector3Int nextCoordinates = _infos.Coordinates + direction;
|
|
|
|
if (!_references.Map)
|
|
{
|
|
Debug.LogWarning("[OctoTile] No Map found");
|
|
return null;
|
|
}
|
|
|
|
if (_references.Map.GetTile(nextCoordinates, out OctoLevelBuilderTile nexTile))
|
|
return nexTile;
|
|
|
|
return null;
|
|
}
|
|
|
|
private void ClearChildren()
|
|
{
|
|
for (int i = transform.childCount - 1; i >= 0; i--)
|
|
{
|
|
GameObject instance = transform.GetChild(i).gameObject;
|
|
|
|
if (instance != _references.CeilingInstance &&
|
|
instance != _references.WallInstance &&
|
|
instance != _references.FloorInstance)
|
|
{
|
|
DestroyInstance(instance);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RefreshWall()
|
|
{
|
|
if (!HasWall)
|
|
{
|
|
DestroyInstance(_references.WallInstance);
|
|
_references.WallInstance = null;
|
|
return;
|
|
}
|
|
|
|
RefreshShape();
|
|
|
|
if (!Style)
|
|
return;
|
|
|
|
GameObject prefab = GetWallPrefab(Style.Wall, _wall.Shape, _wall.Type);
|
|
|
|
if (prefab == null)
|
|
{
|
|
DestroyInstance(_references.WallInstance);
|
|
_references.WallInstance = null;
|
|
return;
|
|
}
|
|
|
|
if (IsInstanceCorrespondingToPrefab(_references.WallInstance, prefab))
|
|
{
|
|
DestroyInstance(_references.WallInstance);
|
|
_references.WallInstance = InstantiatePrefab(prefab, "Wall");
|
|
}
|
|
|
|
if (_references.WallInstance != null)
|
|
{
|
|
float angle = _wall.Orientation * 90f;
|
|
_references.WallInstance.transform.localRotation = Quaternion.Euler(0, angle, 0);
|
|
}
|
|
}
|
|
|
|
private GameObject GetWallPrefab(OctoLevelBuilderWallData wallData, WallShape shape, WallType type)
|
|
{
|
|
if (type == WallType.Door)
|
|
return wallData.WallDoor;
|
|
if (type == WallType.Window)
|
|
return wallData.WallWindow;
|
|
|
|
return wallData.GetPrefab(_wall.Orientation, shape);
|
|
}
|
|
|
|
private void RefreshFloor()
|
|
{
|
|
if (!HasFloor)
|
|
{
|
|
DestroyInstance(_references.FloorInstance);
|
|
_references.FloorInstance = null;
|
|
return;
|
|
}
|
|
|
|
if (!Style)
|
|
return;
|
|
|
|
GameObject prefab = Style.Floor.Prefab;
|
|
|
|
if (prefab == null)
|
|
{
|
|
DestroyInstance(_references.FloorInstance);
|
|
_references.FloorInstance = null;
|
|
return;
|
|
}
|
|
|
|
if (IsInstanceCorrespondingToPrefab(_references.FloorInstance, prefab))
|
|
{
|
|
DestroyInstance(_references.FloorInstance);
|
|
_references.FloorInstance = InstantiatePrefab(prefab, "Floor");
|
|
}
|
|
}
|
|
|
|
private void RefreshCeiling()
|
|
{
|
|
if (!HasCeiling)
|
|
{
|
|
DestroyInstance(_references.CeilingInstance);
|
|
_references.CeilingInstance = null;
|
|
return;
|
|
}
|
|
|
|
if (!Style)
|
|
return;
|
|
|
|
GameObject prefab = Style.Ceiling.Prefab;
|
|
|
|
if (prefab == null)
|
|
{
|
|
DestroyInstance(_references.CeilingInstance);
|
|
_references.CeilingInstance = null;
|
|
return;
|
|
}
|
|
|
|
if (IsInstanceCorrespondingToPrefab(_references.CeilingInstance, prefab))
|
|
{
|
|
DestroyInstance(_references.CeilingInstance);
|
|
_references.CeilingInstance = InstantiatePrefab(prefab, "Ceiling");
|
|
}
|
|
}
|
|
|
|
private bool IsInstanceCorrespondingToPrefab(GameObject instance, GameObject prefab)
|
|
{
|
|
if (instance == null)
|
|
return true;
|
|
|
|
#if UNITY_EDITOR
|
|
GameObject source = (GameObject)UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(instance);
|
|
return source != prefab;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
private void DestroyInstance(GameObject instance)
|
|
{
|
|
if (instance != null)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
DestroyImmediate(instance);
|
|
else
|
|
#endif
|
|
Destroy(instance);
|
|
instance = null;
|
|
}
|
|
}
|
|
|
|
private GameObject InstantiatePrefab(GameObject prefab, string childName)
|
|
{
|
|
#if UNITY_EDITOR
|
|
GameObject instance = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(prefab, transform);
|
|
SceneVisibilityManager.instance.DisablePicking(instance, true);
|
|
#else
|
|
GameObject instance = Instantiate(prefab, transform);
|
|
#endif
|
|
instance.name = childName;
|
|
instance.transform.localPosition = Vector3.zero;
|
|
|
|
return instance;
|
|
}
|
|
|
|
private void GetMap()
|
|
{
|
|
if (!_references.Map)
|
|
_references.Map = GetComponentInParent<OctoLevelBuilderMap>();
|
|
}
|
|
|
|
private bool GetMap(out OctoLevelBuilderMap map)
|
|
{
|
|
GetMap();
|
|
map = _references.Map;
|
|
return map;
|
|
}
|
|
|
|
private void RequireMapRefresh()
|
|
{
|
|
_isMapUpToDate = false;
|
|
if (GetMap(out OctoLevelBuilderMap map))
|
|
map.RequireMapRefresh();
|
|
}
|
|
|
|
private void RegisterToMap()
|
|
{
|
|
if (GetMap(out OctoLevelBuilderMap map))
|
|
map.RegisterTile(this);
|
|
}
|
|
|
|
private void UnregisterFromMap()
|
|
{
|
|
if (GetMap(out OctoLevelBuilderMap map))
|
|
map.UnregisterTile(this);
|
|
}
|
|
|
|
private void SetName()
|
|
{
|
|
int x = _infos.Coordinates.x;
|
|
int y = _infos.Coordinates.z;
|
|
|
|
//string tmpName = "Tile " + (x < 0 ? '-' : '+') + Mathf.Abs(x).ToString().PadLeft(3, '0') + " " + (y < 0 ? '-' : '+') + Mathf.Abs(y).ToString().PadLeft(3, '0');
|
|
string tmpName = "Tile_" + transform.GetSiblingIndex().ToString().PadLeft(3, '0');
|
|
|
|
if (name != tmpName)
|
|
name = tmpName;
|
|
}
|
|
}
|
|
} |