NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/UI/UI_BuyEnergyController.cs

650 lines
19 KiB
C#
Raw Normal View History


/****************************************************
2024-05-15 13:18:54 +08:00
文件:Assets/Code/Scripts/Gameplay/UI/UI_BuyEnergyController.cs
作者:yuntao-Hu
时间:2024/5
描述:补充体力UI界面
说明:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
using Button = UnityEngine.UI.Button;
using Image = UnityEngine.UI.Image;
using Toggle = UnityEngine.UI.Toggle;
using ScrollView = PhxhSDK.PhxhScrollView;
using Framework;
using System;
using Gameplay;
using Cysharp.Threading.Tasks;
using UnityEngine.Events;
using System.Threading;
using static UnityEngine.Rendering.DebugUI;
using Gameplay.Net;
using Gameplay.Common;
using cfg.ActorCfg;
using PhxhSDK;
using LeanCloud.Realtime.Internal.Protocol;
2024-05-15 13:18:54 +08:00
using static UI_BuyEnergyController;
2024-05-15 13:18:54 +08:00
public class UI_BuyEnergyController : UIWindow
{
#region enum
/// <summary>
/// 补充体力方式 ( 与窗口内顺序对应 )
/// </summary>
public enum RechargeMethod
{
Recharge_byItem,
Recharge_byCurrency
}//TODO 考虑后续以json方式存储补充体力方式信息
#endregion
#region Temporary_Constant
private const int DEFAULT_USAGE_AMOUNT = 1;//默认使用数目,使用时的最小量
/// <summary>
/// 体力药物品ID
/// </summary>
private const int ENERGY_ITEM_ID = 8;//TODO 体力药物品ID,考虑加入globalconfig
private readonly int[] itemIDs = { ENERGY_ITEM_ID, GLConfig.Inst.data.DiamondItemId };//TODO 考虑后续将补充体力方式改为json配置后需要调整传参
#endregion
#region UI GameObj
//UI GameObj
///Auto Gen Start///
public Image Image_BG;
public TMP_Text Text_Title;
public TMP_Text Text_Currency;
public Button Button_Close;
public Image Image_DetailBG;
public TMP_Text Text_Detail;
public Image Image_BtnBar;
public TMP_InputField Text_NumInputField;
public Button Button_Min;
public Image Image_Min;
public TMP_Text Text_Min;
public Button Button_Minus;
public Button Button_Max;
public Image Image_Max;
public TMP_Text Text_Max;
public Button Button_Plus;
public Image Image_ItemBG;
public Image Image_ItemRect;
public Image Image_MovePoint;
public Image Image_Arrow;
public TMP_Text Text_NowCount;
public TMP_Text Text_AfterCount;
public Button Button_Return;
public TMP_Text Text_Return;
public Button Button_Buy;
public TMP_Text Text_Buy;
///Auto Gen End///
private RawImage rawImageGaussianBlurMask;
#endregion
#region class
/// <summary>
/// 体力恢复计时器
/// </summary>
class EnergyRefreshTimer : IDisposable
{
private TimeSpan EnergyRecoveryTimer;
private uint m_CurrEnergy;
public uint CurrEnergy
{
get
{
return m_CurrEnergy;
}
}
public UnityEvent onTimerRunOut;
private CancellationTokenSource cts = new CancellationTokenSource();
public void Init()
{
onTimerRunOut = new UnityEvent();
}
public void OnEnable()
{
GetDataFromNet();
onTimerRunOut.AddListener(GetDataFromNet);
}
public void OnDisable()
{
onTimerRunOut.RemoveAllListeners();
Dispose();
}
public void GetDataFromNet()
{
m_CurrEnergy = PlayUIInfoHelper.Instance.GetNowEnergy();
if (m_CurrEnergy >= PlayUIInfoHelper.Instance.GetMaxEnergyPoint())
{
onTimerRunOut.RemoveAllListeners();
return;
}//if energy full, stop timer
DateTime curTime = CommonUtils.GetCurTime(CommonUtils.E_TimeType.Server);
EnergyRecoveryTimer = PlayUIInfoHelper.Instance.GetNextEnergyRecoverTime() - curTime;
StartRefresh(cts.Token);
}
public async void StartRefresh(CancellationToken token)
{
await UniTask.Delay((int)EnergyRecoveryTimer.TotalSeconds, cancellationToken: token);
onTimerRunOut.Invoke();
}
public void Dispose()
{
cts.Cancel();
}
void OnDestroy()
{
onTimerRunOut.RemoveAllListeners();
Dispose();
}
~EnergyRefreshTimer()
{
OnDestroy();
}
}
/// <summary>
/// 用于存放补充体力消耗物与获得体力转化率的类
/// </summary>
class ConvertRatesWrap
{
private const int CONVERSION_RATE_ITEM_TOENERGY = 1;
private const int CONVERSION_RATE_ENERGY_FROMITEM = 30;
public List<ConversionRate> convertRates;
public ConvertRatesWrap()
{
convertRates = new List<ConversionRate>();
//TODO 根据后续需求,考虑用配置读入转化率
AddRate(CONVERSION_RATE_ITEM_TOENERGY, CONVERSION_RATE_ENERGY_FROMITEM);
AddRate(GLConfig.Inst.data.DiamondExchangeEnergy);
}
void AddRate(int consume, int recharge)
{
convertRates.Add(new ConversionRate(consume, recharge));
}
void AddRate(List<int> exchangeRate)
{
if (exchangeRate.Count >= 2)
convertRates.Add(new ConversionRate(exchangeRate[0], exchangeRate[1]));
else
DebugUtil.LogError("转化率list传入参数不足!");
}
//API
public int GetConsume(RechargeMethod method, int usageAmount = 1)
{
if ((int)method < convertRates.Count)
{
return usageAmount * convertRates[(int)method].consume;
}
else
{
DebugUtil.LogError("补充体力方式枚举类与转换率数组长度不匹配!请检查!");
return usageAmount * convertRates[0].consume;
}
}
public int GetRecharge(RechargeMethod method, int usageAmount = 1)
{
if ((int)method < convertRates.Count)
{
return usageAmount * convertRates[(int)method].recharge;
}
else
{
DebugUtil.LogError("补充体力方式枚举类与转换率数组长度不匹配!请检查!");
return usageAmount * convertRates[0].recharge;
}
}
//class
public class ConversionRate
{
public int consume;
public int recharge;
public ConversionRate(int consume, int recharge)
{
this.consume = consume;
this.recharge = recharge;
}
}
}
class MethodButtonObj: UIGameObjectWrapper
{
private GameObject chosenCircle;
private TextMeshProUGUI itemAmountText;
private Image buttonIcon;
//TODO 考虑后续,将补充体力方式改为json配置后,可能需要调整传参
public MethodButtonObj(GameObject root, int i, int itemID, bool isScaleShow = true) : base(root, i, isScaleShow)
{
chosenCircle = FindObj("ChooseRect");
itemAmountText = GetComponent<TextMeshProUGUI>("ImageCount/TextCount");
buttonIcon = GetComponent<Image>("ItemIcon");
RefreshIcon(itemID);
}
public void BindListener(Action callback)
{
BindButton(GameObject.GetComponent<Button>(), callback);
}
async void RefreshIcon(int itemID)
{
var spritePath = CommonUtils.GetItemPath(itemID);
buttonIcon.sprite = await LoadAssetAsync<Sprite>(spritePath);
}
public void SetChosen(bool isChosen)
{
chosenCircle.SetActive(isChosen);
}
public void SetItemAmount(uint amount)
{
itemAmountText.text = amount.ToString();
}
public void SetActive(bool isActive)
{
GameObject.SetActive(isActive);
}
}
#endregion
#region Button_Listener
public void OnClickClose()
{
CloseWindow();
}
public void OnMinClick()
{
SetUsageAmountAndRefresh(DEFAULT_USAGE_AMOUNT);
}
public void OnMinusClick()
{
if (m_UsageAmount > DEFAULT_USAGE_AMOUNT)
{
m_UsageAmount--;
SetUsageAmountAndRefresh(m_UsageAmount);
}
}
public void OnMaxClick()
{
SetUsageAmountAndRefresh(MaxConsumeAmountWithoutConvert());
}
public void OnPlusClick()
{
if (m_UsageAmount < MaxConsumeAmountWithoutConvert())
{
m_UsageAmount++;
SetUsageAmountAndRefresh(m_UsageAmount);
}
}
public void OnReturnClick()
{
CloseWindow();
}
public void OnBuyClick()
{
if (m_UsageAmount <= MaxConsumeAmountWithoutConvert() && m_UsageAmount > 0)
{
SendMessageToServer();
CloseWindow();
}
else
{
#if UNITY_EDITOR
DebugUtil.Log("购买体力数目超出可选择范围!");
#endif
//TODO 弹出提示消息框,消息内容后续考虑配置方式
CommonUtils.ShowMessageTips("体力补充失败!");
}
}
#endregion
private List<MethodButtonObj> rechargeMethodButtons = new List<MethodButtonObj>();
private Dictionary<RechargeMethod, uint> storageItemCount = new Dictionary<RechargeMethod, uint>();
private int m_UsageAmount;
private RechargeMethod m_RechargeMethod;
private ConvertRatesWrap convertRatesWrap;
private EnergyRefreshTimer energyRefreshController;
/// <summary>
/// 打开购买体力界面
/// </summary>
public static async UniTask<UIWindow> Open()
{
CommonUtils.CaptureScreenshot();
2024-05-15 13:18:54 +08:00
return await UIManager.Instance.OpenWindow(UINameConst.UI_BuyEnergy);
}
// deal with input data
public override void PreLoad(object data = null)
{
}
// Use this for initialization
public override void OnInit()
{
//bind UI GameObj
2024-05-15 13:18:54 +08:00
UI_BuyEnergyBinder.GetComponents(this);
convertRatesWrap = new ConvertRatesWrap();
rawImageGaussianBlurMask = GetComponent<RawImage>("GaussianBlurMask");
rawImageGaussianBlurMask.texture = CommonUtils.screenTexture;
GetStorageFromServer();
BindButton();
BindInputFieldListener();
InitEnergyRefreshTimer();
}
/// <summary>
/// 根据体力刷新情况即时更新界面内体力数目显示
/// </summary>
void InitEnergyRefreshTimer()
{
energyRefreshController = new EnergyRefreshTimer();
energyRefreshController.Init();
energyRefreshController.onTimerRunOut.AddListener(() =>
{
RefreshDetailText(convertRatesWrap.GetConsume(m_RechargeMethod, m_UsageAmount), convertRatesWrap.GetRecharge(m_RechargeMethod, m_UsageAmount));
});
}
void BindInputFieldListener()
{
Text_NumInputField.onEndEdit.AddListener(SnapInputToStep);
}
/// <summary>
/// 由于货币输入框输入的数值不一定是阶梯值,需要对输入的数值做向下取整到阶梯值
/// </summary>
/// <param name="inputAmount"></param>
void SnapInputToStep(string inputAmount)
{
int parsedValue;
if (int.TryParse(inputAmount, out parsedValue))
{
// 对输入的数据做向下取整到阶梯值
int snappedValue = (int)(parsedValue / convertRatesWrap.GetConsume(m_RechargeMethod));
if (snappedValue < DEFAULT_USAGE_AMOUNT)
{
snappedValue = DEFAULT_USAGE_AMOUNT;
}
if (snappedValue > MaxConsumeAmountWithoutConvert())
{
snappedValue = MaxConsumeAmountWithoutConvert();
}
m_UsageAmount = snappedValue;
SetUsageAmountAndRefresh(m_UsageAmount);
}
else
{
DebugUtil.LogError("错误输入内容,已舍弃");
SetUsageAmountAndRefresh(m_UsageAmount);
}
}
/// <summary>
/// 注意先初始化storageItemCount字典,再绑定按钮
/// </summary>
void BindButton()
{
BindMethodButton();
Button_Buy.onClick.AddListener(OnBuyClick);
Button_Close.onClick.AddListener(OnClickClose);
Button_Min.onClick.AddListener(OnMinClick);
Button_Minus.onClick.AddListener(OnMinusClick);
Button_Max.onClick.AddListener(OnMaxClick);
Button_Plus.onClick.AddListener(OnPlusClick);
Button_Return.onClick.AddListener(OnReturnClick);
}
/// <summary>
/// 注意先初始化storageItemCount字典,再绑定补充体力方式选择按钮
/// </summary>
void BindMethodButton()
{
//体力补充方式选择按钮
ScrollView methodButtonView = Image_ItemBG.GetComponentInChildren<ScrollView>();
if (methodButtonView == null)
{
DebugUtil.LogError("体力补充方式按钮列表获取失败!");
return;
}
else
{
GameObject buttonPrefab = null;
foreach (Transform buttonGameObj in methodButtonView.content)
{
buttonGameObj.gameObject.SetActive(false);
if (buttonGameObj.GetComponent<Button>() != null)
buttonPrefab = buttonGameObj.gameObject;
}
for (int i = 0; i < Enum.GetValues(typeof(RechargeMethod)).Length; i++)
{
var methodButton = new MethodButtonObj(buttonPrefab, i, itemIDs[i]);
methodButton.SetActive(true);
rechargeMethodButtons.Add(methodButton);
RechargeMethod method = (RechargeMethod)i;
methodButton.BindListener(() =>
{
if (method != m_RechargeMethod)
OnChooseMethod(method);
});
if (storageItemCount.ContainsKey(method))
methodButton.SetItemAmount(storageItemCount[method]);
else
DebugUtil.LogError("未找到" + method.ToString() + "对应的物品数目!检查正确性或是否初始化storage字典");
}
}
}
//invoke when open window
protected override void OnShowWindow(object data = null)
{
energyRefreshController.OnEnable();
//TODO 后续补充体力方式增加后,可能要考虑其他物品
if (isItemRemained(RechargeMethod.Recharge_byItem))
{
OnChooseMethod(RechargeMethod.Recharge_byItem);
}
else
{
HideMethodButton(RechargeMethod.Recharge_byItem);
OnChooseMethod(RechargeMethod.Recharge_byCurrency);
}
SetUsageAmountAndRefresh(amount: DEFAULT_USAGE_AMOUNT);
}
void HideMethodButton(RechargeMethod method)
{
if((int)method < rechargeMethodButtons.Count)
{
rechargeMethodButtons[(int)method].SetActive(false);
}
else
{
DebugUtil.LogError("不存在" + method.ToString() + "对应的按钮!");
}
}
//invoke when reload window
protected override void OnReloadWindow(object data = null)
{
}
//invoke when close window
protected override void OnHideWindow()
{
energyRefreshController.OnDisable();
}
//invoke when destroy
public override void OnRelease()
{
foreach(var btn in rechargeMethodButtons)
{
btn.Dispose();
}
base.OnRelease();
}
/// <summary>
/// 初始化补充体力物品数目
/// </summary>
void GetStorageFromServer()
{
//storageItemCount字典查找物品数目,为后续扩展
uint m_ItemAmount = Actor.Instance.Item.GetItemCount(ENERGY_ITEM_ID);
if (!storageItemCount.ContainsKey(RechargeMethod.Recharge_byItem))
{
storageItemCount.Add(RechargeMethod.Recharge_byItem, m_ItemAmount);
}
uint m_CurrencyAmount = Actor.Instance.Item.GetItemCount(GLConfig.Inst.data.DiamondItemId);
if (!storageItemCount.ContainsKey(RechargeMethod.Recharge_byCurrency))
{
storageItemCount.Add(RechargeMethod.Recharge_byCurrency, m_CurrencyAmount);
}
}
/// <summary>
/// 体力药是否剩余(可用于判断初始化时的补充体力方式)
/// </summary>
/// <returns></returns>
bool isItemRemained(RechargeMethod method)
{
if (storageItemCount.ContainsKey(method))
return storageItemCount[method] > 0;
else
{
DebugUtil.LogError("未找到" + method.ToString() + "对应的物品数目!检查正确性或是否初始化storage字典");
return false;
}
}
/// <summary>
/// 选中补充体力方式Button
/// </summary>
/// <param name="chosenMethod"></param>
void OnChooseMethod(RechargeMethod chosenMethod)
{
MethodButtonObj preChosenButton = rechargeMethodButtons[(int)m_RechargeMethod];
MethodButtonObj curChosenButton = rechargeMethodButtons[(int)chosenMethod];
m_RechargeMethod = chosenMethod;
preChosenButton.SetChosen(false);
curChosenButton.SetChosen(true);
//切换按钮后,重置界面内相关数目显示
SetUsageAmountAndRefresh(amount: DEFAULT_USAGE_AMOUNT);
}
/// <summary>
/// 获取当前选中状态下的最大消耗数目(实际数据需要乘比例转化)
/// </summary>
/// <returns></returns>
int MaxConsumeAmountWithoutConvert()
{
//道具数目限制
uint AmountInStorage = storageItemCount[m_RechargeMethod];
int maxConsumeRate = (int)(AmountInStorage / convertRatesWrap.GetConsume(m_RechargeMethod));
//体力上限限制
DataItem energyData = TableManager.Instance.Tables.Item.Get(GLConfig.Inst.data.EnergyPointID);
uint energyToMax = (uint)energyData.MaxCount - energyRefreshController.CurrEnergy;
int maxConsumeRateByEnergy = (int)(energyToMax / convertRatesWrap.GetRecharge(m_RechargeMethod));
return Math.Min(maxConsumeRate, maxConsumeRateByEnergy);
}
/// <summary>
/// 设定使用数目,并刷新界面内相关数目显示
/// </summary>
/// <param name="amount"></param>
void SetUsageAmountAndRefresh(int amount)
{
m_UsageAmount = amount;
int consume = convertRatesWrap.GetConsume(m_RechargeMethod, m_UsageAmount);
int recharge = convertRatesWrap.GetRecharge(m_RechargeMethod, m_UsageAmount);
RefreshDetailText(consume, recharge);
}
/// <summary>
/// 刷新文字显示
/// </summary>
/// <param name="consume">实际道具消耗显示数</param>
/// <param name="recharge">转化体力数量</param>
void RefreshDetailText(int consume,int recharge)
{
uint curEnergy = energyRefreshController.CurrEnergy;
Text_NumInputField.text = consume.ToString();
Text_NowCount.text = curEnergy.ToString();
Text_AfterCount.text = (curEnergy + recharge).ToString();
//TODO 配置对应消耗物的json数据
DataItem dataItem = TableManager.Instance.Tables.Item.Get(ENERGY_ITEM_ID);
DataItem diamond = TableManager.Instance.Tables.Item.Get(GLConfig.Inst.data.DiamondItemId);
string[] itemNames = { "个" + CommonUtils.GetLocalizeText(dataItem.NameLocal), CommonUtils.GetLocalizeText(diamond.NameLocal) };
//TODO UI提示信息考虑用其他方式存储方便后续调整
Text_Detail.text = string.Format("是否使用{0}{1}补充{2}指挥点数?", consume, itemNames[(int)m_RechargeMethod], recharge);
}
void SendMessageToServer()
{
int id = itemIDs[(int)m_RechargeMethod];
#if UNITY_EDITOR
DebugUtil.Log("体力购买请求");
#endif
NetHelper.ConvertResources((uint)id, (uint)m_UsageAmount, (uint)GLConfig.Inst.data.EnergyPointID);
}
}