(client) feat:实现热重载,实现多维度,实现武器,实现掉落物,实现状态UI,实现攻击AI (#44)
Co-authored-by: zzdxxz <2079238449@qq.com> Co-committed-by: zzdxxz <2079238449@qq.com>
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Base;
|
||||
using Data;
|
||||
using Managers;
|
||||
using Prefab;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
@ -19,6 +21,7 @@ namespace AI
|
||||
{
|
||||
entity = target;
|
||||
}
|
||||
|
||||
public bool Update()
|
||||
{
|
||||
if (!Running)
|
||||
@ -27,11 +30,13 @@ namespace AI
|
||||
timeoutTicks--;
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void StopJob()
|
||||
{
|
||||
timeoutTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class WanderJob : JobBase
|
||||
{
|
||||
public override void StartJob(Entity.Entity target)
|
||||
@ -55,6 +60,7 @@ namespace AI
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IdleJob : JobBase
|
||||
{
|
||||
override public void StartJob(Entity.Entity target)
|
||||
@ -62,10 +68,12 @@ namespace AI
|
||||
base.StartJob(target);
|
||||
timeoutTicks = 500;
|
||||
}
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MoveJob : JobBase
|
||||
{
|
||||
protected override void UpdateJob()
|
||||
@ -74,222 +82,203 @@ namespace AI
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackPlayerJob : JobBase
|
||||
public class AttackJob : JobBase
|
||||
{
|
||||
private EntityPrefab currentTarget; // 当前追踪的目标玩家
|
||||
private LinkedList<EntityPrefab> players; // 玩家实体列表
|
||||
private Entity.Entity attackTarget;
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
// StartJob 方法:用于初始化任务,寻找初始攻击目标
|
||||
override public void StartJob(Entity.Entity performerEntityContext) // 参数名更明确,通常是发起任务的实体
|
||||
{
|
||||
base.StartJob(target);
|
||||
UpdateTarget();
|
||||
base.StartJob(performerEntityContext);
|
||||
// 1. 任务执行者自身有效性检查
|
||||
if (entity == null)
|
||||
{
|
||||
StopJob(); // 调用StopJob来结束任务
|
||||
return;
|
||||
}
|
||||
|
||||
attackTarget = FindNewHostileTarget();
|
||||
|
||||
if (attackTarget == null)
|
||||
{
|
||||
StopJob(); // 调用StopJob来结束任务
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!currentTarget || currentTarget.entity.IsDead)
|
||||
// 1. 任务执行者的基本检查
|
||||
if (entity == null || entity.IsDead)
|
||||
{
|
||||
// 如果当前目标无效,则重新查找最近的玩家
|
||||
UpdateTarget();
|
||||
StopJob();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTarget)
|
||||
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
|
||||
{
|
||||
var targetPosition = new Vector3(currentTarget.Position.x, currentTarget.Position.y, 0);
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTarget()
|
||||
/// <summary>
|
||||
/// 查找执行实体最近的敌对目标。
|
||||
/// </summary>
|
||||
/// <returns>找到的敌对实体,如果没有则返回null。</returns>
|
||||
private Entity.Entity FindNewHostileTarget()
|
||||
{
|
||||
players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
if (!entity) return null;
|
||||
return EntityManage.Instance.FindNearestEntityByRelation(
|
||||
entity.currentDimensionId, // 搜索维度ID
|
||||
entity.entityPrefab, // 执行实体的Prefab ID,用于关系判断
|
||||
Relation.Hostile)?.entity; // 寻找敌对关系的目标
|
||||
}
|
||||
}
|
||||
|
||||
if (players == null || players.Count == 0)
|
||||
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)
|
||||
{
|
||||
currentTarget = null;
|
||||
StopJob();
|
||||
return;
|
||||
}
|
||||
currentTarget = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
attackTarget = FindNewHostileTarget();
|
||||
if (attackTarget == null)
|
||||
{
|
||||
if (player.entity.IsDead) continue; // 跳过无效玩家
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
}
|
||||
StopJob();
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
}
|
||||
public class AttackPlayerJob : JobBase
|
||||
{
|
||||
private EntityPrefab player;
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!player || !IsPlayerInRange())
|
||||
// 1. 任务执行者的基本检查
|
||||
if (entity == null || entity.IsDead)
|
||||
{
|
||||
StopJob(); // 如果玩家不在范围内,停止攻击工作
|
||||
return;
|
||||
}
|
||||
entity.TryAttack();
|
||||
}
|
||||
|
||||
private bool IsPlayerInRange()
|
||||
{
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
return distance <= entity.attributes.attackRange;
|
||||
}
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
{
|
||||
base.StartJob(target);
|
||||
|
||||
// 查找最近的玩家作为目标
|
||||
LinkedList<EntityPrefab> players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
player = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (!IsPlayerValid(player)) continue;
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
private bool IsPlayerValid(EntityPrefab player)
|
||||
{
|
||||
return player && !player.entity.IsDead;
|
||||
}
|
||||
}
|
||||
public class RangedAttackJob : JobBase
|
||||
{
|
||||
private EntityPrefab player;
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!player || !IsPlayerValid(player))
|
||||
{
|
||||
StopJob(); // 如果当前目标无效,停止工作
|
||||
StopJob();
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance <= entity.attributes.attackRange)
|
||||
// 2. 攻击目标检查
|
||||
if (attackTarget == null || attackTarget.IsDead)
|
||||
{
|
||||
// 如果在攻击范围内,进行攻击
|
||||
entity.TryAttack();
|
||||
}
|
||||
else if (distance > entity.attributes.attackRange && distance < MaxTrackDistance)
|
||||
{
|
||||
// 如果距离过远,靠近目标
|
||||
MoveTowardsPlayer();
|
||||
}
|
||||
else if (distance < entity.attributes.attackRange)
|
||||
{
|
||||
// 如果距离过近,远离目标
|
||||
MoveAwayFromPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveTowardsPlayer()
|
||||
{
|
||||
Vector3 targetPosition = new Vector3(player.Position.x, player.Position.y, 0);
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
|
||||
private void MoveAwayFromPlayer()
|
||||
{
|
||||
Vector3 direction = entity.Position - player.Position;
|
||||
direction.Normalize();
|
||||
Vector3 targetPosition = entity.Position + direction * RetreatDistance;
|
||||
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
{
|
||||
base.StartJob(target);
|
||||
|
||||
// 查找最近的玩家作为目标
|
||||
var players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
player = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (!IsPlayerValid(player)) continue;
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
attackTarget = FindNewHostileTarget(); // 尝试寻找新的攻击目标
|
||||
if (attackTarget == null)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
StopJob();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
// 获取武器和其属性
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPlayerValid(EntityPrefab player)
|
||||
/// <summary>
|
||||
/// 查找执行实体最近的敌对目标。
|
||||
/// </summary>
|
||||
/// <returns>找到的敌对实体,如果没有则返回null。</returns>
|
||||
private Entity.Entity FindNewHostileTarget()
|
||||
{
|
||||
return player && !player.entity.IsDead;
|
||||
if (!entity) return null;
|
||||
return EntityManage.Instance.FindNearestEntityByRelation(
|
||||
entity.currentDimensionId,
|
||||
entity.entityPrefab,
|
||||
Relation.Hostile)?.entity;
|
||||
}
|
||||
|
||||
private const float MaxTrackDistance = 20f; // 最大追踪距离
|
||||
private const float RetreatDistance = 3f; // 后退距离
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user