Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/AI/JobBase.cs

280 lines
8.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Data;
using Managers;
using UnityEngine;
namespace AI
{
public abstract class JobBase
{
public Entity.Entity entity;
protected int timeoutTicks = 300;
public bool Running => timeoutTicks > 0;
protected abstract void UpdateJob();
public virtual void StartJob(Entity.Entity target)
{
entity = target;
}
public bool Update()
{
if (!Running)
return false;
UpdateJob();
timeoutTicks--;
return true;
}
public virtual void StopJob()
{
timeoutTicks = 0;
}
}
public class WanderJob : JobBase
{
public override void StartJob(Entity.Entity target)
{
base.StartJob(target);
Vector3 move = new(Random.Range(-10, 10), Random.Range(-10, 10));
var targetPosition = entity.transform.position + move;
entity.SetTarget(targetPosition);
entity.IsChase = false;
}
protected override void UpdateJob()
{
entity.TryMove();
}
override public void StopJob()
{
base.StopJob();
entity.IsChase = true;
}
}
public class IdleJob : JobBase
{
override public void StartJob(Entity.Entity target)
{
base.StartJob(target);
timeoutTicks = 500;
}
protected override void UpdateJob()
{
}
}
public class MoveJob : JobBase
{
protected override void UpdateJob()
{
entity.TryMove();
}
}
public class AttackJob : JobBase
{
private Entity.Entity attackTarget;
// StartJob 方法:用于初始化任务,寻找初始攻击目标
override public void StartJob(Entity.Entity performerEntityContext) // 参数名更明确,通常是发起任务的实体
{
base.StartJob(performerEntityContext);
// 1. 任务执行者自身有效性检查
if (entity == null)
{
StopJob(); // 调用StopJob来结束任务
return;
}
attackTarget = FindNewHostileTarget();
if (attackTarget == null)
{
StopJob(); // 调用StopJob来结束任务
}
}
protected override void UpdateJob()
{
// 1. 任务执行者的基本检查
if (entity == null || entity.IsDead)
{
StopJob();
return;
}
if (attackTarget == null || attackTarget.IsDead)
{
attackTarget = FindNewHostileTarget(); // 尝试寻找新的攻击目标
if (attackTarget == null)
{
StopJob();
return;
}
}
var weapon = entity.GetCurrentWeapon();
var attackRange = 0f;
if (weapon != null)
{
attackRange = weapon.Attributes.attackRange;
}
var distanceSq = (entity.Position - attackTarget.Position).sqrMagnitude;
var effectiveAttackRangeSq = attackRange * attackRange; // 将攻击范围也平方
entity.SetTarget(attackTarget.Position);
if (weapon != null && distanceSq <= effectiveAttackRangeSq)
{
entity.TryAttack();
}
else
{
entity.TryMove();
}
}
/// <summary>
/// 查找执行实体最近的敌对目标。
/// </summary>
/// <returns>找到的敌对实体如果没有则返回null。</returns>
private Entity.Entity FindNewHostileTarget()
{
if (!entity) return null;
return EntityManage.Instance.FindNearestEntityByRelation(
entity.currentDimensionId, // 搜索维度ID
entity.entityPrefab, // 执行实体的Prefab ID用于关系判断
Relation.Hostile)?.entity; // 寻找敌对关系的目标
}
}
public class AdvancedAttackJob : JobBase
{
private Entity.Entity attackTarget;
// 常量用于配置远程AI行为的风筝参数
private const float KITING_THRESHOLD_MULTIPLIER = 0.5f; // 当目标距离小于 (攻击范围 * 此乘数) 时,远程单位开始尝试远离
private const float KITING_BUFFER_DISTANCE = 5.0f; // 当远程单位远离时,目标点会是当前位置向反方向偏移此距离
// StartJob 方法:用于初始化任务,寻找初始攻击目标
override public void StartJob(Entity.Entity performerEntityContext)
{
base.StartJob(performerEntityContext);
if (entity == null)
{
StopJob();
return;
}
attackTarget = FindNewHostileTarget();
if (attackTarget == null)
{
StopJob();
}
}
protected override void UpdateJob()
{
// 1. 任务执行者的基本检查
if (entity == null || entity.IsDead)
{
StopJob();
return;
}
// 2. 攻击目标检查
if (attackTarget == null || attackTarget.IsDead)
{
attackTarget = FindNewHostileTarget(); // 尝试寻找新的攻击目标
if (attackTarget == null)
{
StopJob();
return;
}
}
// 获取武器和其属性
var weapon = entity.GetCurrentWeapon();
var attackRange = 0f;
var isRangedWeapon = false; // 标识是否为远程武器
if (weapon != null)
{
if (weapon.Attributes != null)
{
attackRange = weapon.Attributes.attackRange;
}
// 使用 WeaponType 来判断武器类型,更明确
isRangedWeapon = weapon.Type == WeaponType.Ranged;
}
var distanceSq = (entity.Position - attackTarget.Position).sqrMagnitude;
var effectiveAttackRangeSq = attackRange * attackRange; // 将攻击范围平方
// ---- 核心AI行为决策 ( AdvancedAttackJob 的智能之处) ----
if (isRangedWeapon)
{
// 远程单位的风筝Kiting逻辑
var kitingDistance = attackRange * KITING_THRESHOLD_MULTIPLIER;
var kitingThresholdSq = kitingDistance * kitingDistance; // 过近距离的平方
// 1. 如果目标过于接近 (小于风筝阈值),尝试远离
if (distanceSq < kitingThresholdSq)
{
// 计算一个远离目标的点作为移动目标
var directionAway = (entity.Position - attackTarget.Position).normalized;
var fleePosition = entity.Position + directionAway * KITING_BUFFER_DISTANCE;
entity.SetTarget(fleePosition); // 设置远离点为新的移动目标
entity.TryMove(); // 优先执行移动操作以拉开距离
// 在此状态下不进行攻击,专注于 reposition
}
// 2. 如果目标在最佳攻击范围内 (即在风筝阈值和有效攻击范围之间),则停止移动并攻击
else if (distanceSq <= effectiveAttackRangeSq)
{
entity.SetTarget(entity.Position); // 设定目标为当前位置,使其停止移动,专注于攻击
entity.TryAttack();
}
// 3. 如果目标太远 (超出有效攻击范围),则移动靠近目标
else
{
entity.SetTarget(attackTarget.Position); // 设置目标位置为移动目标
entity.TryMove();
}
}
else // 近战单位或没有武器的单位
{
entity.SetTarget(attackTarget.Position);
if (weapon != null && distanceSq <= effectiveAttackRangeSq)
{
entity.TryAttack();
}
else
{
entity.TryMove();
}
}
}
/// <summary>
/// 查找执行实体最近的敌对目标。
/// </summary>
/// <returns>找到的敌对实体如果没有则返回null。</returns>
private Entity.Entity FindNewHostileTarget()
{
if (!entity) return null;
return EntityManage.Instance.FindNearestEntityByRelation(
entity.currentDimensionId,
entity.entityPrefab,
Relation.Hostile)?.entity;
}
}
}