using System.Collections; using System.Collections.Generic; using BehaviorDesigner.Runtime.Tasks; using DragonLi.Behaviour; using UnityEngine; using Action = BehaviorDesigner.Runtime.Tasks.Action; using System.Collections; using BehaviorDesigner.Runtime.Tasks; using DG.Tweening; using Pathfinding; using UnityEngine; /// /// 吐舌攻击 /// public class FrogBleepAttack : Action { private FrogBoss boss; private bool isFinished; public float tongueSpeed = 25f; public float damage = 30f; private float rotateSpeed = 360f; // 每秒旋转角度 public float retreatDistance = 8f; public float retreatSpeed = 6f; public override void OnStart() { boss = GetComponent(); isFinished = false; // 标记技能正在执行,防止被中断 boss.isSkillExecuting = true; boss.ResetSkill1(); boss.StartCoroutine(SkillRoutine()); } public override TaskStatus OnUpdate() { return isFinished ? TaskStatus.Success : TaskStatus.Running; } IEnumerator SkillRoutine() { // 1️⃣ 不在攻击范围 → 先移动 if (!boss.IsPlayerInTongueRange()) { yield return boss.StartCoroutine( boss.MoveToTongueRange() ); } // 2️⃣ 锁定玩家位置 Vector3 lockPos = GameManager.Ins.player.transform.position; // 3️⃣ 面向玩家 yield return RotateToPlayer(); // 4️⃣ 吐舌 boss.TongueAttack(); yield return new WaitForSeconds(3f); float dis = Vector3.Distance(boss.mouthPoint.position, lockPos); yield return new WaitForSeconds(dis / tongueSpeed); // 5️⃣ 吐舌结束立即返回待机(解决延迟问题) boss.Idle(); // 6️⃣ 后撤(并行执行,不阻塞动画切换) boss.StartCoroutine(RetreatFromPlayer()); // 等待后撤完成 yield return new WaitForSeconds(0.1f); boss.Idle(); boss.isSkillExecuting = false; // 标记技能执行结束 isFinished = true; } IEnumerator RetreatFromPlayer() { Transform player = GameManager.Ins.player.transform; Vector3 dir = (transform.position - player.position); dir.y = 0; dir.Normalize(); Vector3 targetPos = transform.position + dir * retreatDistance; NNInfo nn = AstarPath.active.GetNearest(targetPos); if (nn.node == null || !nn.node.Walkable) yield break; boss.aiPath.isStopped = false; boss.aiPath.maxSpeed = retreatSpeed; boss.aiPath.destination = nn.position; boss.Retreat(); while (!boss.aiPath.reachedEndOfPath) yield return null; boss.aiPath.isStopped = true; } IEnumerator RotateToPlayer() { Transform player = GameManager.Ins.player.transform; Vector3 dir = player.position - transform.position; dir.y = 0; if (dir.sqrMagnitude < 0.01f) yield break; Quaternion targetRot = Quaternion.LookRotation(dir); // 快速转向,减少等待时间 while (Quaternion.Angle(transform.rotation, targetRot) > 15f) { transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRot, 720f * Time.deltaTime ); yield return null; } // 立即对齐到目标方向 transform.rotation = targetRot; } } /// /// 毒液弹攻击 /// public class FrogPoisonAttack : Action { private FrogBoss boss; private bool isFinished; private float rotateSpeed = 360f; public override void OnStart() { boss = GetComponent(); isFinished = false; // 标记技能正在执行,防止被中断 boss.isSkillExecuting = true; boss.ResetSkill2(); boss.StartCoroutine(Attack()); } public override TaskStatus OnUpdate() { return isFinished ? TaskStatus.Success : TaskStatus.Running; } IEnumerator Attack() { yield return RotateToPlayer(); boss.Spit(); yield return new WaitForSeconds(0.5f); for (int i = 0; i < 3; i++) { Vector2 rand = Random.insideUnitCircle * 3f; Vector3 target = GameManager.Ins.player.transform.position + new Vector3(rand.x, 0, rand.y); GameObject ball = Object.Instantiate( boss.poisonBallPrefab, boss.mouthPoint.position, Quaternion.identity ); ball.GetComponent().Launch( boss.mouthPoint.position, target, boss.Data.Atk_2dot, boss.Data.Atk_2Time ); yield return new WaitForSeconds(0.5f); } boss.Idle(); boss.isSkillExecuting = false; // 标记技能执行结束 isFinished = true; } IEnumerator RotateToPlayer() { Transform player = GameManager.Ins.player.transform; Vector3 dir = player.position - transform.position; dir.y = 0; if (dir.sqrMagnitude < 0.01f) yield break; Quaternion targetRot = Quaternion.LookRotation(dir); while (Quaternion.Angle(transform.rotation, targetRot) > 1f) { transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRot, rotateSpeed * Time.deltaTime ); yield return null; } } } /// /// 吐卵 召唤小虫怪 /// public class FrogEggAttack : Action { private FrogBoss boss; private bool isFinished; [Header("Egg Config")] public int eggCount = 12; public float shootInterval = 0.15f; public float fanAngle = 60f; public float launchHeight = 3f; public float flightTime = 1.1f; public override void OnStart() { boss = GetComponent(); isFinished = false; boss.StartCoroutine(Attack()); } public override TaskStatus OnUpdate() { return isFinished ? TaskStatus.Success : TaskStatus.Running; } IEnumerator Attack() { boss.EggAttack(); yield return new WaitForSeconds(0.5f); boss.aiPath.isStopped = true; Transform firePoint = boss.eggFirePoint; // 嘴巴 float step = fanAngle / (eggCount - 1); float startAngle = -fanAngle * 0.5f; int eggBugCount = boss.GetAliveTentacles().Count; int insCount; if (eggBugCount >= 12) { boss.Idle(); isFinished = true; yield break; } insCount=12-eggBugCount; for (int i = 0; i < insCount; i++) { float angle = startAngle + step * i; // 🔥 轻微左右转头 boss.transform .DORotate( boss.transform.eulerAngles + new Vector3(0, angle * 0.15f, 0), 0.1f ); SpawnEgg(firePoint, angle); yield return new WaitForSeconds(shootInterval); } yield return new WaitForSeconds(0.5f); // 吐完跑向左右 Vector3 side = Random.value > 0.5f ? -boss.transform.right : boss.transform.right; boss.transform.DOMove(boss.transform.position + side * 5f, 1.5f); boss.Idle(); isFinished = true; } void SpawnEgg(Transform firePoint, float angle) { GameObject egg = Object.Instantiate( boss.eggPrefab, firePoint.position, Quaternion.identity ); FrogBugEnemy bug = egg.GetComponent(); bug.SetAtk(boss.Data.Atk_2,boss.Data.Hp_CS2); // 扇形方向 Vector3 dir = Quaternion.Euler(0, angle, 0) * boss.transform.forward; // 落点 Vector3 targetPos = firePoint.position + dir * Random.Range(3.5f, 5.5f); bug.LaunchEgg( firePoint.position, targetPos, launchHeight, flightTime, boss.Data.Atk_3, boss.Data.Hp_CS2 ); boss.frogBugs.Add(bug); } } public class FrogIdleMove : Action { [Header("Move Area (Fan Shape)")] private float moveRadius = 40f; // 可移动半径 private float moveAngle = 90f; // 扇形角度(玩家正前方) [Header("Stay")] public float stayTime = 1.5f; // 到点后停留时间 [Header("Rotate")] public float rotateSpeed = 360f; [Header("Distance To Player")] public float minPlayerDistance = 10f; // 离玩家最近距离 public float maxPlayerDistance = 25f; // 离玩家最远距离 [Header("Patrol")] public float minMoveStep = 10f; // 每次移动的最小距离(关键) private FrogBoss boss; private Transform player; private Vector3 curTarget; private float stayTimer; private bool isStaying; public override void OnStart() { boss = GetComponent(); player = GameManager.Ins.player.transform; isStaying = false; stayTimer = 0f; PickNewTarget(); } public override TaskStatus OnUpdate() { if (player == null) return TaskStatus.Running; // 🔥 有技能可放 → 退出待机行为 if (boss.HasAnySkillReady()) { boss.aiPath.isStopped = true; boss.Idle(); return TaskStatus.Failure; } FaceToPlayer(); HandleMove(); return TaskStatus.Running; } #region Core Logic void FaceToPlayer() { Vector3 dir = player.position - transform.position; dir.y = 0; if (dir.sqrMagnitude < 0.01f) return; Quaternion targetRot = Quaternion.LookRotation(dir); // 使用Slerp实现更平滑的转向 transform.rotation = Quaternion.Slerp( transform.rotation, targetRot, 5f * Time.deltaTime ); } void HandleMove() { float distToTarget = Vector3.Distance(transform.position, curTarget); // ===== 已到达目标点 ===== if (distToTarget < 0.4f) { boss.aiPath.isStopped = true; boss.Idle(); isStaying = true; stayTimer += Time.deltaTime; if (stayTimer >= stayTime) { PickNewTarget(); } return; } // ===== 正在移动 ===== isStaying = false; stayTimer = 0f; boss.Run(); boss.aiPath.isStopped = false; boss.aiPath.destination = curTarget; } void PickNewTarget() { stayTimer = 0f; isStaying = false; Vector3 bossPos = transform.position; Vector3 playerPos = player.position; const int MAX_TRY = 25; for (int i = 0; i < MAX_TRY; i++) { float baseAngle = Quaternion.LookRotation(bossPos - playerPos).eulerAngles.y; float angle = baseAngle + Random.Range(-120f, 120f); float radius = Random.Range(minPlayerDistance, maxPlayerDistance); Vector3 offset = Quaternion.Euler(0, angle, 0) * Vector3.forward * radius; Vector3 candidate = playerPos + offset; candidate.y = bossPos.y; // 强制不要靠太近当前位置 if (Vector3.Distance(candidate, bossPos) < minMoveStep) continue; NNInfo nn = AstarPath.active.GetNearest(candidate); if (nn.node != null && nn.node.Walkable) { curTarget = nn.position; boss.aiPath.isStopped = false; return; } } // fallback:侧移 Vector3 side = Vector3.Cross(Vector3.up, (bossPos - playerPos)).normalized; curTarget = bossPos + side * minMoveStep; boss.aiPath.isStopped = false; } #endregion }