Files
5.24BuYu/Assets/MyGame/aa/Game/FLZY/BOSS/BJEventBase.cs
T
2026-06-15 21:51:42 +08:00

425 lines
16 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 DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using Random = UnityEngine.Random;
/// <summary>
/// Boss 爆奖事件基类
/// 完整流程:死亡特效 → (多次 CallBJ 发红包 → 协程模拟金币逐个到达累分)→ EffEnd →
/// DelayThenScore → StartAddScore → MoveToSit → OnScoreEnd → JiaFen → die
///
/// CallBJ 调用 BigPrize 发包后,不立即累加分数,而是启动实例级协程,
/// 按金币飞行动画的节奏(0.5s 飞行 + 0.1s 间隔)逐个累分到 tMP_Score。
/// 每个 BJEventBase 实例独立管理自己的协程,支持多个 Boss 同时爆奖。
/// </summary>
public class BJEventBase : MonoBehaviour
{
public BJEndUI bjEndUI;
// ==================== Inspector 字段 ====================
[Header("分数")]
public bool useScorePanel = true; // 是否启用分数面板效果(关闭则跳过创建/滚分/移动)
public TextMeshProUGUI tMP_Score; // 分数显示文本(空则自动从预制体创建)
public GameObject scorePanelPrefab; // 分数版预制体(优先;空则用 Manager 自带 dieFXPrefabAwardTmp)
public int currScore; // 当前显示分数
public int maxScore; // 最终总分
[Header("红包")]
public LayeredPool layeredPool; // 红包分层对象池
public int scoreCount; // 红包分包数量(每次 CallBJ 发几个红包)
[Header("玩家")]
public int posID; // 击杀者座位ID
public int paoBei; // 炮倍
public int fishScore; // 底分
public int bJbeishu;
[Header("移动")]
public float showDelay = 3f; // 报完奖后停留时间(让玩家看清爆奖效果)
public float moveDuration = 0.5f; // UI 移动到座位的时间
public Ease moveEase = Ease.OutQuad; // 移动缓动曲线
public Vector3 startOffset = new Vector3(0, 0, -100);
public float bjEndUIOffsetY = 150f; // BJEndUI 在座位上方偏移(useScorePanel=false 时生效)
/// <summary>金币飞行到达面板的模拟时间(秒),应匹配 coinEvent DOMove 时长</summary>
public float coinFlyDuration = 0.5f;
/// <summary>金币之间到达的间隔时间(秒)</summary>
public float coinArrivalInterval = 0.1f;
// ==================== 回调 ====================
public Action JiaFen; // 加分完成回调(回加到玩家金币)
// ==================== 归属 ====================
public bool isSelf; // 是否是自己打出的爆奖(非自己只播特效不加分)
// ==================== 运行时状态 ====================
public int totalBJCount; // 计划爆奖总次数(由 boss 设置)
public int currBJCount; // 已爆奖次数
public bool isEffEnd; // 特效阶段已结束
public bool isScoreStart; // 加分阶段已开始
public bool isScoreEnd; // 加分阶段已结束
public bool isGoldEffectStart; // 红包特效阶段已开始
private SitPosData _targetSit; // 目标座位引用
private Coroutine _scoreCoroutine; // 加分协程
private GameObject _scoreBoard; // 自动创建的分数版(用于移动)
private int _activeCoinRoutines; // 正在运行的 CoinArrival 协程数量
public object obj;
// ==================== 初始化 ====================
/// <summary>
/// 初始化爆奖参数。由 Boss 的 DieFish → action_effect_DieAward 回调中调用。
/// </summary>
/// <param name="goldCount">服务器返回的总金币</param>
/// <param name="posID">击杀者座位 ID</param>
/// <param name="paobei">炮倍</param>
/// <param name="action">最终加分回调(回加到玩家金币)</param>
/// <param name="action1">初始化完成后的附加回调(如设置龙珠值)</param>
public virtual void Init(int goldCount, int posID, int paobei,int fishScore, int beishu,Action<int> action, Action action1 = null)
{
JiaFen = () => { action?.Invoke(0); };
maxScore = goldCount;
this.posID = posID;
this.fishScore = fishScore;
paoBei = paobei;
bJbeishu = beishu;
// 自动检测:tMP_Score 为空则按优先级创建分数版
EnsureScoreBoard();
// 初始位置:座位坐标 + 手调偏移
ApplySeatOffset();
// 是否是本地玩家自己打的
isSelf = _targetSit != null && _targetSit.user == GameManager.Instance.user.id;
action1?.Invoke();
}
private void Start()
{
totalBJCount = scoreCount;
}
/// <summary>
/// 自动补充分数版:tMP_Score 为空 → 优先用 scorePanelPrefab → 否则用 Manager 的 dieFXPrefabAwardTmp
/// 父对象挂在 CreateFishManager.Instance.currGameType.subPanelParent 下
/// </summary>
protected virtual void EnsureScoreBoard()
{
if (tMP_Score != null) return;
if (!useScorePanel) return;
if (CreateFishManager.Instance == null || CreateFishManager.Instance.currGameType == null) return;
var parent = CreateFishManager.Instance.currGameType.subPanelParent;
if (parent == null) return;
// 1. 优先用 Inspector 设置的预制体
GameObject prefab = scorePanelPrefab;
// 2. 否则用 Manager 自带的 dieFXPrefabAwardTmp
if (prefab == null)
prefab = CreateFishManager.Instance.dieFXPrefabAwardTmp;
if (prefab == null) return;
var board = Instantiate(prefab, parent);
_scoreBoard = board;
var tmp = board.GetComponentInChildren<TextMeshProUGUI>();
if (tmp != null) tMP_Score = tmp;
}
// ==================== 特效相关 ====================
/// <summary>死亡特效阶段结束回调(由动画事件/子类调用)</summary>
public virtual void EffEnd()
{
if (isEffEnd) return;
isEffEnd = true;
// 停留 showDelay 秒让玩家看清爆奖效果 → 开始加分滚动
StartCoroutine(DelayThenScore());
}
private IEnumerator DelayThenScore()
{
yield return new WaitForSeconds(showDelay);
StartAddScore(); // → AddScoreRoutine → MoveToSit → OnScoreEnd
}
/// <summary>触发红包特效 + 开始滚分</summary>
public virtual void GoldEffect()
{
isGoldEffectStart = true;
BigPrize();
}
// ==================== 移动到座位 ====================
/// <summary>
/// 设置爆奖初始位置:RectTransform → UI 画布中间;普通 Transform → 场景原点 (0,0,0)
/// </summary>
private void ApplySeatOffset()
{
var rect = GetComponent<RectTransform>();
if (rect != null)
{
var canvas = GetComponentInParent<Canvas>();
if (canvas != null && canvas.renderMode != RenderMode.WorldSpace)
{
Vector2 screenCenter = new Vector2(Screen.width / 2f, Screen.height / 2f);
RectTransformUtility.ScreenPointToWorldPointInRectangle(
canvas.GetComponent<RectTransform>(), screenCenter, canvas.worldCamera, out Vector3 worldPos);
transform.position = worldPos + startOffset;
}
else
{
transform.position = Vector3.zero + startOffset;
}
}
else
{
transform.SetParent(null);
transform.position = Vector3.zero + startOffset;
}
// 缓存座位引用
if (CreateFishManager.Instance == null || CreateFishManager.Instance.currGameType == null) return;
var sits = CreateFishManager.Instance.currGameType.list_Sits;
if (posID >= 0 && posID < sits.Count && sits[posID] != null)
_targetSit = sits[posID];
}
/// <summary>分数版飞回到座位上方,爆奖对象本身不动</summary>
public virtual void MoveToSit()
{
if (!useScorePanel)
{
OnScoreEnd();
return;
}
if (_scoreBoard == null && tMP_Score == null)
{
OnScoreEnd();
return;
}
float y = bjEndUIOffsetY;
if (posID == 2 || posID == 3) y *= -1f;
Vector3 seatPos = GetSeatPosition() + Vector3.up * y;
Transform moveTarget = _scoreBoard != null ? _scoreBoard.transform : tMP_Score.transform.parent;
moveTarget.DOMove(seatPos, moveDuration)
.SetEase(moveEase)
.OnComplete(() =>
{
DOVirtual.DelayedCall(0.5f, () => OnScoreEnd());
});
}
// ==================== 爆奖(发红包) ====================
/// <summary>
/// 执行一次爆奖(发红包)。可在动画事件中多次调用。
/// 每次调用通过 LayeredPool 分发红包数据,然后启动协程按金币飞行节奏逐个累加分数到 tMP_Score。
/// </summary>
/// <param name="minCount">每次最小红包数</param>
/// <param name="maxCount">每次最大红包数</param>
public virtual void CallBJ(int minCount = 10, int maxCount = 15)
{
if (isScoreEnd) return;
var records = BigPrize(minCount, maxCount);
if (records != null && records.Count > 0)
{
// 启动协程:模拟金币逐个飞到面板的节奏
_activeCoinRoutines++;
StartCoroutine(CoinArrivalRoutine(records));
}
currBJCount++;
}
/// <summary>
/// 协程:模拟金币飞行到达后逐个累加分数。
/// 等待 coinFlyDuration(匹配 coinEvent DOMove 0.5s),
/// 然后按 coinArrivalInterval 间隔逐个累加。
/// </summary>
private IEnumerator CoinArrivalRoutine(List<RedPacketRecord> records)
{
// 等待金币飞行时间(匹配 DOMove)
yield return new WaitForSeconds(coinFlyDuration);
foreach (var r in records)
{
currScore += (int)r.Amount;
if (tMP_Score != null)
tMP_Score.text = currScore.ToString();
// 加分弹出特效 + 音效
Transform parent = tMP_Score != null ? tMP_Score.transform.parent : transform;
GameObject addScorePrefab = CreateFishManager.Instance.dieFXPrefabAwardTmp_AddScore;
if (addScorePrefab != null)
{
var inst = Instantiate(addScorePrefab, parent.position + Vector3.up * 60, Quaternion.identity, parent);
inst.transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = CoinScript.NumAdd(r.Amount.ToString());
}
AudioManager.Instance.PlayerFX(61009);
// 金币之间间隔
yield return new WaitForSeconds(coinArrivalInterval);
}
_activeCoinRoutines--;
// 所有协程完成 + 轮次满足 → 进入收尾
if (_activeCoinRoutines <= 0 && totalBJCount > 0 && currBJCount >= totalBJCount)
{
isGoldEffectStart = false;
EffEnd(); // → DelayThenScore → StartAddScore → MoveToSit → OnScoreEnd → JiaFen → die
}
}
/// <summary>
/// 包一个红包分层
/// </summary>
public virtual List<RedPacketRecord> BigPrize(int min = 10, int max = 15)
{
if (layeredPool == null)
layeredPool = new LayeredPool(maxScore, scoreCount > 0 ? scoreCount : 1);
var v = layeredPool.BossGlodCoinRandrom(
tMP_Score != null ? tMP_Score.transform.position : transform.position,
min, max);
return v;
}
// ==================== 加分滚动 ====================
/// <summary>开始加分滚动动画(子类可 override 自定义)</summary>
public virtual void StartAddScore()
{
if (!useScorePanel)
{
OnScoreEnd();
return;
}
if (_scoreCoroutine != null)
StopCoroutine(_scoreCoroutine);
_scoreCoroutine = StartCoroutine(AddScoreRoutine());
}
/// <summary>默认加分滚动协程</summary>
protected virtual IEnumerator AddScoreRoutine()
{
if (currBJCount > 0)
{
// CallBJ 模式:等所有金币协程跑完
while (_activeCoinRoutines > 0)
yield return null;
maxScore = currScore;
if (tMP_Score != null)
tMP_Score.text = maxScore.ToString();
}
else
{
// 非 CallBJ 模式(如雷龙): 滚动加分
while (currScore < maxScore - paoBei)
{
int step = Random.Range(paoBei , paoBei * 2);
currScore += step;
if (tMP_Score != null)
tMP_Score.text = currScore.ToString();
yield return null;
}
if (tMP_Score != null)
tMP_Score.text = maxScore.ToString();
yield return new WaitForSeconds(0.5f);
}
// 加分完毕 → 分数版飞回座位 → 结束后 JiaFen → die
MoveToSit();
}
/// <summary>加分完成——加金币、显示结束UI、删面板、销毁自身</summary>
protected virtual void OnScoreEnd()
{
if (isScoreEnd) return;
isScoreEnd = true;
// 回加金币到玩家
JiaFen?.Invoke();
// BJEndUI:无论是否开启分数面板都显示
if (bjEndUI != null)
{
Vector3 uiPos;
if (useScorePanel)
{
// 开启分数面板:定位在分数面板位置
if (_scoreBoard != null)
uiPos = _scoreBoard.transform.position;
else if (tMP_Score != null)
uiPos = tMP_Score.transform.position;
else
uiPos = transform.position;
}
else
{
float y = bjEndUIOffsetY;
if (posID == 2 || posID == 3) y *= -1f;
uiPos = GetSeatPosition() + Vector3.up * y;
}
var endUI = Instantiate(bjEndUI, uiPos, Quaternion.identity, UIManager.Instance.uiRoot);
endUI.Init(paoBei, maxScore);
}
// 删除分数面板
if (_scoreBoard != null)
Destroy(_scoreBoard);
// 销毁爆奖对象自身
die();
}
/// <summary>获取座位坐标</summary>
public Vector3 GetSeatPosition()
{
if (_targetSit != null)
{
if (_targetSit.currentTurret != null)
return _targetSit.currentTurret.transform.position;
return _targetSit.transform.position;
}
if (CreateFishManager.Instance != null && CreateFishManager.Instance.currGameType != null)
{
var sits = CreateFishManager.Instance.currGameType.list_Sits;
if (posID >= 0 && posID < sits.Count && sits[posID] != null)
{
_targetSit = sits[posID];
if (_targetSit.currentTurret != null)
return _targetSit.currentTurret.transform.position;
return _targetSit.transform.position;
}
}
return transform.position;
}
// ==================== 销毁 ====================
/// <summary>缩小隐藏爆奖本体(不影响分数面板)</summary>
public virtual void ShrinkEffect()
{
transform.DOScale(Vector3.zero, 0.5f);
}
/// <summary>销毁自身 GameObject</summary>
public virtual void die()
{
Destroy(gameObject);
}
}