using System;
|
using System.Collections.Generic;
|
using Core.Utilities;
|
using KTGMGemClient;
|
using TowerDefense.Level;
|
using TowerDefense.UI.HUD;
|
using UnityEngine;
|
|
namespace TowerDefense.Towers.Placement
|
{
|
/// <summary>
|
/// 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
|
/// </summary>
|
[RequireComponent(typeof(BoxCollider))]
|
public class TowerPlacementGridEndless : Singleton<TowerPlacementGridEndless>, IPlacementArea
|
{
|
public static float GRID_OPENCASH = 200;
|
|
/// <summary>
|
/// Prefab used to visualise the grid.
|
/// 如果在游戏内并不需要Grid显示或者用其它的方式显示,则可以去掉这个数据。
|
/// </summary>
|
public PlacementTile placementTilePrefab;
|
|
/// <summary>
|
/// Visualisation prefab to instantiate on mobile platforms
|
/// </summary>
|
public PlacementTile placementTilePrefabMobile;
|
|
/// <summary>
|
/// 塔位血条对应的UI界面.
|
/// </summary>
|
public GameObject towerBloodUIPrefab;
|
|
/// <summary>
|
/// 等待购买开启对应按钮.
|
/// </summary>
|
public GameObject waitBuyBtnPrefab;
|
|
/// <summary>
|
/// 最后一行格子与前一行格子之间的空位长度.
|
/// </summary>
|
public float gridFreePos;
|
|
public float gridFreePos2;
|
|
public float gridFreePos3;
|
|
public float gridFreePos4;
|
|
/// <summary>
|
/// The dimensions of the grid
|
/// </summary>
|
public IntVector2 dimensions;
|
|
/// <summary>
|
/// Size of the edge of a cell
|
/// </summary>
|
[Tooltip("The size of the edge of one grid cell for area. Should match the physical grid size of towers")]
|
public float gridSize = 1;//1.109
|
|
/// <summary>
|
/// Inverted grid size, to multiply with
|
/// </summary>
|
private float m_InvGridSize;
|
|
/// <summary>
|
/// Array of available cells
|
/// </summary>
|
private bool[,] m_AvailableCells;
|
|
/// <summary>
|
/// 每一个格子的对应的类型。不同的类型可以转化,但可合成的类型不能转化。
|
/// 初始化的时候会根据配置数据来初始化不同的格子状态
|
/// </summary>
|
private PlacementGridType[,] m_arrGridType;
|
|
/// <summary>
|
/// 等待开启塔位的按钮
|
/// </summary>
|
private EndlessTowerGridOpen[,] m_arrTGO;
|
|
public GameObject[] TowerGridOpenObjArr;
|
|
private float[,] m_arrCoinGenTime;
|
|
public GameObject GridContainer;
|
|
/// <summary>
|
/// 所有格子对应的世界坐标
|
/// </summary>
|
private Vector3[,] GridWorldPosArr;
|
|
/// <summary>
|
/// 每一个Tile格子的中心店的屏幕坐标
|
/// </summary>
|
private Vector2[,] arrGridCentPos;
|
|
/// <summary>
|
/// 攻击塔位对应的UI位置信息.
|
/// </summary>
|
private Vector2[,] m_arrTowerBloodUIPos;
|
|
/// <summary>
|
/// 所有塔位血条UI
|
/// </summary>
|
private TowerBloodVis[,] arrTowerBloodUi;
|
|
/// <summary>
|
/// Array of <see cref="PlacementTile"/>s
|
/// </summary>
|
private PlacementTile[,] m_Tiles;
|
|
/// <summary>
|
/// 攻击塔行数,i.e. 前两排是攻击塔位
|
/// </summary>
|
/// <value></value>
|
public int AttackRowNumbers { get; } = 2;
|
|
/// <summary>
|
/// 攻击塔位对应的子弹UI位置信息.
|
/// </summary>
|
private Vector2[,] m_arrTowerBulletUIPos;
|
|
private BulletUICtl[,] arrTowerBulletUi;
|
|
private EnergyUICtl[,] arrTowerEnergyUi;
|
|
private FreezeBreath[,] arrTowerFreezeBreathUi;
|
|
private GameObject[,] arrTowerEnergyEffect;
|
|
/// <summary>
|
/// 水精灵充能满特效
|
/// </summary>
|
private GameObject[,] arrTowerFreezeBreathEffect;
|
|
/// <summary>
|
/// 充能特效对应的Prefab.
|
/// </summary>
|
public GameObject energyEffectPrefab;
|
|
/// <summary>
|
/// 此位置上塔对应的子弹充能Prefab.塔放置到当前的塔位后,如果是对应的塔防类型,需要把
|
/// 相应的界面指针传到塔防的数据结构内。
|
/// </summary>
|
public GameObject towerBulletUIPrefab;
|
|
/// <summary>
|
/// 充能条对应的界面
|
/// </summary>
|
public GameObject towerEnergyUIPrefab;
|
|
/// <summary>
|
/// 水精灵塔技能(冷冻气息)充能条
|
/// </summary>
|
public GameObject FreezeBreathChargePrefab;
|
|
/// <summary>
|
/// 水精灵塔充能条特效
|
/// </summary>
|
public GameObject FreezeBreathChargeEffect;
|
|
/// <summary>
|
/// Converts a location in world space into local grid coordinates.
|
/// </summary>
|
/// <param name="worldLocation"><see cref="Vector3"/> indicating world space coordinates to convert.</param>
|
/// <param name="sizeOffset"><see cref="IntVector2"/> indicating size of object to center.</param>
|
/// <returns><see cref="IntVector2"/> containing the grid coordinates corresponding to location.</returns>
|
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;
|
}
|
|
/// <summary>
|
/// 实现引用.
|
/// </summary>
|
public bool opponent { get; set; }
|
public bool isOpponent()
|
{
|
return opponent;
|
}
|
|
/// <summary>
|
/// 获取对应位置的充能子弹界面指针.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <returns></returns>
|
public BulletUICtl GetBulletUICtl(int x, int y)
|
{
|
return arrTowerBulletUi[x, 3 - y];
|
}
|
|
/// <summary>
|
/// 获取对应位置的能量条界面指针.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <returns></returns>
|
public EnergyUICtl GetEnergyUICtl(int x, int y)
|
{
|
return arrTowerEnergyUi[x, 3 - y];
|
}
|
|
/// <summary>
|
/// 获取水精灵对应位置的充能条
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
public FreezeBreath GetFreezeBreath(int x, int y)
|
{
|
return arrTowerFreezeBreathUi[x, 3 - y];
|
}
|
|
/// <summary>
|
/// 获取一个可以放置塔防的位置.
|
/// </summary>
|
/// <returns></returns>
|
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);
|
}
|
|
/// <summary>
|
/// 判断是否是一个空置的攻击位
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 是否是等待购买的攻击塔位.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 开启金币生产模式
|
/// </summary>
|
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);
|
}
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 设置格子为合成状态。
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 设置某一个格子为已破坏塔位。
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// Returns the world coordinates corresponding to a grid location.
|
/// 把格子坐标转换成世界坐标
|
/// </summary>
|
/// <param name="gridPosition">The coordinate in grid space</param>
|
/// <param name="sizeOffset"><see cref="IntVector2"/> indicating size of object to center.</param>
|
/// <returns>Vector3 containing world coordinates for specified grid cell.</returns>
|
public Vector3 GridToWorld(IntVector2 gridPosition, IntVector2 sizeOffset)
|
{
|
float freePos = 0;
|
if (gridPosition.y == 3)
|
freePos = gridFreePos;
|
else if (gridPosition.y == 2)
|
freePos = gridFreePos2;
|
else if (gridPosition.y == 1)
|
{
|
freePos = gridFreePos3;
|
}
|
else if (gridPosition.y == 0)
|
{
|
freePos = gridFreePos4;
|
}
|
// 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);
|
}
|
|
/// <summary>
|
/// Tests whether the indicated cell range represents a valid placement location.
|
/// </summary>
|
/// <param name="gridPos">The grid location</param>
|
/// <param name="size">The size of the item</param>
|
/// <returns>Whether the indicated range is valid for placement.</returns>
|
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;
|
}
|
|
/// <summary>
|
/// Sets a cell range as being occupied by a tower.
|
/// </summary>
|
/// <param name="gridPos">The grid location</param>
|
/// <param name="size">The size of the item</param>
|
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;
|
}
|
}
|
}
|
|
/// <summary>
|
/// Removes a tower from a grid, setting its cells as unoccupied.
|
/// </summary>
|
/// <param name="gridPos">The grid location</param>
|
/// <param name="size">The size of the item</param>
|
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;
|
}
|
}
|
}
|
|
/// <summary>
|
/// Initialize values
|
/// </summary>
|
protected override void Awake()
|
{
|
base.Awake();
|
ResizeCollider();
|
|
// Initialize empty bool array (defaults are false, which is what we want)
|
m_AvailableCells = new bool[dimensions.x, dimensions.y];
|
|
opponent = false;
|
}
|
|
/// <summary>
|
/// 预计算每一个塔位格子的屏幕坐标。
|
/// </summary>
|
void Start()
|
{
|
// 初始化塔位类型.
|
initTileGridType();
|
InitGridWorldPos();
|
|
// Precalculate inverted grid size, to save a division every time we translate coords
|
m_InvGridSize = 1 / gridSize;
|
SetUpGrid();
|
// 初始化格子对应的屏幕坐标数据 延迟执行
|
Invoke("preCalculateGridUIPos", 0.3f);
|
EventCenter.Ins.Add<int>((int)KTGMGemClient.EventType.EndlessCritBulletNumChange, OnCritBulletNumChange);
|
EventCenter.Ins.Add((int)KTGMGemClient.EventType.OpenAllTowerGrid, OpenAllTowerGrid);
|
}
|
|
/// <summary>
|
/// 初始化所有格子的世界坐标(中心)
|
/// </summary>
|
private void InitGridWorldPos()
|
{
|
GridWorldPosArr = new Vector3[dimensions.x, dimensions.y];
|
|
for (int i = 0; i < dimensions.x; ++i)
|
{
|
for (int j = 0; j < dimensions.y; ++j)
|
{
|
GridWorldPosArr[i, j] = GridToWorld(new IntVector2(i, j), new IntVector2(1, 1));
|
}
|
}
|
}
|
|
/// <summary>
|
/// 根据坐标获取格子世界坐标(中心)
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
public Vector3 GetGridWorldPos(int x, int y)
|
{
|
if (x < 0 || x >= dimensions.x || y < 0 || y >= dimensions.y) return new Vector3();
|
|
return GridWorldPosArr[x, y];
|
}
|
|
/// <summary>
|
/// 木属性暴击子弹数量改变
|
/// </summary>
|
/// <param name="count"></param>
|
private void OnCritBulletNumChange(int count)
|
{
|
for (int i = 0; i < dimensions.x; ++i)
|
{
|
for (int j = 0; j < AttackRowNumbers; ++j)
|
{
|
arrTowerBulletUi[i, j].CritBulletNum = count;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 预计算塔位格子对应的屏幕坐标以及塔位格子的屏幕尺寸
|
/// WORK START: 计算屏幕坐标,然后开搞屏幕相关的内容。下午要把塔位上显示界面搞出来。
|
/// WORK START: 为什么OppoGrid对应的屏幕坐标不对?
|
/// </summary>
|
void preCalculateGridUIPos()
|
{
|
arrGridCentPos = new Vector2[dimensions.x, dimensions.y];
|
|
Vector3 targetPos = GetGridWorldPos(0, 0);
|
|
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();
|
|
float[] gapArr = { 0.5f, 1f };
|
m_arrTGO = new EndlessTowerGridOpen[dimensions.x, dimensions.y];
|
|
GameObject container = GameObject.Find("BuyButtonContainer");
|
TowerPlacementGridEndless.GRID_OPENCASH = 200;
|
//Debug.Log("修改了数值:" + TowerPlacementGridEndless.GRID_OPENCASH);
|
//float[] xup = { -0.6f, -0.3f, 0, 0.3f, 0.6f };
|
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 buyButton = Instantiate(waitBuyBtnPrefab);
|
buyButton.transform.SetParent(container.transform, false);
|
|
Vector3 pos = buyButton.transform.position;
|
//pos.x = arrGridCentPos[x, y].x + (x - 2) * gapArr[3 - y] + xup[x];
|
pos.x = arrGridCentPos[x, y].x + (x - 2) * gapArr[3 - y];
|
pos.z = arrGridCentPos[x, y].y + 6f;
|
// if (y == 2)
|
// {
|
// pos.z = arrGridCentPos[x, y].y + 9f;
|
// }
|
// else if (y == 3)
|
// {
|
// pos.z = arrGridCentPos[x, y].y + 7f;
|
// }
|
pos.y = 30;
|
buyButton.transform.position = pos;
|
|
// 设置按钮对应的点击功能
|
EndlessTowerGridOpen tgo = buyButton.GetComponent<EndlessTowerGridOpen>();
|
if (tgo)
|
{
|
tgo.SetBuyBtnInfo(x, y, this);
|
tgo.cashText.SetText(TowerPlacementGridEndless.GRID_OPENCASH.ToString());
|
m_arrTGO[x, y] = tgo;
|
tgo.CheckCurrencyEnough();
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 设置某一个位置的血条.
|
/// </summary>
|
/// <param name="ix">Tower在X方向上的位置信息</param>
|
/// <param name="health"></param>
|
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);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 处理Tower血量的UI界面
|
/// </summary>
|
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];
|
arrTowerFreezeBreathUi = new FreezeBreath[dimensions.x, AttackRowNumbers];
|
arrTowerEnergyEffect = new GameObject[dimensions.x, AttackRowNumbers];
|
arrTowerFreezeBreathEffect = new GameObject[dimensions.x, AttackRowNumbers];
|
|
float[] gapArr = { 0.4f, 0.6f };
|
|
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<TowerBloodVis>();
|
arrTowerBloodUi[x, y] = tbv;
|
tbv.gameObject.SetActive(false);
|
|
Vector3 targetPos = GridToWorld(new IntVector2(x, dy - y), new IntVector2(2, 1));
|
|
// 子弹条
|
img = Instantiate(towerBulletUIPrefab);
|
img.transform.SetParent(transform, false);
|
img.transform.position = targetPos;
|
Vector3 pos = img.transform.position;
|
pos.x -= 1f;
|
img.transform.position = pos;
|
|
BulletUICtl buc = img.GetComponent<BulletUICtl>();
|
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.4f + (x - 2) * gapArr[y];
|
tpos.z = m_arrTowerBulletUIPos[x, y].y + 5f;
|
tpos.y = 30f;
|
img.transform.position = tpos;
|
img.transform.localScale = Vector3.one;
|
img.transform.localRotation = Quaternion.identity;
|
|
EnergyUICtl euc = img.GetComponent<EnergyUICtl>();
|
arrTowerEnergyUi[x, y] = euc;
|
euc.gameObject.SetActive(false);
|
|
// 设置播放特效对应的3D坐标:
|
Vector3 vpos = GridToWorld(new IntVector2(x, dy - y), new IntVector2(2, 1));
|
vpos.x -= 1f;
|
|
arrTowerEnergyEffect[x, y] = Instantiate(energyEffectPrefab);
|
arrTowerEnergyEffect[x, y].transform.position = vpos;
|
|
// 创建水精灵充能条
|
img = Instantiate(FreezeBreathChargePrefab);
|
img.transform.SetParent(container.transform);
|
tpos = img.transform.position;
|
tpos.x = m_arrTowerBulletUIPos[x, y].x + 4.4f + (x - 2) * gapArr[y];
|
tpos.z = m_arrTowerBulletUIPos[x, y].y + 5f;
|
tpos.y = 30f;
|
img.transform.position = tpos;
|
img.transform.localScale = Vector3.one;
|
img.transform.localRotation = Quaternion.identity;
|
|
FreezeBreath freezeBreath = img.GetComponent<FreezeBreath>();
|
arrTowerFreezeBreathUi[x, y] = freezeBreath;
|
freezeBreath.gameObject.SetActive(false);
|
|
arrTowerFreezeBreathEffect[x, y] = Instantiate(FreezeBreathChargeEffect);
|
arrTowerFreezeBreathEffect[x, y].transform.position = vpos;
|
}
|
}
|
}
|
|
public void updateGridOpenCoin(int ix, int iy)
|
{
|
GRID_OPENCASH = Mathf.Floor(GRID_OPENCASH * 1.25f);
|
|
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());
|
m_arrTGO[x, y].CheckCurrencyEnough();
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 是否有已开启的可放置攻击位置。
|
/// </summary>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 购买塔位.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
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_Tiles[ix, iy].BuyPs();
|
// 开启金币获取模式.
|
m_arrCoinGenTime[ix, dimensions.y - 1 - iy] = 0;
|
}
|
|
/// <summary>
|
/// 交换塔后播放动效
|
/// </summary>
|
/// <param name="ix"></param>
|
/// <param name="iy"></param>
|
public void PlayPutPs(int ix, int iy)
|
{
|
m_Tiles[ix, iy].PutPs();
|
}
|
|
/// <summary>
|
/// 免费开启所有塔位
|
/// </summary>
|
public void OpenAllTowerGrid()
|
{
|
int sy = dimensions.y - 1;
|
|
for (int tx = 0; tx < dimensions.x; tx++)
|
{
|
if (m_arrGridType[tx, sy - 1] == PlacementGridType.EGridWaitBuy)
|
{
|
BuyTowerGrid(tx, sy - 1);
|
Destroy(m_arrTGO[tx, sy - 1].gameObject);
|
++GameConfig.EndlessOpenAttackTowerCount;
|
}
|
if (m_arrGridType[tx, sy] == PlacementGridType.EGridWaitBuy)
|
{
|
BuyTowerGrid(tx, sy);
|
Destroy(m_arrTGO[tx, sy].gameObject);
|
++GameConfig.EndlessOpenAttackTowerCount;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 购买对应的待购攻击塔位.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
public bool buyWaitBuyGrid(int x, int y)
|
{
|
EndlessTowerGridOpen tgo = m_arrTGO[x, y];
|
if (tgo)
|
tgo.OnClick();
|
else
|
return false;
|
return true;
|
}
|
|
/// <summary>
|
/// 初始化当前塔防位的类型信息.
|
/// </summary>
|
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, 0] = PlacementGridType.EGridOpen;
|
m_arrGridType[tx, 1] = PlacementGridType.EGridOpen;
|
m_arrGridType[tx, 2] = PlacementGridType.EGridWaitBuy;
|
m_arrGridType[tx, 3] = PlacementGridType.EGridOpen;
|
}
|
++GameConfig.EndlessOpenAttackTowerCount;
|
}
|
|
/// <summary>
|
/// 在指定的位置播放充能成功的特效.
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <param name="play">是播放还是停止播放</param>
|
public void PlayEnergyEffect(int x, int y, bool play = true)
|
{
|
int dy = dimensions.y - 1 - y;
|
|
if (!arrTowerEnergyEffect[x, dy]) return;
|
|
ParticleSystem ps = arrTowerEnergyEffect[x, dy].GetComponent<ParticleSystem>();
|
|
if (ps == null)
|
ps = arrTowerEnergyEffect[x, dy].transform.GetChild(0).GetComponent<ParticleSystem>();
|
|
if (play)
|
{
|
if (!arrTowerEnergyEffect[x, dy].gameObject.activeSelf)
|
arrTowerEnergyEffect[x, dy].gameObject.SetActive(true);
|
ps.Play();
|
}
|
else
|
{
|
|
ps.Stop();
|
arrTowerEnergyEffect[x, dy].gameObject.SetActive(false);
|
}
|
}
|
|
public void PlayFreezeBreathEffect(int x, int y, bool play = true)
|
{
|
int dy = dimensions.y - 1 - y;
|
|
if (!arrTowerFreezeBreathEffect[x, dy]) return;
|
|
ParticleSystem ps = arrTowerFreezeBreathEffect[x, dy].GetComponent<ParticleSystem>();
|
|
if (ps == null)
|
ps = arrTowerFreezeBreathEffect[x, dy].transform.GetChild(0).GetComponent<ParticleSystem>();
|
|
if (play)
|
{
|
if (!arrTowerFreezeBreathEffect[x, dy].gameObject.activeSelf)
|
arrTowerFreezeBreathEffect[x, dy].gameObject.SetActive(true);
|
ps.Play();
|
}
|
else
|
{
|
ps.Stop();
|
arrTowerFreezeBreathEffect[x, dy].gameObject.SetActive(false);
|
}
|
}
|
|
/// <summary>
|
/// Set collider's size and center
|
/// </summary>
|
void ResizeCollider()
|
{
|
var myCollider = GetComponent<BoxCollider>();
|
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;
|
}
|
|
/// <summary>
|
/// Instantiates Tile Objects to visualise the grid and sets up the <see cref="m_AvailableCells" />
|
/// </summary>
|
protected void SetUpGrid()
|
{
|
PlacementTile tileToUse;
|
#if UNITY_STANDALONE
|
//tileToUse = placementTilePrefab;
|
tileToUse = placementTilePrefabMobile;
|
#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 = 0; y < dimensions.y; y++)
|
{
|
for (int x = 0; x < dimensions.x; x++)
|
{
|
Vector3 targetPos = GetGridWorldPos(x, y);
|
targetPos.z -= 1.0f;
|
PlacementTile newTile = Instantiate(tileToUse);
|
newTile.GridPosition = new IntVector2(x, y);
|
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]);
|
newTile.SetRender(false, "");//初始化不显示
|
|
if (m_arrGridType[x, y] == PlacementGridType.EGridWaitBuy)
|
{
|
newTile.SetWarning(true);
|
}
|
}
|
}
|
|
}
|
}
|
|
PlacementTile currentCanPlace;//记录当前标记
|
|
/// <summary>
|
/// 开始拖拽的时候判断哪些可以放置
|
/// </summary>
|
/// <param name="allTowerP">不符合条件的数组</param>
|
public void CheckAllCanPlace(List<IntVector2> allTowerP)
|
{
|
int iy = dimensions.y - 1;//3
|
bool canPlace;
|
currentCanPlace = null;
|
for (int ix = 0; ix < dimensions.x; ix++)
|
{
|
for (int y = iy; y >= dimensions.y - AttackRowNumbers; --y)
|
{
|
if (m_arrGridType[ix, y] == PlacementGridType.EGridOpen)
|
{
|
canPlace = true;
|
if (allTowerP != null)
|
{
|
for (int i = 0; i < allTowerP.Count; i++)
|
{
|
if (allTowerP[i].x == ix && allTowerP[i].y == y)
|
{
|
canPlace = false;
|
break;
|
}
|
}
|
}
|
//m_Tiles[ix, y].CheckCanPlace(canPlace);
|
}
|
}
|
}
|
}
|
|
|
/// <summary>
|
/// 播放升级动画
|
/// </summary>
|
/// <param name="allTowerP"></param>
|
public void PlayPS(List<IntVector2> allTowerP)
|
{
|
// for (int i = 0; i < allTowerP.Count; i++)
|
// {
|
// m_Tiles[allTowerP[i].x, allTowerP[i].y].SetParticleSystem(true);
|
// }
|
}
|
/// <summary>
|
/// /// 停止所有升级动画
|
/// </summary>
|
public void StopPS()
|
{
|
// int iy = dimensions.y - 1;//3
|
// for (int ix = 0; ix < dimensions.x; ix++)
|
// {
|
// for (int y = iy; y >= dimensions.y - AttackRowNumbers; --y)
|
// {
|
// if (m_arrGridType[ix, y] == PlacementGridType.EGridOpen)
|
// {
|
// m_Tiles[ix, y].SetParticleSystem(false);
|
// }
|
// }
|
// }
|
}
|
|
/// <summary>
|
/// 拖动时候实时检查距离哪个格子近
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
public void CheckCanPlaceUpdate(int x, int y, bool isEmpty, string towerName)
|
{
|
if (currentCanPlace == null)
|
{
|
//Debug.Log("第一次");
|
currentCanPlace = m_Tiles[x, y];
|
currentCanPlace?.SetRender(true, isEmpty ? towerName : "");
|
}
|
else if (currentCanPlace.GridPosition.x != x || currentCanPlace.GridPosition.y != y)
|
{
|
CloseCanPlace();
|
currentCanPlace = m_Tiles[x, y];
|
currentCanPlace?.SetRender(true, isEmpty ? towerName : "");
|
}
|
// if (isEmpty)
|
// {
|
// currentCanPlace.SetTowerVirtualshadow(towerName);
|
// }
|
}
|
|
/// <summary>
|
/// 关闭上一个
|
/// </summary>
|
public void CloseCanPlace()
|
{
|
//Debug.Log("CloseCanPlace:" + currentCanPlace);
|
if (currentCanPlace != null)
|
{
|
currentCanPlace.SetRender(false, "");
|
currentCanPlace = null;
|
}
|
}
|
|
/// <summary>
|
/// 关闭所有
|
/// </summary>
|
public void CloseCanPlaceRenderer()
|
{
|
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)
|
{
|
//m_Tiles[ix, y].CheckCanPlace(false);
|
m_Tiles[ix, y].SetRender(false, "");
|
}
|
}
|
}
|
//StopPS();
|
}
|
|
#if UNITY_EDITOR
|
/// <summary>
|
/// 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.
|
/// </summary>
|
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<BoxCollider>().hideFlags = HideFlags.HideInInspector;
|
}
|
|
/// <summary>
|
/// Draw the grid in the scene view
|
/// </summary>
|
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;
|
else if (y > 0 && y == dimensions.y - 3)
|
freePos = gridFreePos3;
|
else if (y == dimensions.y - 4)
|
freePos = gridFreePos4;
|
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
|
}
|
}
|