using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Core.Utilities; using KTGMGemClient; using Microsoft.VisualBasic; using TMPro.Examples; using TowerDefense.Level; using TowerDefense.UI.HUD; using UnityEditor; using UnityEngine; using UnityEngine.Analytics; 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 TowerPlacementGrid : MonoBehaviour, IPlacementArea { public static float GRID_OPENCASH_SELF = 100; public static float GRID_OPENCASH_OPPO = 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; /// /// 此位置上塔对应的子弹充能Prefab.塔放置到当前的塔位后,如果是对应的塔防类型,需要把 /// 相应的界面指针传到塔防的数据结构内。 /// public GameObject towerBulletUIPrefab; /// /// 充能条对应的界面 /// public GameObject towerEnergyUIPrefab; /// /// 等待购买开启对应按钮. /// public Button waitBuyBtnPrefab; /// /// 充能特效对应的Prefab. /// public ParticleSystem energyEffectPrefab; // TEST CODE TO DELETE: public ParticleSystem TestParticle; protected ParticleSystem PlayParticle; protected Timer effectStopTimer; protected bool bTimerStart = false; /// /// 用于调试位置信息的Image. /// public Image debugImg; /// /// 最后一行格子与前一行格子之间的空位长度. /// public float gridFreePos; /// /// 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 this area. Should match the physical grid size of towers")] public float gridSize = 1; /// /// Inverted grid size, to multiply with /// float m_InvGridSize; /// /// Array of available cells /// bool[,] m_AvailableCells; /// /// 每一个格子的对应的类型。不同的类型可以转化,但可合成的类型不能转化。 /// 初始化的时候会根据配置数据来初始化不同的格子状态 /// PlacementGridType[,] m_arrGridType; TowerGridOpen[] m_arrTGO; float[] m_arrCoinGenTime; /// /// 每一个Tile格子的中心点对应的屏幕坐标. /// Vector2[,] m_arrGridCentUIPos; /// /// 攻击塔位对应的UI位置信息. /// Vector2[] m_arrTowerBloodUIPos; TowerBloodVis[] arrTowerBloodUi; /// /// 攻击塔位对应的子弹UI位置信息. /// Vector2[] m_arrTowerBulletUIPos; BulletUICtl[] arrTowerBulletUi; EnergyUICtl[] arrTowerEnergyUi; ParticleSystem[] arrTowerEnergyEffect; /// /// 塔位对应的屏幕坐标尺寸 /// float m_fGridUISize; /// /// Array of s /// PlacementTile[,] m_Tiles; /// /// 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 this 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 this.dimensions; } /// /// 实现引用. /// public bool opponent { get; set; } public bool isOpponent() { return opponent; } /// /// 获取对应位置的充能子弹界面指针. /// /// /// public BulletUICtl GetBulletUICtl(int x, int y) { return this.arrTowerBulletUi[x]; } /// /// 获取对应位置的能量条界面指针. /// /// /// public EnergyUICtl GetEnergyUICtl(int x, int y) { return this.arrTowerEnergyUi[x]; } public FreezeBreath GetFreezeBreath(int x, int y) { return null; } /// /// 获取一个可以放置塔防的位置. /// /// public IntVector2 getFreePos(int xdim, int ydim, bool forceGet = false) { IntVector2 resvec = new IntVector2(); int subval = 1; if (forceGet) subval = 0; // Range产生的随机数是包括min,排除max. Y值再减1是因为最前面一排是战斗塔位。 int maxXdim = this.dimensions.x - xdim + 1; int maxYdim = (this.dimensions.y - ydim + 1) - subval; resvec.x = UnityEngine.Random.Range(0, maxXdim); resvec.y = UnityEngine.Random.Range(0, maxYdim); TowerFitStatus tfs = this.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 = this.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] == true) 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] == true) return false; if (m_arrGridType[x, y] == PlacementGridType.EGridWaitBuy) return true; return false; } /// /// 开启金币生产模式 /// public void startCoinGenMode() { m_arrCoinGenTime = new float[dimensions.x]; int ty = dimensions.y - 1; for (int tx = 0; tx < dimensions.x; tx++) { if ((m_arrGridType[tx, ty] == PlacementGridType.EGridOpen) || (m_arrGridType[tx, ty] == PlacementGridType.EGridCombo)) m_arrCoinGenTime[tx] = 0.0f; else m_arrCoinGenTime[tx] = -1f; } } protected void partilceUpdate() { if (bTimerStart) { if (effectStopTimer.Tick(Time.deltaTime)) { bTimerStart = false; effectStopTimer.Reset(); } } if (UnityEngine.Input.GetKeyDown(KeyCode.Q)) { this.PlayEnergyEffect(2, true); return; } if (UnityEngine.Input.GetKeyDown(KeyCode.W)) { this.PlayEnergyEffect(2, false); return; } // TEST CODE TO DELETE: if (PlayParticle != null) { if (UnityEngine.Input.GetKeyDown(KeyCode.E)) { PlayParticle = Instantiate(TestParticle); Vector3 tpos = this.transform.position; tpos.y += 5.0f; PlayParticle.transform.position = tpos; //Vector3 lookVec = Vector3.zero; //lookVec.x = 1; //PlayParticle.transform.LookAt(lookVec); PlayParticle.Play(); //effectStopTimer.SetTime(0.15f); //bTimerStart = true; } if (UnityEngine.Input.GetKeyDown(KeyCode.F)) { PlayParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); } } } void Update() { // // TEST CODE TO DELETE: partilceUpdate(); if (m_arrCoinGenTime == null) return; float delta = Time.deltaTime; float timePe = JsonDataCenter.GOLD_TIME / 1000.0f; // 处理每一开启的格子,用于金币数据增加: for (int ti = 0; ti < dimensions.x; ti++) { // 是否是可以增加金币的格子: if (m_arrCoinGenTime[ti] < 0) continue; // 时间判断. if (m_arrCoinGenTime[ti] >= 0) { m_arrCoinGenTime[ti] += delta; if (m_arrCoinGenTime[ti] > timePe) { m_arrCoinGenTime[ti] -= timePe; // 增加金币: if (opponent) OpponentMgr.instance.currency.AddCurrency(JsonDataCenter.GOLD_ADD); else LevelManager.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]) { m_arrTGO[x].Release(); m_arrTGO[x] = null; } } // 任何情况下,都可以破坏,没有购买,上面有塔,都得破坏 m_arrGridType[x, y] = PlacementGridType.EGridDestroyed; if (arrTowerBloodUi[x] != null) arrTowerBloodUi[x].gameObject.SetActive(false); if (arrTowerBulletUi[x] != null) arrTowerBulletUi[x].gameObject.SetActive(false); if (arrTowerEnergyUi[x] != null) arrTowerEnergyUi[x].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 == 2) freePos = gridFreePos; // 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 this 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]; // 暂使这种简单的模式来处理正反双方 if (dimensions.y > 1) opponent = false; else opponent = true; /* // 初始化塔位类型. initTileGridType(); // Precalculate inverted grid size, to save a division every time we translate coords m_InvGridSize = 1 / gridSize; SetUpGrid(); */ } protected void addDebugImg(Vector2 pos) { Image timg = Instantiate(this.debugImg); GameObject go = GameObject.Find("BattleMainUI"); if (!go) return; Transform tp = go.GetComponent(); timg.GetComponent().SetParent(tp, true); Vector3 tpos = timg.transform.position; tpos.x = pos.x; tpos.y = pos.y; timg.transform.position = tpos; return; } /// /// 预计算塔位格子对应的屏幕坐标以及塔位格子的屏幕尺寸 /// WORK START: 计算屏幕坐标,然后开搞屏幕相关的内容。下午要把塔位上显示界面搞出来。 /// WORK START: 为什么OppoGrid对应的屏幕坐标不对? /// /// void preCalculateGridUIPos() { m_arrGridCentUIPos = new Vector2[dimensions.x, dimensions.y]; Vector3 targetPos = GridToWorld(new IntVector2(0, 0), new IntVector2(1, 1)); Vector3 svec = this.transform.position; if (!ViewPortAdj.instance.bAdjViewPort) ViewPortAdj.instance.adjViewportRect(); UnityEngine.Camera sceneCam = ViewPortAdj.instance.cachedCamera; Vector3 centPos = sceneCam.WorldToScreenPoint(targetPos); Vector3 lbPos = sceneCam.WorldToScreenPoint(svec); m_fGridUISize = (centPos.y - lbPos.y) * 2.0f; for (int ty = 0; ty < dimensions.y; ty++) { for (int tx = 0; tx < dimensions.x; tx++) { m_arrGridCentUIPos[tx, ty].x = centPos.x + tx * m_fGridUISize; m_arrGridCentUIPos[tx, ty].y = centPos.y + ty * m_fGridUISize; } if ((gridFreePos > 0) && (ty == (dimensions.y - 1))) { targetPos = GridToWorld(new IntVector2(0, ty), new IntVector2(1, 1)); centPos = sceneCam.WorldToScreenPoint(targetPos); for (int tx = 0; tx < dimensions.x; tx++) { m_arrGridCentUIPos[tx, ty].x = centPos.x + tx * m_fGridUISize; m_arrGridCentUIPos[tx, ty].y = centPos.y; } } } // 血条位置的设定 preCalculateTowerBloodUi(); m_arrTGO = new TowerGridOpen[dimensions.x]; // 设置界面相关的内容: for (int y = dimensions.y - 1; y < dimensions.y; y++) { for (int x = 0; x < dimensions.x; x++) { if (m_arrGridType[x, y] != PlacementGridType.EGridWaitBuy) continue; Button tbtn = Instantiate(this.waitBuyBtnPrefab); GameObject go = GameObject.Find("BattleMainUI"); if (!go) continue; Transform tp = go.GetComponent(); tbtn.GetComponent().SetParent(tp, true); Vector3 tpos = tbtn.transform.position; tpos.x = m_arrGridCentUIPos[x, y].x; tpos.y = m_arrGridCentUIPos[x, y].y + m_fGridUISize / 10; tbtn.transform.position = tpos; // 设置为界面最下层: tbtn.transform.SetAsFirstSibling(); // 设置按钮对应的点击功能 TowerGridOpen tgo = tbtn.GetComponent(); if (tgo) { tgo.SetBuyBtnInfo(x, y, this); if (opponent) tgo.cashText.SetText(TowerPlacementGrid.GRID_OPENCASH_OPPO.ToString()); else tgo.cashText.SetText(TowerPlacementGrid.GRID_OPENCASH_SELF.ToString()); m_arrTGO[x] = tgo; } } } } /// /// 设置某一个位置的血条. /// /// Tower在X方向上的位置信息 /// public void setTowerPosHealth(int ix, float health) { if (m_arrGridType[ix, dimensions.y - 1] == PlacementGridType.EGridDestroyed) return; if ((arrTowerBloodUi[ix] != null)) { if (health < 1.0f) { arrTowerBloodUi[ix].gameObject.SetActive(true); arrTowerBloodUi[ix].SetHealthScale(health); } } } /// /// 处理Tower血量的UI界面 /// protected void preCalculateTowerBloodUi() { // 处理攻击塔位的血条位置信息. m_arrTowerBloodUIPos = new Vector2[dimensions.x]; arrTowerBloodUi = new TowerBloodVis[dimensions.x]; int dy = dimensions.y - 1; for (int x = 0; x < dimensions.x; x++) { m_arrTowerBloodUIPos[x].x = m_arrGridCentUIPos[x, dy].x; m_arrTowerBloodUIPos[x].y = m_arrGridCentUIPos[x, dy].y + m_fGridUISize / 10 * 3; GameObject img = Instantiate(towerBloodUIPrefab); GameObject go = GameObject.Find("BattleMainUI"); if (!go) continue; Transform tp = go.GetComponent(); img.GetComponent().SetParent(tp, true); Vector3 tpos = img.transform.position; tpos.x = m_arrTowerBloodUIPos[x].x; tpos.y = m_arrTowerBloodUIPos[x].y; img.transform.position = tpos; // 设置为界面最下层: img.transform.SetAsFirstSibling(); TowerBloodVis tbv = img.GetComponent(); arrTowerBloodUi[x] = tbv; tbv.gameObject.SetActive(false); } // 处理攻击塔位对应的血条 m_arrTowerBulletUIPos = new Vector2[dimensions.x]; arrTowerBulletUi = new BulletUICtl[dimensions.x]; arrTowerEnergyUi = new EnergyUICtl[dimensions.x]; arrTowerEnergyEffect = new ParticleSystem[dimensions.x]; for (int x = 0; x < dimensions.x; x++) { m_arrTowerBulletUIPos[x].x = m_arrGridCentUIPos[x, dy].x + m_fGridUISize / 2.0f - 10; m_arrTowerBulletUIPos[x].y = m_arrGridCentUIPos[x, dy].y; GameObject go = GameObject.Find("BattleMainUI"); if (!go) continue; Transform tp = go.GetComponent(); GameObject img; img = Instantiate(towerBulletUIPrefab); img.GetComponent().SetParent(tp, true); Vector3 tpos = img.transform.position; tpos.x = m_arrTowerBulletUIPos[x].x; tpos.y = m_arrTowerBulletUIPos[x].y; img.transform.position = tpos; // 设置为界面最下层,并记录相应的数据指针。 img.transform.SetAsFirstSibling(); BulletUICtl buc = img.GetComponent(); arrTowerBulletUi[x] = buc; buc.gameObject.SetActive(false); // 把充能条也创建出来了. img = Instantiate(towerEnergyUIPrefab); img.GetComponent().SetParent(tp, true); tpos = img.transform.position; tpos.x = m_arrTowerBulletUIPos[x].x; tpos.y = m_arrTowerBulletUIPos[x].y; img.transform.position = tpos; img.transform.SetAsFirstSibling(); EnergyUICtl euc = img.GetComponent(); arrTowerEnergyUi[x] = euc; euc.gameObject.SetActive(false); // 设置播放特效对应的3D坐标: Vector3 vpos = GridToWorld(new IntVector2(x, dy), new IntVector2(2, 1)); vpos.x -= (gridSize / 2.0f); vpos.y += 5.0f; arrTowerEnergyEffect[x] = Instantiate(energyEffectPrefab); arrTowerEnergyEffect[x].transform.position = vpos; arrTowerEnergyEffect[x].Stop(); } return; } /// /// 在指定的位置播放充能成功的特效. /// /// /// 是播放还是停止播放 public void PlayEnergyEffect(int x, bool play = true) { if (!arrTowerEnergyEffect[x]) return; if (play) { arrTowerEnergyEffect[x].Play(); } else { arrTowerEnergyEffect[x].Stop(); } return; } public void updateGridOpenCoin(int ix, int iy) { // 更新显示相关的信息, if (opponent) GRID_OPENCASH_OPPO += GRID_OPENCASH_OPPO; else GRID_OPENCASH_SELF += GRID_OPENCASH_SELF; for (int x = 0; x < dimensions.x; x++) { if (x == ix) { m_arrTGO[x] = null; } else { if (m_arrGridType[x, iy] != PlacementGridType.EGridWaitBuy) continue; if (m_arrTGO[x] != null) { if (opponent) m_arrTGO[x].cashText.SetText(GRID_OPENCASH_OPPO.ToString()); else m_arrTGO[x].cashText.SetText(GRID_OPENCASH_SELF.ToString()); } } } } /// /// 是否有已开启的可放置攻击位置。 /// /// public bool hasFreeAttackPos() { int iy = dimensions.y - 1; for (int ix = 0; ix < dimensions.x; ix++) { if (m_arrGridType[ix, iy] == 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; /* // 更新显示相关的信息, if (opponent) GRID_OPENCASH_OPPO += GRID_OPENCASH_OPPO; else GRID_OPENCASH_SELF += GRID_OPENCASH_SELF;*/ m_Tiles[ix, iy].SetTileType(PlacementGridType.EGridOpen); // 开启金币获取模式. m_arrCoinGenTime[ix] = 0.0f; } /// /// 购买对应的待购攻击塔位. /// /// /// /// public bool buyWaitBuyGrid(int x, int y) { TowerGridOpen tgo = m_arrTGO[x]; if (tgo) tgo.OnClick(); else return false; return true; } /// /// 预计算每一个塔位格子的屏幕坐标。 /// void Start() { // 初始化塔位类型. initTileGridType(); // Precalculate inverted grid size, to save a division every time we translate coords m_InvGridSize = 1 / gridSize; SetUpGrid(); // 初始化格子对应的屏幕坐标数据 preCalculateGridUIPos(); // TEST CODE TO DELETE: if (TestParticle != null) PlayParticle = Instantiate(TestParticle); effectStopTimer = new Timer(1.0f, stopParticle); bTimerStart = false; } protected void stopParticle() { if (PlayParticle) PlayParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); //if (fireParticleSystem) //fireParticleSystem.gameObject.SetActive(false); //fireParticleSystem.Stop(true,ParticleSystemStopBehavior.StopEmittingAndClear ); //fireParticleSystem.Clear(); //Vector3 vpos = fireParticleSystem.gameObject.transform.position; //vpos.y = 3000; //fireParticleSystem.gameObject.transform.position = vpos; } /// /// 初始化当前塔防位的类型信息. /// 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; if ((tx * 2 + 1) == dimensions.x) m_arrGridType[tx, sy] = PlacementGridType.EGridOpen; } } /// /// 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]; for (int y = dimensions.y - 1; 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; 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 } }