下面展示 Unity 行为树节点开发——远程目标检测控制
。
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Pathfinding;
using MR_LBS.Common;
using MR_LBS.Client.Unity3D;
namespace BehaviorDesigner.Runtime.Tasks.Movement
{
[TaskDescription("Check to see if the any objects are within sight of the agent.")]
[TaskCategory("GC_YS")]
[TaskIcon("Assets/MR_LBS/Behavior Designer Movement/Editor/Icons/{SkinColor}CanSeeObjectIcon.png")]
public class CanFindObject : Conditional
{
public SharedLayerMask objectLayerMask;
public SharedFloat checkDistance;
public SharedGameObject returnedObject;
[Tooltip("如果为真,则是 seek 模式,如果为假,则是攻击模式")]
public bool IsSeek;
/// <summary>
/// 是否移除 跟自己不一样标签的结果
/// </summary>
public bool IfRemoveTag = false;
/// <summary>
/// Agent 身上的碰撞体
/// </summary>
CapsuleCollider collider;
/// <summary>
/// 物理检测的中心点
/// </summary>
Vector3 centerPos;
BehaviorTree tree;
/// <summary>
/// 攻击模型下是否有上一个目标
/// </summary>
private bool hasTarget;
/// <summary>
/// 物理检测返回的碰撞体数组
/// </summary>
Collider[] hitColliders;
/// <summary>
/// 上一次攻击的目标
/// </summary>
private GameObject lastTarget;
public override void OnAwake()
{
collider = this.gameObject.GetComponent<CapsuleCollider>();
tree = this.gameObject.GetComponent<BehaviorTree>();
}
public override void OnStart()
{
try
{
centerPos = transform.TransformPoint(collider.center);
//Debug.Log(this.FriendlyName + "Start");
//每次检查目标之前需要将上一次的结果清空
hitColliders = null;
if (this.gameObject.CompareTag("AirForce"))
{
hitColliders = Physics.OverlapSphere(centerPos - Vector3.up * 2f, checkDistance.Value, objectLayerMask.Value);
}
else if (!gameObject.CompareTag("AirForce"))
{
hitColliders = Physics.OverlapSphere(centerPos, checkDistance.Value, objectLayerMask.Value);
//近战的怪物应该只能看到和自己 tag 一样的物体
if (IfRemoveTag)
{
//近战单位在用物理检测后要剔除数组里面标签和自己不一样的单位
List<Collider> temp = hitColliders.ToList();
List<Collider> tag = new List<Collider>();
temp.ForEach(t =>
{
if (t.gameObject.tag != this.gameObject.tag)
{
tag.Add(t);
}
});
tag.ForEach(t => temp.Remove(t));
hitColliders = temp.ToArray();
}
}
//下面这段代码移除的是处于不可攻击或者不可 Seek 状态的单位
List<Collider> middle = hitColliders.ToList();
List<Collider> remove = new List<Collider>();
middle.ForEach(t =>
{
//凡是处于已下两种情况的都要移除
if (t.gameObject.GetComponentInPorC<MonsterControlBase>().OnInvisible ||
!t.gameObject.GetComponentInPorC<MonsterControlBase>().getPetTypes)
{
remove.Add(t);
}
});
remove.ForEach(t => middle.Remove(t));
hitColliders = middle.ToArray();
//Debug.Log(hitColliders.Length);
}
catch (Exception e)
{
Debug.Log(e.StackTrace);
}
}
public override TaskStatus OnUpdate()
{
//Debug.Log(this.FriendlyName + "Update");
//如果是攻击模式下
if (!IsSeek)
{
// 判断上一次的攻击目标是否为空
if (hitColliders != null && hitColliders.Length > 0)
{
// 如果上一次的目标不为空 则在结果里面寻找上一次的目标
if (returnedObject.Value != null)
{
GameObject target = null;
foreach (var item in hitColliders)
{
//如果这次和上次看到的目标一样
if (item.gameObject.GetInstanceID() == returnedObject.Value.GetInstanceID())
{
returnedObject.Value = item.gameObject;
break;
}
}
//如果上一次的攻击目标没死,但是这次没找到,则返回一个距离最近的目标
if (target == null)
{
var DisMin = hitColliders.GetMin(
t => Vector3.Distance(t.transform.position, transform.position));
returnedObject.Value = DisMin.gameObject;
transform.LookAtAroundY(DisMin.transform);
return TaskStatus.Success;
}
//这次和上次的目标一样 可以直接返回成功
else
{
transform.LookAtAroundY(returnedObject.Value.transform);
return TaskStatus.Success;
}
}
// 这个情况和在结果里面没找到目标属于一种情况,直接返回一个距离最近的
else
{
//Debug.Log("找一个距离最近的");
var DisMin = hitColliders.GetMin(
t => Vector3.Distance(t.transform.position, transform.position));
returnedObject.Value = DisMin.gameObject;
transform.LookAtAroundY(returnedObject.Value.transform);
return TaskStatus.Success;
}
}
else return TaskStatus.Failure;
}
// Seek 模式下
else
{
if (hitColliders != null && hitColliders.Length > 0)
{
var target = hitColliders.GetMin(
t => Vector3.Distance(transform.position, t.transform.position));
transform.LookAtAroundY(target.transform);
returnedObject.Value = target.gameObject;
return TaskStatus.Success;
}
else return TaskStatus.Failure;
}
}
}
}