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().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(); 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(); // 随机优先级,降低 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 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 = 3f; // 最小距离 public SharedFloat maxDistance = 8f; // 最大距离 public SharedFloat minAngle = 30f; // 最小角度(度) public SharedFloat maxAngle = 40f; // 最大角度(度) public SharedFloat attackRange = 10f; // 攻击范围 public SharedFloat attackInterval = 1f; // 攻击间隔(秒) //分散参数 public SharedFloat minSeparation = 2.5f;//最小间距 public int maxPlacementAttempts = 15;//最大尝试次数 private NavMeshAgent agent; private Transform playerTransform; private bool hasReachedDestination; private float lastAttackTime; private Enemy1 enemyComponent; private Vector3 targetPosition; //静态列表跟踪所有小兵位置 private static List occupiedPositions = new List(); public override void OnStart() { agent = GetComponent(); playerTransform = GameManager.Ins.player.transform; enemyComponent = GetComponent(); agent.stoppingDistance = 0.1f; agent.autoBraking = true; // 计算分散位置 targetPosition = CalculateFrontPosition(); agent.SetDestination(targetPosition); hasReachedDestination = false; //添加到已占用位置列表 occupiedPositions.Add(targetPosition); //初始化攻击计时器 lastAttackTime = Time.time; } public override void OnEnd() { //从占用列表中移除 occupiedPositions.Remove(targetPosition); } public override TaskStatus OnUpdate() { // 1. 如果还没到达目标位置,继续移动 if (!hasReachedDestination) { if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance) { // 到达后停止移动并保持原位 agent.isStopped = true; hasReachedDestination = true; } else { return TaskStatus.Running; } } // 2. 持续面向玩家 FacePlayer(); // 3. 在攻击范围内且冷却结束,执行攻击 if (CanAttackPlayer()) { AttackPlayer(); } // 4. 保持在原位执行攻击 return TaskStatus.Running; } private Vector3 CalculateFrontPosition() { int attempts = 0; Vector3 candidatePosition; bool positionValid; do { // 1. 生成候选位置 candidatePosition = CalculateRandomFrontPosition(); // 2. 检查位置是否有效 positionValid = IsPositionValid(candidatePosition); // 3. 增加尝试计数 attempts++; } while (!positionValid && attempts < maxPlacementAttempts); return candidatePosition; } private Vector3 CalculateRandomFrontPosition() { //获取玩家前方向 Vector3 playerForward = playerTransform.forward; playerForward.y = 0; playerForward.Normalize(); //随机角度(30-40°之间,随机左右) float randomAngle = Random.Range(minAngle.Value, maxAngle.Value); randomAngle *= (Random.value > 0.5f) ? 1 : -1;//随机左右方向 //随机距离 float randomDistance = Random.Range(minDistance.Value, maxDistance.Value); //计算方向向量 Quaternion rotation = Quaternion.Euler(0, randomAngle, 0); Vector3 direction = rotation * playerForward; //计算目标位置 Vector3 targetPosition = playerTransform.position + direction * randomDistance; //NavMesh采样 if(NavMesh.SamplePosition(targetPosition,out NavMeshHit hit, 2f, NavMesh.AllAreas)) { return hit.position; } return playerTransform.position + playerForward * minDistance.Value; } private bool IsPositionValid(Vector3 position) { //检查是否与其他小怪距离太近 foreach(Vector3 occupiedPos in occupiedPositions) { if (Vector3.Distance(position, occupiedPos) < minSeparation.Value) { return false; } } //检查与玩家的距离 float distanceToPlayer = Vector3.Distance(position, playerTransform.position); if (distanceToPlayer < minDistance.Value || distanceToPlayer > maxDistance.Value) { return false; } //检查NavMesh是否可以行走 if(!NavMesh.SamplePosition(position,out NavMeshHit hit, 0.5f, NavMesh.AllAreas)) { return false; } return true; } private void FacePlayer() { //平滑转向玩家 Vector3 direction = (playerTransform.position - transform.position).normalized; direction.y = 0; if (direction != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(direction); transform.rotation = Quaternion.Slerp( transform.rotation, targetRotation, Time.deltaTime * 5f ); } } private bool CanAttackPlayer() { // 1. 检查距离 float distance = Vector3.Distance(transform.position, playerTransform.position); if (distance > attackRange.Value) return false; // 2. 检查攻击冷却 if (Time.time - lastAttackTime < attackInterval.Value) return false; //// 3. 检查视线(可选) //RaycastHit hit; //if (Physics.Raycast(transform.position, playerTransform.position - transform.position, out hit, attackRange.Value)) //{ // return hit.transform == playerTransform; //} return true; ; } private void AttackPlayer() { if (enemyComponent != null) { enemyComponent.Attack(); lastAttackTime = Time.time; } } // 调试可视化 private void OnDrawGizmosSelected() { if (!Application.isPlaying || playerTransform == null) return; Gizmos.color = Color.cyan; Gizmos.DrawSphere(agent.destination, 0.5f); // 绘制扇形区域 DrawAngleSector(minAngle.Value, maxAngle.Value, minDistance.Value, maxDistance.Value); } private void DrawAngleSector(float minDeg, float maxDeg, float minDist, float maxDist) { Gizmos.color = new Color(1, 0.5f, 0, 0.3f); // 绘制左右扇形 DrawSingleSector(minDeg, maxDeg, minDist, maxDist); DrawSingleSector(-minDeg, -maxDeg, minDist, maxDist); } private void DrawSingleSector(float startDeg, float endDeg, float minDist, float maxDist) { Vector3 playerPos = playerTransform.position; Vector3 baseDir = playerTransform.forward; int segments = 10; float angleStep = (endDeg - startDeg) / segments; // 绘制外弧 for (int i = 0; i <= segments; i++) { float angle = startDeg + i * angleStep; Quaternion rot = Quaternion.Euler(0, angle, 0); Vector3 dir = rot * baseDir; Vector3 point = playerPos + dir * maxDist; if (i > 0) { float prevAngle = startDeg + (i - 1) * angleStep; Quaternion prevRot = Quaternion.Euler(0, prevAngle, 0); Vector3 prevDir = prevRot * baseDir; Vector3 prevPoint = playerPos + prevDir * maxDist; Gizmos.DrawLine(prevPoint, point); } } // 绘制内弧 for (int i = 0; i <= segments; i++) { float angle = startDeg + i * angleStep; Quaternion rot = Quaternion.Euler(0, angle, 0); Vector3 dir = rot * baseDir; Vector3 point = playerPos + dir * minDist; if (i > 0) { float prevAngle = startDeg + (i - 1) * angleStep; Quaternion prevRot = Quaternion.Euler(0, prevAngle, 0); Vector3 prevDir = prevRot * baseDir; Vector3 prevPoint = playerPos + prevDir * minDist; Gizmos.DrawLine(prevPoint, point); } } // 绘制连接线 Vector3 startMin = playerPos + Quaternion.Euler(0, startDeg, 0) * baseDir * minDist; Vector3 startMax = playerPos + Quaternion.Euler(0, startDeg, 0) * baseDir * maxDist; Vector3 endMin = playerPos + Quaternion.Euler(0, endDeg, 0) * baseDir * minDist; Vector3 endMax = playerPos + Quaternion.Euler(0, endDeg, 0) * baseDir * maxDist; Gizmos.DrawLine(startMin, startMax); Gizmos.DrawLine(endMin, endMax); } } /// /// 检测boss是否到阶段血量 /// public class CheckEnemyHp : Conditional { public SharedFloat hpIndex; public override TaskStatus OnUpdate() { var curHpIndex = transform.GetComponent().health / transform.GetComponent().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(); } 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(); if (curEnemy.userSillIng) return TaskStatus.Success; return TaskStatus.Failure; } } public class BossIsAttackState : Conditional { public override TaskStatus OnUpdate() { Enemy curEnemy = GetComponent(); 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(); if (curEnemy) { curEnemy.isShield = false; curEnemy.shieldObj.SetActive(false); } return TaskStatus.Success; } return TaskStatus.Running; } } public class IsBossGunDie : Conditional { public SharedString GunIdStr; private List gunIdList; public override void OnStart() { base.OnStart(); gunIdList = new List(); string[] str = GunIdStr.Value.Split(','); foreach (var item in str) { gunIdList.Add(int.Parse(item)); } } public override TaskStatus OnUpdate() { Enemy curEnemy = GetComponent(); 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(); 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(); 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(); 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; } }