Files
FutureMen/Assets/Scripts/Actions.cs
2025-07-22 11:42:28 +08:00

577 lines
17 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 System.Collections.Generic;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using DragonLi.Core;
using DragonLi.Frame;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;
public class CheckEnemyState : Conditional
{
public SharedInt curEnemyState;
public override TaskStatus OnUpdate()
{
if (transform.GetComponent<Enemy>().enemyState == (EnemyState)curEnemyState.Value)
{
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
}
public class SetEnemyState : Action
{
public SharedInt curEnemyState;
public override TaskStatus OnUpdate()
{
var enemy= transform.GetComponent<Enemy>();
if (enemy != null)
{
enemy.enemyState = (EnemyState)curEnemyState.Value;
return TaskStatus.Success;
}
return base.OnUpdate();
}
}
public class MoveToPlayerNearby3 : Action
{
public SharedGameObject self;
public SharedFloat minRange = new SharedFloat { Value = 3f };
public SharedFloat maxRange = new SharedFloat { Value = 10f };
public SharedFloat avoidRadius = new SharedFloat { Value = 2f };
public SharedLayerMask enemyLayer;
public int maxAttempts = 15;
private NavMeshAgent agent;
private Vector3 targetPosition;
public override void OnStart()
{
base.OnStart();
agent = GetComponent<NavMeshAgent>();
// 随机优先级,降低 agent 之间争路
agent.avoidancePriority = Random.Range(0, 100);
agent.obstacleAvoidanceType = ObstacleAvoidanceType.HighQualityObstacleAvoidance;
agent.autoBraking = false;
Transform player = GameManager.Ins.player.transform;
targetPosition = FindValidPosition(player);
if (agent.isActiveAndEnabled)
agent.SetDestination(targetPosition);
}
public override TaskStatus OnUpdate()
{
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
return TaskStatus.Success;
return TaskStatus.Running;
}
private Vector3 FindValidPosition(Transform player)
{
// 从 Manager 或其他地方拿到所有敌人的引用列表
List<Enemy> allEnemies = GameManager.Ins.curEnemyList;
Vector3 bestPos = Vector3.zero;
float bestScore = -1f;
for (int i = 0; i < maxAttempts; i++)
{
// 1. 随机取点
Vector3 dir = Random.insideUnitSphere;
dir.y = 0;
dir.Normalize();
float dist = Random.Range(minRange.Value, maxRange.Value);
Vector3 raw = player.position + dir * dist;
// 2. NavMesh 采样
if (!NavMesh.SamplePosition(raw, out NavMeshHit hit, 2f, NavMesh.AllAreas))
continue;
var cand = hit.position;
// 3. 与玩家最小距离硬性筛掉
if (Vector3.Distance(cand, player.position) < minRange.Value)
continue;
// 4. 计算到其它每个敌人的最小距离
float minDistToOthers = float.MaxValue;
foreach (var go in allEnemies)
{
if (go == null || go == self.Value) continue;
float d = Vector3.Distance(cand, go.transform.position);
minDistToOthers = Mathf.Min(minDistToOthers, d);
if (minDistToOthers < avoidRadius.Value)
break; // 太近了,提前跳出
}
//5.如果通过了 avoidRadius就直接返回
if (minDistToOthers >= avoidRadius.Value)
return cand;
// 6. 否则记录最佳分数点
if (minDistToOthers > bestScore)
{
bestScore = minDistToOthers;
bestPos = cand;
}
}
// 如果所有尝试都没严格过,也选“最远离群”的那个
if (bestScore > 0f)
return bestPos;
// 兜底:玩家前方一个安全距离
Vector3 fb = player.position + player.forward * minRange.Value;
if (NavMesh.SamplePosition(fb, out NavMeshHit fh, 2f, NavMesh.AllAreas))
return fh.position;
// 最后随机返回一个点
return player.position + Random.insideUnitSphere * (minRange.Value + 0.5f);
}
// 调试画出可行区域
void OnDrawGizmosSelected()
{
if (self == null || self.Value != gameObject) return;
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(targetPosition, 0.2f);
}
}
public class MoveToPlayerFront : Action
{
public SharedFloat minDistance = new SharedFloat { Value = 3f };
public SharedFloat maxDistance = new SharedFloat { Value = 5f };
public SharedFloat minAngle = new SharedFloat { Value = 30f };
public SharedFloat maxAngle = new SharedFloat { Value = 40f };
// 添加静态字典记录每个怪物的移动状态
private static Dictionary<GameObject, bool> hasMoved = new Dictionary<GameObject, bool>();
private NavMeshAgent agent;
private bool hasReachedPosition;
private Vector3 finalPosition; // 存储最终位置
// 添加静态字典记录每个敌人的移动状态
private static Dictionary<int, bool> hasMovedDict = new Dictionary<int, bool>();
private int instanceID;
public override void OnAwake()
{
instanceID = gameObject.GetInstanceID();
}
public override void OnStart()
{
// 检查是否已经移动过
if (hasMovedDict.ContainsKey(instanceID) && hasMovedDict[instanceID])
{
hasReachedPosition = true;
return;
}
agent = GetComponent<NavMeshAgent>();
agent.autoBraking = true;
agent.stoppingDistance = 0.1f;
// 计算玩家正前方扇形区域内的随机位置
finalPosition = CalculateFrontPosition();
if (agent.isActiveAndEnabled)
{
agent.SetDestination(finalPosition);
hasReachedPosition = false;
}
}
public override TaskStatus OnUpdate()
{
// 如果已经移动过,直接返回成功
if (hasMovedDict.ContainsKey(instanceID) && hasMovedDict[instanceID])
{
return TaskStatus.Success;
}
if (hasReachedPosition)
{
//标记为已移动
hasMovedDict[instanceID] = true;
return TaskStatus.Success;
}
// 检查是否到达目标位置
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
{
// 完全停止导航系统
agent.isStopped = true;
agent.ResetPath();
agent.enabled = false; // 禁用导航组件
// 固定位置
transform.position = finalPosition;
// 添加物理约束(如果有刚体)
Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.isKinematic = true;
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
// 标记为已移动
hasMoved[gameObject] = true;
hasReachedPosition = true;
return TaskStatus.Success;
}
return TaskStatus.Running;
}
private Vector3 CalculateFrontPosition()
{
Transform player = GameManager.Ins.player.transform;
Vector3 playerPos = player.position;
Vector3 playerForward = player.forward;
// 随机角度 (30-40度之间随机左右侧)
float randomAngle = Random.Range(minAngle.Value, maxAngle.Value) *
(Random.value > 0.5f ? 1f : -1f);
// 随机距离
float randomDistance = Random.Range(minDistance.Value, maxDistance.Value);
// 计算方向向量 (玩家前方旋转随机角度)
Quaternion rotation = Quaternion.Euler(0f, randomAngle, 0f);
Vector3 direction = rotation * playerForward;
// 计算目标位置
Vector3 targetPos = playerPos + direction * randomDistance;
// 确保位置在NavMesh上
if (NavMesh.SamplePosition(targetPos, out NavMeshHit hit, 5f, NavMesh.AllAreas)) // 增加采样范围
{
// 确保不会出现在半空中
if (Mathf.Abs(hit.position.y - playerPos.y) > 2f)
{
// 如果高度差太大,调整到玩家高度
return new Vector3(hit.position.x, playerPos.y, hit.position.z);
}
return hit.position;
}
// 如果采样失败,使用玩家位置(确保不会出现在半空中)
return playerPos + Vector3.up * 0.1f; // 稍微高于地面
}
// 当敌人被销毁时清理记录
public override void OnEnd()
{
if (hasMovedDict.ContainsKey(instanceID))
{
hasMovedDict.Remove(instanceID);
}
}
}
public class SetAttackState : Action
{
public SharedBool attackState;
// 添加静态字典记录每个敌人的攻击状态
private static Dictionary<int, bool> attackStateSet = new Dictionary<int, bool>();
private int instanceID;
public override void OnAwake()
{
instanceID = gameObject.GetInstanceID();
}
public override TaskStatus OnUpdate()
{
// 如果已经设置过攻击状态,直接返回成功
if (attackStateSet.ContainsKey(instanceID) && attackStateSet[instanceID])
{
return TaskStatus.Success;
}
Enemy1 enemy = GetComponent<Enemy1>();
if (enemy != null)
{
enemy.SetAttackState(attackState.Value);
// 标记为已设置
attackStateSet[instanceID] = true;
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
// 当敌人被销毁时清理记录
public override void OnEnd()
{
if (attackStateSet.ContainsKey(instanceID))
{
attackStateSet.Remove(instanceID);
}
}
}
/// <summary>
/// 检测boss是否到阶段血量
/// </summary>
public class CheckEnemyHp : Conditional
{
public SharedFloat hpIndex;
public override TaskStatus OnUpdate()
{
var curHpIndex = transform.GetComponent<Enemy>().health/transform.GetComponent<Enemy>().maxHealth;
if (curHpIndex <= hpIndex.Value)
{
return TaskStatus.Success;
}
return TaskStatus.Failure;
}
}
public class UserSkill : Action
{
public Enemy EnemyObj;
public SharedFloat hpIndex;
public override void OnStart()
{
base.OnStart();
EnemyObj = transform.GetComponent<Enemy>();
}
public override TaskStatus OnUpdate()
{
if (EnemyObj == null)
return TaskStatus.Failure;
var curHpIndex = EnemyObj.health/EnemyObj.maxHealth;
if (curHpIndex <= hpIndex.Value)
{
EnemyObj.userSillIng = false;
}
var isUserIng = EnemyObj.userSillIng;
if (isUserIng) return TaskStatus.Running;
return TaskStatus.Success;
}
}
public class BossIsUserSkill : Conditional
{
public override TaskStatus OnUpdate()
{
Enemy curEnemy = GetComponent<Enemy>();
if(curEnemy.userSillIng)
return TaskStatus.Success;
return TaskStatus.Failure;
}
}
public class BossIsAttackState : Conditional
{
public override TaskStatus OnUpdate()
{
Enemy curEnemy = GetComponent<Enemy>();
if(curEnemy.isAttack)
return TaskStatus.Success;
return TaskStatus.Failure;
}
}
public class BossIsQteAttack : Conditional
{
public SharedBool isQteAttack;
public override TaskStatus OnUpdate()
{
if(isQteAttack.Value)
return TaskStatus.Success;
return TaskStatus.Failure;
}
}
public class WaitAllPetDie : Action
{
public override TaskStatus OnUpdate()
{
if (GameManager.Ins.IsAllBossPetDie())
{
Enemy curEnemy = GetComponent<Enemy>();
if (curEnemy)
{
curEnemy.isShield = false;
curEnemy.shieldObj.SetActive(false);
}
return TaskStatus.Success;
}
return TaskStatus.Running;
}
}
public class IsBossGunDie : Conditional
{
public SharedString GunIdStr;
private List<int> gunIdList;
public override void OnStart()
{
base.OnStart();
gunIdList=new List<int>();
string[] str=GunIdStr.Value.Split(',');
foreach (var item in str)
{
gunIdList.Add(int.Parse(item));
}
}
public override TaskStatus OnUpdate()
{
Enemy curEnemy = GetComponent<Enemy>();
foreach (var id in gunIdList)
{
if (curEnemy.components[id] != null && !curEnemy.components[id].isDead)
{
return TaskStatus.Failure;
}
}
return TaskStatus.Success;
}
}
public class BossQteAttack : Action
{
private Enemy _curEnemy;
public float QteTime;
public SharedBool IsQteAttacked;
public SharedBool IsStopQteAttacked;
private float _currentQteTime;
public override void OnStart()
{
base.OnStart();
//QteTime = 5;
_curEnemy = GetComponent<Enemy>();
if (_curEnemy)
{
IsQteAttacked.Value = false;
_curEnemy.StartQteAttack();
}
}
public override TaskStatus OnUpdate()
{
_currentQteTime += Time.deltaTime;
if (_curEnemy.components[^1] != null && _curEnemy.components[^1].isDead)
{
IsStopQteAttacked.Value =true;
_curEnemy.StopQteAttack();
return TaskStatus.Success;
}
if (_currentQteTime >= QteTime)
{
IsStopQteAttacked.Value =false;
_curEnemy.ThreeAttackMode();
return TaskStatus.Success;
}
return TaskStatus.Running;
}
}
public class CooldownTick : Action
{
public SharedFloat cooldownTimer;
public SharedBool skillReady;
public SharedFloat hp;
public override TaskStatus OnUpdate()
{
Enemy curEnemy = GetComponent<Enemy>();
var curHpIndex = curEnemy.health/curEnemy.maxHealth;
if (curHpIndex <= hp.Value)
{
skillReady.Value = false;
cooldownTimer.Value = 15;
return TaskStatus.Success;
}
if (cooldownTimer.Value > 0f) {
cooldownTimer.Value -= Time.deltaTime;
if (cooldownTimer.Value <= 0f) {
cooldownTimer.Value = 0f;
skillReady.Value = true;
return TaskStatus.Success;
}
}
return TaskStatus.Running;
}
}
public class ReleaseSkill : Action
{
public SharedFloat cooldownTimer;
public SharedBool skillReady;
public float skillCooldown = 15f;
public override void OnStart()
{
base.OnStart();
// 进入冷却
cooldownTimer.Value = skillCooldown;
skillReady.Value = false;
}
}
public class NormalAttack : Action
{
private Enemy1 enemy;
public override void OnAwake()
{
enemy = GetComponent<Enemy1>();
}
public override void OnStart()
{
//// 你的普攻逻辑
//Debug.Log("执行普攻");
//Enemy curEnemy = GetComponent<Enemy>();
//curEnemy.Attack();
if(enemy != null && !enemy.isAttack)
{
enemy.Attack();
}
}
}
public class IsSkillReady : Conditional
{
public SharedBool skillReady;
public override TaskStatus OnUpdate()
{
return skillReady.Value ? TaskStatus.Success : TaskStatus.Failure;
}
}
public class ConditionalStopQte : Conditional
{
public SharedBool IsStopQteAttack;
public override TaskStatus OnUpdate()
{
if(!IsStopQteAttack.Value)
return TaskStatus.Success;
return TaskStatus.Failure;
}
}