Files
XMen/Assets/Scripts/Actions.cs
2025-07-02 17:56:55 +08:00

386 lines
11 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);
}
}
/// <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
{
public override void OnStart()
{
// 你的普攻逻辑
Debug.Log("执行普攻");
Enemy curEnemy = GetComponent<Enemy>();
curEnemy.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;
}
}