using UnityEngine; using System.Collections.Generic; namespace IsoTools { [ExecuteInEditMode] public class IsoWorld : MonoBehaviour { /// World tile types. public enum TileTypes { Isometric, UpDown } /// World tile type. public TileTypes TileType = TileTypes.Isometric; /// Isometric tile size. public float TileSize = 32.0f; /// Start sorting depth value. public float MinDepth = 0.0f; /// Step sorting depth value. public float MaxDepth = 100.0f; class ObjectInfo { public IsoObject IsoObject; public bool Visited; public int BeginDepend; public int EndDepend; public ObjectInfo(IsoObject obj) { IsoObject = obj; } public void Reset(int first_depend) { Visited = false; BeginDepend = first_depend; EndDepend = first_depend; } } bool _dirty = true; float _lastTileSize = 0.0f; TileTypes _lastTileType = TileTypes.Isometric; // ------------------------------------------------------------------------ /// /// Marks world for resorting. /// // ------------------------------------------------------------------------ public void MarkDirty() { _dirty = true; } /// /// Marks world for resorting one object only /// /// Isometric object for resorting. public void MarkDirty(IsoObject obj) { if ( !_dirty ) { _manualSort(obj); } } // ------------------------------------------------------------------------ /// /// Convert isometric coordinates to screen coordinates /// /// Screen coordinates /// Isometric coordinates. // ------------------------------------------------------------------------ public Vector2 IsoToScreen(Vector3 pos) { switch ( TileType ) { case TileTypes.Isometric: return new Vector2( (pos.x - pos.y), (pos.x + pos.y) * 0.5f + pos.z) * TileSize; case TileTypes.UpDown: return new Vector2( pos.x, pos.y + pos.z) * TileSize; default: throw new UnityException("IsoWorld. Type is wrong!"); } } // ------------------------------------------------------------------------ /// /// Convert screen coordinates to isometric coordinates /// /// Isometric coordinates /// Screen coordinates. // ------------------------------------------------------------------------ public Vector3 ScreenToIso(Vector2 pos) { switch ( TileType ) { case TileTypes.Isometric: return new Vector3( (pos.x * 0.5f + pos.y), (pos.y - pos.x * 0.5f), 0.0f) / TileSize; case TileTypes.UpDown: return new Vector3( pos.x, pos.y, 0.0f) / TileSize; default: throw new UnityException("IsoWorld. Type is wrong!"); } } // ------------------------------------------------------------------------ /// /// Convert screen coordinates to isometric coordinates with specified isometric height /// /// Isometric coordinates /// Screen coordinates. /// Point isometric height. // ------------------------------------------------------------------------ public Vector3 ScreenToIso(Vector2 pos, float iso_z) { switch ( TileType ) { case TileTypes.Isometric: { var iso_pos = ScreenToIso(new Vector2(pos.x, pos.y - iso_z * TileSize)); iso_pos.z = iso_z; return iso_pos; } case TileTypes.UpDown: { var iso_pos = ScreenToIso(new Vector2(pos.x, pos.y - iso_z * TileSize)); iso_pos.z = iso_z; return iso_pos; } default: throw new UnityException("IsoWorld. Type is wrong!"); } } void _fixAllTransforms() { var objects = _scanObjects(false); foreach ( var obj in objects ) { obj.IsoObject.FixTransform(); } } void _fixTileSize() { MarkDirty(); _fixAllTransforms(); _lastTileSize = TileSize; } void _fixTileType() { MarkDirty(); _fixAllTransforms(); _lastTileType = TileType; } void _fixDirty() { _manualSort(); Debug.Log("Resort!"); _dirty = false; } void _fixDisable() { var objects = _scanObjects(false); foreach ( var obj in objects ) { obj.IsoObject.ResetIsoWorld(); } } IList _scanObjects(bool onlySorting) { var iso_objects = GameObject.FindObjectsOfType(); var objects = new List(iso_objects.Length); foreach ( var iso_object in iso_objects ) { if ( !onlySorting || iso_object.Sorting ) { var info = new ObjectInfo(iso_object); objects.Add(info); } } return objects; } IList _scanDepends(IList objects) { var depends = new List(objects.Count); foreach ( var obj_a in objects ) { obj_a.Reset(depends.Count); var obj_ao = obj_a.IsoObject; var max_ax = obj_ao.Position.x + obj_ao.Size.x; var max_ay = obj_ao.Position.y + obj_ao.Size.y; for ( int i = 0; i < objects.Count; ++i ) { var obj_bo = objects[i].IsoObject; if ( obj_ao != obj_bo ) { if ( obj_bo.Position.x < max_ax && obj_bo.Position.y < max_ay ) { var max_bz = obj_bo.Position.z + obj_bo.Size.z; if ( obj_ao.Position.z < max_bz ) { depends.Add(i); ++obj_a.EndDepend; } } } } } return depends; } void _manualSort() { var objects = _scanObjects(true); var depends = _scanDepends(objects); var depth = MinDepth; foreach ( var info in objects ) { _placeObject(info, objects, depends, ref depth); } } bool _isDepends(IsoObject obj_ao, IsoObject obj_bo) { if ( obj_ao != obj_bo ) { var max_ax = obj_ao.Position.x + obj_ao.Size.x; var max_ay = obj_ao.Position.y + obj_ao.Size.y; if ( obj_bo.Position.x < max_ax && obj_bo.Position.y < max_ay ) { var max_bz = obj_bo.Position.z + obj_bo.Size.z; if ( obj_ao.Position.z < max_bz ) { return true; } } } return false; } void _manualSort(IsoObject obj) { var objects = _scanObjects(true); var min_depth = float.MinValue; foreach ( var obj_b in objects ) { if ( _isDepends(obj, obj_b.IsoObject) ) { min_depth = Mathf.Max(min_depth, obj_b.IsoObject.transform.position.z); } } var max_depth = float.MaxValue; foreach ( var obj_a in objects ) { if ( _isDepends(obj_a.IsoObject, obj) ) { max_depth = Mathf.Min(max_depth, obj_a.IsoObject.transform.position.z); } } if ( max_depth == float.MaxValue ) { max_depth = MaxDepth; } if ( min_depth == float.MinValue ) { min_depth = MinDepth; } //TODO: Epsilon!!!!! if ( Mathf.Abs(max_depth - min_depth) <= Mathf.Epsilon ) { MarkDirty(); } else { _placeObject(obj, (min_depth + max_depth) / 2.0f); } } void _placeObject(IsoObject obj, float depth) { var pos = obj.gameObject.transform.position; obj.gameObject.transform.position = new Vector3(pos.x, pos.y, depth); } void _placeObject(ObjectInfo info, IList objects, IList depends, ref float depth) { if ( !info.Visited ) { info.Visited = true; for ( int i = info.BeginDepend; i < info.EndDepend && i < depends.Count; ++i ) { var object_index = depends[i]; var obj = objects[object_index]; _placeObject(obj, objects, depends, ref depth); } _placeObject(info.IsoObject, depth); depth += (MaxDepth - MinDepth) / objects.Count; } } void Start() { _fixTileSize(); _fixTileType(); _fixDirty(); } void LateUpdate() { if ( _lastTileSize != TileSize ) { _fixTileSize(); } if ( _lastTileType != TileType ) { _fixTileType(); } if ( _dirty ) { _fixDirty(); } } void OnDisable() { _fixDisable(); } } } // namespace IsoTools