233 lines
8.7 KiB
C#
233 lines
8.7 KiB
C#
using Assets.MyGame.Scripts.Net;
|
||
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
|
||
using Assets.MyGame.Scripts.Net.APIResponses;
|
||
using DG.Tweening;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
public class UIHome_BY_UISignViewPanel : SubUIBase
|
||
{
|
||
//text_CoinNums是转盘各个位置的详细值 他从GameManager的activityConfig里面读取signConfigs后端配置的个数和text_CoinNums的个数是对应的 这里赋值的时候需要加上其他的数据公式是 值+值*GameManager的gameConfig的vip的signInAdd签到加成
|
||
/*签到API已封装在APIRequestList.SignIn(GET)
|
||
实测后端返回 {"data":440,"code":0,"info":null}——data直接是本次奖励的金币数字(不是SignInfo对象)
|
||
转盘停靠位置用奖励金额反查signConfigs得到(见FindSlotByReward)*/
|
||
//SLZPDieEvent这个脚本有之前写好的多层转盘的逻辑 签到这里只有单层 你可以直接看一下里面正确的
|
||
public List<Text> text_CoinNums;
|
||
public List<TextMeshProUGUI> text_CoinNums_TMP;
|
||
public TextMeshProUGUI tmp_VipJiaCheng; // 显示VIP签到加成百分比
|
||
|
||
public TextMeshProUGUI tmp_EndCoin; // 显示最终奖励金币数
|
||
public Button btn_ChouJiang;
|
||
public GameObject zhuanPan;
|
||
Dictionary<int, SignConfig> order_singConfig;
|
||
|
||
[Header("转盘参数(参照SLZPDieEvent)")]
|
||
public int spinRounds = 5; // 到达目标前转的圈数
|
||
public float rotateDuration = 2f; // 旋转总时长
|
||
public float dir = -1f; // 旋转方向(-1逆时针,1顺时针)
|
||
|
||
private bool isSpinning;
|
||
|
||
public override void Awake()
|
||
{
|
||
base.Awake();
|
||
btn_ChouJiang.onClick.AddListener(OnChouJiangClick);
|
||
}
|
||
|
||
protected override void OnShow(object args = null)
|
||
{
|
||
base.OnShow(args);
|
||
OnChouJiangClick();
|
||
RefreshPanel();
|
||
}
|
||
|
||
private void RefreshPanel()
|
||
{
|
||
BuildOrderSignConfig();
|
||
UpdateSlotTexts();
|
||
UpdateBtnState();
|
||
UpdateVipAddText();
|
||
}
|
||
|
||
/// <summary>显示VIP签到加成百分比(signInAdd 0.15 → +15%)</summary>
|
||
private void UpdateVipAddText()
|
||
{
|
||
if (tmp_VipJiaCheng == null) return;
|
||
double add = GetSignInAdd();
|
||
tmp_VipJiaCheng.text = $"+{add * 100:0}%";
|
||
}
|
||
|
||
/// <summary>从GameManager的activityConfig读取signConfigs,按order建立字典</summary>
|
||
private void BuildOrderSignConfig()
|
||
{
|
||
order_singConfig = new Dictionary<int, SignConfig>();
|
||
var signConfigs = GameManager.Instance.activityConfig?.signConfigs;
|
||
if (signConfigs == null) return;
|
||
foreach (var cfg in signConfigs)
|
||
{
|
||
if (cfg == null) continue;
|
||
order_singConfig[cfg.order] = cfg;
|
||
}
|
||
}
|
||
|
||
/// <summary>当前用户的VIP签到加成(gameConfig.vipLevelConfigs按用户vipLevel匹配signInAdd)</summary>
|
||
private double GetSignInAdd()
|
||
{
|
||
var vipConfigs = GameManager.Instance.gameConfig?.vipLevelConfigs;
|
||
if (vipConfigs == null || vipConfigs.Count == 0) return 0;
|
||
var user = GameManager.Instance.user;
|
||
if (user?.assets == null) return 0;
|
||
var vipCfg = vipConfigs.Find(v => v.level == user.assets.vipLevel);
|
||
return vipCfg?.signInAdd ?? 0;
|
||
}
|
||
|
||
/// <summary>转盘格子数量:UGUI Text列表有绑定就用它,否则用TMP列表</summary>
|
||
private int SlotCount()
|
||
{
|
||
if (text_CoinNums != null && text_CoinNums.Count > 0) return text_CoinNums.Count;
|
||
return text_CoinNums_TMP != null ? text_CoinNums_TMP.Count : 0;
|
||
}
|
||
|
||
/// <summary>给第index个转盘格子设置文本(兼容UGUI Text与TMP)</summary>
|
||
private void SetSlotText(int index, string value)
|
||
{
|
||
if (text_CoinNums != null && index < text_CoinNums.Count)
|
||
text_CoinNums[index].text = value;
|
||
else if (text_CoinNums_TMP != null && index < text_CoinNums_TMP.Count)
|
||
text_CoinNums_TMP[index].text = value;
|
||
}
|
||
|
||
/// <summary>转盘格子赋值:值+值*signInAdd(VIP签到加成),按order排序后与text_CoinNums一一对应</summary>
|
||
private void UpdateSlotTexts()
|
||
{
|
||
int slotCount = SlotCount();
|
||
if (order_singConfig == null || order_singConfig.Count == 0 || slotCount == 0) return;
|
||
|
||
var list = new List<SignConfig>(order_singConfig.Values);
|
||
list.Sort((a, b) => a.order - b.order);
|
||
|
||
double add = GetSignInAdd();
|
||
int count = Mathf.Min(list.Count, slotCount);
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
long gold = list[i].gold;
|
||
long showGold = gold + (long)Math.Round(gold * add);
|
||
SetSlotText(i, FormatGold(showGold));
|
||
}
|
||
}
|
||
|
||
/// <summary>今日是否已签到</summary>
|
||
private bool IsSignedToday()
|
||
{
|
||
var sign = GameManager.Instance.user?.sign;
|
||
return sign != null && sign.isSign;
|
||
}
|
||
|
||
private void UpdateBtnState()
|
||
{
|
||
btn_ChouJiang.interactable = !isSpinning && !IsSignedToday();
|
||
}
|
||
|
||
private void OnChouJiangClick()
|
||
{
|
||
if (isSpinning) return;
|
||
btn_ChouJiang.interactable = false;
|
||
|
||
// 后端SignIn的data直接返回本次奖励的金币数(数字)
|
||
APIRequestList.SignIn(reward =>
|
||
{
|
||
int slot = FindSlotByReward(reward);
|
||
if (slot < 0)
|
||
{
|
||
// 奖励金额匹配不到任何格子:不转转盘直接提示,避免停错位置
|
||
Debug.LogWarning($"[签到] 奖励{reward}在signConfigs里匹配不到对应格子");
|
||
OnSignInDone(reward);
|
||
return;
|
||
}
|
||
Debug.Log($"[签到] 奖励{reward} → 转盘格子{slot}");
|
||
RotateToSlot(slot, () => OnSignInDone(reward));
|
||
});
|
||
}
|
||
|
||
/// <summary>签到完成:提示奖励并刷新用户数据</summary>
|
||
private void OnSignInDone(long reward)
|
||
{
|
||
UIManager.Instance.ShowBoardError($"签到成功,获得{FormatGold(reward)}金币");
|
||
|
||
if (tmp_EndCoin != null)
|
||
tmp_EndCoin.text = FormatGold(reward);
|
||
|
||
APIRequestList.GetUser(user =>
|
||
{
|
||
GameManager.Instance.UpdataUserData(user);
|
||
UpdateBtnState();
|
||
});
|
||
}
|
||
|
||
/// <summary>按奖励金额反查转盘格子:优先匹配加了VIP加成后的值,其次匹配原始配置值;匹配不到返回-1</summary>
|
||
private int FindSlotByReward(long reward)
|
||
{
|
||
if (order_singConfig == null || order_singConfig.Count == 0 || SlotCount() == 0) return -1;
|
||
|
||
var list = new List<SignConfig>(order_singConfig.Values);
|
||
list.Sort((a, b) => a.order - b.order);
|
||
|
||
double add = GetSignInAdd();
|
||
int count = Mathf.Min(list.Count, SlotCount());
|
||
int fallback = -1;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
long gold = list[i].gold;
|
||
long withAdd = gold + (long)Math.Round(gold * add);
|
||
if (withAdd == reward) return i;
|
||
if (fallback < 0 && gold == reward) fallback = i;
|
||
}
|
||
return fallback;
|
||
}
|
||
|
||
/// <summary>单层转盘旋转停靠(参照SLZPDieEvent:LocalAxisAdd增量旋转+OutCubic缓动+完成强制校正)</summary>
|
||
private void RotateToSlot(int slot, Action onComplete)
|
||
{
|
||
isSpinning = true;
|
||
int cellCount = Mathf.Max(SlotCount(), 1);
|
||
float targetAngle = slot * (360f / cellCount);
|
||
|
||
float curr = zhuanPan.transform.localRotation.eulerAngles.z;
|
||
float deltaToTarget = AngleDiff(curr, targetAngle, dir);
|
||
float totalDelta = dir * spinRounds * 360f + deltaToTarget;
|
||
|
||
Tween tween = zhuanPan.transform.DOLocalRotate(new Vector3(0, 0, totalDelta), rotateDuration, RotateMode.LocalAxisAdd);
|
||
tween.SetEase(Ease.OutCubic);
|
||
tween.OnComplete(() =>
|
||
{
|
||
// 强制校正到精确目标角度,消除浮点误差
|
||
zhuanPan.transform.localRotation = Quaternion.Euler(0, 0, targetAngle);
|
||
isSpinning = false;
|
||
onComplete?.Invoke();
|
||
});
|
||
}
|
||
|
||
/// <summary>从curr到target的同向角度差(参照SLZPDieEvent.AngleDiff)</summary>
|
||
private float AngleDiff(float curr, float target, float dir)
|
||
{
|
||
float diff = (target - curr) % 360f;
|
||
if (diff < 0) diff += 360f;
|
||
if (dir < 0 && diff > 0) diff -= 360f;
|
||
return diff;
|
||
}
|
||
|
||
/// <summary>金币数值格式化(万/亿单位)</summary>
|
||
private static string FormatGold(long gold)
|
||
{
|
||
if (gold >= 100000000) // 亿
|
||
return $"{(gold / 100000000f):F1}亿";
|
||
if (gold >= 10000) // 万
|
||
return $"{(gold / 10000f):F1}万";
|
||
return gold.ToString("N0");
|
||
}
|
||
}
|