using System; using Core.Utilities; using KTGMGemClient; using TowerDefense.Level; using TowerDefense.UI.HUD; using UnityEngine; using UnityEngine.UI; namespace TowerDefense.Towers.Placement { /// /// A tower placement location made from a grid. /// Its origin is centered in the middle of the lower-right cell. It can be oriented in any direction /// [RequireComponent(typeof(BoxCollider))] public class TowerPlacementGridEndless : MonoBehaviour, IPlacementArea { public static float GRID_OPENCASH = 100; /// /// Prefab used to visualise the grid. /// 如果在游戏内并不需要Grid显示或者用其它的方式显示,则可以去掉这个数据。 /// public PlacementTile placementTilePrefab; /// /// Visualisation prefab to instantiate on mobile platforms /// public PlacementTile placementTilePrefabMobile; /// /// 塔位血条对应的UI界面. /// public GameObject towerBloodUIPrefab; /// /// 等待购买开启对应按钮. /// public Button waitBuyBtnPrefab; /// /// 最后一行格子与前一行格子之间的空位长度. /// public float gridFreePos; public float gridFreePos2; /// /// The dimensions of the grid /// public IntVector2 dimensions; /// /// Size of the edge of a cell /// [Tooltip("The size of the edge of one grid cell for area. Should match the physical grid size of towers")] public float gridSize = 1; /// /// Inverted grid size, to multiply with /// private float m_InvGridSize; /// /// Array of available cells /// private bool[,] m_AvailableCells; /// /// 每一个格子的对应的类型。不同的类型可以转化,但可合成的类型不能转化。 /// 初始化的时候会根据配置数据来初始化不同的格子状态 /// private PlacementGridType[,] m_arrGridType; /// /// 等待开启塔位的按钮 /// private EndlessTowerGridOpen[,] m_arrTGO; private float[,] m_arrCoinGenTime; /// /// 每一个Tile格子的中心店的世界坐标 /// private Vector2[,] arrGridCentPos; /// /// 攻击塔位对应的UI位置信息. /// private Vector2[,] m_arrTowerBloodUIPos; /// /// 所有塔位血条UI /// private TowerBloodVis[,] arrTowerBloodUi; /// /// Array of s /// private PlacementTile[,] m_Tiles; /// /// 攻击塔行数,i.e. 前两排是攻击塔位 /// /// public int AttackRowNumbers { get; } = 2; /// /// 攻击塔位对应的子弹UI位置信息. /// private Vector2[,] m_arrTowerBulletUIPos; private BulletUICtl[,] arrTowerBulletUi; private EnergyUICtl[,] arrTowerEnergyUi; private ParticleSystem[,] arrTowerEnergyEffect; /// /// 充能特效对应的Prefab. /// public ParticleSystem energyEffectPrefab; /// /// 此位置上塔对应的子弹充能Prefab.塔放置到当前的塔位后,如果是对应的塔防类型,需要把 /// 相应的界面指针传到塔防的数据结构内。 /// public GameObject towerBulletUIPrefab; /// /// 充能条对应的界面 /// public GameObject towerEnergyUIPrefab; /// /// Converts a location in world space into local grid coordinates. /// /// indicating world space coordinates to convert. /// indicating size of object to center. /// containing the grid coordinates corresponding to location. public IntVector2 WorldToGrid(Vector3 worldLocation, IntVector2 sizeOffset) { Vector3 localLocation = transform.InverseTransformPoint(worldLocation); // Scale by inverse grid size localLocation *= m_InvGridSize; // Offset by half size var offset = new Vector3(sizeOffset.x * 0.5f, 0.0f, sizeOffset.y * 0.5f); localLocation -= offset; int xPos = Mathf.RoundToInt(localLocation.x); int yPos = Mathf.RoundToInt(localLocation.z); return new IntVector2(xPos, yPos); } public IntVector2 getdimsize() { return dimensions; } /// /// 实现引用. /// public bool opponent { get; set; } public bool isOpponent() { return opponent; } /// /// 获取对应位置的充能子弹界面指针. /// /// /// public BulletUICtl GetBulletUICtl(int x, int y) { return arrTowerBulletUi[x, 3 - y]; } /// /// 获取对应位置的能量条界面指针. /// /// /// public EnergyUICtl GetEnergyUICtl(int x, int y) { return arrTowerEnergyUi[x, 3 - y]; } /// /// 获取一个可以放置塔防的位置. /// /// public IntVector2 getFreePos(int xdim, int ydim, bool forceGet = false) { IntVector2 resvec = new IntVector2(); int subval = AttackRowNumbers; if (forceGet) subval = 0; // Range产生的随机数是 [min, max),Y值再减2是因为最前面两排是战斗塔位。 int maxXdim = dimensions.x - xdim + 1; int maxYdim = (dimensions.y - ydim + 1) - subval; resvec.x = UnityEngine.Random.Range(0, maxXdim); resvec.y = UnityEngine.Random.Range(0, maxYdim); TowerFitStatus tfs = Fits(resvec, new IntVector2(xdim, ydim)); // 处理ForceGet bool legalVal = (tfs == TowerFitStatus.Fits); if (legalVal && forceGet) { if (m_arrGridType[resvec.x, resvec.y] != PlacementGridType.EGridOpen) legalVal = false; } if (legalVal) { return resvec; } else { int looptime = 0; while (looptime < 10) { looptime++; resvec.x = UnityEngine.Random.Range(0, maxXdim); resvec.y = UnityEngine.Random.Range(0, maxYdim); tfs = Fits(resvec, new IntVector2(xdim, ydim)); // 处理forceGet. if (forceGet) { if (m_arrGridType[resvec.x, resvec.y] != PlacementGridType.EGridOpen) continue; } if (tfs == TowerFitStatus.Fits) return resvec; } // 循环超过10次,则直接寻找空位,找不到则返回错误. for (int x = 0; x < maxXdim; x++) { for (int y = 0; y < maxYdim; y++) { if (!m_AvailableCells[x, y]) { // 处理ForceGet if (forceGet) { if (m_arrGridType[x, y] != PlacementGridType.EGridOpen) continue; } resvec.x = x; resvec.y = y; return resvec; } } } } return new IntVector2(-1, -1); } /// /// 判断是否是一个空置的攻击位 /// /// /// /// public bool isFreeAtackPos(int x, int y) { if (m_AvailableCells[x, y]) return false; if (m_arrGridType[x, y] == PlacementGridType.EGridOpen) return true; return false; } /// /// 是否是等待购买的攻击塔位. /// /// /// /// public bool isWaitBuyGrid(int x, int y) { if (m_AvailableCells[x, y]) return false; if (m_arrGridType[x, y] == PlacementGridType.EGridWaitBuy) return true; return false; } /// /// 开启金币生产模式 /// public void startCoinGenMode() { m_arrCoinGenTime = new float[dimensions.x, AttackRowNumbers]; int ty = dimensions.y - 1; for (int tx = 0; tx < dimensions.x; tx++) { for (int y = 0; y < AttackRowNumbers; ++y) { if (m_arrGridType[tx, ty - y] == PlacementGridType.EGridOpen || m_arrGridType[tx, ty - y] == PlacementGridType.EGridCombo) m_arrCoinGenTime[tx, y] = 0; else m_arrCoinGenTime[tx, y] = -1f; } } } void Update() { if (m_arrCoinGenTime == null || !EndlessUIStart.instance.IsGameRunning) return; float delta = Time.deltaTime; float timePe = JsonDataCenter.GOLD_TIME / 1000.0f; // 处理每一开启的格子,用于金币数据增加: for (int ti = 0; ti < dimensions.x; ti++) { for (int y = 0; y < AttackRowNumbers; ++y) { // 是否是可以增加金币的格子: if (m_arrCoinGenTime[ti, y] < 0) continue; // 时间判断. if (m_arrCoinGenTime[ti, y] >= 0) { m_arrCoinGenTime[ti, y] += delta; if (m_arrCoinGenTime[ti, y] > timePe) { m_arrCoinGenTime[ti, y] -= timePe; // 增加金币: EndlessLevelManager.instance.Currency.AddCurrency(JsonDataCenter.GOLD_ADD); } } } } } /// /// 设置格子为合成状态。 /// /// /// /// public bool SetComboGrid(int x, int y) { if (m_arrGridType[x, y] == PlacementGridType.EGridOpen) { m_arrGridType[x, y] = PlacementGridType.EGridCombo; return true; } else return false; } /// /// 设置某一个格子为已破坏塔位。 /// /// /// /// public bool SetDestroyedGrid(int x, int y) { // 释放掉界面显示 if (m_arrGridType[x, y] == PlacementGridType.EGridWaitBuy) { if (m_arrTGO[x, y]) { m_arrTGO[x, y].Release(); m_arrTGO[x, y] = null; } } // 任何情况下,都可以破坏,没有购买,上面有塔,都得破坏 m_arrGridType[x, y] = PlacementGridType.EGridDestroyed; if (arrTowerBloodUi[x, dimensions.y - 1 - y] != null) arrTowerBloodUi[x, dimensions.y - 1 - y].gameObject.SetActive(false); m_Tiles[x, y].SetTileType(PlacementGridType.EGridDestroyed); return true; } /// /// Returns the world coordinates corresponding to a grid location. /// 把格子坐标转换成世界坐标 /// /// The coordinate in grid space /// indicating size of object to center. /// Vector3 containing world coordinates for specified grid cell. public Vector3 GridToWorld(IntVector2 gridPosition, IntVector2 sizeOffset) { float freePos = 0; if (gridPosition.y == 3) freePos = gridFreePos; else if (gridPosition.y == 2) freePos = gridFreePos2; // Calculate scaled local position Vector3 localPos = new Vector3(gridPosition.x + (sizeOffset.x * 0.5f), 0, gridPosition.y + (sizeOffset.y * 0.5f) + freePos) * gridSize; return transform.TransformPoint(localPos); } /// /// Tests whether the indicated cell range represents a valid placement location. /// /// The grid location /// The size of the item /// Whether the indicated range is valid for placement. public TowerFitStatus Fits(IntVector2 gridPos, IntVector2 size) { // If the tile size of the tower exceeds the dimensions of the placement area, immediately decline placement. if ((size.x > dimensions.x) || (size.y > dimensions.y)) { return TowerFitStatus.OutOfBounds; } IntVector2 extents = gridPos + size; // Out of range of our bounds if ((gridPos.x < 0) || (gridPos.y < 0) || (extents.x > dimensions.x) || (extents.y > dimensions.y)) { return TowerFitStatus.OutOfBounds; } // Ensure there are no existing towers within our tile silhuette. for (int y = gridPos.y; y < extents.y; y++) { for (int x = gridPos.x; x < extents.x; x++) { if (m_AvailableCells[x, y]) { return TowerFitStatus.Overlaps; } } } // If we've got far, we've got a valid position. return TowerFitStatus.Fits; } /// /// Sets a cell range as being occupied by a tower. /// /// The grid location /// The size of the item public void Occupy(IntVector2 gridPos, IntVector2 size) { IntVector2 extents = gridPos + size; // Validate the dimensions and size if ((size.x > dimensions.x) || (size.y > dimensions.y)) { throw new ArgumentOutOfRangeException("size", "Given dimensions do not fit in our grid"); } // Out of range of our bounds if ((gridPos.x < 0) || (gridPos.y < 0) || (extents.x > dimensions.x) || (extents.y > dimensions.y)) { Debug.Log("目标GridPos is:" + gridPos.x + "," + gridPos.y + "," + size.x + "," + size.y); throw new ArgumentOutOfRangeException("gridPos", "Given footprint is out of range of our grid"); } // 填充对应格子为占用状态 for (int y = gridPos.y; y < extents.y; y++) { for (int x = gridPos.x; x < extents.x; x++) { m_AvailableCells[x, y] = true; } } } /// /// Removes a tower from a grid, setting its cells as unoccupied. /// /// The grid location /// The size of the item public void Clear(IntVector2 gridPos, IntVector2 size) { IntVector2 extents = gridPos + size; // Validate the dimensions and size if ((size.x > dimensions.x) || (size.y > dimensions.y)) { throw new ArgumentOutOfRangeException("size", "Given dimensions do not fit in our grid"); } // Out of range of our bounds if ((gridPos.x < 0) || (gridPos.y < 0) || (extents.x > dimensions.x) || (extents.y > dimensions.y)) { throw new ArgumentOutOfRangeException("gridPos", "Given footprint is out of range of our grid"); } // 清空对应格子为占用状态 for (int y = gridPos.y; y < extents.y; y++) { for (int x = gridPos.x; x < extents.x; x++) { m_AvailableCells[x, y] = false; } } } /// /// Initialize values /// protected virtual void Awake() { ResizeCollider(); // Initialize empty bool array (defaults are false, which is what we want) m_AvailableCells = new bool[dimensions.x, dimensions.y]; opponent = false; } /// /// 预计算每一个塔位格子的屏幕坐标。 /// void Start() { // 初始化塔位类型. initTileGridType(); // Precalculate inverted grid size, to save a division every time we translate coords m_InvGridSize = 1 / gridSize; SetUpGrid(); // 初始化格子对应的屏幕坐标数据 延迟执行 Invoke("preCalculateGridUIPos", 0.3f); //preCalculateGridUIPos(); } /// /// 预计算塔位格子对应的屏幕坐标以及塔位格子的屏幕尺寸 /// WORK START: 计算屏幕坐标,然后开搞屏幕相关的内容。下午要把塔位上显示界面搞出来。 /// WORK START: 为什么OppoGrid对应的屏幕坐标不对? /// void preCalculateGridUIPos() { arrGridCentPos = new Vector2[dimensions.x, dimensions.y]; Vector3 targetPos = GridToWorld(new IntVector2(0, 0), new IntVector2(1, 1)); if (!ViewPortAdj.instance.bAdjViewPort) ViewPortAdj.instance.adjViewportRect(); float size = 10.29f; for (int x = 0; x < dimensions.x; ++x) { for (int y = 0; y < dimensions.y; ++y) { arrGridCentPos[x, y].x = targetPos.x + x * size; arrGridCentPos[x, y].y = targetPos.y + y * size - 44.5f; } } // 血条位置的设定 PreCalculateTowerBloodUi(); m_arrTGO = new EndlessTowerGridOpen[dimensions.x, dimensions.y]; for (int x = 0; x < dimensions.x; ++x) { for (int y = dimensions.y - AttackRowNumbers; y < dimensions.y; ++y) { if (m_arrGridType[x, y] != PlacementGridType.EGridWaitBuy) continue; GameObject container = GameObject.Find("BuyButtonContainer"); Button buyButton = Instantiate(waitBuyBtnPrefab); buyButton.transform.SetParent(container.transform); Vector3 pos = buyButton.transform.position; pos.x = arrGridCentPos[x, y].x; pos.z = arrGridCentPos[x, y].y; pos.y = 30; buyButton.transform.position = pos; buyButton.transform.localRotation = Quaternion.identity; buyButton.transform.localScale = Vector3.one; // 设置按钮对应的点击功能 EndlessTowerGridOpen tgo = buyButton.GetComponent(); if (tgo) { tgo.SetBuyBtnInfo(x, y, this); tgo.cashText.SetText(TowerPlacementGrid.GRID_OPENCASH_SELF.ToString()); m_arrTGO[x, y] = tgo; } } } } /// /// 设置某一个位置的血条. /// /// Tower在X方向上的位置信息 /// public void setTowerPosHealth(int ix, float health) { if (m_arrGridType[ix, dimensions.y - 1] == PlacementGridType.EGridDestroyed && m_arrGridType[ix, dimensions.y - 2] == PlacementGridType.EGridDestroyed) return; int index = 0; if (m_arrGridType[ix, dimensions.y - 1] == PlacementGridType.EGridDestroyed) index = 1; // todo if (arrTowerBloodUi[ix, index] != null) { if (health < 1.0f) { arrTowerBloodUi[ix, index].gameObject.SetActive(true); arrTowerBloodUi[ix, index].SetHealthScale(health); } } } /// /// 处理Tower血量的UI界面 /// protected void PreCalculateTowerBloodUi() { // 处理攻击塔位的血条位置信息. m_arrTowerBloodUIPos = new Vector2[dimensions.x, AttackRowNumbers]; // 前两排是攻击塔位 arrTowerBloodUi = new TowerBloodVis[dimensions.x, AttackRowNumbers]; int dy = dimensions.y - 1; // 处理攻击塔位对应的血条 m_arrTowerBulletUIPos = new Vector2[dimensions.x, AttackRowNumbers]; arrTowerBulletUi = new BulletUICtl[dimensions.x, AttackRowNumbers]; arrTowerEnergyUi = new EnergyUICtl[dimensions.x, AttackRowNumbers]; arrTowerEnergyEffect = new ParticleSystem[dimensions.x, AttackRowNumbers]; for (int x = 0; x < dimensions.x; x++) { for (int y = 0; y < AttackRowNumbers; ++y) { m_arrTowerBloodUIPos[x, y].x = arrGridCentPos[x, dy - y].x; m_arrTowerBloodUIPos[x, y].y = arrGridCentPos[x, dy - y].y; m_arrTowerBulletUIPos[x, y].x = arrGridCentPos[x, dy - y].x; m_arrTowerBulletUIPos[x, y].y = arrGridCentPos[x, dy - y].y; // 现在PVE基地不需要血条 GameObject img = Instantiate(towerBloodUIPrefab); GameObject container = GameObject.Find("BuyButtonContainer"); img.transform.SetParent(container.transform); Vector3 tpos = img.transform.position; tpos.x = m_arrTowerBloodUIPos[x, y].x; tpos.z = m_arrTowerBloodUIPos[x, y].y + 4.2f - y * 1.66f; tpos.y = 30f; img.transform.position = tpos; img.transform.localScale = Vector3.one; img.transform.localRotation = Quaternion.identity; TowerBloodVis tbv = img.GetComponent(); arrTowerBloodUi[x, y] = tbv; tbv.gameObject.SetActive(false); img = Instantiate(towerBulletUIPrefab); img.transform.SetParent(container.transform, true); tpos = img.transform.position; tpos.x = m_arrTowerBulletUIPos[x, y].x + 4.2f; tpos.z = m_arrTowerBulletUIPos[x, y].y - 0.5f; tpos.y = 30f; img.transform.position = tpos; img.transform.localScale = Vector3.one; img.transform.localRotation = Quaternion.identity; BulletUICtl buc = img.GetComponent(); arrTowerBulletUi[x, y] = buc; buc.gameObject.SetActive(false); // 把充能条也创建出来了. img = Instantiate(towerEnergyUIPrefab); img.transform.SetParent(container.transform); tpos = img.transform.position; tpos.x = m_arrTowerBulletUIPos[x, y].x + 4.2f; tpos.z = m_arrTowerBulletUIPos[x, y].y - 0.5f; tpos.y = 30f; img.transform.position = tpos; img.transform.localScale = Vector3.one; img.transform.localRotation = Quaternion.identity; EnergyUICtl euc = img.GetComponent(); arrTowerEnergyUi[x, y] = euc; euc.gameObject.SetActive(false); // 设置播放特效对应的3D坐标: Vector3 vpos = GridToWorld(new IntVector2(x, dy - y), new IntVector2(2, 1)); vpos.x -= (gridSize / 2.0f); vpos.y += 5.0f; arrTowerEnergyEffect[x, y] = Instantiate(energyEffectPrefab); arrTowerEnergyEffect[x, y].transform.position = vpos; arrTowerEnergyEffect[x, y].Stop(); } } } public void updateGridOpenCoin(int ix, int iy) { GRID_OPENCASH = Mathf.Floor(GRID_OPENCASH * 1.2f); for (int x = 0; x < dimensions.x; x++) { for (int y = 0; y < dimensions.y; y++) { if (m_arrGridType[x, y] != PlacementGridType.EGridWaitBuy) continue; if (m_arrTGO[x, y] != null) { m_arrTGO[x, y].cashText.SetText(GRID_OPENCASH.ToString()); } } } } /// /// 是否有已开启的可放置攻击位置。 /// /// public bool hasFreeAttackPos() { int iy = dimensions.y - 1; for (int ix = 0; ix < dimensions.x; ix++) { for (int y = iy; y > dimensions.y - AttackRowNumbers; --y) { if (m_arrGridType[ix, y] == PlacementGridType.EGridOpen) return true; } } return false; } /// /// 购买塔位. /// /// /// public void BuyTowerGrid(int ix, int iy) { // 更新核心的数据信息 if (m_arrGridType[ix, iy] != PlacementGridType.EGridWaitBuy) return; m_arrGridType[ix, iy] = PlacementGridType.EGridOpen; m_AvailableCells[ix, iy] = false; m_Tiles[ix, iy].SetTileType(PlacementGridType.EGridOpen); // 开启金币获取模式. m_arrCoinGenTime[ix, dimensions.y - 1 - iy] = 0; } /// /// 购买对应的待购攻击塔位. /// /// /// /// public bool buyWaitBuyGrid(int x, int y) { EndlessTowerGridOpen tgo = m_arrTGO[x, y]; if (tgo) tgo.OnClick(); else return false; return true; } /// /// 初始化当前塔防位的类型信息. /// void initTileGridType() { m_arrGridType = new PlacementGridType[dimensions.x, dimensions.y]; int sy = dimensions.y - 1; for (int tx = 0; tx < dimensions.x; tx++) { m_arrGridType[tx, sy] = PlacementGridType.EGridWaitBuy; m_arrGridType[tx, sy - 1] = PlacementGridType.EGridWaitBuy; } // 设置塔位默认开启,后面需要根据配置来 m_arrGridType[2, 3] = PlacementGridType.EGridOpen; ++GameConfig.EndlessOpenAttackTowerCount; } /// /// 在指定的位置播放充能成功的特效. /// /// /// /// 是播放还是停止播放 public void PlayEnergyEffect(int x, int y, bool play = true) { int dy = dimensions.y - 1 - y; if (!arrTowerEnergyEffect[x, dy]) return; if (play) arrTowerEnergyEffect[x, dy].Play(); else arrTowerEnergyEffect[x, dy].Stop(); } /// /// Set collider's size and center /// void ResizeCollider() { var myCollider = GetComponent(); Vector3 size = new Vector3(dimensions.x, 0, dimensions.y) * gridSize; myCollider.size = size; // Collider origin is our bottom-left corner myCollider.center = size * 0.5f; } /// /// Instantiates Tile Objects to visualise the grid and sets up the /// protected void SetUpGrid() { PlacementTile tileToUse; #if UNITY_STANDALONE tileToUse = placementTilePrefab; #else tileToUse = placementTilePrefabMobile; #endif if (tileToUse != null) { // Create a container that will hold the cells. var tilesParent = new GameObject("Container"); tilesParent.transform.parent = transform; tilesParent.transform.localPosition = Vector3.zero; tilesParent.transform.localRotation = Quaternion.identity; m_Tiles = new PlacementTile[dimensions.x, dimensions.y]; // 无尽模式上面两排都是可上阵的,所以这里-2 for (int y = dimensions.y - 2; y < dimensions.y; y++) { for (int x = 0; x < dimensions.x; x++) { Vector3 targetPos = GridToWorld(new IntVector2(x, y), new IntVector2(1, 1)); targetPos.z -= 1.0f; PlacementTile newTile = Instantiate(tileToUse); newTile.transform.parent = tilesParent.transform; newTile.transform.position = targetPos; newTile.transform.localRotation = Quaternion.identity; m_Tiles[x, y] = newTile; newTile.SetTileType(m_arrGridType[x, y]); } } } } #if UNITY_EDITOR /// /// On editor/inspector validation, make sure we size our collider correctly. /// Also make sure the collider component is hidden so nobody can mess with its settings to ensure its integrity. /// Also communicates the idea that the user should not need to modify those values ever. /// void OnValidate() { // Validate grid size if (gridSize <= 0) { Debug.LogError("Negative or zero grid size is invalid"); gridSize = 1; } // Validate dimensions if (dimensions.x <= 0 || dimensions.y <= 0) { Debug.LogError("Negative or zero grid dimensions are invalid"); dimensions = new IntVector2(Mathf.Max(dimensions.x, 1), Mathf.Max(dimensions.y, 1)); } // Ensure collider is the correct size ResizeCollider(); GetComponent().hideFlags = HideFlags.HideInInspector; } /// /// Draw the grid in the scene view /// void OnDrawGizmos() { Color prevCol = Gizmos.color; Gizmos.color = Color.yellow; Matrix4x4 originalMatrix = Gizmos.matrix; Gizmos.matrix = transform.localToWorldMatrix; // Draw local space flattened cubes for (int y = 0; y < dimensions.y; y++) { float freePos = 0; if ((y > 0) && (y == dimensions.y - 1)) freePos = gridFreePos; else if (y > 0 && y == dimensions.y - 2) freePos = gridFreePos2; for (int x = 0; x < dimensions.x; x++) { var position = new Vector3((x + 0.5f) * gridSize, 0, (y + 0.5f) * gridSize + freePos); Gizmos.DrawWireCube(position, new Vector3(gridSize, 0, gridSize)); } } Gizmos.matrix = originalMatrix; Gizmos.color = prevCol; // Draw icon too, in center of position Vector3 center = transform.TransformPoint(new Vector3(gridSize * dimensions.x * 0.5f, 1, gridSize * dimensions.y * 0.5f)); Gizmos.DrawIcon(center, "build_zone.png", true); } #endif } }