1574 lines
66 KiB
C#
1574 lines
66 KiB
C#
|
#if UNITY_EDITOR
|
||
|
using UnityEngine;
|
||
|
using UnityEditor;
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
|
||
|
namespace GSpawn
|
||
|
{
|
||
|
public class TileRuleGridRayHit
|
||
|
{
|
||
|
private TileRuleGrid _hitGrid;
|
||
|
private float _hitEnter;
|
||
|
private Vector3 _hitPoint;
|
||
|
private Vector3 _hitNormal;
|
||
|
private Vector3Int _hitCellCoords;
|
||
|
|
||
|
public TileRuleGrid hitGrid { get { return _hitGrid; } }
|
||
|
public float hitEnter { get { return _hitEnter; } }
|
||
|
public Vector3 hitPoint { get { return _hitPoint; } }
|
||
|
public Vector3 hitNormal { get { return _hitNormal; } }
|
||
|
public Vector3Int hitCellCoords { get { return _hitCellCoords; } }
|
||
|
|
||
|
public TileRuleGridRayHit(Ray hitRay, TileRuleGrid hitGrid, Vector3 hitNormal, float hitEnter, Vector3Int hitCellCoords)
|
||
|
{
|
||
|
_hitGrid = hitGrid;
|
||
|
_hitEnter = hitEnter;
|
||
|
_hitPoint = hitRay.GetPoint(hitEnter);
|
||
|
_hitNormal = hitNormal;
|
||
|
_hitCellCoords = hitCellCoords;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public struct TileRuleGridCellRange
|
||
|
{
|
||
|
private Vector3Int _min;
|
||
|
private Vector3Int _max;
|
||
|
|
||
|
public Vector3Int min { get { return _min; } set { _min = value; sortCoords(); } }
|
||
|
public Vector3Int max { get { return _max; } set { _max = value; sortCoords(); } }
|
||
|
|
||
|
public TileRuleGridCellRange(Vector3Int minCoords, Vector3Int maxCoords)
|
||
|
{
|
||
|
_min = minCoords;
|
||
|
_max = maxCoords;
|
||
|
sortCoords();
|
||
|
}
|
||
|
|
||
|
private void sortCoords()
|
||
|
{
|
||
|
if (_min.x > _max.x) { int t = _min.x; _min.x = _max.x; _max.x = t; }
|
||
|
if (_min.y > _max.y) { int t = _min.y; _min.y = _max.y; _max.y = t; }
|
||
|
if (_min.z > _max.z) { int t = _min.z; _min.z = _max.z; _max.z = t; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
public class TileRuleGridRampCells : SerializableHashSet<Vector3Int> { }
|
||
|
|
||
|
[Serializable]
|
||
|
public class TileRuleGridState
|
||
|
{
|
||
|
[SerializeField]
|
||
|
public TileRuleGridRampCells rampCells = new TileRuleGridRampCells();
|
||
|
|
||
|
public void clear()
|
||
|
{
|
||
|
rampCells.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class TileRuleGrid : ScriptableObject, IUIItemStateProvider
|
||
|
{
|
||
|
private class EditData
|
||
|
{
|
||
|
public HashSet<Vector3Int> addedRampCells = new HashSet<Vector3Int>();
|
||
|
public HashSet<Vector3Int> removedRampCells = new HashSet<Vector3Int>();
|
||
|
|
||
|
public void clear()
|
||
|
{
|
||
|
addedRampCells.Clear();
|
||
|
removedRampCells.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private enum TilePaintReason
|
||
|
{
|
||
|
Paint = 0,
|
||
|
Erase,
|
||
|
Connect,
|
||
|
Refresh,
|
||
|
}
|
||
|
|
||
|
private class TilePaintParams
|
||
|
{
|
||
|
public Vector3Int cellCoords = new Vector3Int();
|
||
|
public bool paintingRamp = false;
|
||
|
public TilePaintReason paintReason = TilePaintReason.Paint;
|
||
|
|
||
|
public void clear()
|
||
|
{
|
||
|
paintingRamp = false;
|
||
|
paintReason = TilePaintReason.Paint;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class TileSpawnData
|
||
|
{
|
||
|
[NonSerialized]
|
||
|
public TileRule rule;
|
||
|
[NonSerialized]
|
||
|
public TileRulePrefab rulePrefab;
|
||
|
[NonSerialized]
|
||
|
public Quaternion rotation = Quaternion.identity;
|
||
|
[NonSerialized]
|
||
|
public Vector3 scale = Vector3.one;
|
||
|
[NonSerialized]
|
||
|
public bool flipSpriteX = false;
|
||
|
[NonSerialized]
|
||
|
public bool flipSpriteY = false;
|
||
|
[NonSerialized]
|
||
|
public bool isRamp = false;
|
||
|
|
||
|
public void reset()
|
||
|
{
|
||
|
rule = null;
|
||
|
rulePrefab = null;
|
||
|
rotation = Quaternion.identity;
|
||
|
scale = Vector3.one;
|
||
|
flipSpriteX = false;
|
||
|
flipSpriteY = false;
|
||
|
isRamp = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static TileRuleGrid()
|
||
|
{
|
||
|
_neighOffsets_R1 = new Vector2Int[8];
|
||
|
_neighOffsets_R2 = new Vector2Int[24];
|
||
|
_neighOffsets_R3 = new Vector2Int[48];
|
||
|
|
||
|
int arrayIndex = 0;
|
||
|
for (int r = -1; r <= 1; ++r)
|
||
|
{
|
||
|
for(int c = -1; c <= 1; ++c)
|
||
|
{
|
||
|
if (r != 0 || c != 0)
|
||
|
{
|
||
|
_neighOffsets_R1[arrayIndex++] = new Vector2Int(c, r);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arrayIndex = 0;
|
||
|
for (int r = -2; r <= 2; ++r)
|
||
|
{
|
||
|
for (int c = -2; c <= 2; ++c)
|
||
|
{
|
||
|
if (r != 0 || c != 0)
|
||
|
{
|
||
|
_neighOffsets_R2[arrayIndex++] = new Vector2Int(c, r);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arrayIndex = 0;
|
||
|
for (int r = -3; r <= 3; ++r)
|
||
|
{
|
||
|
for (int c = -3; c <= 3; ++c)
|
||
|
{
|
||
|
if (r != 0 || c != 0)
|
||
|
{
|
||
|
_neighOffsets_R3[arrayIndex++] = new Vector2Int(c, r);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static Vector2Int[] _neighOffsets_R1;
|
||
|
private static Vector2Int[] _neighOffsets_R2;
|
||
|
private static Vector2Int[] _neighOffsets_R3;
|
||
|
|
||
|
private Dictionary<Vector3Int, GameObject> _tileMap = new Dictionary<Vector3Int, GameObject>();
|
||
|
|
||
|
[SerializeField]
|
||
|
private GameObject _gameObject;
|
||
|
[NonSerialized]
|
||
|
private Transform _transform;
|
||
|
[SerializeField]
|
||
|
private TileRuleGridSettings _settings;
|
||
|
[SerializeField]
|
||
|
private PluginGuid _guid = new PluginGuid(Guid.NewGuid());
|
||
|
[SerializeField]
|
||
|
private string _gridName = string.Empty;
|
||
|
[SerializeField]
|
||
|
private bool _uiSelected;
|
||
|
[NonSerialized]
|
||
|
private CopyPasteMode _uiCopyPasteMode = CopyPasteMode.None;
|
||
|
[SerializeField]
|
||
|
private bool _uiExpanded = false;
|
||
|
[SerializeField]
|
||
|
private TileRuleGridState _state = new TileRuleGridState();
|
||
|
[NonSerialized]
|
||
|
private EditData _editData = new EditData();
|
||
|
[SerializeField]
|
||
|
private bool _usingSprites = false;
|
||
|
|
||
|
[SerializeField]
|
||
|
private bool _mirroringEnabled = false;
|
||
|
[SerializeField]
|
||
|
private ObjectMirrorGizmo _mirrorGizmo = null;
|
||
|
[SerializeField]
|
||
|
private ObjectMirrorGizmoSettings _mirrorGizmoSettings = null;
|
||
|
|
||
|
[NonSerialized]
|
||
|
private Vector2Int[] _neighborOffsets = null;
|
||
|
[NonSerialized]
|
||
|
private TileRuleProfile _ruleProfile;
|
||
|
[NonSerialized]
|
||
|
private HashSet<Vector3Int> _occupiedCells = new HashSet<Vector3Int>();
|
||
|
[NonSerialized]
|
||
|
private List<Vector3Int> _cellsAroundVertBorder = new List<Vector3Int>();
|
||
|
[NonSerialized]
|
||
|
private List<Vector3Int> _cellsBelow = new List<Vector3Int>();
|
||
|
[NonSerialized]
|
||
|
private HashSet<Vector3Int> _eraseBrushCells = new HashSet<Vector3Int>();
|
||
|
[NonSerialized]
|
||
|
private TilePaintParams _tilePaintParams = new TilePaintParams();
|
||
|
[NonSerialized]
|
||
|
private TileSpawnData _tileSpawnData = new TileSpawnData();
|
||
|
[NonSerialized]
|
||
|
private TileRulePrefabPickParams _prefabPickParams = new TileRulePrefabPickParams();
|
||
|
|
||
|
[NonSerialized]
|
||
|
private List<TileRule> _sortedStdRules = new List<TileRule>();
|
||
|
[NonSerialized]
|
||
|
private List<TileRule> _sortedPlatformRules = new List<TileRule>();
|
||
|
[NonSerialized]
|
||
|
private List<TileRule> _sortedRampRules = new List<TileRule>();
|
||
|
|
||
|
[NonSerialized]
|
||
|
private List<GameObject> _objectBuffer = new List<GameObject>();
|
||
|
[NonSerialized]
|
||
|
private List<GameObject> _prefabInstanceRoots = new List<GameObject>();
|
||
|
[NonSerialized]
|
||
|
private Func<GameObject, bool> _prefabInstanceRootFilter;
|
||
|
[NonSerialized]
|
||
|
private SceneRaycastFilter _pickCellCoordsFilter = new SceneRaycastFilter();
|
||
|
[NonSerialized]
|
||
|
private ObjectOverlapFilter _foreignEraseOverlapFilter = new ObjectOverlapFilter();
|
||
|
[NonSerialized]
|
||
|
private List<GameObject> _foreignObjectBuffer = new List<GameObject>();
|
||
|
[NonSerialized]
|
||
|
private List<Vector3> _shadowCorners = new List<Vector3>();
|
||
|
[NonSerialized]
|
||
|
private List<Vector3Int> _cellCoordsBuffer = new List<Vector3Int>();
|
||
|
[NonSerialized]
|
||
|
private List<GameObject> _meshObjectBuffer = new List<GameObject>();
|
||
|
[NonSerialized]
|
||
|
private List<GameObject> _objectOverlapBuffer = new List<GameObject>();
|
||
|
|
||
|
[NonSerialized]
|
||
|
private Vector3[] _tileFrame = new Vector3[3];
|
||
|
|
||
|
private TileRuleObjectSpawnSettings spawnSettings { get { return ObjectSpawn.instance.tileRuleObjectSpawn.settings; } }
|
||
|
|
||
|
public PluginGuid guid { get { return _guid; } }
|
||
|
public TileRuleGridSettings settings { get { return _settings; } }
|
||
|
public string gridName { get { return _gridName; } set { if (!string.IsNullOrEmpty(value)) { UndoEx.record(this); _gridName = value; } } }
|
||
|
public bool uiSelected { get { return _uiSelected; } set { UndoEx.record(this); _uiSelected = value; } }
|
||
|
public CopyPasteMode uiCopyPasteMode { get { return _uiCopyPasteMode; } set { _uiCopyPasteMode = value; } }
|
||
|
public bool uiExpanded { get { return _uiExpanded; } set { _uiExpanded = value; EditorUtility.SetDirty(this); } }
|
||
|
|
||
|
public Vector3 gridOrigin { get { return _transform.position; } set { UndoEx.record(_transform); _transform.position = value; } }
|
||
|
public Vector3 gridRight { get { return _transform.right; } }
|
||
|
public Vector3 gridUp { get { return _transform.up; } }
|
||
|
public Vector3 gridLook { get { return _transform.forward; } }
|
||
|
public Quaternion gridRotation { get { return _transform.rotation; } set { UndoEx.record(_transform); _transform.rotation = value; } }
|
||
|
public Plane gridPlane { get { return new Plane(gridUp, gridOrigin); } }
|
||
|
public GameObject gameObject { get { return _gameObject; } }
|
||
|
public Transform transform { get { return _transform; } }
|
||
|
|
||
|
public bool mirroringEnabled { get { return _mirroringEnabled; } set { UndoEx.record(this); _mirroringEnabled = value; EditorUtility.SetDirty(this); } }
|
||
|
public ObjectMirrorGizmo mirrorGizmo { get { return _mirrorGizmo; } }
|
||
|
public ObjectMirrorGizmoSettings mirrorGizmoSettings { get { return _mirrorGizmoSettings; } }
|
||
|
public Vector3Int mirrorGizmoCellCoords { get { return worldPointToCellCoords(_mirrorGizmo.position); } set { UndoEx.record(_mirrorGizmo); _mirrorGizmo.position = cellCoordsToCellPosition(value); SceneView.RepaintAll(); } }
|
||
|
|
||
|
public static void getCellsAroundVerticalBorder(Vector3Int coords, List<Vector3Int> cellCoords)
|
||
|
{
|
||
|
cellCoords.Clear();
|
||
|
cellCoords.Add(new Vector3Int(coords.x - 1, coords.y, coords.z));
|
||
|
cellCoords.Add(new Vector3Int(coords.x + 1, coords.y, coords.z));
|
||
|
cellCoords.Add(new Vector3Int(coords.x, coords.y, coords.z - 1));
|
||
|
cellCoords.Add(new Vector3Int(coords.x, coords.y, coords.z + 1));
|
||
|
cellCoords.Add(new Vector3Int(coords.x - 1, coords.y, coords.z - 1));
|
||
|
cellCoords.Add(new Vector3Int(coords.x - 1, coords.y, coords.z + 1));
|
||
|
cellCoords.Add(new Vector3Int(coords.x + 1, coords.y, coords.z + 1));
|
||
|
cellCoords.Add(new Vector3Int(coords.x + 1, coords.y, coords.z - 1));
|
||
|
}
|
||
|
|
||
|
public static void getCellsAroundVerticalBorder(Vector3Int coords, int radius, List<Vector3Int> cellCoords)
|
||
|
{
|
||
|
cellCoords.Clear();
|
||
|
if (radius < 1) return;
|
||
|
|
||
|
int minX = coords.x - radius;
|
||
|
int maxX = coords.x + radius;
|
||
|
int minZ = coords.z - radius;
|
||
|
int maxZ = coords.z + radius;
|
||
|
|
||
|
for (int x = minX; x <= maxX; ++x)
|
||
|
{
|
||
|
for (int z = minZ; z <= maxZ; ++z)
|
||
|
{
|
||
|
if (x == coords.x && z == coords.z) continue;
|
||
|
|
||
|
cellCoords.Add(new Vector3Int(x, coords.y, z));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void initialize(TileRuleGridSettings initialGridSettings)
|
||
|
{
|
||
|
_settings.copy(initialGridSettings);
|
||
|
|
||
|
_gameObject = new GameObject(gridName);
|
||
|
UndoEx.registerCreatedObject(_gameObject);
|
||
|
EditorUtility.SetDirty(_gameObject);
|
||
|
|
||
|
_transform = _gameObject.transform;
|
||
|
|
||
|
_mirrorGizmoSettings.moveSnapStep = settings.cellSize;
|
||
|
_mirrorGizmoSettings.hasRotationHandles = false;
|
||
|
_mirrorGizmoSettings.mirrorRotation = false;
|
||
|
_mirrorGizmoSettings.mirrorSpanning = false;
|
||
|
|
||
|
_usingSprites = false;
|
||
|
var ruleProfile = initialGridSettings.tileRuleProfile;
|
||
|
int numRules = ruleProfile.numTileRules;
|
||
|
for (int i = 0; i < numRules; ++i)
|
||
|
{
|
||
|
var rule = ruleProfile.getTileRule(i);
|
||
|
int numPrefabs = rule.numPrefabs;
|
||
|
for (int j = 0; j < numPrefabs; ++j)
|
||
|
{
|
||
|
var prefab = rule.getPrefab(j);
|
||
|
if (prefab.prefabAsset.hierarchyHasOnlySprites(false, false))
|
||
|
{
|
||
|
_usingSprites = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void rotateRamp(Vector3Int cellCoords)
|
||
|
{
|
||
|
if (isRamp(cellCoords))
|
||
|
{
|
||
|
var rampTile = _tileMap[cellCoords];
|
||
|
UndoEx.recordTransform(rampTile.transform);
|
||
|
rampTile.transform.rotateAround(Quaternion.AngleAxis(90.0f, gridUp), rampTile.transform.position);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void snapMirrorGizmoToView(bool enableGizmo)
|
||
|
{
|
||
|
if (enableGizmo && !mirrorGizmo.enabled) mirrorGizmo.enabled = true;
|
||
|
if (mirrorGizmo.enabled)
|
||
|
{
|
||
|
mirrorGizmo.snapToView();
|
||
|
snapMirrorGizmoPositionToCellBaseCenter();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void deleteAllTiles()
|
||
|
{
|
||
|
UndoEx.record(this);
|
||
|
_state.clear();
|
||
|
|
||
|
_gameObject.getAllChildren(true, true, _objectBuffer);
|
||
|
foreach(var go in _objectBuffer)
|
||
|
{
|
||
|
if (!ObjectGroupDb.instance.isObjectGroup(go))
|
||
|
{
|
||
|
// Note: Might have been previously deleted.
|
||
|
if (go != null) UndoEx.destroyGameObjectImmediate(go);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_tileMap.Clear();
|
||
|
}
|
||
|
|
||
|
public void refreshTiles()
|
||
|
{
|
||
|
prepareForTileUpdate();
|
||
|
_editData.clear();
|
||
|
|
||
|
var tileMapPairs = _tileMap.ToList();
|
||
|
int numTileMapPairs = tileMapPairs.Count;
|
||
|
bool hasRamps = _sortedRampRules.Count > 0;
|
||
|
if (hasRamps)
|
||
|
{
|
||
|
int numRampPrefabs = 0;
|
||
|
foreach (var rule in _sortedRampRules)
|
||
|
numRampPrefabs += rule.numPrefabs;
|
||
|
|
||
|
if (numRampPrefabs == 0) hasRamps = false;
|
||
|
}
|
||
|
|
||
|
PluginProgressDialog.begin("Refreshing");
|
||
|
_tilePaintParams.clear();
|
||
|
_tilePaintParams.paintReason = TilePaintReason.Refresh;
|
||
|
for (int pairIndex = 0; pairIndex < numTileMapPairs; ++pairIndex)
|
||
|
{
|
||
|
var pair = tileMapPairs[pairIndex];
|
||
|
PluginProgressDialog.updateProgress("Tile " + pairIndex, (pairIndex + 1) / (float)(numTileMapPairs));
|
||
|
|
||
|
_tilePaintParams.cellCoords = pair.Key;
|
||
|
if (isRamp(_tilePaintParams.cellCoords))
|
||
|
{
|
||
|
if (hasRamps)
|
||
|
{
|
||
|
// Note: Store rotation because the ramp might have been rotated using the keyboard.
|
||
|
Quaternion rampRotation = pair.Value.transform.rotation;
|
||
|
_tilePaintParams.paintingRamp = true;
|
||
|
GameObject ramp = paintTile(_tilePaintParams);
|
||
|
|
||
|
// Store ramp rotation.
|
||
|
// Note: This can produce incorrect results when multiple ramp prefabs are used which open up
|
||
|
// in different directions in their model pose.
|
||
|
if (ramp != null) ramp.transform.rotation = rampRotation;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
eraseTile(pair.Key);
|
||
|
updateSurroundingTiles_IgnoreRamps(pair.Key);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_tilePaintParams.paintingRamp = false;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Note: Commit edit data here. It has to be done this way for Undo/Redo to work.
|
||
|
commitEditData();
|
||
|
|
||
|
PluginProgressDialog.end();
|
||
|
ObjectSelection.instance.onSelectedObjectsMightHaveBeenDeleted(true);
|
||
|
}
|
||
|
|
||
|
[NonSerialized]
|
||
|
private List<Vector3> _obbCornerBuffer = new List<Vector3>();
|
||
|
public void fixObjectOverlaps()
|
||
|
{
|
||
|
if (_usingSprites) return;
|
||
|
|
||
|
UndoEx.saveEnabledState();
|
||
|
UndoEx.enabled = false;
|
||
|
|
||
|
var tileMapPairs = _tileMap.ToList();
|
||
|
int numTileMapPairs = tileMapPairs.Count;
|
||
|
|
||
|
ObjectOverlapConfig overlapConfig = ObjectOverlapConfig.defaultConfig;
|
||
|
ObjectOverlapFilter overlapFilter = new ObjectOverlapFilter();
|
||
|
overlapFilter.objectTypes = GameObjectType.Mesh;
|
||
|
// Note: Only children of the tile rule grid will be taken into account.
|
||
|
overlapFilter.customFilter = (GameObject go) => { return go.transform.IsChildOf(_transform); };
|
||
|
|
||
|
ObjectBounds.QueryConfig boundsQConfig = ObjectBounds.QueryConfig.defaultConfig;
|
||
|
boundsQConfig.objectTypes = GameObjectType.Mesh;
|
||
|
|
||
|
PluginProgressDialog.begin("Fixing Overlaps");
|
||
|
for (int pairIndex = 0; pairIndex < numTileMapPairs; ++pairIndex)
|
||
|
{
|
||
|
var pair = tileMapPairs[pairIndex];
|
||
|
GameObject tile = pair.Value;
|
||
|
PluginProgressDialog.updateProgress("Tile: " + tile.name, (pairIndex + 1) / (float)(numTileMapPairs));
|
||
|
|
||
|
// Get all meshes in the tiles hierarchy
|
||
|
tile.getMeshObjectsInHierarchy(false, false, _meshObjectBuffer);
|
||
|
|
||
|
// Loop through each mesh
|
||
|
foreach (var meshObject in _meshObjectBuffer)
|
||
|
{
|
||
|
OBB obb = ObjectBounds.calcWorldOBB(meshObject, boundsQConfig);
|
||
|
if (!obb.isValid) continue;
|
||
|
|
||
|
obb.calcCorners(_obbCornerBuffer, false);
|
||
|
|
||
|
GameObject prefabAsset = meshObject.getPrefabAsset();
|
||
|
if (prefabAsset == null) continue; // Note: Should not happen.
|
||
|
|
||
|
// Gather overlapping objects
|
||
|
PluginScene.instance.overlapBox(obb, overlapFilter, overlapConfig, _objectOverlapBuffer);
|
||
|
|
||
|
// Loop through each overlapped object and disable its renderer if:
|
||
|
// -if it's not a child of the current tile we are processing;
|
||
|
// -is part of the same prefab asset;
|
||
|
// -it has the same position;
|
||
|
foreach (var go in _objectOverlapBuffer)
|
||
|
{
|
||
|
if (!go.transform.IsChildOf(tile.transform))
|
||
|
{
|
||
|
// Calculate object OBB
|
||
|
OBB otherOBB = ObjectBounds.calcWorldOBB(go, boundsQConfig);
|
||
|
if (!obb.isValid) continue;
|
||
|
|
||
|
// Same center?
|
||
|
const float posEps = 1e-3f;
|
||
|
if (Vector3.Magnitude(otherOBB.center - obb.center) < posEps)
|
||
|
{
|
||
|
// Sizes should roughly match along the grid X and Z axes
|
||
|
const float sizeEsp = 1e-2f;
|
||
|
float s0 = Vector3Ex.getSizeAlongAxis(obb.size, obb.rotation, gridRight);
|
||
|
float s1 = Vector3Ex.getSizeAlongAxis(otherOBB.size, otherOBB.rotation, gridRight);
|
||
|
if (Mathf.Abs(s0 - s1) > sizeEsp) continue;
|
||
|
|
||
|
s0 = Vector3Ex.getSizeAlongAxis(obb.size, obb.rotation, gridLook);
|
||
|
s1 = Vector3Ex.getSizeAlongAxis(otherOBB.size, otherOBB.rotation, gridLook);
|
||
|
if (Mathf.Abs(s0 - s1) > sizeEsp) continue;
|
||
|
|
||
|
// All conditions are met. Disable renderer and colliders.
|
||
|
go.setMeshOrSkinnedMeshRendererEnabled(false);
|
||
|
go.setAllCollidersEnabled(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UndoEx.restoreEnabledState();
|
||
|
PluginProgressDialog.end();
|
||
|
}
|
||
|
|
||
|
public bool calcShadowCasterOBBCorners(OBB obb, int cellY, List<Vector3> corners)
|
||
|
{
|
||
|
corners.Clear();
|
||
|
|
||
|
if (cellY <= -1) Box3D.calcFaceCorners(obb.center, obb.size, obb.rotation, Box3DFace.Top, corners);
|
||
|
else if (cellY >= 1) Box3D.calcFaceCorners(obb.center, obb.size, obb.rotation, Box3DFace.Bottom, corners);
|
||
|
else return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public Plane getGridPlane(int yOffset)
|
||
|
{
|
||
|
return new Plane(gridUp, gridOrigin + yOffset * gridUp * settings.cellSize.y);
|
||
|
}
|
||
|
|
||
|
public bool pickTileCellCoords(Ray ray, bool pickAdjacent, out Vector3Int cellCoords)
|
||
|
{
|
||
|
cellCoords = Vector3Int.zero;
|
||
|
if (pickAdjacent) return pickTileCellCoordsAdjacent(ray, out cellCoords);
|
||
|
else return pickTileCellCoords(ray, out cellCoords);
|
||
|
}
|
||
|
|
||
|
public bool pickTileCellCoords(Ray ray, out Vector3Int cellCoords)
|
||
|
{
|
||
|
cellCoords = Vector3Int.zero;
|
||
|
|
||
|
_pickCellCoordsFilter.raycastGrid = false;
|
||
|
_pickCellCoordsFilter.objectTypes = GameObjectType.Mesh | GameObjectType.Sprite;
|
||
|
|
||
|
var raycastConfig = ObjectRaycastConfig.defaultConfig;
|
||
|
raycastConfig.raycastPrecision = ObjectRaycastPrecision.BestFit;
|
||
|
|
||
|
var sceneHit = PluginScene.instance.raycastClosest(ray, _pickCellCoordsFilter, raycastConfig);
|
||
|
if (sceneHit.wasObjectHit && sceneHit.objectHit.hitObject.transform.IsChildOf(_transform))
|
||
|
{
|
||
|
Vector3 hitPt = sceneHit.objectHit.hitPoint;
|
||
|
hitPt -= sceneHit.objectHit.hitNormal * 1e-2f;
|
||
|
cellCoords = worldPointToVisualCellCoords(hitPt);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool pickTileCellCoordsAdjacent(Ray ray, out Vector3Int cellCoords)
|
||
|
{
|
||
|
cellCoords = Vector3Int.zero;
|
||
|
|
||
|
_pickCellCoordsFilter.raycastGrid = false;
|
||
|
_pickCellCoordsFilter.objectTypes = GameObjectType.Mesh | GameObjectType.Sprite;
|
||
|
|
||
|
var raycastConfig = ObjectRaycastConfig.defaultConfig;
|
||
|
raycastConfig.raycastPrecision = ObjectRaycastPrecision.BestFit;
|
||
|
|
||
|
var sceneHit = PluginScene.instance.raycastClosest(ray, _pickCellCoordsFilter, raycastConfig);
|
||
|
if (sceneHit.wasObjectHit && sceneHit.objectHit.hitObject.transform.IsChildOf(_transform))
|
||
|
{
|
||
|
Vector3 hitPt = sceneHit.objectHit.hitPoint;
|
||
|
hitPt += sceneHit.objectHit.hitNormal * 1e-2f;
|
||
|
cellCoords = worldPointToVisualCellCoords(hitPt);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool pickCellCoords(Ray ray, int yOffset, out Vector3Int cellCoords)
|
||
|
{
|
||
|
cellCoords = Vector3Int.zero;
|
||
|
|
||
|
_pickCellCoordsFilter.raycastGrid = false;
|
||
|
_pickCellCoordsFilter.objectTypes = GameObjectType.Mesh | GameObjectType.Sprite;
|
||
|
|
||
|
var sceneHit = PluginScene.instance.raycastClosest(ray, _pickCellCoordsFilter, ObjectRaycastConfig.defaultConfig);
|
||
|
if (sceneHit.wasObjectHit)
|
||
|
{
|
||
|
Vector3 hitPt = sceneHit.objectHit.hitPoint;
|
||
|
hitPt += gridUp * 1e-3f; // Note: When clicking near the top of a cell, favor sitting on top.
|
||
|
|
||
|
cellCoords = worldPointToCellCoords(hitPt);
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var gridHit = raycast(ray, yOffset);
|
||
|
if (gridHit != null)
|
||
|
{
|
||
|
cellCoords = gridHit.hitCellCoords;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public TileRuleGridRayHit raycast(Ray ray)
|
||
|
{
|
||
|
float t;
|
||
|
if (gridPlane.Raycast(ray, out t))
|
||
|
{
|
||
|
var cellCoords = worldPointToCellCoords(ray.GetPoint(t));
|
||
|
cellCoords.y = 0;
|
||
|
|
||
|
return new TileRuleGridRayHit(ray, this, gridPlane.normal, t, cellCoords);
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public TileRuleGridRayHit raycast(Ray ray, int yOffset)
|
||
|
{
|
||
|
float t;
|
||
|
Plane plane = getGridPlane(yOffset);
|
||
|
if (plane.Raycast(ray, out t))
|
||
|
{
|
||
|
var cellCoords = worldPointToCellCoords(ray.GetPoint(t));
|
||
|
cellCoords.y = yOffset;
|
||
|
|
||
|
return new TileRuleGridRayHit(ray, this, plane.normal, t, cellCoords);
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public OBB calcVisualCellOBB(Vector3Int cellCoords)
|
||
|
{
|
||
|
return new OBB(cellCoordsToVisualCellPosition(cellCoords), settings.cellSize, gridRotation);
|
||
|
}
|
||
|
|
||
|
public OBB calcCellRangeOBB(Vector3Int minCell, Vector3Int maxCell)
|
||
|
{
|
||
|
OBB obb = new OBB(true);
|
||
|
|
||
|
Plane plane = gridPlane;
|
||
|
obb.center = plane.projectPoint(cellCoordsToCellPosition(minCell));
|
||
|
obb.center += plane.projectPoint(cellCoordsToCellPosition(maxCell));
|
||
|
obb.center *= 0.5f;
|
||
|
|
||
|
int width = maxCell.x - minCell.x + 1;
|
||
|
int height = maxCell.y - minCell.y + 1;
|
||
|
int depth = maxCell.z - minCell.z + 1;
|
||
|
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
obb.center += plane.normal * height * 0.5f * cellSize.y;
|
||
|
obb.center += plane.normal * minCell.y * cellSize.y;
|
||
|
obb.size = new Vector3(width * cellSize.x, height * cellSize.y, depth * cellSize.z);
|
||
|
obb.rotation = gridRotation;
|
||
|
|
||
|
return obb;
|
||
|
}
|
||
|
|
||
|
public Vector3Int worldPointToCellCoords(Vector3 pt)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
Vector3 toPt = pt - gridOrigin;
|
||
|
|
||
|
return new Vector3Int (Mathf.RoundToInt(Vector3.Dot(toPt, gridRight) / cellSize.x),
|
||
|
Mathf.RoundToInt(Vector3.Dot(toPt, gridUp) / cellSize.y),
|
||
|
Mathf.RoundToInt(Vector3.Dot(toPt, gridLook) / cellSize.z));
|
||
|
}
|
||
|
|
||
|
public Vector3Int worldPointToVisualCellCoords(Vector3 pt)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
Vector3 toPt = pt - gridOrigin;
|
||
|
|
||
|
float dotY = Vector3.Dot(toPt, gridUp);
|
||
|
|
||
|
return new Vector3Int(Mathf.RoundToInt(Vector3.Dot(toPt, gridRight) / cellSize.x),
|
||
|
Mathf.RoundToInt((dotY - cellSize.y * 0.5f) / cellSize.y),
|
||
|
Mathf.RoundToInt(Vector3.Dot(toPt, gridLook) / cellSize.z));
|
||
|
}
|
||
|
|
||
|
public Vector3 cellCoordsToCellPosition(Vector3Int cellCoords)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
return gridOrigin + gridRight * (cellCoords.x * cellSize.x)
|
||
|
+ gridUp * (cellCoords.y * cellSize.y)
|
||
|
+ gridLook * (cellCoords.z * cellSize.z);
|
||
|
}
|
||
|
|
||
|
public Vector3 cellCoordsToCellPosition(int x, int y, int z)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
return gridOrigin + gridRight * (x * cellSize.x)
|
||
|
+ gridUp * (y * cellSize.y)
|
||
|
+ gridLook * (z * cellSize.z);
|
||
|
}
|
||
|
|
||
|
public Vector3 cellCoordsToVisualCellPosition(Vector3Int cellCoords)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
return gridOrigin + gridRight * (cellCoords.x * cellSize.x)
|
||
|
+ gridUp * (cellCoords.y * cellSize.y + cellSize.y * 0.5f)
|
||
|
+ gridLook * (cellCoords.z * cellSize.z);
|
||
|
}
|
||
|
|
||
|
public Vector3 cellCoordsToVisualCellPosition(int x, int y, int z)
|
||
|
{
|
||
|
Vector3 cellSize = settings.cellSize;
|
||
|
return gridOrigin + gridRight * (x * cellSize.x)
|
||
|
+ gridUp * (y * cellSize.y + cellSize.y * 0.5f)
|
||
|
+ gridLook * (z * cellSize.z);
|
||
|
}
|
||
|
|
||
|
public void onSceneGUI(int gridYOffset)
|
||
|
{
|
||
|
prepareForTileUpdate();
|
||
|
_mirrorGizmo.enabled = _mirroringEnabled;
|
||
|
|
||
|
Event e = Event.current;
|
||
|
if (e.isLeftMouseButtonDownEvent()) _editData.clear();
|
||
|
else if (e.isLeftMouseButtonUpEvent()) commitEditData();
|
||
|
|
||
|
draw(gridYOffset);
|
||
|
}
|
||
|
|
||
|
public void paintTiles(TileRuleBrush brush)
|
||
|
{
|
||
|
_tilePaintParams.clear();
|
||
|
_tilePaintParams.paintReason = TilePaintReason.Paint;
|
||
|
|
||
|
// Update tiles inside brush
|
||
|
brush.getCellCoords(_occupiedCells);
|
||
|
foreach (var cellCoords in _occupiedCells)
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
|
||
|
// Update platforms
|
||
|
brush.getCellCoordsBelowBrush(_cellsBelow);
|
||
|
foreach (var cellCoords in _cellsBelow)
|
||
|
{
|
||
|
if (getTileObject(cellCoords) != null)
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update tiles around brush borders
|
||
|
brush.getCellsAroundVerticalBorder((int)settings.tileRuleNeighborRadius, _cellsAroundVertBorder);
|
||
|
foreach (var cellCoords in _cellsAroundVertBorder)
|
||
|
{
|
||
|
// Note: We don't update ramps.
|
||
|
if ((getTileObject(cellCoords) != null) && !isRamp(cellCoords))
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_occupiedCells.Clear();
|
||
|
}
|
||
|
|
||
|
public void paintRamps(TileRuleBrush brush)
|
||
|
{
|
||
|
if (_sortedRampRules.Count == 0) return;
|
||
|
|
||
|
_tilePaintParams.clear();
|
||
|
_tilePaintParams.paintReason = TilePaintReason.Paint;
|
||
|
_tilePaintParams.paintingRamp = true;
|
||
|
|
||
|
// Update tiles inside brush
|
||
|
bool paintedRamp = false;
|
||
|
brush.getCellCoords(_occupiedCells);
|
||
|
foreach (var cellCoords in _occupiedCells)
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
if (paintTile(_tilePaintParams) != null) paintedRamp = true;
|
||
|
}
|
||
|
|
||
|
// Update platforms
|
||
|
_tilePaintParams.paintingRamp = false;
|
||
|
brush.getCellCoordsBelowBrush(_cellsBelow);
|
||
|
foreach (var cellCoords in _cellsBelow)
|
||
|
{
|
||
|
if (getTileObject(cellCoords) != null &&
|
||
|
getTileObject(new Vector3Int(cellCoords.x, cellCoords.y + 1, cellCoords.z)) != null)
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (paintedRamp)
|
||
|
{
|
||
|
// Update tiles around brush borders
|
||
|
brush.getCellsAroundVerticalBorder((int)settings.tileRuleNeighborRadius, _cellsAroundVertBorder);
|
||
|
foreach (var cellCoords in _cellsAroundVertBorder)
|
||
|
{
|
||
|
// Note: We don't update ramps.
|
||
|
if ((getTileObject(cellCoords) != null) && !isRamp(cellCoords))
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_occupiedCells.Clear();
|
||
|
}
|
||
|
|
||
|
public void eraseTiles(TileRuleBrush brush)
|
||
|
{
|
||
|
_tilePaintParams.clear();
|
||
|
_tilePaintParams.paintReason = TilePaintReason.Erase;
|
||
|
|
||
|
// Delete tiles inside brush
|
||
|
brush.getCellCoords(_eraseBrushCells);
|
||
|
if (spawnSettings.eraseForeignObjects)
|
||
|
{
|
||
|
foreach (var cellCoords in _eraseBrushCells)
|
||
|
{
|
||
|
eraseForeignObjects(cellCoords);
|
||
|
eraseTile(cellCoords);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foreach (var cellCoords in _eraseBrushCells)
|
||
|
eraseTile(cellCoords);
|
||
|
}
|
||
|
_eraseBrushCells.Clear();
|
||
|
|
||
|
// Update tiles around brush borders
|
||
|
brush.getCellsAroundVerticalBorder((int)settings.tileRuleNeighborRadius, _cellsAroundVertBorder);
|
||
|
foreach (var cellCoords in _cellsAroundVertBorder)
|
||
|
{
|
||
|
// Note: We don't update ramps.
|
||
|
if ((getTileObject(cellCoords) != null) && !isRamp(cellCoords))
|
||
|
{
|
||
|
_tilePaintParams.cellCoords = cellCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
}
|
||
|
}
|
||
|
_cellsAroundVertBorder.Clear();
|
||
|
}
|
||
|
|
||
|
public void connect(TileRuleConnect tileRuleConnect)
|
||
|
{
|
||
|
int numConnectionPaths = tileRuleConnect.numConnectionPaths;
|
||
|
for (int i = 0; i < numConnectionPaths; ++i)
|
||
|
connect(tileRuleConnect.getConnectionPath(i));
|
||
|
}
|
||
|
|
||
|
private void connect(TileRuleConnectionPath connectionPath)
|
||
|
{
|
||
|
if (connectionPath.cells.Count == 0) return;
|
||
|
|
||
|
_tilePaintParams.clear();
|
||
|
_tilePaintParams.paintReason = TilePaintReason.Connect;
|
||
|
|
||
|
int numConnectionCells = connectionPath.cells.Count;
|
||
|
if (numConnectionCells == 0) return;
|
||
|
|
||
|
// Note: Need to fill the occupied cell set for correctly updating the tiles.
|
||
|
_occupiedCells.Clear();
|
||
|
foreach (var cell in connectionPath.cells)
|
||
|
_occupiedCells.Add(cell);
|
||
|
|
||
|
// Store needed data
|
||
|
Vector3Int platformCoords = Vector3Int.zero;
|
||
|
bool fillCorners = spawnSettings.connectFillCorners;
|
||
|
bool generateRamps = spawnSettings.connectGenerateRamps && _sortedRampRules.Count != 0;
|
||
|
bool movingUp = connectionPath.firstCell.y < connectionPath.lastCell.y;
|
||
|
if (connectionPath.firstCell.y == connectionPath.lastCell.y)
|
||
|
{
|
||
|
fillCorners = false;
|
||
|
generateRamps = false;
|
||
|
}
|
||
|
|
||
|
// Paint tiles
|
||
|
for (int i = 0; i < numConnectionCells; ++i)
|
||
|
{
|
||
|
_tilePaintParams.paintingRamp = false;
|
||
|
_tilePaintParams.cellCoords = connectionPath.cells[i];
|
||
|
paintTile(_tilePaintParams);
|
||
|
|
||
|
// Update tiles around this tile. Don't update ramps.
|
||
|
updateSurroundingTiles_IgnoreRamps(_tilePaintParams.cellCoords);
|
||
|
|
||
|
// Update the tile below (i.e. turn it into a platform)
|
||
|
convertTileBelowToPlatform(connectionPath.cells[i]);
|
||
|
|
||
|
// Generate ramp if necessary
|
||
|
if (generateRamps)
|
||
|
{
|
||
|
// Check if a ramp must be generated
|
||
|
bool paintRamp = false;
|
||
|
if (movingUp) paintRamp = (i < numConnectionCells - 1) && (connectionPath.cells[i + 1].y - connectionPath.cells[i].y == 1);
|
||
|
else paintRamp = (i >= 1) && (connectionPath.cells[i - 1].y - connectionPath.cells[i].y == 1);
|
||
|
|
||
|
if (paintRamp)
|
||
|
{
|
||
|
// Generate ramp
|
||
|
_tilePaintParams.paintingRamp = true;
|
||
|
_tilePaintParams.cellCoords = connectionPath.cells[i];
|
||
|
++_tilePaintParams.cellCoords.y;
|
||
|
paintTile(_tilePaintParams);
|
||
|
|
||
|
// Update tiles
|
||
|
updateSurroundingTiles_IgnoreRamps(_tilePaintParams.cellCoords);
|
||
|
convertTileBelowToPlatform(_tilePaintParams.cellCoords);
|
||
|
|
||
|
// We need to check if the ramp is sitting in a corner, in which case,
|
||
|
// we need to paint tiles in order to make the ramp accessible.
|
||
|
if (i >= 1 && i < numConnectionCells - 1)
|
||
|
{
|
||
|
Vector3Int currentCoords = connectionPath.cells[i];
|
||
|
Vector3Int prevCoords = connectionPath.cells[i - 1];
|
||
|
Vector3Int nextCoords = connectionPath.cells[i + 1];
|
||
|
if (movingUp)
|
||
|
{
|
||
|
if (currentCoords.z == prevCoords.z && currentCoords.z != nextCoords.z)
|
||
|
{
|
||
|
int zCoord = currentCoords.z + (int)Mathf.Sign(currentCoords.z - nextCoords.z);
|
||
|
|
||
|
_cellCoordsBuffer.Clear();
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(currentCoords.x, currentCoords.y, zCoord));
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(prevCoords.x, currentCoords.y, zCoord));
|
||
|
paintTilesAndUpdateSurroundings(_cellCoordsBuffer);
|
||
|
}
|
||
|
else
|
||
|
if (currentCoords.x == prevCoords.x && currentCoords.x != nextCoords.x)
|
||
|
{
|
||
|
int xCoord = currentCoords.x + (int)Mathf.Sign(currentCoords.x - nextCoords.x);
|
||
|
|
||
|
_cellCoordsBuffer.Clear();
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(xCoord, currentCoords.y, currentCoords.z));
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(xCoord, currentCoords.y, prevCoords.z));
|
||
|
paintTilesAndUpdateSurroundings(_cellCoordsBuffer);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (currentCoords.z == prevCoords.z && currentCoords.z != nextCoords.z)
|
||
|
{
|
||
|
int xCoord = currentCoords.x + (int)Mathf.Sign(currentCoords.x - prevCoords.x);
|
||
|
|
||
|
_cellCoordsBuffer.Clear();
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(xCoord, currentCoords.y, currentCoords.z));
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(xCoord, currentCoords.y, nextCoords.z));
|
||
|
paintTilesAndUpdateSurroundings(_cellCoordsBuffer);
|
||
|
}
|
||
|
else
|
||
|
if (currentCoords.x == prevCoords.x && currentCoords.x != nextCoords.x)
|
||
|
{
|
||
|
int zCoord = currentCoords.z + (int)Mathf.Sign(currentCoords.z - prevCoords.z);
|
||
|
|
||
|
_cellCoordsBuffer.Clear();
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(currentCoords.x, currentCoords.y, zCoord));
|
||
|
_cellCoordsBuffer.Add(new Vector3Int(nextCoords.x, currentCoords.y, zCoord));
|
||
|
paintTilesAndUpdateSurroundings(_cellCoordsBuffer);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Generate tiles below to fill corners
|
||
|
if (fillCorners)
|
||
|
{
|
||
|
// Is the previous or next tile lower?
|
||
|
bool paintTileBelow = (i >= 1 && connectionPath.cells[i - 1].y < connectionPath.cells[i].y);
|
||
|
paintTileBelow |= (i < (numConnectionCells - 1) && connectionPath.cells[i + 1].y < connectionPath.cells[i].y);
|
||
|
|
||
|
// Paint tile if necessary
|
||
|
if (paintTileBelow)
|
||
|
{
|
||
|
// Paint tile
|
||
|
platformCoords = connectionPath.cells[i];
|
||
|
--platformCoords.y;
|
||
|
_tilePaintParams.cellCoords = platformCoords;
|
||
|
paintTile(_tilePaintParams);
|
||
|
|
||
|
// Update tiles
|
||
|
updateSurroundingTiles_IgnoreRamps(_tilePaintParams.cellCoords);
|
||
|
convertTileBelowToPlatform(_tilePaintParams.cellCoords);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NonSerialized]
|
||
|
private TilePaintParams _paintParams_PaintAndUpdate = new TilePaintParams();
|
||
|
private void paintTilesAndUpdateSurroundings(List<Vector3Int> cellCoords)
|
||
|
{
|
||
|
foreach (var coords in cellCoords)
|
||
|
{
|
||
|
_paintParams_PaintAndUpdate.paintingRamp = false;
|
||
|
_paintParams_PaintAndUpdate.cellCoords = coords;
|
||
|
paintTile(_paintParams_PaintAndUpdate);
|
||
|
updateSurroundingTiles_IgnoreRamps(_paintParams_PaintAndUpdate.cellCoords);
|
||
|
convertTileBelowToPlatform(coords);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NonSerialized]
|
||
|
private TilePaintParams _paintParams_UpdateSurrounding = new TilePaintParams();
|
||
|
private void updateSurroundingTiles_IgnoreRamps(Vector3Int tileCoords)
|
||
|
{
|
||
|
_paintParams_UpdateSurrounding.paintingRamp = false;
|
||
|
getCellsAroundVerticalBorder(tileCoords, (int)settings.tileRuleNeighborRadius, _cellsAroundVertBorder);
|
||
|
foreach (var cellCoords in _cellsAroundVertBorder)
|
||
|
{
|
||
|
if ((getTileObject(cellCoords) != null) && !isRamp(cellCoords))
|
||
|
{
|
||
|
_paintParams_UpdateSurrounding.cellCoords = cellCoords;
|
||
|
paintTile(_paintParams_UpdateSurrounding);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NonSerialized]
|
||
|
private TilePaintParams _paintParams_ConvertToPlatform = new TilePaintParams();
|
||
|
private void convertTileBelowToPlatform(Vector3Int tileAbove)
|
||
|
{
|
||
|
_paintParams_ConvertToPlatform.paintingRamp = false;
|
||
|
Vector3Int cellBelow = new Vector3Int(tileAbove.x, tileAbove.y - 1, tileAbove.z);
|
||
|
if (getTileObject(cellBelow) != null)
|
||
|
{
|
||
|
_paintParams_ConvertToPlatform.cellCoords = cellBelow;
|
||
|
paintTile(_paintParams_ConvertToPlatform);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void draw(int yOffset)
|
||
|
{
|
||
|
GridHandles.DrawConfig drawConfig = new GridHandles.DrawConfig();
|
||
|
drawConfig.cellSizeX = settings.cellSize.x;
|
||
|
drawConfig.cellSizeZ = settings.cellSize.z;
|
||
|
drawConfig.wireColor = settings.wireColor;
|
||
|
drawConfig.fillColor = settings.fillColor;
|
||
|
drawConfig.origin = gridOrigin + gridUp * yOffset * settings.cellSize.y;
|
||
|
drawConfig.right = gridRight;
|
||
|
drawConfig.look = gridLook;
|
||
|
drawConfig.planeNormal = gridUp;
|
||
|
drawConfig.drawCoordSystem = GridPrefs.instance.drawCoordSystem;
|
||
|
drawConfig.xAxisColor = GridPrefs.instance.xAxisColor;
|
||
|
drawConfig.yAxisColor = GridPrefs.instance.yAxisColor;
|
||
|
drawConfig.zAxisColor = GridPrefs.instance.zAxisColor;
|
||
|
drawConfig.finiteAxisLength = GridPrefs.instance.finiteAxisLength;
|
||
|
drawConfig.infiniteXAxis = GridPrefs.instance.infiniteXAxis;
|
||
|
drawConfig.infiniteYAxis = GridPrefs.instance.infiniteYAxis;
|
||
|
drawConfig.infiniteZAxis = GridPrefs.instance.infiniteXAxis;
|
||
|
GridHandles.drawInfinite(drawConfig, PluginCamera.camera);
|
||
|
|
||
|
// Note: Draw the gizmo over the grid.
|
||
|
_mirrorGizmo.rotation = gridRotation;
|
||
|
_mirrorGizmo.tileRuleGrid = this;
|
||
|
_mirrorGizmo.tileRuleGridYOffset = yOffset;
|
||
|
_mirrorGizmo.midSnapMode = MirrorGizmoMidSnapMode.TileRuleGrid;
|
||
|
_mirrorGizmo.onSceneGUI();
|
||
|
|
||
|
// Note: Always force the gizmo to sit at the base of a cell. Random positions not allowed inside a tile grid.
|
||
|
snapMirrorGizmoPositionToCellBaseCenter();
|
||
|
|
||
|
if (_mirrorGizmo.isDraggingHandles) TileRuleObjectSpawnUI.instance.refresh();
|
||
|
}
|
||
|
|
||
|
public void drawShadow(List<Vector3> shadowCasterCorners, Color shadowLineColor, Color shadowColor)
|
||
|
{
|
||
|
HandlesEx.saveColor();
|
||
|
Handles.color = shadowLineColor;
|
||
|
|
||
|
Plane plane = gridPlane;
|
||
|
_shadowCorners.Clear();
|
||
|
foreach (var pt in shadowCasterCorners)
|
||
|
{
|
||
|
Vector3 prjPt = plane.projectPoint(pt);
|
||
|
Handles.DrawLine(pt, prjPt);
|
||
|
|
||
|
_shadowCorners.Add(prjPt);
|
||
|
}
|
||
|
|
||
|
Handles.color = shadowColor;
|
||
|
Handles.DrawLine(_shadowCorners[0], _shadowCorners[1]);
|
||
|
Handles.DrawLine(_shadowCorners[1], _shadowCorners[2]);
|
||
|
Handles.DrawLine(_shadowCorners[2], _shadowCorners[3]);
|
||
|
Handles.DrawLine(_shadowCorners[3], _shadowCorners[0]);
|
||
|
|
||
|
HandlesEx.restoreColor();
|
||
|
}
|
||
|
|
||
|
private void snapMirrorGizmoPositionToCellBaseCenter()
|
||
|
{
|
||
|
_mirrorGizmo.position = cellCoordsToCellPosition(worldPointToCellCoords(_mirrorGizmo.position));
|
||
|
}
|
||
|
|
||
|
private GameObject paintTile(TilePaintParams paintParams)
|
||
|
{
|
||
|
// Must we paint a platform?
|
||
|
Vector3Int cellCoords = paintParams.cellCoords;
|
||
|
bool paintingPlatform = _sortedPlatformRules.Count != 0 && mustPaintPlatform(cellCoords);
|
||
|
|
||
|
// Identify the tile rule list that we're going to use
|
||
|
Vector3Int neighborMaskCoords = cellCoords;
|
||
|
var tileRules = _sortedStdRules;
|
||
|
if (paintingPlatform)
|
||
|
{
|
||
|
tileRules = _sortedPlatformRules;
|
||
|
|
||
|
/*var moveUpCoords = neighborMaskCoords;
|
||
|
++moveUpCoords.y;
|
||
|
while (getTileObject(moveUpCoords) != null)
|
||
|
{
|
||
|
neighborMaskCoords = moveUpCoords;
|
||
|
++moveUpCoords.y;
|
||
|
}*/
|
||
|
}
|
||
|
else if (paintParams.paintingRamp) tileRules = _sortedRampRules;
|
||
|
|
||
|
// Check if we have a matching rule for this position. If not, we can exit.
|
||
|
ulong neighborMask = calcNeighborMask(neighborMaskCoords, paintParams);
|
||
|
TileRuleMaskMatchResult matchResult;
|
||
|
TileRule tileRule = matchRule(neighborMask, tileRules, !paintParams.paintingRamp, out matchResult);
|
||
|
if (tileRule == null) return null;
|
||
|
|
||
|
// Destroy the old tile that resides at this position
|
||
|
GameObject oldTile = getTileObject(cellCoords);
|
||
|
if (oldTile != null)
|
||
|
{
|
||
|
// Remove the tile record and destroy the game object
|
||
|
removeTileRecord(cellCoords);
|
||
|
UndoEx.destroyGameObjectImmediate(oldTile);
|
||
|
|
||
|
// Note: If we are painting ramps, don't do anything. The tile
|
||
|
// will just be replaced with another ramp. Otherwise,
|
||
|
// we need to update the edit data accordingly.
|
||
|
if (!paintParams.paintingRamp)
|
||
|
{
|
||
|
if (isRamp(cellCoords))
|
||
|
_editData.removedRampCells.Add(cellCoords);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Just in case tile objects were deleted using means other than the tile rule interface
|
||
|
if (!paintParams.paintingRamp)
|
||
|
{
|
||
|
if (isRamp(cellCoords))
|
||
|
_editData.removedRampCells.Add(cellCoords);
|
||
|
}
|
||
|
removeTileRecordIfExists(cellCoords);
|
||
|
}
|
||
|
|
||
|
// Setup the prefab pick params
|
||
|
_prefabPickParams.grid = this;
|
||
|
_prefabPickParams.cellCoords = cellCoords;
|
||
|
|
||
|
// Finally, we can spawn a new tile. Calculate the spawn data.
|
||
|
_tileSpawnData.reset();
|
||
|
_tileSpawnData.rule = tileRule;
|
||
|
_tileSpawnData.rulePrefab = tileRule.pickPrefab(_prefabPickParams);
|
||
|
if (_tileSpawnData.rulePrefab == null) return null;
|
||
|
|
||
|
_tileSpawnData.isRamp = paintParams.paintingRamp && !paintingPlatform;
|
||
|
// Note: Always apply rotation even if mirroring was used. We need to orient the object
|
||
|
// so that it sits on the grid plane.
|
||
|
//Quaternion baseRotation = _tileSpawnData.rulePrefab.prefabAsset.transform.rotation;
|
||
|
if (_usingSprites) _tileSpawnData.rotation = Quaternion.LookRotation(-gridUp, TileRuleMask.maskRotationToLookAxis(matchResult.maskRotation, gridRotation));// * baseRotation;
|
||
|
else _tileSpawnData.rotation = Quaternion.LookRotation(TileRuleMask.maskRotationToLookAxis(matchResult.maskRotation, gridRotation), gridUp);// * baseRotation;
|
||
|
|
||
|
// Check if mirroring was used to match the rule. In that case
|
||
|
// we will have to apply scale to the object.
|
||
|
if (matchResult.maskMirrorAxis != TileRuleMaskMirrorAxis.None)
|
||
|
{
|
||
|
_tileSpawnData.scale = _tileSpawnData.rulePrefab.prefabAsset.transform.localScale;
|
||
|
_tileFrame[0] = _tileSpawnData.rotation * Vector3.right;
|
||
|
_tileFrame[1] = _tileSpawnData.rotation * Vector3.up;
|
||
|
_tileFrame[2] = _tileSpawnData.rotation * Vector3.forward;
|
||
|
|
||
|
int affectedAxisIndex = 0;
|
||
|
switch (matchResult.maskMirrorAxis)
|
||
|
{
|
||
|
case TileRuleMaskMirrorAxis.X:
|
||
|
|
||
|
affectedAxisIndex = TransformEx.findIndexOfMostAlignedAxis(_tileFrame, gridRight);
|
||
|
break;
|
||
|
|
||
|
case TileRuleMaskMirrorAxis.Z:
|
||
|
|
||
|
affectedAxisIndex = TransformEx.findIndexOfMostAlignedAxis(_tileFrame, gridLook);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (_usingSprites)
|
||
|
{
|
||
|
if (affectedAxisIndex == 0) _tileSpawnData.flipSpriteX = true;
|
||
|
else if (affectedAxisIndex == 1) _tileSpawnData.flipSpriteY = true;
|
||
|
}
|
||
|
else _tileSpawnData.scale[affectedAxisIndex] = -_tileSpawnData.scale[affectedAxisIndex];
|
||
|
}
|
||
|
else _tileSpawnData.scale = _tileSpawnData.rulePrefab.prefabAsset.transform.localScale;
|
||
|
|
||
|
// Spawn the tile and return
|
||
|
return spawnTile(cellCoords, _tileSpawnData);
|
||
|
}
|
||
|
|
||
|
private TilePaintParams _eraseTile_PaintParams = new TilePaintParams();
|
||
|
private void eraseTile(Vector3Int cellCoords)
|
||
|
{
|
||
|
// Destroy tile if present
|
||
|
if (destroyTileForErase(cellCoords))
|
||
|
{
|
||
|
// A tile was present. We need to account for any platform that
|
||
|
// might have been sitting below it.
|
||
|
Vector3Int platformCoords = new Vector3Int(cellCoords.x, cellCoords.y - 1, cellCoords.z);
|
||
|
if (getTileObject(platformCoords) != null)
|
||
|
{
|
||
|
_eraseTile_PaintParams.cellCoords = platformCoords;
|
||
|
paintTile(_eraseTile_PaintParams);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void eraseForeignObjects(Vector3Int cellCoords)
|
||
|
{
|
||
|
var overlapOBB = calcVisualCellOBB(cellCoords);
|
||
|
overlapOBB.inflate(-1e-2f);
|
||
|
var overlapConfig = ObjectOverlapConfig.defaultConfig;
|
||
|
overlapConfig.prefabMode = ObjectOverlapPrefabMode.OnlyPrefabInstanceRoot;
|
||
|
|
||
|
PluginScene.instance.overlapBox(overlapOBB, _foreignEraseOverlapFilter, overlapConfig, _foreignObjectBuffer);
|
||
|
foreach (var go in _foreignObjectBuffer)
|
||
|
UndoEx.destroyGameObjectImmediate(go);
|
||
|
}
|
||
|
|
||
|
private void commitEditData()
|
||
|
{
|
||
|
UndoEx.record(this);
|
||
|
|
||
|
foreach (var cell in _editData.removedRampCells)
|
||
|
_state.rampCells.Remove(cell);
|
||
|
|
||
|
foreach (var cell in _editData.addedRampCells)
|
||
|
_state.rampCells.Add(cell);
|
||
|
|
||
|
_editData.clear();
|
||
|
}
|
||
|
|
||
|
private void prepareForTileUpdate()
|
||
|
{
|
||
|
#pragma warning disable 0612
|
||
|
_ruleProfile = settings.tileRuleProfile;
|
||
|
|
||
|
switch (settings.tileRuleNeighborRadius)
|
||
|
{
|
||
|
case TileRuleNeighborRadius.One:
|
||
|
|
||
|
_neighborOffsets = _neighOffsets_R1;
|
||
|
break;
|
||
|
|
||
|
case TileRuleNeighborRadius.Two:
|
||
|
|
||
|
_neighborOffsets = _neighOffsets_R2;
|
||
|
break;
|
||
|
|
||
|
case TileRuleNeighborRadius.Three:
|
||
|
|
||
|
_neighborOffsets = _neighOffsets_R3;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Note: Sort the tile rules based on the number of bits which are set to 1.
|
||
|
_ruleProfile.getTileRules(TileRuleType.Standard, _sortedStdRules);
|
||
|
_sortedStdRules.RemoveAll(item => item.numReqOnBitsSet == 0);
|
||
|
sortRulesDescending(_sortedStdRules);
|
||
|
|
||
|
_ruleProfile.getTileRules(TileRuleType.Platform, _sortedPlatformRules);
|
||
|
_sortedPlatformRules.RemoveAll(item => item.numReqOnBitsSet == 0);
|
||
|
sortRulesDescending(_sortedPlatformRules);
|
||
|
|
||
|
_ruleProfile.getTileRules(TileRuleType.Ramp, _sortedRampRules);
|
||
|
_sortedRampRules.RemoveAll(item => item.numReqOnBitsSet == 0);
|
||
|
sortRulesDescending(_sortedRampRules);
|
||
|
#pragma warning restore 0612
|
||
|
}
|
||
|
|
||
|
private void sortRulesDescending(List<TileRule> tileRules)
|
||
|
{
|
||
|
tileRules.Sort(delegate (TileRule r0, TileRule r1)
|
||
|
{
|
||
|
return (r1.numReqOnBitsSet + r1.numReqOffBitsSet).CompareTo(r0.numReqOnBitsSet + r0.numReqOffBitsSet); });
|
||
|
}
|
||
|
|
||
|
private bool mustPaintPlatform(Vector3Int cellCoords)
|
||
|
{
|
||
|
if (_sortedPlatformRules.Count == 0) return false;
|
||
|
|
||
|
// First, check if we are below any of the cells that are marked as occupied
|
||
|
var checkCell = new Vector3Int(cellCoords.x, cellCoords.y + 1, cellCoords.z);
|
||
|
if (_occupiedCells.Contains(checkCell)) return true;
|
||
|
|
||
|
// Just check if there is an object above
|
||
|
return getTileObject(checkCell) != null;
|
||
|
}
|
||
|
|
||
|
private bool isRamp(Vector3Int cell)
|
||
|
{
|
||
|
if (_editData.removedRampCells.Contains(cell)) return false;
|
||
|
return _editData.addedRampCells.Contains(cell) || _state.rampCells.Contains(cell);
|
||
|
}
|
||
|
|
||
|
private TileRule matchRule(ulong ruleMask, List<TileRule> tileRules, bool pickFirstWithPrefabs, out TileRuleMaskMatchResult matchResult)
|
||
|
{
|
||
|
matchResult = new TileRuleMaskMatchResult(false);
|
||
|
int numRules = tileRules.Count;
|
||
|
if (numRules == 0) return null;
|
||
|
|
||
|
for (int i = 0; i < numRules; ++i)
|
||
|
{
|
||
|
var tileRule = tileRules[i];
|
||
|
|
||
|
// Skip rules with no prefabs
|
||
|
if (tileRule.numPrefabs == 0) continue;
|
||
|
|
||
|
matchResult = tileRule.match(ruleMask);
|
||
|
if (matchResult.matched) return tileRule;
|
||
|
}
|
||
|
|
||
|
if (pickFirstWithPrefabs)
|
||
|
{
|
||
|
foreach (var tileRule in tileRules)
|
||
|
{
|
||
|
if (tileRule.numPrefabs != 0)
|
||
|
{
|
||
|
matchResult.matched = true;
|
||
|
matchResult.maskRotation = TileRuleMaskRotation.None;
|
||
|
return tileRule;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private GameObject spawnTile(Vector3Int cellCoords, TileSpawnData spawnData)
|
||
|
{
|
||
|
var rulePrefab = spawnData.rulePrefab;
|
||
|
if (rulePrefab != null)
|
||
|
{
|
||
|
Vector3 position = cellCoordsToCellPosition(cellCoords);
|
||
|
GameObject tileObject = rulePrefab.pluginPrefab.spawn(position, spawnData.rotation, spawnData.scale);
|
||
|
|
||
|
// Flip sprite if necessary
|
||
|
if (spawnData.flipSpriteX)
|
||
|
{
|
||
|
var spriteRenderer = tileObject.getSpriteRendererInChildren();
|
||
|
spriteRenderer.flipX = !spriteRenderer.flipX; // Note: It may already be flipped. So always flip relative to the current flip state.
|
||
|
}
|
||
|
else
|
||
|
if (spawnData.flipSpriteY)
|
||
|
{
|
||
|
var spriteRenderer = tileObject.getSpriteRendererInChildren();
|
||
|
spriteRenderer.flipY = !spriteRenderer.flipY;
|
||
|
}
|
||
|
|
||
|
// If the rule prefab is associated with an object group, we need to ensure
|
||
|
// that the object group is a child of the grid. Otherwise, we detach the
|
||
|
// object from the group and attach it to the grid.
|
||
|
if (tileObject.transform.parent != _transform)
|
||
|
{
|
||
|
// The object is attached to an object group or it doesn't have a parent.
|
||
|
if (tileObject.transform.parent == null || !tileObject.transform.parent.IsChildOf(_transform))
|
||
|
tileObject.transform.parent = _transform;
|
||
|
}
|
||
|
|
||
|
// Store the new tile
|
||
|
addTileRecord(cellCoords, tileObject);
|
||
|
|
||
|
// If we are spawning a ramp, store the cell coordinates of the ramp
|
||
|
if (spawnData.isRamp) _editData.addedRampCells.Add(cellCoords);
|
||
|
|
||
|
return tileObject;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private bool destroyTileForErase(Vector3Int cellCoords)
|
||
|
{
|
||
|
var tileObject = getTileObject(cellCoords);
|
||
|
if (tileObject != null)
|
||
|
{
|
||
|
if (isRamp(cellCoords))
|
||
|
_editData.removedRampCells.Add(cellCoords);
|
||
|
|
||
|
removeTileRecord(cellCoords);
|
||
|
UndoEx.destroyGameObjectImmediate(tileObject);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (isRamp(cellCoords))
|
||
|
_editData.removedRampCells.Add(cellCoords);
|
||
|
|
||
|
removeTileRecordIfExists(cellCoords);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private ulong calcNeighborMask(Vector3Int cellCoords, TilePaintParams paintParams)
|
||
|
{
|
||
|
ulong ruleMask = TileRuleMask.defaultReqOnMask;
|
||
|
for (int i = 0; i < _neighborOffsets.Length; ++i)
|
||
|
{
|
||
|
var offset = _neighborOffsets[i];
|
||
|
|
||
|
var coords = cellCoords;
|
||
|
coords.x += offset.x;
|
||
|
coords.z += offset.y;
|
||
|
|
||
|
var tile = getTileObject(coords);
|
||
|
|
||
|
if ((tile != null && (!paintParams.paintingRamp || !isRamp(coords))) || _occupiedCells.Contains(coords))
|
||
|
{
|
||
|
// Note: Subtract offset.y instead of adding, because in bit mask space, rows decrease upwards.
|
||
|
ruleMask |= TileRuleMask.setBit(ruleMask, TileRuleMask.middleBitRow - offset.y, TileRuleMask.middleBitCol + offset.x);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ruleMask;
|
||
|
}
|
||
|
|
||
|
private void addTileRecord(Vector3Int cellCoords, GameObject tileObject)
|
||
|
{
|
||
|
_tileMap.Add(cellCoords, tileObject);
|
||
|
}
|
||
|
|
||
|
private GameObject getTileObject(Vector3Int cellCoords)
|
||
|
{
|
||
|
GameObject tileObject = null;
|
||
|
_tileMap.TryGetValue(cellCoords, out tileObject);
|
||
|
|
||
|
return tileObject;
|
||
|
}
|
||
|
|
||
|
private void removeTileRecord(Vector3Int cellCoords)
|
||
|
{
|
||
|
_tileMap.Remove(cellCoords);
|
||
|
}
|
||
|
|
||
|
private void removeTileRecordIfExists(Vector3Int cellCoords)
|
||
|
{
|
||
|
if (_tileMap.ContainsKey(cellCoords))
|
||
|
_tileMap.Remove(cellCoords);
|
||
|
}
|
||
|
|
||
|
private void onUndoRedo()
|
||
|
{
|
||
|
registerTilesWithGrid();
|
||
|
}
|
||
|
|
||
|
private void onHierarchyChanged()
|
||
|
{
|
||
|
if (_gameObject == null)
|
||
|
{
|
||
|
UndoEx.saveEnabledState();
|
||
|
UndoEx.enabled = false;
|
||
|
TileRuleGridDb.instance.deleteGrid(this);
|
||
|
UndoEx.restoreEnabledState();
|
||
|
TileRuleObjectSpawnUI.instance.refresh();
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
if (_gameObject.name != _gridName)
|
||
|
{
|
||
|
UndoEx.record(this);
|
||
|
_gridName= _gameObject.name;
|
||
|
TileRuleObjectSpawnUI.instance.refresh();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void registerTilesWithGrid()
|
||
|
{
|
||
|
// Note: Could happen if the grid has just been created (it's being called from OnEnable).
|
||
|
if (_gameObject == null) return;
|
||
|
|
||
|
_tileMap.Clear();
|
||
|
|
||
|
getChildTileRulePrefabInstances(_prefabInstanceRoots);
|
||
|
foreach (var go in _prefabInstanceRoots)
|
||
|
{
|
||
|
Vector3Int cellCoords = worldPointToCellCoords(go.transform.position);
|
||
|
addTileRecord(cellCoords, go);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void getChildTileRulePrefabInstances(List<GameObject> tileRulePrefabInstances)
|
||
|
{
|
||
|
_prefabInstanceRootFilter = (GameObject go) =>
|
||
|
{ return !ObjectGroupDb.instance.isObjectGroup(go) && _ruleProfile.containsPrefab(go.getPrefabAsset()); };
|
||
|
_gameObject.getAllChildren(true, true, _objectBuffer);
|
||
|
GameObjectEx.getOutermostPrefabInstanceRoots(_objectBuffer, tileRulePrefabInstances, _prefabInstanceRootFilter);
|
||
|
}
|
||
|
|
||
|
private void OnEnable()
|
||
|
{
|
||
|
if (_settings == null) _settings = ScriptableObject.CreateInstance<TileRuleGridSettings>();
|
||
|
if (_gameObject != null) _transform = _gameObject.transform;
|
||
|
|
||
|
EditorApplication.hierarchyChanged += onHierarchyChanged;
|
||
|
Undo.undoRedoPerformed += onUndoRedo;
|
||
|
|
||
|
_ruleProfile = settings.tileRuleProfile;
|
||
|
registerTilesWithGrid();
|
||
|
|
||
|
_foreignEraseOverlapFilter.objectTypes = GameObjectType.All & ~(GameObjectType.Light | GameObjectType.Camera | GameObjectType.Terrain);
|
||
|
_foreignEraseOverlapFilter.customFilter = (GameObject go) => { return !go.transform.IsChildOf(_transform); };
|
||
|
|
||
|
if (_mirrorGizmo == null) _mirrorGizmo = ScriptableObject.CreateInstance<ObjectMirrorGizmo>();
|
||
|
if (_mirrorGizmoSettings == null) _mirrorGizmoSettings = ScriptableObject.CreateInstance<ObjectMirrorGizmoSettings>();
|
||
|
|
||
|
_mirrorGizmo.sharedSettings = _mirrorGizmoSettings;
|
||
|
}
|
||
|
|
||
|
private void OnDisable()
|
||
|
{
|
||
|
EditorApplication.hierarchyChanged -= onHierarchyChanged;
|
||
|
Undo.undoRedoPerformed -= onUndoRedo;
|
||
|
}
|
||
|
|
||
|
private void OnDestroy()
|
||
|
{
|
||
|
if (_settings != null) UndoEx.destroyObjectImmediate(_settings);
|
||
|
if (_mirrorGizmo != null) UndoEx.destroyObjectImmediate(_mirrorGizmo);
|
||
|
if (_mirrorGizmoSettings != null) UndoEx.destroyObjectImmediate(_mirrorGizmoSettings);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|