7.1 基本概念
炮塔炮塔,顾名思义首先脑袋要会转,其次是要冲着坏蛋转,这是基本要求。此外炮塔还要有一定的美感,毕竟是在玩游戏。
7.2 做一个炮塔的样子其实不容易
美工这一块确实是老余的短板,流行的3D软件都不会玩。不过世上无难事,我们有某宝。功夫不负有心人,老余发现某宝上面U3D的素材还是挺多的,当下决定买了一款自己觉得还不错的,没曾想,等老余右键菜单选择Import Package导入后,让休息好了的小余看见了,居然给老余第一个想用的,威力最小的一款起了一个外号,叫马桶炮塔,好吧马桶炮塔看起来确实像马桶,谁让老余舍不得掏银子呢
老余把马桶炮塔码好位置,给马桶炮塔起了个名字StdTurret,给可以转动的部分起了个名字Head。
接下来,他要做一件必要的事情,就是给敌人做一个标签,所有带上这个标签的物件,不管长成啥样,都被视为敌人
把自定义的Tag放到Enemy的Prefab里面去,这样就不会有漏网得了:
7.3 发现敌人
现在老余需要给炮塔绑定一段炮塔专有C#脚本:Turret (Script)
发现敌人的要点就是:
- 给敌人冠以Tag标签,这样就全都在炮塔的监视范围内了
- 筛选出在射程范围内的离的最近的那个小蛋
怎么看射程范围呢,U3D给了一个小Tip,在脚本里面写这么一段:
/// <summary>
/// 重载设计图中的旋转圈,可以用这个功能看到射界
/// </summary>
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position,range);
}
等回到设计界面,看到的就是这个样子,讲真的,老余还挺需要这个功能,不然不是射程太近,就是射程超远,自己都看不见,啥时候不想看了,再注释掉这段代码就行:
筛选离的最近的敌人是UpdateTarget
using UnityEngine;
public class Turret : MonoBehaviour
{
public Transform target;
public float range = 15f;
public string enemyTag = "Enemy";
// Start is called before the first frame update
void Start()
{
InvokeRepeating("UpdateTarget", 0f, 0.5f); // 每间隔0.5秒,调用一下UpdateTarget,让炮塔保持指向敌人
}
void UpdateTarget()
{
// 找出游戏里面出来的所有敌人
GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
float shortestDistance = Mathf.Infinity;
GameObject nearestEnemy = null; // 记录最近距离的敌人信息,初始为空
foreach (GameObject enemy in enemies)
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
// 找到最近的敌人,而且在射程内,则实体化target
if (nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
}
else
{
target = null;
}
}
}
// Update is called once per frame
void Update()
{
if (target == null)
{
return;
}
}
/// <summary>
/// 重载设计图中的旋转圈,可以用这个功能看到射界
/// </summary>
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position,range);
}
}
7.4 瞄准敌人
下一步就是让这个炮塔转脑袋了,当然这个脑袋要冲着坏蛋转,在Turret那个C#中增加下面的内容,关键点就是LockOnTarget,Shoot只是一个空函数,预留给炮弹用的:
public Transform partToRotate; // 旋转部分的引用
public float turnSpeed = 10f; // 炮塔转动速度
void Update()
{
if (target == null)
{
return;
}
LockOnTarget();
if (fireCountdown <= 0)
{
Shoot();
fireCountdown = 1 / fireRate; // 连续发射及考量频率
}
fireCountdown -= Time.deltaTime;
}
/// <summary>
/// 锁定敌人
/// </summary>
void LockOnTarget()
{
// Target lock on
Vector3 dir = target.position - transform.position; // 炮塔和敌人的间距向量
Quaternion lookRotation = Quaternion.LookRotation(dir);
// 使用Lerp转动更加柔和,简单写的话可以Vector3 rotation = lookRotation.eulerAngles, 不过这个转动就有点硬
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles;
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
记住把Head拉到PartToRotate这边来:
从效果图上面看,我们的小马桶炮塔一直在摆着脑袋指向敌人,上图有真相: