309 lines
11 KiB
C#
309 lines
11 KiB
C#
using DG.Tweening;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// Boss 爆奖事件基类
|
||
/// 完整流程:死亡特效 → EffEnd → MoveToSit → 滚动加分(期间可多次 CallBJ 发红包) → JiaFen → die
|
||
/// </summary>
|
||
public class BJEventBase : MonoBehaviour
|
||
{
|
||
// ==================== Inspector 字段 ====================
|
||
[Header("分数")]
|
||
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; // 炮倍
|
||
|
||
[Header("移动")]
|
||
public float moveDuration = 0.5f; // UI 移动到座位的时间
|
||
public Ease moveEase = Ease.OutQuad; // 移动缓动曲线
|
||
public Vector3 startOffset = new Vector3(0, 0, -100); // 起始位置偏移(归零后再加此值,可手调)
|
||
|
||
[Header("加分")]
|
||
public float addScoreDuration = 0.5f; // 加分滚动持续时间
|
||
|
||
// ==================== 回调 ====================
|
||
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 void Start()
|
||
{
|
||
|
||
}
|
||
|
||
// ==================== 初始化 ====================
|
||
/// <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, Action<int> action, Action action1 = null)
|
||
{
|
||
JiaFen = () => { action?.Invoke(0); };
|
||
maxScore = goldCount;
|
||
this.posID = posID;
|
||
paoBei = paobei;
|
||
|
||
// 自动检测:tMP_Score 为空则按优先级创建分数版
|
||
EnsureScoreBoard();
|
||
|
||
// 初始位置:座位坐标 + 手调偏移
|
||
ApplySeatOffset();
|
||
|
||
// 是否是本地玩家自己打的
|
||
isSelf = _targetSit != null && _targetSit.user == GameManager.Instance.user.id;
|
||
|
||
action1?.Invoke();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动补充分数版:tMP_Score 为空 → 优先用 scorePanelPrefab → 否则用 Manager 的 dieFXPrefabAwardTmp
|
||
/// 父对象挂在 CreateFishManager.Instance.currGameType.subPanelParent 下
|
||
/// </summary>
|
||
protected virtual void EnsureScoreBoard()
|
||
{
|
||
if (tMP_Score != null) 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);
|
||
board.transform.position = transform.position;
|
||
|
||
var tmp = board.GetComponentInChildren<TextMeshProUGUI>();
|
||
if (tmp != null) tMP_Score = tmp;
|
||
}
|
||
|
||
// ==================== 特效相关 ====================
|
||
/// <summary>死亡特效阶段结束回调(由动画事件/子类调用)</summary>
|
||
public virtual void EffEnd()
|
||
{
|
||
if (isEffEnd) return;
|
||
isEffEnd = true;
|
||
MoveToSit();
|
||
}
|
||
|
||
/// <summary>触发红包特效(可在子类 Update/动画事件中重复调用)</summary>
|
||
public virtual void GoldEffect()
|
||
{
|
||
BigPrize();
|
||
}
|
||
|
||
// ==================== 移动到座位 ====================
|
||
/// <summary>将 UI 定位到座位坐标 + startOffset(在 Inspector 中手动调偏移)</summary>
|
||
/// <summary>
|
||
/// 设置爆奖初始位置:RectTransform → UI 画布中间;普通 Transform → 场景原点 (0,0,0)
|
||
/// </summary>
|
||
private void ApplySeatOffset()
|
||
{
|
||
// 判断类型,确定基础位置
|
||
var rect = GetComponent<RectTransform>();
|
||
if (rect != null)
|
||
{
|
||
// UI 元素 → 画布中间
|
||
var canvas = GetComponentInParent<Canvas>();
|
||
if (canvas != null && canvas.renderMode != RenderMode.WorldSpace)
|
||
{
|
||
// ScreenSpace → 屏幕中间转世界坐标
|
||
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
|
||
{
|
||
// WorldSpace 或无 Canvas → 归零加偏移
|
||
transform.position = Vector3.zero + startOffset;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
transform.SetParent(null);
|
||
|
||
// 普通 3D Transform → 场景原点
|
||
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>将爆奖 UI 从当前位置移动到击杀者座位</summary>
|
||
public virtual void MoveToSit()
|
||
{
|
||
if (CreateFishManager.Instance == null || CreateFishManager.Instance.currGameType == null)
|
||
{
|
||
OnMoveComplete();
|
||
return;
|
||
}
|
||
|
||
var sits = CreateFishManager.Instance.currGameType.list_Sits;
|
||
if (posID < 0 || posID >= sits.Count || sits[posID] == null)
|
||
{
|
||
OnMoveComplete();
|
||
return;
|
||
}
|
||
|
||
_targetSit = sits[posID];
|
||
Transform sitTransform = _targetSit.transform;
|
||
|
||
// 获取座位上的目标位置(优先用当前炮台位置)
|
||
Vector3 targetPos = sitTransform.position;
|
||
if (_targetSit.currentTurret != null)
|
||
{
|
||
targetPos = _targetSit.currentTurret.transform.position;
|
||
}
|
||
|
||
// DOTween 移动到座位
|
||
transform.DOMove(targetPos, moveDuration)
|
||
.SetEase(moveEase)
|
||
.OnComplete(() =>
|
||
{
|
||
OnMoveComplete();
|
||
});
|
||
}
|
||
|
||
/// <summary>移动到座位完成 → 开始加分</summary>
|
||
protected virtual void OnMoveComplete()
|
||
{
|
||
isScoreStart = true;
|
||
StartAddScore();
|
||
}
|
||
|
||
// ==================== 爆奖(发红包) ====================
|
||
/// <summary>
|
||
/// 执行一次爆奖(发红包)。可在动画事件中多次调用。
|
||
/// 每次调用会通过 LayeredPool 分发金币红包。
|
||
/// </summary>
|
||
/// <param name="minCount">每次最小红包数</param>
|
||
/// <param name="maxCount">每次最大红包数</param>
|
||
public virtual void CallBJ(int minCount = 10, int maxCount = 15)
|
||
{
|
||
if (isScoreEnd) return;
|
||
|
||
BigPrize(minCount, maxCount);
|
||
currBJCount++;
|
||
|
||
// 达到总次数 → 标记爆奖结束
|
||
if (totalBJCount > 0 && currBJCount >= totalBJCount)
|
||
{
|
||
isGoldEffectStart = false;
|
||
}
|
||
}
|
||
|
||
/// <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 (_scoreCoroutine != null)
|
||
StopCoroutine(_scoreCoroutine);
|
||
|
||
_scoreCoroutine = StartCoroutine(AddScoreRoutine());
|
||
}
|
||
|
||
/// <summary>默认加分滚动协程</summary>
|
||
protected virtual IEnumerator AddScoreRoutine()
|
||
{
|
||
// 根据炮倍计算每次步进值
|
||
int stepBase = Mathf.Max(1, paoBei / 10);
|
||
float elapsed = 0f;
|
||
|
||
while (elapsed < addScoreDuration && currScore < maxScore)
|
||
{
|
||
int step = UnityEngine.Random.Range(stepBase, stepBase * 5);
|
||
currScore = Mathf.Min(currScore + step, maxScore);
|
||
|
||
if (tMP_Score != null)
|
||
tMP_Score.text = currScore.ToString();
|
||
|
||
elapsed += Time.deltaTime * 0.2f; // 控制滚动速度
|
||
yield return null;
|
||
}
|
||
|
||
// 最终值
|
||
currScore = maxScore;
|
||
if (tMP_Score != null)
|
||
tMP_Score.text = maxScore.ToString();
|
||
|
||
OnScoreEnd();
|
||
}
|
||
|
||
/// <summary>加分完成</summary>
|
||
protected virtual void OnScoreEnd()
|
||
{
|
||
if (isScoreEnd) return;
|
||
isScoreEnd = true;
|
||
|
||
JiaFen?.Invoke();
|
||
|
||
// 延迟一会儿再销毁,让玩家看一下
|
||
Invoke(nameof(die), 0.5f);
|
||
}
|
||
|
||
// ==================== 销毁 ====================
|
||
public virtual void die()
|
||
{
|
||
transform.DOScale(Vector3.zero, 0.5f).OnComplete(() =>
|
||
{
|
||
Destroy(gameObject);
|
||
});
|
||
}
|
||
}
|