一:敌人移动路线
用的是AI导航,将道路设为静态,烘焙导航路线。
脚本思路:获取目的地gameObject,设置导航目的Destination为目标地址。
二: 敌人生成
单独写一个脚本wave存放生成敌人生成的相关参数,如波数,生成什么样的敌人,生成速率。
脚本敌人生成器spawnedEnemy思路:用协程IEnumerator控制敌人间隔在Star出现。全局参数 waveRate控制延迟时间。
wave脚本不继承behavior,方便在生成器脚本用数组public wave[]。
注意事项:
wave设置序列化:system.Serializable,否则在unity无法看到public的参数。这样就可以在unity直接挂参数prefab,rate,count等参数。
设置敌人生存数,每生成一个++。
敌人消失条件:到达目的地进行销毁,被炮塔击毁(尚未实现)。
到达目的地条件判断:因为y轴的存在,不能单纯的直接equal两个位置。所以要单独判断x和z的位置关系。
注意事项:烘焙时选择的代理模型半径最好是整数!因为地图设置的路线都是整数坐标,如果代理半径有0.5等,会出现敌人的x或z坐标出现0.5等浮点数,导致判断失败。
三:炮塔生成及炮塔ui
可以从商店找到想要的炮塔素材,我是用了三种炮塔,每种炮塔有两种形态。分别为炮塔的常态和升级态生成预制体以备后用。
创建数据类TurretDate保存炮塔相关数据如:要生成的炮塔类型预制体,炮塔花费以及升级后的炮塔花费。其中用enum枚举保存炮塔的三种类型。
TurretManege监听炮塔行为,当前完成度:
1.监听到玩家选择的是哪一种炮塔,通过toggle的值改变获得点击目标。
2.监听玩家的鼠标选择的是地图上的哪一块。首先要判断地图是否和UI重合了:可以使用Even System中的current下的IsPointerOverGameObject方法(如果是做手机端游戏的话要传递参数,详见API)。将地图单独设置一个图层,这样用射线检测只要和单一图层做碰撞,获得碰撞变量hit。然后对是否碰撞做判断,如果有碰撞到就可以用hit中的Collider.GameObject获得碰撞目标。至此监听碰撞cube完成。注意:如果出现ray射线报错空指针的话首先检查一下camera的tag是否设置为main camera
3.获得碰撞地图块对象后,要判断其上是否已经建立了炮塔,可以写单独脚本MapCube单独完成部分功能:如建塔; 继续在TurretManage判断是否有建立炮塔。无:执行键塔操作,金额做对应减少(金钱更新操作可以单独用一个方法实现)同时更新UI金钱的显示,方法是获得UI Money的引用,直接用Text=“¥”+money,要注意的是:新版unity的文本是TextMeshPro,所以在定义和使用的时候要引用TextMeshProUGUI命名空间。
ui:用toggle来设计ui,toggleGroup组件实现单一选择,添加text作为花费金额,额外text做为当前拥有金额。
在ui设计界面,记住按alt可以同时设置锚点,这样可以保证图像随屏幕缩放但是相对位置不变。我是选择居右。CheckMark的源图像是用于选中某炮台时,出现背景变暗功能。
c#源码:
enemy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Enemy : MonoBehaviour
{
private NavMeshAgent agent;
public Transform end;
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
// Update is called once per frame
void Update()
{
enemyMove();
}
void enemyMove()
{
agent.SetDestination(end.localPosition);
if (transform.localPosition.x == end.localPosition.x && transform.localPosition.z == end.localPosition.z)
{
reachDestination();
}
}
private void reachDestination()
{
Destroy(this.gameObject);
}
private void OnDestroy()
{
SpawnedEnemy.enemyCount--;
}
}
SpawnedEnemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnedEnemy : MonoBehaviour
{
public static int enemyCount;
public Wave[] waves;
public Transform star;
//波次间隔时间
public float waveRate=3;
// Start is called before the first frame update
void Start()
{
StartCoroutine(spawnedEnemy());
}
IEnumerator spawnedEnemy()
{
foreach(Wave wave in waves)
{
for (int i = 0; i < wave.count; i++)
{
Instantiate(wave.prefab, star.localPosition, Quaternion.identity);
enemyCount++;
if (i<wave.count)
{
yield return new WaitForSeconds(wave.rate);
}
}
while (enemyCount > 0)
{
yield return 0;
}
yield return new WaitForSeconds(waveRate);
}
}
}
Wave:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Wave
{
public GameObject prefab;
public int count;
public float rate;
}