1030 lines
36 KiB
C#
1030 lines
36 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using asap.core;
|
||
using cfg;
|
||
using DataCenter;
|
||
using game;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
namespace UI.PartnerGather.Mining
|
||
{
|
||
/// <summary>
|
||
/// 事件合作伙伴抽奖挖矿面板控制器
|
||
/// 负责管理抽奖转盘UI、挖矿进度显示、场景控制器绑定等核心功能
|
||
///
|
||
/// 建议挂载在:EventPartnerDrawMiningPanel UI预制体的根对象上
|
||
///
|
||
/// UI结构建议:
|
||
/// EventPartnerDrawMiningPanel (挂载此脚本)
|
||
/// ├── root/
|
||
/// │ ├── turntable/Scroll_item (转盘滚动区域)
|
||
/// │ ├── btn_common_green/btn_green (抽奖按钮)
|
||
/// │ └── turnable/select (选中效果)
|
||
/// ├── btn_close (关闭按钮)
|
||
/// └── SettlementPanel/ (结算面板,可选)
|
||
/// ├── Rewards/ (奖励显示区域)
|
||
/// └── Trophy/ (奖杯显示区域)
|
||
///
|
||
/// 重要说明:
|
||
/// - 此脚本会自动查找和绑定采集场景控制器
|
||
/// - 需要正确设置转盘相关的UI引用
|
||
/// - 支持节点结算过场动画和奖杯显示系统
|
||
/// </summary>
|
||
public class EventPartnerDrawMiningPanel : MonoBehaviour
|
||
{
|
||
|
||
// 管理器
|
||
private EventPartnerGatherManager _eventPartnerGatherManager;
|
||
|
||
[Header("采集场景配置(运行时自动设置)")]
|
||
[SerializeField]
|
||
private EventPartnerMiningSceneController miningScene;
|
||
|
||
private bool _isProcessingResult = false;
|
||
|
||
// 更新点数即等级
|
||
[Header("=== 等级进度显示 ===")]
|
||
[Tooltip("等级节点控制器数组 - 每个节点对应一个挖矿阶段")]
|
||
public EventPartnerMiningLevelController[] levels;
|
||
|
||
public float contentDeltaX = 24;
|
||
|
||
public Image bar;
|
||
|
||
|
||
private Transform _level;
|
||
private Transform _turntable; // 转盘
|
||
private PanelScroll _scrollList; // 滚动列表
|
||
private Transform _select; // 选中效果
|
||
private Transform _priceItem; // 列表滚动中的数据
|
||
|
||
// 按钮
|
||
private Button _btnClose;
|
||
private Button _btnSpin;
|
||
private ScrollRect _scrollRect;
|
||
private RectTransform _viewport;
|
||
private RectTransform _content;
|
||
private RectTransform _itemTemplate;
|
||
|
||
// 记录当前的Item
|
||
private readonly List<EventPartnerSpinItemView> _spinItemViews = new();
|
||
// private readonly List<RectTransform> _rects = new List<RectTransform>();
|
||
private readonly List<int> _dataIndexMap = new List<int>();
|
||
|
||
[Header("结算过场配置")]
|
||
public Transform settlementPanel; // 结算面板
|
||
public Transform trophyDisplay; // 奖杯显示区域
|
||
public Transform nodeCompletionEffect; // 节点完成特效
|
||
public AudioClip settlementSound; // 结算音效
|
||
public AudioClip trophySound; // 奖杯音效
|
||
|
||
private bool _isShowingSettlement = false;
|
||
|
||
// 当前点数列表
|
||
private List<EventPartnerSpinPoint> _spinPointDatas;
|
||
// 0,1,2 停靠在第二个格子
|
||
private readonly int _starGrid = 2;
|
||
|
||
|
||
private int _dataCount;
|
||
private int _poolSize;
|
||
private float _itemWidth;
|
||
private bool _animating;
|
||
private float _autoScrollSpeed = 60f;
|
||
private bool _autoScroll = false;
|
||
|
||
// 自动滚动累积位移(用于与步进动画叠加)
|
||
private float _autoScrollAccumulation = 0f;
|
||
|
||
private void Awake()
|
||
{
|
||
//
|
||
_eventPartnerGatherManager = GContext.container.Resolve<EventPartnerGatherManager>();
|
||
//
|
||
_btnSpin = transform.Find("root/btn_common_green/btn_green").GetComponent<Button>();
|
||
_btnClose = transform.Find("btn_close").GetComponent<Button>();
|
||
_scrollList = transform.Find("root/turntable/Scroll_item").GetComponent<PanelScroll>();
|
||
_priceItem = transform.Find("root/turntable/Scroll_item/Viewport/Content/item");
|
||
_select = transform.Find("root/turnable/select");
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
_btnSpin.onClick.AddListener(OnSpin);
|
||
_btnClose.onClick.AddListener(OnClickCloseBtn);
|
||
|
||
// 运行时自动查找并设置采集场景控制器
|
||
FindAndSetMiningScene();
|
||
|
||
_ = UpdateScrollList();
|
||
}
|
||
|
||
private void FindAndSetMiningScene()
|
||
{
|
||
if (miningScene == null)
|
||
{
|
||
// 在场景中查找EventPartnerMiningSceneController
|
||
miningScene = FindObjectOfType<EventPartnerMiningSceneController>();
|
||
|
||
if (miningScene == null)
|
||
{
|
||
Log("警告:未找到EventPartnerMiningSceneController,采集功能将不可用");
|
||
}
|
||
else
|
||
{
|
||
Log($"已自动设置采集场景控制器:{miningScene.name}");
|
||
// 验证关键组件是否存在
|
||
ValidateMiningSceneComponents();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证采集场景的关键组件
|
||
/// </summary>
|
||
private void ValidateMiningSceneComponents()
|
||
{
|
||
if (miningScene == null) return;
|
||
|
||
if (miningScene.character == null)
|
||
{
|
||
Log("警告:采集场景缺少角色控制器组件");
|
||
}
|
||
|
||
if (miningScene.trainController == null)
|
||
{
|
||
Log("警告:采集场景缺少火车控制器组件");
|
||
}
|
||
|
||
if (miningScene.miningPoints == null)
|
||
{
|
||
Log("警告:采集场景缺少挖矿点管理器组件");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 供外部代码设置采集场景控制器
|
||
/// </summary>
|
||
public void SetMiningScene(EventPartnerMiningSceneController sceneController)
|
||
{
|
||
miningScene = sceneController;
|
||
Log($"已设置采集场景控制器:{sceneController?.name ?? "null"}");
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (_autoScroll && _dataCount > 0 && _content)
|
||
{
|
||
AutoScroll(Time.deltaTime);
|
||
}
|
||
}
|
||
|
||
private async Task UpdateScrollList()
|
||
{
|
||
_scrollRect = _scrollList.GetComponent<ScrollRect>();
|
||
_viewport = _scrollRect.viewport;
|
||
_content = _priceItem.parent as RectTransform;
|
||
_itemTemplate = (RectTransform)_priceItem;
|
||
_spinPointDatas = _eventPartnerGatherManager.EventPartnerSpinPointList;
|
||
_dataCount = _spinPointDatas?.Count ?? 0;
|
||
if (_dataCount == 0) { if (_btnSpin) _btnSpin.interactable = false; return; }
|
||
_itemWidth = _itemTemplate.rect.width;
|
||
_poolSize = Mathf.CeilToInt(_viewport.rect.width / _itemWidth) + 4;
|
||
BuildItemPool();
|
||
_priceItem.gameObject.SetActive(false);
|
||
// await AlignToIndexAsync(0, 2, 0.1f);
|
||
await Task.CompletedTask;
|
||
}
|
||
|
||
private void BuildItemPool()
|
||
{
|
||
// _rects.Clear();
|
||
_spinItemViews.Clear();
|
||
_dataIndexMap.Clear();
|
||
for (int i = 0; i < _poolSize; i++)
|
||
{
|
||
var r = Instantiate(_itemTemplate, _content);
|
||
r.gameObject.SetActive(true);
|
||
// _rects.Add(r);
|
||
|
||
Log($"r -> {r.sizeDelta}");
|
||
|
||
var spinItemView = r.GetComponent<EventPartnerSpinItemView>();
|
||
_spinItemViews.Add(spinItemView);
|
||
int di = i % _dataCount;
|
||
_dataIndexMap.Add(di);
|
||
BindItem(spinItemView, di);
|
||
}
|
||
// _content.rec
|
||
var size = _content.sizeDelta;
|
||
var size2 = _content.rect.size;
|
||
Log($"size -> {size}, {size2}");
|
||
|
||
var num = Mathf.RoundToInt(size2.x / 235.02f);
|
||
Log($"num-> {num}");
|
||
|
||
|
||
_content.anchoredPosition = Vector2.zero;
|
||
_scrollRect.enabled = false;
|
||
}
|
||
|
||
private void BindItem(EventPartnerSpinItemView spinItemView, int dataIndex)
|
||
{
|
||
// var v = r.GetComponent<SpinItemView>();
|
||
// if (!v) v = r.gameObject.AddComponent<SpinItemView>();
|
||
// v.Bind(_data[dataIndex]);
|
||
spinItemView?.Bind(_spinPointDatas[dataIndex],dataIndex);
|
||
}
|
||
private int GetStartDataIndex()
|
||
{
|
||
var idx = Mathf.Clamp(_starGrid,0,+_spinItemViews.Count);
|
||
return _dataIndexMap[idx];
|
||
}
|
||
private int GetCenterDataIndex()
|
||
{
|
||
int idx = _spinItemViews.Count / 2;
|
||
return _dataIndexMap[idx];
|
||
}
|
||
private void UpdateSelectionVisual(int targetIdxId)
|
||
{
|
||
// var targetIdx = this.targetIdx;
|
||
for (int i = 0; i < _spinItemViews.Count; i++)
|
||
{
|
||
// _spinItemViews[i].SetSelected(i == 2);
|
||
_spinItemViews[i].UpdateSelected(targetIdxId);
|
||
}
|
||
}
|
||
public async Task AlignToIndexAsync(int targetIndex, int loops, float duration)
|
||
{
|
||
if (_animating) return;
|
||
if (_dataCount == 0) return;
|
||
_animating = true;
|
||
SetInteractable(false);
|
||
// 不再停用自动滚动
|
||
int current = GetStartDataIndex(); // 恢复使用起始索引
|
||
int delta = (targetIndex - current + _dataCount) % _dataCount;
|
||
int steps = loops * _dataCount + delta;
|
||
for (int i = 0; i < steps; i++)
|
||
{
|
||
await MoveStepRight(duration);
|
||
}
|
||
SetInteractable(true);
|
||
_animating = false;
|
||
}
|
||
|
||
private async Task MoveStepRight(float duration)
|
||
{
|
||
// 记录步进开始时的自动滚动累积位移
|
||
float startAutoScroll = _autoScrollAccumulation;
|
||
float stepOffset = 0f; // 步进相对偏移
|
||
float t = 0f;
|
||
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / Mathf.Max(0.0001f, duration);
|
||
stepOffset = -_itemWidth * Mathf.SmoothStep(0f, 1f, t);
|
||
// 使用统一位置更新方法,传入步进偏移
|
||
UpdateContentPosition(stepOffset);
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
// 步进完成,执行标准化并调整自动滚动累积
|
||
_autoScrollAccumulation = (_autoScrollAccumulation + _itemWidth) % _itemWidth;
|
||
NormalizeAfterStepRight();
|
||
}
|
||
|
||
private void NormalizeAfterStepRight()
|
||
{
|
||
// 统一锚点驱动:只调整数据映射,不直接修改位置
|
||
// 位置由AutoScroll或步进动画统一控制
|
||
var first = _spinItemViews[0];
|
||
_spinItemViews.RemoveAt(0);
|
||
_spinItemViews.Add(first);
|
||
first.transform.SetSiblingIndex(_content.childCount - 1);
|
||
|
||
int lastData = _dataIndexMap[^1];
|
||
int ni = (lastData + 1) % _dataCount;
|
||
_dataIndexMap.RemoveAt(0);
|
||
_dataIndexMap.Add(ni);
|
||
|
||
BindItem(first, ni);
|
||
|
||
// 重置content位置到标准位置,避免累积误差
|
||
if (!_animating)
|
||
{
|
||
_content.anchoredPosition = new Vector2(-_autoScrollAccumulation, _content.anchoredPosition.y);
|
||
}
|
||
}
|
||
|
||
private void UpdateContentPosition(float additionalOffset = 0f)
|
||
{
|
||
// 统一位置计算:基准位置 + 自动滚动累积 + 额外偏移
|
||
float finalX = -_autoScrollAccumulation + additionalOffset;
|
||
_content.anchoredPosition = new Vector2(finalX, _content.anchoredPosition.y);
|
||
}
|
||
private void AutoScroll(float dt)
|
||
{
|
||
_autoScrollAccumulation += _autoScrollSpeed * dt;
|
||
|
||
// 应用累积的自动滚动位移
|
||
while (_autoScrollAccumulation >= _itemWidth)
|
||
{
|
||
_autoScrollAccumulation -= _itemWidth;
|
||
NormalizeAfterStepRight();
|
||
}
|
||
|
||
// 使用统一位置更新方法
|
||
UpdateContentPosition();
|
||
}
|
||
|
||
public EventPartnerSpinPoint GetSelectedData()
|
||
{
|
||
return _dataCount == 0 ? null : _spinPointDatas[_dataIndexMap[_spinItemViews.Count / 2]];
|
||
}
|
||
|
||
private void ClearSelection()
|
||
{
|
||
foreach (var elem in _spinItemViews)
|
||
{
|
||
// var itemView = elem.GetComponent<SpinItemView>();
|
||
elem.SetSelected(false);
|
||
}
|
||
}
|
||
|
||
private async Task SpinToAsync(EventPartnerSpinPoint target, int loops, float fast, float slow)
|
||
{
|
||
if (_animating || target == null || _dataCount == 0) return;
|
||
var dataTargetIndex = _spinPointDatas.IndexOf(target);
|
||
if (dataTargetIndex < 0)
|
||
dataTargetIndex = 0;
|
||
_animating = true;
|
||
|
||
SetInteractable(false);
|
||
// 不再停用自动滚动,允许持续运行
|
||
ClearSelection();
|
||
var targetId = target.ID;
|
||
var current = GetStartDataIndex(); // 恢复使用起始索引(设计的中心位置)
|
||
int delta = (dataTargetIndex - current + _dataCount) % _dataCount;
|
||
int steps = loops * _dataCount + delta;
|
||
int accel = Mathf.Min(steps / 4, 6);
|
||
int decel = Mathf.Min(steps / 4, 6);
|
||
int steady = Mathf.Max(0, steps - accel - decel);
|
||
|
||
Log($"SpinToAsync -> current -> {current} targetIndex:{dataTargetIndex} delta: {delta}" );
|
||
for (int i = 0; i < accel; i++)
|
||
{
|
||
var d = Mathf.Lerp(fast, slow, (i + 1f) / accel);
|
||
await MoveStepRight(d);
|
||
}
|
||
for (int i = 0; i < steady; i++)
|
||
{
|
||
await MoveStepRight(slow);
|
||
}
|
||
for (int i = 0; i < decel; i++)
|
||
{
|
||
float d = Mathf.Lerp(slow, fast, (i + 1f) / decel);
|
||
await MoveStepRight(d);
|
||
}
|
||
|
||
UpdateSelectionVisual(targetId);
|
||
|
||
// 精确校正到中心位置
|
||
await AlignToCenterAsync();
|
||
|
||
// 抽中奖品后暂停自动滚动2秒,然后重新开始
|
||
EnableAutoScroll(false);
|
||
await Task.Delay(2000);
|
||
EnableAutoScroll(true);
|
||
|
||
SetInteractable(true);
|
||
_animating = false;
|
||
}
|
||
|
||
private async Task AlignToCenterAsync()
|
||
{
|
||
// 添加明显的回中心动画效果
|
||
float currentX = _content.anchoredPosition.x;
|
||
float targetX = -_autoScrollAccumulation;
|
||
|
||
// 如果已经很接近中心,就不需要动画
|
||
if (Mathf.Abs(currentX - targetX) < 1f)
|
||
{
|
||
UpdateContentPosition();
|
||
await Task.Delay(50);
|
||
return;
|
||
}
|
||
|
||
// 平滑回中心动画
|
||
float t = 0f;
|
||
float duration = 0.3f; // 回中心动画持续时间
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / duration;
|
||
float x = Mathf.Lerp(currentX, targetX, Mathf.SmoothStep(0f, 1f, t));
|
||
_content.anchoredPosition = new Vector2(x, _content.anchoredPosition.y);
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
// 确保最终位置精确
|
||
UpdateContentPosition();
|
||
}
|
||
|
||
public void SetInteractable(bool v)
|
||
{
|
||
_btnSpin.interactable = v;
|
||
_btnClose.interactable = v && !_animating;
|
||
}
|
||
|
||
public void EnableAutoScroll(bool on)
|
||
{
|
||
_autoScroll = on;
|
||
}
|
||
|
||
public void SetAutoScrollSpeed(float s)
|
||
{
|
||
_autoScrollSpeed = Mathf.Max(0f, s);
|
||
}
|
||
|
||
public void ResumeAutoScroll()
|
||
{
|
||
// 供后续逻辑调用,恢复自动滚动
|
||
EnableAutoScroll(true);
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
_btnClose.onClick.RemoveAllListeners();
|
||
_btnSpin.onClick.RemoveAllListeners();
|
||
}
|
||
|
||
public void SetPoints(int points)
|
||
{
|
||
Log($"SetPoints ->{points}");
|
||
}
|
||
|
||
private void UpdateLevel(int level)
|
||
{
|
||
if (levels == null || levels.Length == 0) return;
|
||
level = Mathf.Clamp(level, 0, levels.Length);
|
||
for (int i = 0; i < levels.Length; ++i)
|
||
{
|
||
if (i < level) levels[i].UpdateStatus(LevelStatus.Done);
|
||
else if (i == level) levels[i].UpdateStatus(LevelStatus.Current);
|
||
else levels[i].UpdateStatus(LevelStatus.Normal);
|
||
}
|
||
}
|
||
public void SetMiningProgress(int progress)
|
||
{
|
||
Log($"SetMiningProgress {progress}");
|
||
var stage = _eventPartnerGatherManager.EventPartnerMain2Data?.StagePoint;
|
||
int completed = 0;
|
||
if (stage != null && stage.Count > 0)
|
||
{
|
||
int sum = 0;
|
||
for (int i = 0; i < stage.Count; i++)
|
||
{
|
||
sum += stage[i];
|
||
if (progress >= sum) completed++; else break;
|
||
}
|
||
}
|
||
UpdateLevel(Mathf.Min(completed, levels?.Length ?? 0));
|
||
}
|
||
public void SetTrainProgress(int cur, int max)
|
||
{
|
||
Log($"SetTrainProgress {cur} {max}");
|
||
// if (trainSlider != null)
|
||
// {
|
||
// trainSlider.minValue = 0;
|
||
// trainSlider.maxValue = max;
|
||
// trainSlider.value = cur;
|
||
// }
|
||
// if (txtTrainNode != null) txtTrainNode.text = $"{cur}/{max}";
|
||
}
|
||
public async void OnSpin()
|
||
{
|
||
var spinPoint = _eventPartnerGatherManager.GetSpinPointElem();
|
||
if (spinPoint == null) return;
|
||
Log($"OnSpin -> {spinPoint.ID} {spinPoint.SpinPointParam.json}");
|
||
// if (true)
|
||
// {
|
||
// await miningScene.StartMiningSequence(50);
|
||
// }
|
||
|
||
await SpinToAsync(spinPoint, 1, 0.05f, 0.12f);
|
||
//
|
||
// // 直接处理采集流程(整合了GameController的功能)
|
||
await OnSpinComplete(spinPoint);
|
||
//
|
||
// // 检查是否需要触发结算
|
||
CheckForNodeCompletion();
|
||
}
|
||
|
||
private async Task OnSpinComplete(EventPartnerSpinPoint spinResult)
|
||
{
|
||
if (_isProcessingResult || spinResult == null) return;
|
||
_isProcessingResult = true;
|
||
|
||
try
|
||
{
|
||
// 解析抽奖结果
|
||
var points = _eventPartnerGatherManager.ResolveSpinAndGetPoints(spinResult);
|
||
|
||
// 显示积分飘字
|
||
await ShowPointsFloatingText(points);
|
||
|
||
// 开始采集流程
|
||
if (miningScene != null)
|
||
{
|
||
await miningScene.StartMiningSequence(points);
|
||
}
|
||
|
||
// 更新积分和进度
|
||
if (points > 0)
|
||
{
|
||
_eventPartnerGatherManager.AddPoints(points);
|
||
UpdateProgressUI();
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
_isProcessingResult = false;
|
||
}
|
||
}
|
||
|
||
private async Task ShowPointsFloatingText(int points)
|
||
{
|
||
// 在人物头顶显示积分飘字
|
||
if (miningScene?.character != null)
|
||
{
|
||
var floatingText = CreateFloatingText($"+{points}", miningScene.character.transform.position);
|
||
await AnimateFloatingText(floatingText);
|
||
}
|
||
}
|
||
|
||
private GameObject CreateFloatingText(string text, Vector3 worldPosition)
|
||
{
|
||
// 创建飘字UI
|
||
GameObject textObj = new GameObject("FloatingText");
|
||
textObj.transform.position = worldPosition + Vector3.up * 2f;
|
||
|
||
// 添加TextMesh组件
|
||
var textMesh = textObj.AddComponent<TextMesh>();
|
||
textMesh.text = text;
|
||
textMesh.fontSize = 20;
|
||
textMesh.color = Color.yellow;
|
||
textMesh.anchor = TextAnchor.MiddleCenter;
|
||
|
||
return textObj;
|
||
}
|
||
|
||
private async Task AnimateFloatingText(GameObject textObj)
|
||
{
|
||
if (textObj == null) return;
|
||
|
||
Vector3 startPos = textObj.transform.position;
|
||
Vector3 endPos = startPos + Vector3.up * 1.5f;
|
||
|
||
float duration = 1.5f;
|
||
float t = 0f;
|
||
|
||
var textMesh = textObj.GetComponent<TextMesh>();
|
||
Color startColor = textMesh.color;
|
||
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / duration;
|
||
|
||
// 位置动画
|
||
textObj.transform.position = Vector3.Lerp(startPos, endPos, t);
|
||
|
||
// 透明度动画
|
||
if (textMesh != null)
|
||
{
|
||
Color color = startColor;
|
||
color.a = Mathf.Lerp(1f, 0f, t);
|
||
textMesh.color = color;
|
||
}
|
||
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
DestroyImmediate(textObj);
|
||
}
|
||
|
||
private void UpdateProgressUI()
|
||
{
|
||
// 更新进度显示UI - 直接调用现有的UI更新方法
|
||
var currentMiningProgress = _eventPartnerGatherManager.GatherData?.CurrentMiningProgress ?? 0;
|
||
|
||
// 使用现有的SetMiningProgress方法更新进度显示
|
||
SetMiningProgress(currentMiningProgress);
|
||
|
||
// 检查是否需要触发节点结算
|
||
CheckForNodeCompletion();
|
||
}
|
||
public void OnClickCloseBtn()
|
||
{
|
||
GContext.Publish(new UnloadActToNextAct());
|
||
}
|
||
#region 节点结算过场与奖杯显示
|
||
|
||
private void CheckForNodeCompletion()
|
||
{
|
||
if (_eventPartnerGatherManager?.GatherData == null) return;
|
||
|
||
var trainProgress = _eventPartnerGatherManager.GatherData.TrainProgress;
|
||
var maxNodes = _eventPartnerGatherManager.MaxTrainNodes;
|
||
|
||
// 检查是否达到节点完成条件
|
||
if (trainProgress >= maxNodes && !_isShowingSettlement)
|
||
{
|
||
_ = ShowNodeCompletionSequence();
|
||
}
|
||
}
|
||
|
||
public async Task ShowNodeCompletionSequence()
|
||
{
|
||
if (_isShowingSettlement) return;
|
||
_isShowingSettlement = true;
|
||
|
||
try
|
||
{
|
||
Log("开始节点结算过场动画");
|
||
|
||
// 1. 禁用交互
|
||
SetInteractable(false);
|
||
EnableAutoScroll(false);
|
||
|
||
// 2. 显示节点完成特效
|
||
await ShowNodeCompletionEffect();
|
||
|
||
// 3. 计算奖励并显示结算面板
|
||
var rewards = CalculateNodeRewards();
|
||
await ShowSettlementPanel(rewards);
|
||
|
||
// 4. 显示奖杯(如果获得)
|
||
var trophy = GetTrophyForCompletion();
|
||
if (trophy != null)
|
||
{
|
||
await ShowTrophySequence(trophy);
|
||
}
|
||
|
||
// 5. 等待用户确认
|
||
await WaitForSettlementConfirmation();
|
||
|
||
// 6. 重置场景准备下一轮
|
||
await ResetForNextRound();
|
||
}
|
||
finally
|
||
{
|
||
_isShowingSettlement = false;
|
||
SetInteractable(true);
|
||
EnableAutoScroll(true);
|
||
}
|
||
}
|
||
|
||
private async Task ShowNodeCompletionEffect()
|
||
{
|
||
if (nodeCompletionEffect == null) return;
|
||
|
||
// 播放完成音效
|
||
if (settlementSound != null)
|
||
{
|
||
AudioSource.PlayClipAtPoint(settlementSound, Camera.main.transform.position);
|
||
}
|
||
|
||
// 显示完成特效
|
||
nodeCompletionEffect.gameObject.SetActive(true);
|
||
|
||
// 特效动画
|
||
var scale = nodeCompletionEffect.localScale;
|
||
nodeCompletionEffect.localScale = Vector3.zero;
|
||
|
||
float duration = 1f;
|
||
float t = 0f;
|
||
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / duration;
|
||
float ease = Mathf.SmoothStep(0f, 1f, t);
|
||
nodeCompletionEffect.localScale = Vector3.Lerp(Vector3.zero, scale, ease);
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
await Task.Delay(1000); // 特效持续时间
|
||
|
||
nodeCompletionEffect.gameObject.SetActive(false);
|
||
}
|
||
|
||
private NodeRewards CalculateNodeRewards()
|
||
{
|
||
var gatherData = _eventPartnerGatherManager?.GatherData;
|
||
if (gatherData == null) return new NodeRewards();
|
||
|
||
return new NodeRewards
|
||
{
|
||
Points = gatherData.CurrentPoints,
|
||
Coins = gatherData.CurrentPoints * 10, // 积分转币率
|
||
Items = GetBonusItems(),
|
||
Experience = 100 // 固定经验奖励
|
||
};
|
||
}
|
||
|
||
private List<RewardItem> GetBonusItems()
|
||
{
|
||
// 根据完成度给予奖励物品
|
||
var items = new List<RewardItem>();
|
||
var trainProgress = _eventPartnerGatherManager?.GatherData?.TrainProgress ?? 0;
|
||
|
||
// 基础奖励
|
||
items.Add(new RewardItem { ItemId = 1001, Count = 5, Name = "挖矿工具" });
|
||
|
||
// 根据进度给予额外奖励
|
||
if (trainProgress >= 50)
|
||
{
|
||
items.Add(new RewardItem { ItemId = 1002, Count = 3, Name = "稀有矿石" });
|
||
}
|
||
|
||
if (trainProgress >= 100)
|
||
{
|
||
items.Add(new RewardItem { ItemId = 1003, Count = 1, Name = "传说装备" });
|
||
}
|
||
|
||
return items;
|
||
}
|
||
|
||
private async Task ShowSettlementPanel(NodeRewards rewards)
|
||
{
|
||
if (settlementPanel == null) return;
|
||
|
||
Log($"显示结算面板,奖励:{rewards.Points}积分, {rewards.Coins}金币");
|
||
|
||
settlementPanel.gameObject.SetActive(true);
|
||
|
||
// 更新结算面板数据
|
||
UpdateSettlementPanelData(rewards);
|
||
|
||
// 面板进入动画
|
||
var canvasGroup = settlementPanel.GetComponent<CanvasGroup>();
|
||
if (canvasGroup == null) canvasGroup = settlementPanel.gameObject.AddComponent<CanvasGroup>();
|
||
|
||
canvasGroup.alpha = 0f;
|
||
canvasGroup.interactable = false;
|
||
|
||
float duration = 0.5f;
|
||
float t = 0f;
|
||
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / duration;
|
||
canvasGroup.alpha = Mathf.SmoothStep(0f, 1f, t);
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
canvasGroup.interactable = true;
|
||
}
|
||
|
||
private void UpdateSettlementPanelData(NodeRewards rewards)
|
||
{
|
||
// 更新结算面板中的奖励显示
|
||
var pointsText = settlementPanel.Find("Rewards/Points/Text")?.GetComponent<TMP_Text>();
|
||
var coinsText = settlementPanel.Find("Rewards/Coins/Text")?.GetComponent<TMP_Text>();
|
||
var expText = settlementPanel.Find("Rewards/Experience/Text")?.GetComponent<TMP_Text>();
|
||
|
||
if (pointsText) pointsText.text = rewards.Points.ToString();
|
||
if (coinsText) coinsText.text = rewards.Coins.ToString();
|
||
if (expText) expText.text = rewards.Experience.ToString();
|
||
|
||
// 更新物品奖励列表
|
||
var itemsList = settlementPanel.Find("Rewards/Items");
|
||
if (itemsList != null)
|
||
{
|
||
// 清理现有物品显示
|
||
for (int i = itemsList.childCount - 1; i >= 0; i--)
|
||
{
|
||
DestroyImmediate(itemsList.GetChild(i).gameObject);
|
||
}
|
||
|
||
// 创建物品显示
|
||
foreach (var item in rewards.Items)
|
||
{
|
||
CreateRewardItemDisplay(item, itemsList);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void CreateRewardItemDisplay(RewardItem item, Transform parent)
|
||
{
|
||
GameObject itemObj = new GameObject($"Item_{item.ItemId}");
|
||
itemObj.transform.SetParent(parent);
|
||
|
||
var text = itemObj.AddComponent<TMP_Text>();
|
||
text.text = $"{item.Name} x{item.Count}";
|
||
text.fontSize = 14;
|
||
text.color = Color.white;
|
||
}
|
||
|
||
private TrophyData GetTrophyForCompletion()
|
||
{
|
||
var trainProgress = _eventPartnerGatherManager?.GatherData?.TrainProgress ?? 0;
|
||
|
||
// 根据完成度决定奖杯等级
|
||
if (trainProgress >= 100)
|
||
{
|
||
return new TrophyData
|
||
{
|
||
Id = 3001,
|
||
Name = "挖矿大师",
|
||
Description = "完美完成挖矿任务",
|
||
Rarity = TrophyRarity.Legendary
|
||
};
|
||
}
|
||
else if (trainProgress >= 80)
|
||
{
|
||
return new TrophyData
|
||
{
|
||
Id = 3002,
|
||
Name = "挖矿专家",
|
||
Description = "出色完成挖矿任务",
|
||
Rarity = TrophyRarity.Epic
|
||
};
|
||
}
|
||
else if (trainProgress >= 50)
|
||
{
|
||
return new TrophyData
|
||
{
|
||
Id = 3003,
|
||
Name = "挖矿能手",
|
||
Description = "良好完成挖矿任务",
|
||
Rarity = TrophyRarity.Rare
|
||
};
|
||
}
|
||
|
||
return null; // 未达到奖杯要求
|
||
}
|
||
|
||
private async Task ShowTrophySequence(TrophyData trophy)
|
||
{
|
||
if (trophyDisplay == null || trophy == null) return;
|
||
|
||
Log($"获得奖杯:{trophy.Name} - {trophy.Description}");
|
||
|
||
// 播放奖杯音效
|
||
if (trophySound != null)
|
||
{
|
||
AudioSource.PlayClipAtPoint(trophySound, Camera.main.transform.position);
|
||
}
|
||
|
||
trophyDisplay.gameObject.SetActive(true);
|
||
|
||
// 更新奖杯显示信息
|
||
UpdateTrophyDisplay(trophy);
|
||
|
||
// 奖杯出现动画
|
||
await AnimateTrophyAppearance();
|
||
|
||
// 显示3秒
|
||
await Task.Delay(3000);
|
||
}
|
||
|
||
private void UpdateTrophyDisplay(TrophyData trophy)
|
||
{
|
||
var nameText = trophyDisplay.Find("Trophy/Name")?.GetComponent<TMP_Text>();
|
||
var descText = trophyDisplay.Find("Trophy/Description")?.GetComponent<TMP_Text>();
|
||
var rarityText = trophyDisplay.Find("Trophy/Rarity")?.GetComponent<TMP_Text>();
|
||
|
||
if (nameText) nameText.text = trophy.Name;
|
||
if (descText) descText.text = trophy.Description;
|
||
if (rarityText) rarityText.text = trophy.Rarity.ToString();
|
||
|
||
// 根据稀有度设置颜色
|
||
Color rarityColor = trophy.Rarity switch
|
||
{
|
||
TrophyRarity.Legendary => Color.yellow,
|
||
TrophyRarity.Epic => Color.magenta,
|
||
TrophyRarity.Rare => Color.blue,
|
||
_ => Color.white
|
||
};
|
||
|
||
if (rarityText) rarityText.color = rarityColor;
|
||
}
|
||
|
||
private async Task AnimateTrophyAppearance()
|
||
{
|
||
var trophyIcon = trophyDisplay.Find("Trophy/Icon");
|
||
if (trophyIcon == null) return;
|
||
|
||
// 缩放动画
|
||
Vector3 originalScale = trophyIcon.localScale;
|
||
trophyIcon.localScale = Vector3.zero;
|
||
|
||
float duration = 1f;
|
||
float t = 0f;
|
||
|
||
while (t < 1f)
|
||
{
|
||
t += Time.deltaTime / duration;
|
||
float ease = Mathf.SmoothStep(0f, 1f, t);
|
||
|
||
// 弹性效果
|
||
float scale = ease * (1f + 0.2f * Mathf.Sin(ease * Mathf.PI * 4f));
|
||
trophyIcon.localScale = originalScale * scale;
|
||
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
trophyIcon.localScale = originalScale;
|
||
}
|
||
|
||
private async Task WaitForSettlementConfirmation()
|
||
{
|
||
// 等待用户点击确认按钮
|
||
var confirmBtn = settlementPanel?.Find("Buttons/ConfirmBtn")?.GetComponent<Button>();
|
||
if (confirmBtn == null)
|
||
{
|
||
// 如果没有确认按钮,等待3秒后自动关闭
|
||
await Task.Delay(3000);
|
||
return;
|
||
}
|
||
|
||
bool confirmed = false;
|
||
confirmBtn.onClick.AddListener(() => confirmed = true);
|
||
|
||
// 等待用户确认
|
||
while (!confirmed)
|
||
{
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
confirmBtn.onClick.RemoveAllListeners();
|
||
}
|
||
|
||
private async Task ResetForNextRound()
|
||
{
|
||
Log("准备下一轮挖矿");
|
||
|
||
// 隐藏结算相关UI
|
||
if (settlementPanel) settlementPanel.gameObject.SetActive(false);
|
||
if (trophyDisplay) trophyDisplay.gameObject.SetActive(false);
|
||
|
||
// 重置采集场景
|
||
if (miningScene != null)
|
||
{
|
||
miningScene.ResetScene();
|
||
}
|
||
|
||
// 重置管理器数据(如果需要)
|
||
// _eventPartnerGatherManager?.ResetForNextRound();
|
||
|
||
await Task.Delay(500); // 短暂延迟确保重置完成
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 数据结构定义
|
||
|
||
[System.Serializable]
|
||
public class NodeRewards
|
||
{
|
||
public int Points;
|
||
public int Coins;
|
||
public int Experience;
|
||
public List<RewardItem> Items = new List<RewardItem>();
|
||
}
|
||
|
||
[System.Serializable]
|
||
public class RewardItem
|
||
{
|
||
public int ItemId;
|
||
public string Name;
|
||
public int Count;
|
||
}
|
||
|
||
[System.Serializable]
|
||
public class TrophyData
|
||
{
|
||
public int Id;
|
||
public string Name;
|
||
public string Description;
|
||
public TrophyRarity Rarity;
|
||
}
|
||
|
||
public enum TrophyRarity
|
||
{
|
||
Common,
|
||
Rare,
|
||
Epic,
|
||
Legendary
|
||
}
|
||
|
||
#endregion
|
||
|
||
private static void Log(object t)
|
||
{
|
||
Debug.Log($"<color=cyan>EventPartnerDrawMiningPanel -> {t} </color>");
|
||
}
|
||
}
|
||
} |