using UnityEngine; using System; using System.Text; using System.Collections; using System.Collections.Generic; using TGS.Geom; using TGS.PathFinding; namespace TGS { public enum HighlightMode { None = 0, Territories = 1, Cells = 2 } public enum OverlayMode { Overlay = 0, Ground = 1 } public enum GridTopology { Irregular = 0, Box = 1, // Rectangular = 2, // deprecated: use Box Hexagonal = 3 } public enum HighlightEffect { Default = 0, DualColors = 6, TextureAdditive = 1, TextureMultiply = 2, TextureColor = 3, TextureScale = 4, None = 5 } public enum CentroidType { GeometricCenter = 0, Centroid = 1, BetterCentroid = 2 } #if UNITY_EDITOR public enum CellDebugInfo { Nothing, CellIndex, CellCoordinates, PathFindingCrossCost } public enum TerritoryDebugInfo { Nothing, TerritoryIndex } #endif public partial class TerrainGridSystem : MonoBehaviour { /// /// Access to the input class /// public IInputProxy input; [SerializeField] Terrain _terrain; [SerializeField] GameObject _terrainObject; /// /// Terrain reference. Assign a terrain to this property to fit the grid to terrain height and dimensions /// public GameObject terrainObject { get { return _terrainObject; } set { if (_terrainObject != value) { _terrainObject = value; _terrainWrapper = null; BuildTerrainWrapper(); isDirty = true; Redraw(); } } } [SerializeField] bool _multiTerrain; public bool multiTerrain { get { return _multiTerrain; } set { if (_multiTerrain != value) { _multiTerrain = value; _terrainWrapper = null; BuildTerrainWrapper(); isDirty = true; Redraw(); } } } /// /// Sets the terrain gameobject. This is equivalent to use the terrainObject property. /// public void SetTerrain(GameObject terrain) { terrainObject = terrain; } [SerializeField] string _terrainObjectsPrefix; public string terrainObjectsPrefix { get { return _terrainObjectsPrefix; } set { if (_terrainObjectsPrefix != value) { _terrainObjectsPrefix = value; _terrainWrapper = null; BuildTerrainWrapper(); isDirty = true; Redraw(); } } } [SerializeField] LayerMask _terrainObjectsLayerMask = -1; public LayerMask terrainObjectsLayerMask { get { return _terrainObjectsLayerMask; } set { if (_terrainObjectsLayerMask != value) { _terrainObjectsLayerMask = value; _terrainWrapper = null; BuildTerrainWrapper(); isDirty = true; Redraw(); } } } [SerializeField] bool _terrainObjectsSearchGlobal; public bool terrainObjectsSearchGlobal { get { return _terrainObjectsSearchGlobal; } set { if (_terrainObjectsSearchGlobal != value) { _terrainObjectsSearchGlobal = value; _terrainWrapper = null; BuildTerrainWrapper(); isDirty = true; Redraw(); } } } // Terrain data wrapper ITerrainWrapper _terrainWrapper; public ITerrainWrapper terrain { get { return _terrainWrapper; } } /// /// Returns the terrain center in world space. /// public Vector3 terrainCenter { get { return _terrainWrapper.transform.position + new Vector3(terrainWidth * 0.5f, 0, terrainDepth * 0.5f); } } [SerializeField] Vector2 _terrainMeshPivot = new Vector2(0.5f, 0.5f); public Vector2 terrainMeshPivot { get { return _terrainMeshPivot; } set { if (_terrainMeshPivot != value) { _terrainMeshPivot = value; if (_terrainWrapper != null && _terrainWrapper is MeshTerrainWrapper) { ((MeshTerrainWrapper)_terrainWrapper).pivot = _terrainMeshPivot; gridNeedsUpdate = true; } } } } public Texture2D canvasTexture; [SerializeField] bool _transparentBackground; /// /// When enabled, grid is visible without background mesh /// public bool transparentBackground { get { return _transparentBackground; } set { if (_transparentBackground != value) { _transparentBackground = value; isDirty = true; Redraw(true); } } } [SerializeField] bool _useStencilBuffer = true; /// /// When enabled, stencil buffer will be used to avoid overdraw and ensure correct rendering order of grid features. You can disable this option if it creates conflicts with other stencil-based renderers. /// public bool useStencilBuffer { get { return _useStencilBuffer; } set { if (_useStencilBuffer != value) { _useStencilBuffer = value; isDirty = true; UpdatePreventOverdrawSettings(); } } } /// /// Returns the current cursor position in grid local coordinates (use transform.TransformPoint(x) to convert to world space) /// public Vector3 localCursorPosition { get { return localCursorPos; } } /// /// Returns the position of the last click on the grid (in local coordinates. Use transform.TransformPoint(x) to convert to world space) /// public Vector3 localCursorLastClickedPosition { get { return localCursorLastClickedPos; } } [SerializeField] bool _useGeometryShaders = true; /// /// When enabled, geometry shaders will be used (if platform supports them) /// public bool useGeometryShaders { get { return _useGeometryShaders; } set { if (_useGeometryShaders != value) { _useGeometryShaders = value; isDirty = true; territoriesGeoMat = territoriesDisputedGeoMat = cellsGeoMat = null; LoadGeometryShaders(); Redraw(true); } } } [SerializeField] int _sortingOrder; /// /// Sets the sorting layer for the grid elements (only valid when rendering in transparent queue) /// public int sortingOrder { get { return _sortingOrder; } set { if (_sortingOrder != value) { _sortingOrder = value; Redraw(true); } } } [UnityEngine.Serialization.FormerlySerializedAs("_heightMapSize")] [SerializeField] int _heightmapWidth = 513; public int heightmapWidth { get { return _heightmapWidth; } set { value = GetFittedSizeForHeightmap(value); if (_heightmapWidth != value) { _heightmapWidth = value; isDirty = true; } } } [SerializeField] int _heightmapHeight = 513; public int heightmapHeight { get { return _heightmapHeight; } set { value = GetFittedSizeForHeightmap(value); if (_heightmapHeight != value) { _heightmapHeight = value; isDirty = true; } } } [SerializeField] Texture2D _gridMask; /// /// Gets or sets the grid mask. The alpha component of this texture is used to determine cells visibility (0 = cell invisible) /// public Texture2D gridMask { get { return _gridMask; } set { if (_gridMask != value) { _gridMask = value; isDirty = true; ReloadGridMask(); } } } [SerializeField] bool _gridMaskUseScale; /// /// When set to true, the mask will be mapped onto the scaled grid rectangle (using offset and scale parameters) instead of the full quad which ignores the offset and scale /// public bool gridMaskUseScale { get { return _gridMaskUseScale; } set { if (_gridMaskUseScale != value) { _gridMaskUseScale = value; isDirty = true; Redraw(true); } } } [SerializeField] int _gridMaskInsideCount = 1; /// /// Minimum number of vertices that must be inside the grid mask to consider the entire cell is inside the mask /// public int gridMaskInsideCount { get { return _gridMaskInsideCount; } set { value = Mathf.Max(1, value); if (_gridMaskInsideCount != value) { _gridMaskInsideCount = value; isDirty = true; Redraw(true); } } } [SerializeField] GridTopology _gridTopology = GridTopology.Irregular; /// /// The grid type (boxed, hexagonal or irregular) /// public GridTopology gridTopology { get { return _gridTopology; } set { if (_gridTopology != value) { _gridTopology = value; needGenerateMap = true; isDirty = true; } } } [SerializeField] int _seed = 1; /// /// Randomize seed used to generate cells. Use this to control randomization. /// public int seed { get { return _seed; } set { if (_seed != value) { _seed = value; needGenerateMap = true; isDirty = true; } } } /// /// Returns the actual number of cells created according to the current grid topology /// /// The cell count. public int cellCount { get { if (_gridTopology == GridTopology.Irregular) { return _numCells; } else { return _cellRowCount * _cellColumnCount; } } } [SerializeField] bool _evenLayout = false; /// /// Toggle even corner in hexagonal topology. /// public bool evenLayout { get { return _evenLayout; } set { if (value != _evenLayout) { _evenLayout = value; isDirty = true; needGenerateMap = true; } } } [SerializeField] bool _pointyTopHexagons; public bool pointyTopHexagons { get { return _pointyTopHexagons; } set { if (value != _pointyTopHexagons) { _pointyTopHexagons = value; isDirty = true; needGenerateMap = true; } } } [SerializeField] bool _regularHexagons; public bool regularHexagons { get { return _regularHexagons; } set { if (value != _regularHexagons) { _regularHexagons = value; isDirty = true; CellsUpdateBounds(); UpdateTerritoriesBoundary(); Redraw(); } } } [SerializeField] float _hexSize = 0.01f; [SerializeField] float _regularHexagonsWidth; public float regularHexagonsWidth { get { return _regularHexagonsWidth; } set { if (value != _regularHexagonsWidth) { _regularHexagonsWidth = value; ComputeGridScale(); isDirty = true; CellsUpdateBounds(); UpdateTerritoriesBoundary(); Redraw(); } } } [SerializeField] int _gridRelaxation = 1; /// /// Sets the relaxation iterations used to normalize cells sizes in irregular topology. /// public int gridRelaxation { get { return _gridRelaxation; } set { if (_gridRelaxation != value) { _gridRelaxation = value; needGenerateMap = true; isDirty = true; } } } [SerializeField] float _gridCurvature = 0.0f; /// /// Gets or sets the grid's curvature factor. /// public float gridCurvature { get { return _gridCurvature; } set { if (_gridCurvature != value) { _gridCurvature = value; needGenerateMap = true; isDirty = true; } } } [SerializeField] HighlightMode _highlightMode = HighlightMode.Cells; public HighlightMode highlightMode { get { return _highlightMode; } set { if (_highlightMode != value) { _highlightMode = value; isDirty = true; ClearLastOver(); HideCellRegionHighlight(); HideTerritoryRegionHighlight(); CheckCells(); CheckTerritories(); } } } [SerializeField] bool _allowHighlightWhileDragging = false; public bool allowHighlightWhileDragging { get { return _allowHighlightWhileDragging; } set { if (_allowHighlightWhileDragging != value) { _allowHighlightWhileDragging = value; isDirty = true; } } } [SerializeField] float _highlightFadeMin = 0f; public float highlightFadeMin { get { return _highlightFadeMin; } set { if (_highlightFadeMin != value) { _highlightFadeMin = value; isDirty = true; } } } [SerializeField] float _highlightFadeAmount = 0.5f; public float highlightFadeAmount { get { return _highlightFadeAmount; } set { if (_highlightFadeAmount != value) { _highlightFadeAmount = value; isDirty = true; } } } [SerializeField] float _highlightScaleMin = 0.75f; public float highlightScaleMin { get { return _highlightScaleMin; } set { if (_highlightScaleMin != value) { _highlightScaleMin = value; isDirty = true; } } } [SerializeField] float _highlightScaleMax = 1.1f; public float highlightScaleMax { get { return _highlightScaleMax; } set { if (_highlightScaleMax != value) { _highlightScaleMax = value; isDirty = true; } } } [SerializeField] float _highlightFadeSpeed = 1f; public float highlightFadeSpeed { get { return _highlightFadeSpeed; } set { if (_highlightFadeSpeed != value) { _highlightFadeSpeed = value; isDirty = true; } } } [SerializeField] float _highlightMinimumTerrainDistance = 1f; /// /// Minimum distance from camera for cells to be highlighted on terrain /// public float highlightMinimumTerrainDistance { get { return _highlightMinimumTerrainDistance; } set { if (_highlightMinimumTerrainDistance != value) { _highlightMinimumTerrainDistance = value; isDirty = true; } } } [SerializeField] HighlightEffect _highlightEffect = HighlightEffect.Default; public HighlightEffect highlightEffect { get { return _highlightEffect; } set { if (_highlightEffect != value) { _highlightEffect = value; isDirty = true; UpdateHighlightEffect(); } } } [SerializeField] Texture2D _highlightMask; public Texture2D highlightMask { get { return _highlightMask; } set { if (_highlightMask != value) { _highlightMask = value; isDirty = true; UpdateHighlightEffect(); } } } [SerializeField] OverlayMode _overlayMode = OverlayMode.Overlay; public OverlayMode overlayMode { get { return _overlayMode; } set { if (_overlayMode != value) { _overlayMode = value; isDirty = true; } } } [SerializeField] Vector2 _gridCenter; /// /// Center of the grid relative to the Terrain (by default, 0,0, which means center of terrain) /// public Vector2 gridCenter { get { return _gridCenter; } set { if (_gridCenter != value) { _gridCenter = value; isDirty = true; CellsUpdateBounds(); UpdateTerritoriesBoundary(); Redraw(true); } } } /// /// Center of the grid in world space coordinates. You can also use this property to reposition the grid on a given world position coordinate. /// public Vector3 gridCenterWorldPosition { get { return GetWorldSpacePosition(_gridCenter); } set { SetGridCenterWorldPosition(value, false); } } [SerializeField] Vector2 _gridScale = new Vector2(1, 1); /// /// Scale of the grid on the Terrain (by default, 1,1, which means occupy entire terrain) /// public Vector2 gridScale { get { return _gridScale; } set { if (_gridScale != value) { _gridScale = value; ComputeGridScale(); isDirty = true; CellsUpdateBounds(); UpdateTerritoriesBoundary(); Redraw(true); } } } [SerializeField] float _gridElevation = 0; public float gridElevation { get { return _gridElevation; } set { if (_gridElevation != value) { _gridElevation = value; isDirty = true; FitToTerrain(); } } } [SerializeField] float _gridElevationBase = 0; public float gridElevationBase { get { return _gridElevationBase; } set { if (_gridElevationBase != value) { _gridElevationBase = value; isDirty = true; FitToTerrain(); } } } public float gridElevationCurrent { get { return _gridElevation + _gridElevationBase; } } [SerializeField] float _gridMinElevationMultiplier = 1f; public float gridMinElevationMultiplier { get { return _gridMinElevationMultiplier; } set { if (_gridMinElevationMultiplier != value && value >= 0) { _gridMinElevationMultiplier = value; isDirty = true; Redraw(true); } } } [SerializeField] float _gridCameraOffset = 0; public float gridCameraOffset { get { return _gridCameraOffset; } set { if (_gridCameraOffset != value) { _gridCameraOffset = value; isDirty = true; FitToTerrain(); } } } [SerializeField] float _gridNormalOffset = 0; public float gridNormalOffset { get { return _gridNormalOffset; } set { if (_gridNormalOffset != value) { _gridNormalOffset = value; isDirty = true; Redraw(); } } } [Obsolete("Use gridMeshDepthOffset or gridSurfaceDepthOffset.")] public int gridDepthOffset { get { return _gridMeshDepthOffset; } set { gridMeshDepthOffset = value; } } [SerializeField] int _gridMeshDepthOffset = -1; public int gridMeshDepthOffset { get { return _gridMeshDepthOffset; } set { if (_gridMeshDepthOffset != value) { _gridMeshDepthOffset = value; UpdateMaterialDepthOffset(); isDirty = true; } } } [SerializeField] int _gridSurfaceDepthOffset = -1; public int gridSurfaceDepthOffset { get { return _gridSurfaceDepthOffset; } set { if (_gridSurfaceDepthOffset != value) { _gridSurfaceDepthOffset = value; UpdateMaterialDepthOffset(); isDirty = true; } } } [SerializeField] int _gridSurfaceDepthOffsetTerritory = -1; public int gridSurfaceDepthOffsetTerritory { get { return _gridSurfaceDepthOffsetTerritory; } set { if (_gridSurfaceDepthOffsetTerritory != value) { _gridSurfaceDepthOffsetTerritory = value; UpdateMaterialDepthOffset(); isDirty = true; } } } [SerializeField] float _gridRoughness = 0.01f; public float gridRoughness { get { return _gridRoughness; } set { if (_gridRoughness != value) { _gridRoughness = value; isDirty = true; Redraw(); } } } [SerializeField] int _cellRowCount = 8; /// /// Returns the number of rows for box and hexagonal grid topologies /// public int rowCount { get { return _cellRowCount; } set { if (value != _cellRowCount) { _cellRowCount = Mathf.Clamp(value, _gridTopology == GridTopology.Hexagonal ? 2 : 1, MAX_ROWS_OR_COLUMNS); isDirty = true; needGenerateMap = true; CheckGridChanges(); } } } [SerializeField] int _cellColumnCount = 8; /// /// Returns the number of columns for box and hexagonal grid topologies /// public int columnCount { get { return _cellColumnCount; } set { if (value != _cellColumnCount) { _cellColumnCount = Mathf.Clamp(value, _gridTopology == GridTopology.Hexagonal ? 2 : 1, MAX_ROWS_OR_COLUMNS); isDirty = true; needGenerateMap = true; CheckGridChanges(); } } } /// /// Sets the dimensions of the grid in one step. This is faster than setting rowCount and columnCount separately. /// /// Rows. /// Columns. /// Ensures the individual cell size is preserved public void SetDimensions(int rows, int columns, bool keepCellSize = false) { _cellRowCount = Mathf.Clamp(rows, 2, MAX_ROWS_OR_COLUMNS); _cellColumnCount = Mathf.Clamp(columns, 2, MAX_ROWS_OR_COLUMNS); if (keepCellSize) { SetScaleByCellSize(); } isDirty = true; needGenerateMap = true; CheckGridChanges(); } public Texture2D[] textures; [SerializeField] bool _respectOtherUI = true; /// /// When enabled, will prevent interaction if pointer is over an UI element /// public bool respectOtherUI { get { return _respectOtherUI; } set { if (value != _respectOtherUI) { _respectOtherUI = value; isDirty = true; } } } [SerializeField] LayerMask _blockingMask; /// /// Used to cancel highlighting then some other objects block raycasting /// public LayerMask blockingMask { get { return _blockingMask; } set { if (value != _blockingMask) { _blockingMask = value; isDirty = true; } } } [SerializeField] bool _nearClipFadeEnabled; /// /// When enabled, lines near the camera will fade out gracefully /// public bool nearClipFadeEnabled { get { return _nearClipFadeEnabled; } set { if (value != _nearClipFadeEnabled) { _nearClipFadeEnabled = value; isDirty = true; UpdateMaterialNearClipFade(); } } } [SerializeField] float _nearClipFade = 25f; public float nearClipFade { get { return _nearClipFade; } set { if (_nearClipFade != value) { _nearClipFade = Mathf.Max(0, value); isDirty = true; UpdateMaterialNearClipFade(); } } } [SerializeField] float _nearClipFadeFallOff = 50f; public float nearClipFadeFallOff { get { return _nearClipFadeFallOff; } set { if (_nearClipFadeFallOff != value) { _nearClipFadeFallOff = Mathf.Max(value, 0.001f); isDirty = true; UpdateMaterialNearClipFade(); } } } [SerializeField] bool _farFadeEnabled; /// /// When enabled, lines far the camera will fade out gracefully /// public bool farFadeEnabled { get { return _farFadeEnabled; } set { if (value != _farFadeEnabled) { _farFadeEnabled = value; isDirty = true; UpdateMaterialFarFade(); } } } [SerializeField] float _farFadeDistance = 500f; public float farFadeDistance { get { return _farFadeDistance; } set { if (_farFadeDistance != value) { _farFadeDistance = Mathf.Max(0, value); isDirty = true; UpdateMaterialFarFade(); } } } [SerializeField] float _farFadeFallOff = 50f; public float farFadeFallOff { get { return _farFadeFallOff; } set { if (_farFadeFallOff != value) { _farFadeFallOff = Mathf.Max(value, 0.001f); isDirty = true; UpdateMaterialFarFade(); } } } [SerializeField] bool _circularFadeEnabled; /// /// When enabled, grid is faded out according to distance to a gameoject /// public bool circularFadeEnabled { get { return _circularFadeEnabled; } set { if (value != _circularFadeEnabled) { _circularFadeEnabled = value; isDirty = true; UpdateMaterialCircularFade(); } } } [SerializeField] Transform _circularFadeTarget; /// /// The gameobject reference for the circular fade out effect /// public Transform circularFadeTarget { get { return _circularFadeTarget; } set { if (_circularFadeTarget != value) { _circularFadeTarget = value; isDirty = true; UpdateMaterialCircularFade(); } } } [SerializeField] float _circularFadeDistance = 500f; public float circularFadeDistance { get { return _circularFadeDistance; } set { if (_circularFadeDistance != value) { _circularFadeDistance = Mathf.Max(0, value); isDirty = true; UpdateMaterialCircularFade(); } } } [SerializeField] float _circularFadeFallOff = 50f; public float circularFadeFallOff { get { return _circularFadeFallOff; } set { if (_circularFadeFallOff != value) { _circularFadeFallOff = Mathf.Max(value, 0.001f); isDirty = true; UpdateMaterialCircularFade(); } } } #if UNITY_EDITOR [SerializeField] CellDebugInfo _displayCellDebugInfo; /// /// Shows cell information over the grid in Scene View /// public CellDebugInfo displayCellDebugInfo { get { return _displayCellDebugInfo; } set { if (value != _displayCellDebugInfo) { _displayCellDebugInfo = value; isDirty = true; } } } [SerializeField] TerritoryDebugInfo _displayTerritoryDebugInfo; /// /// Shows territory information over the grid in Scene View /// public TerritoryDebugInfo displayTerritoryDebugInfo { get { return _displayTerritoryDebugInfo; } set { if (value != _displayTerritoryDebugInfo) { _displayTerritoryDebugInfo = value; isDirty = true; } } } #endif [SerializeField] bool _enableGridEditor; /// /// Enabled grid editing options in Scene View /// public bool enableGridEditor { get { return _enableGridEditor; } set { if (value != _enableGridEditor) { _enableGridEditor = value; if (!_enableGridEditor && !Application.isPlaying) { HideTerritoryRegionHighlight(); HideCellRegionHighlight(); } isDirty = true; } } } public static TerrainGridSystem instance { get { if (_instance == null) { _instance = FindObjectOfType(); if (_instance == null) { Debug.LogWarning("TerrainGridSystem gameobject not found in the scene!"); } else { if (_instance.cells == null) { _instance.OnEnable(); } } } return _instance; } } /// /// Returns a reference of the currently highlighted gameobject (cell or territory) /// public GameObject highlightedObj { get { return _highlightedObj; } } /// /// The camera reference used in certain computations /// public Camera cameraMain; #region Public General Functions /// /// Used to cancel highlighting on a given gameobject. This call is ignored if go is not currently highlighted. /// public void HideHighlightedObject(GameObject go) { if (go != _highlightedObj) return; _cellHighlightedIndex = -1; _cellHighlighted = null; _territoryHighlightedIndex = -1; _territoryHighlightedRegionIndex = -1; _territoryRegionHighlighted = null; _territoryLastOverIndex = -1; _territoryRegionLastOverIndex = -1; _highlightedObj = null; ClearLastOver(); } /// /// Updates grid center position /// /// Snaps new grid center according to cell size public void SetGridCenterWorldPosition(Vector3 position, bool snapToGrid) { if (snapToGrid) { position = SnapToCell(position, true, false); } if (_terrainWrapper != null) { gridCenter = _terrainWrapper.GetLocalPoint(_terrainObject, position); } else { transform.position = position; } } /// /// Snaps a position to the grid /// public Vector3 SnapToCell(Vector3 position, bool worldSpace = true, bool snapToCenter = true) { if (worldSpace) { position = transform.InverseTransformPoint(position); } position.x = (float)FastMath.Round(position.x, 6); position.y = (float)FastMath.Round(position.y, 6); if (_gridTopology == GridTopology.Box) { float stepX = _gridScale.x / _cellColumnCount; position.x -= _gridCenter.x; if (snapToCenter && _cellColumnCount % 2 == 0) { position.x = (Mathf.FloorToInt(position.x / stepX) + 0.5f) * stepX; } else { position.x = (Mathf.FloorToInt(position.x / stepX + 0.5f)) * stepX; } position.x += _gridCenter.x; float stepY = _gridScale.y / _cellRowCount; position.y -= _gridCenter.y; if (snapToCenter && _cellRowCount % 2 == 0) { position.y = (Mathf.FloorToInt(position.y / stepY) + 0.5f) * stepY; } else { position.y = (Mathf.FloorToInt(position.y / stepY + 0.5f)) * stepY; } position.y += _gridCenter.y; } else if (_gridTopology == GridTopology.Hexagonal) { if (snapToCenter) { Cell cell = CellGetAtPosition(position); if (cell != null) { position = cell.scaledCenter; } } else { float qx = 1f + (_cellColumnCount - 1f) * 3f / 4f; float qy = _cellRowCount + 0.5f; float stepX = _gridScale.x / qx; float stepY = _gridScale.y / qy; float halfStepX = stepX * 0.5f; float halfStepY = stepY * 0.5f; int evenLayout = _evenLayout ? 1 : 0; float k = Mathf.FloorToInt(position.x * _cellColumnCount / _gridScale.x); float j = Mathf.FloorToInt(position.y * _cellRowCount / _gridScale.y); position.x = k * stepX; // + halfStepX; position.y = j * stepY; position.x -= k * halfStepX / 2; float offsetY = (k % 2 == evenLayout) ? 0 : -halfStepY; position.y += offsetY; } } else { // try to get cell under position and returns its center Cell c = CellGetAtPosition(position); if (c != null) { position = c.center; } } if (worldSpace) { position = transform.TransformPoint(position); } return position; } /// /// Returns the rectangle area where cells are drawn in local or world space coordinates. /// /// The rect. public Rect GetRect(bool worldSpace = true) { Rect rect = new Rect(); Vector3 min = GetScaledVector(new Vector3(-0.5f, -0.5f, 0)); Vector3 max = GetScaledVector(new Vector3(0.5f, 0.5f, 0)); if (worldSpace) { min = transform.TransformPoint(min); max = transform.TransformPoint(max); } rect.min = min; rect.max = max; return rect; } /// /// Hides current highlighting effect /// public void HideHighlightedRegions() { HideTerritoryRegionHighlight(); HideCellRegionHighlight(); } public void ReloadGridMask() { ReadMaskContents(); CellsApplyVisibilityFilters(); recreateTerritories = true; Redraw(); if (territoriesTexture != null) { CreateTerritories(territoriesTexture, territoriesTextureNeutralColor, territoriesHideNeutralCells); } } public void ReloadFlatMask() { ReadFlatMaskContents(); CellsApplyVisibilityFilters(); recreateTerritories = true; Redraw(); } /// /// Escales the gameobject of a colored/textured surface /// /// Surf. /// Center. /// Scale. public void ScaleSurface(GameObject surf, Vector2 center, float scale) { if (surf == null) return; Transform t = surf.transform; t.localScale = new Vector3(t.localScale.x * scale, t.localScale.y * scale, 1f); Vector3 originShift = center; originShift.x *= t.localScale.x; originShift.y *= t.localScale.y; originShift.x -= center.x; originShift.y -= center.y; originShift.z = 0; t.localPosition -= originShift; } /// /// Returns current bounds of grid in world space /// /// The bounds. public Bounds bounds { get { Vector3 min = transform.TransformPoint(GetScaledVector(new Vector3(-0.5f, -0.5f, 0))); Vector3 max = transform.TransformPoint(GetScaledVector(new Vector3(0.5f, 0.5f, 0))); Vector3 size = max - min; if (size.y <= 0) size.y = 0.0001f; return new Bounds((min + max) * 0.5f, size); } } /// /// Returns true if a given position lies within this grid on the X/Z plane /// /// public bool Contains(Vector3 position) { Bounds bb = bounds; return position.x >= bb.min.x && position.x <= bb.max.x && position.z >= bb.min.z && position.z <= bb.max.z; } #endregion } }