538 lines
20 KiB
C#
538 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Activity;
|
|
using asap.core;
|
|
using Capsule.Pack.Manager;
|
|
using Capsule.Reward;
|
|
using DG.Tweening;
|
|
using game;
|
|
using Game;
|
|
using GameCore;
|
|
using Newtonsoft.Json.Linq;
|
|
using TimeManager;
|
|
using TMPro;
|
|
using UniRx;
|
|
using UnityEngine;
|
|
using UnityEngine.Playables;
|
|
using UnityEngine.UI;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace Capsule.Pack.UIPanel
|
|
{
|
|
public class CapsulePackPanel : BasePanel
|
|
{
|
|
#region public
|
|
private CapsulePackManager _manager;
|
|
private CapsulePackManager Manager => _manager ??= ActivityResolver.Resolve<CapsulePackManager>();
|
|
private CapsulePackData Data => Manager?.LoadData();
|
|
|
|
public void SetData(CapsulePackManager manager)
|
|
{
|
|
_manager = manager;
|
|
}
|
|
|
|
#endregion
|
|
#region UI_ELEMENTS
|
|
|
|
private CapsuleRewardCell[] _rewardItems;
|
|
private TextMeshProUGUI _textTime;
|
|
private Button btn_buy;
|
|
private Button btn_probability;
|
|
private Button btn_close;
|
|
private TextMeshProUGUI p_text;
|
|
private RewardFlyBatchController RewardFlyBatchCtrl;
|
|
private PlayableDirector _panelDirector;
|
|
private Transform _knobAni;
|
|
private CapsuleRewardCell _dropReward;
|
|
|
|
#endregion UI_ELEMENTS
|
|
|
|
#region SerializeField
|
|
|
|
[Header("UI")][SerializeField] private PlayableAsset showFirstTimeline;
|
|
[SerializeField] private PlayableAsset showTimeline;
|
|
[SerializeField] private PlayableAsset outTimeline;
|
|
[SerializeField][Min(0)] private float knobAniTime = 0.3f;
|
|
|
|
[SerializeField]
|
|
[Min(0)]
|
|
[Tooltip("等待多久播放小球进场动画")]
|
|
private float showFirstDelayTime = 0.3f;
|
|
|
|
[SerializeField]
|
|
[Min(0)]
|
|
[Tooltip("小球入场的间隔时间")]
|
|
private float capsuleShowTime = 0.05f;
|
|
|
|
[SerializeField]
|
|
[Min(0)]
|
|
[Tooltip("奖励界面显示时间")]
|
|
private float showRewardPanelTime = 0.05f;
|
|
|
|
[SerializeField]
|
|
[Min(0)]
|
|
[Tooltip("等待小球掉落然后播回正动画")]
|
|
private float delayRevertDropTime = 0.5f;
|
|
|
|
[SerializeField]
|
|
[Min(0)]
|
|
[Tooltip("等待多长时间开始回正旋钮(由于奖励界面打开比较慢)")]
|
|
private float delayRevertKnobTime = 0.5f;
|
|
|
|
[Header("capsule rotation effect ")]
|
|
[SerializeField]
|
|
private float force;
|
|
|
|
[SerializeField] private Vector2 forceCenter;
|
|
[SerializeField] private ForceMode2D forceMode = ForceMode2D.Impulse;
|
|
[SerializeField] private float rotationAngle = 0.5f;
|
|
[SerializeField] private float gravityInRotation = 10f;
|
|
[SerializeField] private float gravityOnEnd = 100f;
|
|
[SerializeField] private Vector2 sizeInRotation = new Vector2(150f, 150f);
|
|
[SerializeField] private Vector2 sizeOnEnd = new Vector2(175f, 175f);
|
|
|
|
[SerializeField]
|
|
[Tooltip("小球回正花费的时间")]
|
|
private float revertZeroMinTime = 0.1f;
|
|
[SerializeField]
|
|
[Tooltip("小球回正花费的时间")]
|
|
private float revertZeroMaxTime = 0.1f;
|
|
[SerializeField]
|
|
[Tooltip("小球速度小于多少播回正动画")]
|
|
private float speedThreshold = 0.1f;
|
|
|
|
[Header("capsule timeline effect ")]
|
|
[SerializeField]
|
|
private PlayableAsset rewardTimeline;
|
|
|
|
[SerializeField] private PlayableAsset rewardLastTimeline;
|
|
|
|
[SerializeField]
|
|
[Tooltip("delay Play reward Timeline Time <color=red>rewardDelayTime+rotationTime的时间是总的旋转时间</color>")]
|
|
private float rewardDelayTime = 0.2f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("remain rotation time <color=red>rewardDelayTime+rotationTime的时间是总的旋转时间</color>")]
|
|
private float rotationTime = 0.2f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("delay Play reward Timeline Time On last <color=red>rewardDelayTime+rotationTime的时间是总的旋转时间</color>")]
|
|
private float lastRewardDelayTime = 0.2f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("remain rotation time On last <color=red>rewardDelayTime+rotationTime的时间是总的旋转时间</color>")]
|
|
private float lastRotationTime = 0.2f;
|
|
|
|
#endregion
|
|
|
|
private bool _isRotating = false;
|
|
private bool _isEnterAniPlaying = false;
|
|
private bool _isRoundCompleted = false;
|
|
private CancellationTokenSource _enterAniCancellationTokenSource;
|
|
private HashSet<CapsuleRewardCell> _rotationCells = new HashSet<CapsuleRewardCell>();
|
|
private ITimeTickService _tickService;
|
|
|
|
private void Awake()
|
|
{
|
|
_tickService = GContext.container.Resolve<ITimeTickService>();
|
|
_textTime = gameObject.FindChildGameObject("text_time").GetComponent<TextMeshProUGUI>();
|
|
_rewardItems = gameObject.FindChildGameObject("rewardContainer")
|
|
.GetComponentsInChildren<CapsuleRewardCell>();
|
|
btn_buy = gameObject.FindChildGameObject(nameof(btn_buy)).GetComponentInChildren<Button>();
|
|
btn_buy.onClick.AddListener(OnClickBuy);
|
|
btn_probability = gameObject.FindChildGameObject(nameof(btn_probability)).GetComponent<Button>();
|
|
btn_probability.onClick.AddListener(OnClickProbability);
|
|
btn_close = gameObject.FindChildGameObject(nameof(btn_close)).GetComponent<Button>();
|
|
btn_close.onClick.AddListener(OnClickClose);
|
|
p_text = btn_buy.gameObject.FindChildGameObject(nameof(p_text)).GetComponentInChildren<TextMeshProUGUI>();
|
|
_panelDirector = GetComponent<PlayableDirector>();
|
|
_knobAni = gameObject.FindChildGameObject("xuanniu").transform;
|
|
_dropReward = gameObject.FindChildGameObject("mask_reward").GetComponentInChildren<CapsuleRewardCell>();
|
|
_tickService?.SecondTick?.Subscribe(UpdateTimer).AddTo(disposables);
|
|
_enterAniCancellationTokenSource = new CancellationTokenSource();
|
|
PlayEnterAni(_enterAniCancellationTokenSource.Token);
|
|
}
|
|
|
|
protected override void OnDestroy()
|
|
{
|
|
// 取消正在进行的动画
|
|
if (_enterAniCancellationTokenSource != null)
|
|
{
|
|
_enterAniCancellationTokenSource.Cancel();
|
|
_enterAniCancellationTokenSource.Dispose();
|
|
_enterAniCancellationTokenSource = null;
|
|
}
|
|
|
|
btn_buy.onClick.RemoveAllListeners();
|
|
btn_probability.onClick.RemoveAllListeners();
|
|
btn_close.onClick.RemoveAllListeners();
|
|
_rotationCells.Clear();
|
|
_rotationCells = null;
|
|
base.OnDestroy();
|
|
}
|
|
|
|
private async void OnClickProbability()
|
|
{
|
|
try
|
|
{
|
|
UITypes.CapsulePackProbabilityPanel.SetType("OfferCapsuleRewardsProbabilityPopupPanel");
|
|
var panelGo = await UIManager.Instance.ShowUILoad(UITypes.CapsulePackProbabilityPanel);
|
|
panelGo.GetComponent<GeneralProbabilityPanel>().Init(Data.GetProbabilityData());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
GContext.OnEvent<RewardPanelClose>().Subscribe(OnRewardPanelClose).AddTo(disposables);
|
|
InitUI();
|
|
ShowUI();
|
|
btn_close.enabled = false;
|
|
}
|
|
|
|
private async void PlayEnterAni(CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
_rotationCells.Clear();
|
|
_isEnterAniPlaying = true;
|
|
if (Data.CurrentProgressCount == 0)
|
|
{
|
|
foreach (var cell in _rewardItems)
|
|
cell.Init();
|
|
_panelDirector.Play(showFirstTimeline);
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
await Awaiters.Seconds(showFirstDelayTime);
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
foreach (var cell in _rewardItems)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
await cell.PlayEnterAni(capsuleShowTime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_panelDirector.Play(showTimeline);
|
|
foreach (var cell in _rewardItems)
|
|
cell.PlayEnterAni2();
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
await Awaiters.Seconds((float)_panelDirector.duration);
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
finally
|
|
{
|
|
_isEnterAniPlaying = false;
|
|
SetButtonState(true);
|
|
}
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (_isEnterAniPlaying) return;
|
|
if (_rewardItems == null) return;
|
|
foreach (var cell in _rewardItems)
|
|
{
|
|
if (!cell.isActiveAndEnabled) continue;
|
|
if (_isRotating)
|
|
{
|
|
var ballPos = cell.RectTransform.anchoredPosition;
|
|
var dir = (ballPos - forceCenter).normalized;
|
|
dir = Quaternion.Euler(0, 0, rotationAngle) * -dir;
|
|
if (cell.Rigidbody2D)
|
|
cell.Rigidbody2D.AddForce(dir * force, forceMode);
|
|
}
|
|
if (_rotationCells.Contains(cell)) continue;
|
|
if (cell.transform.rotation == Quaternion.Euler(Vector3.zero)) continue;
|
|
if (cell.Rigidbody2D.velocity.magnitude <= speedThreshold)
|
|
{
|
|
_rotationCells.Add(cell);
|
|
cell.Rigidbody2D.freezeRotation = true;
|
|
cell.RectTransform.localEulerAngles = new Vector3(0, 0, cell.RectTransform.localEulerAngles.z);
|
|
|
|
var currentAngle = cell.RectTransform.localEulerAngles.z;
|
|
// 将角度转换到 -180~180 范围
|
|
if (currentAngle > 180f) currentAngle -= 360f;
|
|
|
|
var revertZeroTime = Random.Range(revertZeroMinTime, revertZeroMaxTime);
|
|
|
|
// 根据角度正负选择旋转方向
|
|
if (currentAngle < 0)
|
|
cell.transform.DORotate(new Vector3(0, 0, 360f), revertZeroTime, RotateMode.FastBeyond360);
|
|
else
|
|
cell.transform.DORotate(Vector3.zero, revertZeroTime, RotateMode.FastBeyond360);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StopBallRotation()
|
|
{
|
|
_isRotating = false;
|
|
if (!_isRoundCompleted)
|
|
ShowUI();
|
|
else
|
|
foreach (var rewardCell in _rewardItems)
|
|
rewardCell.gameObject.SetActive(false);
|
|
}
|
|
|
|
private void UpdateTimer(long obj)
|
|
{
|
|
_textTime.text = ConvertTools.ConvertTime2(Data.RemainingTime);
|
|
}
|
|
|
|
private void InitUI()
|
|
{
|
|
_textTime.text = string.Empty;
|
|
p_text.text = string.Empty;
|
|
_dropReward.gameObject.SetActive(false);
|
|
}
|
|
|
|
private void ShowUI()
|
|
{
|
|
if (Data == null) return;
|
|
ShowBtn();
|
|
_textTime.text = ConvertTools.ConvertTime2(Data.RemainingTime);
|
|
var rewards = Data.GetRemainRewards();
|
|
for (int i = 0; i < _rewardItems.Length; i++)
|
|
{
|
|
var rewardItem = _rewardItems[i];
|
|
if (i < rewards.Count)
|
|
{
|
|
rewardItem.SetData(rewards[i]);
|
|
rewardItem.ShowUI();
|
|
rewardItem.gameObject.SetActive(true);
|
|
}
|
|
else
|
|
rewardItem.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
private void ShowBtn()
|
|
{
|
|
if (!Data.IsActive)
|
|
{
|
|
this.gameObject.SetActive(false);
|
|
return;
|
|
}
|
|
|
|
var packConfig = Data.GetPackConfig();
|
|
var iapItem = _manager.Tables.TbIAPItemList?.GetOrDefault(packConfig.IAPID);
|
|
var sKUDetailDataEvent = new SKUDetailDataEvent(iapItem);
|
|
GContext.Publish(sKUDetailDataEvent);
|
|
p_text.text = sKUDetailDataEvent.price;
|
|
}
|
|
|
|
[ContextMenu("OnClickBuy")]
|
|
private async void OnClickBuy()
|
|
{
|
|
try
|
|
{
|
|
var packConfig = Data.GetPackConfig();
|
|
|
|
var iapItem = Data.Tables.TbIAPItemList?.GetOrDefault(packConfig.IAPID);
|
|
var reward = Data.GetRandomRewardIndex();
|
|
|
|
var shopBuyTypeData = new ShopBuyTypeData
|
|
{
|
|
NoShow = true,
|
|
extraPurcheData = new JObject
|
|
{
|
|
{nameof(CapsulePackData), reward.Index },
|
|
{"EventID",Data.EventID},
|
|
{"Progress",Data.CurrentProgressCount },
|
|
{"DropID",reward.DropID}
|
|
}
|
|
};
|
|
|
|
var itemDatas = GContext.container.Resolve<PlayerItemData>()
|
|
.GetItemDataByDropIdLureInflation(Data.InflationRate, reward.DropID);
|
|
if (itemDatas == null)
|
|
{
|
|
Debug.LogError($"itemDatas is null, dropID:{reward.DropID}");
|
|
return;
|
|
}
|
|
|
|
if (iapItem == null)
|
|
{
|
|
GContext.Publish(new ShowData(itemDatas, RewardType.NormalOne));
|
|
GContext.container.Resolve<PlayerItemData>().AddItem(itemDatas, null);
|
|
await OnBuySuccess(reward);
|
|
return;
|
|
}
|
|
|
|
var result = await GContext.container.Resolve<PlayerShopData>()
|
|
.OnBuy(packConfig.DropID, shopBuyTypeData, iapItem, null, RewardType.NormalOne);
|
|
if (result)
|
|
{
|
|
GContext.Publish(new ShowData(itemDatas, RewardType.NormalOne));
|
|
GContext.container.Resolve<PlayerItemData>().AddItem(itemDatas, null);
|
|
await OnBuySuccess(reward);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
private async Task OnBuySuccess(ICapsuleReward reward)
|
|
{
|
|
_dropReward.SetData(reward);
|
|
_dropReward.ShowUI();
|
|
_rotationCells.Clear();
|
|
_manager.AddProgress(reward.Index, Data, ref _isRoundCompleted);
|
|
SetButtonState(false);
|
|
await PlayKnobAni();
|
|
await PlayRotationAni();
|
|
await Awaiters.Seconds(showRewardPanelTime);
|
|
GContext.Publish(new ShowData());
|
|
await Awaiters.Seconds(delayRevertKnobTime);
|
|
PlayKnobRevertAni();
|
|
SetButtonState(true);
|
|
}
|
|
|
|
private void SetButtonState(bool isActive)
|
|
{
|
|
btn_buy.transform.parent.gameObject.SetActive(isActive);
|
|
btn_close.enabled = isActive;
|
|
}
|
|
|
|
private async Task PlayKnobAni()
|
|
{
|
|
GContext.Publish(new EventUISound("audio_ui_offercapsuleicebound_knob_turnon"));
|
|
await _knobAni.transform.DORotate(new Vector3(0, 0, -180), knobAniTime, RotateMode.LocalAxisAdd)
|
|
.AsyncWaitForCompletion();
|
|
}
|
|
|
|
private void PlayKnobRevertAni()
|
|
{
|
|
_knobAni.transform.DORotate(new Vector3(0, 0, -180), knobAniTime, RotateMode.LocalAxisAdd);
|
|
}
|
|
|
|
private async Task PlayRotationAni()
|
|
{
|
|
try
|
|
{
|
|
if (_rewardItems == null) return;
|
|
foreach (var cell in _rewardItems)
|
|
{
|
|
if (cell.Rigidbody2D)
|
|
{
|
|
cell.Rigidbody2D.gravityScale = gravityInRotation;
|
|
cell.Rigidbody2D.freezeRotation = false;
|
|
}
|
|
|
|
if (cell.Collider)
|
|
{
|
|
cell.Collider.size = sizeInRotation;
|
|
}
|
|
}
|
|
_isRotating = true;
|
|
float remainTime = 0;
|
|
if (_isRoundCompleted)
|
|
{
|
|
await Awaiters.Seconds(lastRewardDelayTime);
|
|
_panelDirector.Play(rewardLastTimeline);
|
|
remainTime = lastRotationTime;
|
|
}
|
|
else
|
|
{
|
|
await Awaiters.Seconds(rewardDelayTime);
|
|
_panelDirector.Play(rewardTimeline);
|
|
remainTime = rotationTime;
|
|
}
|
|
|
|
_dropReward.gameObject.SetActive(true);
|
|
await Awaiters.Seconds(remainTime);
|
|
|
|
_isRotating = false;
|
|
StopBallRotation();
|
|
|
|
foreach (var cell in _rewardItems)
|
|
{
|
|
if (!cell.Rigidbody2D) continue;
|
|
cell.Rigidbody2D.gravityScale = gravityOnEnd;
|
|
await Awaiters.NextFrame;
|
|
if (cell.Collider) cell.Collider.size = sizeOnEnd;
|
|
}
|
|
var timelineRemainTime = (float)_panelDirector.duration - remainTime;
|
|
await Awaiters.Seconds(timelineRemainTime < 0 ? 0 : timelineRemainTime);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
public void OnRewardPanelClose(RewardPanelClose e)
|
|
{
|
|
try
|
|
{
|
|
if (_dropReward) _dropReward?.gameObject?.SetActive(false);
|
|
if (!_isRoundCompleted) return;
|
|
_isRoundCompleted = false;
|
|
// if(Data.IsActive)
|
|
// RefreshUI();
|
|
// else
|
|
OnClickClose();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogError($"jsd {exception}");
|
|
}
|
|
}
|
|
|
|
private void RefreshUI()
|
|
{
|
|
_panelDirector.Play(outTimeline);
|
|
_panelDirector.stopped -= ShowNewRound;
|
|
_panelDirector.stopped += ShowNewRound;
|
|
}
|
|
|
|
private void ShowNewRound(PlayableDirector director)
|
|
{
|
|
ShowUI();
|
|
// 取消正在进行的动画
|
|
if (_enterAniCancellationTokenSource != null)
|
|
{
|
|
_enterAniCancellationTokenSource.Cancel();
|
|
_enterAniCancellationTokenSource.Dispose();
|
|
_enterAniCancellationTokenSource = null;
|
|
}
|
|
|
|
_enterAniCancellationTokenSource = new CancellationTokenSource();
|
|
PlayEnterAni(_enterAniCancellationTokenSource.Token);
|
|
_panelDirector.stopped -= ShowNewRound;
|
|
}
|
|
private async void OnClickClose()
|
|
{
|
|
try
|
|
{
|
|
SetButtonState(false);
|
|
await PlayOutAni();
|
|
UIManager.Instance.DestroyUI(UITypes.CapsulePackPanel);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
private async Task PlayOutAni()
|
|
{
|
|
_panelDirector.Play(outTimeline);
|
|
await Awaiters.Seconds((float)outTimeline.duration);
|
|
}
|
|
}
|
|
} |