Files
FutureMen2/Assets/_FutureMen2/Scripts/Actions/FrogBossActions.cs
2026-01-29 19:09:02 +08:00

459 lines
12 KiB
C#
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
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;
/// <summary>
/// 吐舌攻击
/// </summary>
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<FrogBoss>();
isFinished = false;
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);
boss.Idle();
// 5⃣ 攻击结束 → 后撤(🔥关键)
yield return RetreatFromPlayer();
boss.Idle();
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) > 1f)
{
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
targetRot,
rotateSpeed * Time.deltaTime
);
yield return null;
}
}
}
/// <summary>
/// 毒液弹攻击
/// </summary>
public class FrogPoisonAttack : Action
{
private FrogBoss boss;
private bool isFinished;
private float rotateSpeed = 360f; // 每秒旋转角度
public override void OnStart()
{
boss = GetComponent<FrogBoss>();
isFinished = false;
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<PoisonBall>().Launch(
boss.mouthPoint.position,
target,
boss.Data.Atk_2dot,
boss.Data.Atk_2Time
);
yield return new WaitForSeconds(0.5f);
}
boss.Idle();
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;
}
}
}
/// <summary>
/// 吐卵 召唤小虫怪
/// </summary>
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<FrogBoss>();
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<FrogBugEnemy>();
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<FrogBoss>();
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);
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
targetRot,
rotateSpeed * 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
}