下面展示 Unity 行为树节点开发——近战目标检测控制
。
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using MR_LBS.Client.Unity3D;
using Pathfinding;
using MR_LBS.Common;
namespace BehaviorDesigner.Runtime.Tasks.Movement
{
[TaskDescription("Check to see if the any objects are within sight of the agent.")]
[TaskCategory("GC_CL")]
[TaskIcon("Assets/MR_LBS/Behavior Designer Movement/Editor/Icons/{SkinColor}CanSeeObjectIcon.png")]
public class CanFindObject_Short : Conditional
{
public SharedLayerMask objectLayerMask;
public SharedFloat checkDistance;//skillD攻击距离/初始距离
public float atkDistance;//当前攻击距离
public SharedGameObject returnedObject;
/// <summary>
/// 当前宠物即将释放的下一个技能
/// </summary>
public SharedAnimalState stateNext;
[Tooltip("如果为真,则是 seek 模式,如果为假,则是攻击模式")]
public bool IsSeek;
/// <summary>
/// 是否移除 跟自己不一样标签的结果
/// </summary>
public bool IfRemoveTag = false;
/// <summary>
/// Agent 身上的碰撞体
/// </summary>
CapsuleCollider collider;
/// <summary>
/// 物理检测的中心点
/// </summary>
Vector3 centerPos;
/// <summary>
/// 行为树
/// </summary>
BehaviorTree tree;
/// <summary>
/// 攻击模型下是否有上一个目标
/// </summary>
private bool hasTarget;
/// <summary>
/// 物理检测返回的碰撞体数组
/// </summary>
Collider[] hitColliders;
/// <summary>
/// 上一次攻击的目标
/// </summary>
private Collider lastTarget;
private MonsterControlBase control;
public override void OnAwake()
{
collider = this.gameObject.GetComponent<CapsuleCollider>();
tree = this.gameObject.GetComponent<BehaviorTree>();
}
public override void OnStart()
{
//每次检查目标之前需要将上一次的结果清空
hitColliders = null;
//中心点是值类型,需要重新获取
centerPos = transform.TransformPoint(collider.center);
//不同技能拥有不同攻击距离
//普攻使用控制器中的距离
if (stateNext != null && stateNext.Value != MonsterState.SkillD)
{
control = gameObject.GetComponentInPorC<MonsterControlBase>();
//float getDistance = 0;
//非普攻使用shootRange
switch (stateNext.Value)
{
case MonsterState.SkillC:
atkDistance = (float)control.Monster_P.SkillC.ShootRange;
//Debug.Log("实际距离:" + atkDistance);
break;
case MonsterState.SkillA:
atkDistance = (float)control.Monster_P.SkillA.ShootRange;
break;
case MonsterState.SkillS:
atkDistance = (float)control.Monster_P.SkillS.ShootRange;
break;
default: break;
}
if (atkDistance > 50)
atkDistance = 50;
//当获取到的距离大于0时,才可以使用(由于近战兵数值文件的攻击距离默认为0,但实际上不是0)
if (atkDistance < checkDistance.Value)
atkDistance = checkDistance.Value;
//Debug.Log("技能:" + stateNext.Value + "实际距离:" + checkDistance.Value + "getDistance:" + atkDistance);
}
else
atkDistance = checkDistance.Value;
//Debug.Log("实际距离:" + atkDistance);
}
public override TaskStatus OnUpdate()
{
//每次进入 Update 之前都要把将要返回的值设置为空
returnedObject.Value = null;
if (lastTarget == null)
{
hitColliders = GetColliders();
if (hitColliders.Length > 0)
{
lastTarget = hitColliders.GetMin(t => Vector3.Distance(t.transform.position, transform.position));
returnedObject.Value = lastTarget.gameObject;
transform.LookAtAroundY(returnedObject.Value.transform);
return TaskStatus.Success;
}
else
{
return TaskStatus.Failure;
}
}
//如果上一次获取到目标
else
{
//判断这一次能否获取到上一次的目标
hitColliders = GetColliders();
if (hitColliders.Length>0)
{
//如果获取到了就返回
if (hitColliders.Contains(lastTarget))
{
returnedObject.Value = lastTarget.gameObject;
}
//如果没有获取到就返回可以返回的距离最近的一个
else
{
returnedObject.Value = hitColliders.GetMin(t => Vector3.Distance
(transform.position, t.transform.position)).gameObject;
}
if (returnedObject.Value != null)
{
transform.LookAtAroundY(returnedObject.Value.transform);
return TaskStatus.Success;
}
else
{
return TaskStatus.Failure;
}
}
else
{
return TaskStatus.Failure;
}
}
}
public Collider[] GetColliders()
{
if (this.gameObject.tag == "AirForce")
{
hitColliders = Physics.OverlapSphere(centerPos, atkDistance, objectLayerMask.Value);
}
else if (gameObject.tag != "AirForce")
{
hitColliders = Physics.OverlapSphere(centerPos, atkDistance, 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();
return hitColliders;
}
}
}