This commit is contained in:
2026-10-19 19:02:29 +08:00
parent 7b1695aa73
commit a71c6873e2
18 changed files with 523 additions and 91 deletions
@@ -79,9 +79,18 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
public GameObject dieFXPrefab; // 基础死亡特效
public GameObject dieFXPrefabAward; // 普通鱼金币奖励特效
public GameObject dieFXPrefabAwardProp; // 道具效果
public GameObject dieFXPrefabAwardTmp; // Boss 分数浮动文本
public GameObject dieFXPrefabAwardTmp_AddScore;
// ----------------------------------------------------------------
// 道具掉落配置(掉落数据由服务端 RoomKillNotice.drop 下发,客户端只负责展示)
// ----------------------------------------------------------------
[Header("道具掉落(服务端驱动)")]
[Tooltip("道具掉落预制体(挂 FishKillProp 脚本)。为空则不展示掉落特效")]
public GameObject propDropPrefab;
// ----------------------------------------------------------------
// 生成系统运行时数据
// ----------------------------------------------------------------
@@ -2015,17 +2024,15 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
// ⭐ 掉落道具推送去重:服务端把"击杀爆奖"和"掉落道具"拆成两条 RoomKillNotice 下发,
// 第二条与击杀消息同 fishId/killtime/gold,但只有 drop、没有 param。
// Boss 鱼 DieFish 是延迟回收(字典未即时移除),第二条若按击杀再处理一次:
// ① not-self 模式下 _pending[key] 被覆盖 → 前一个"爆奖中"BJEndUI 失去引用、永久残留在座位上;
// ② 重复克隆爆奖特效、重复走 DieFish → 特效/音效/爆奖双份(两个 Event 实例各出一次 bjEndUI)。
// 客户端目前没有掉落弹窗逻辑(见 RoomKillNotice.drop 注释),直接跳过;将来做掉落 UI 时在此处理。
// 此条消息不走击杀流程(避免重复爆奖),只创建道具掉落特效并飞向击杀者座位。
// ⚠️ param 是 JToken:JSON "param":null 会被反序列化成 JValue(Null)(Type==JTokenType.Null),
// 不能只判 C# null(与 BossKillParam.cs 的判空写法保持一致),否则第二条漏拦 → 重复爆奖。
if (killFish.drop != null
&& (killFish.param == null || killFish.param.Type == JTokenType.Null))
{
NetLog.Log($"[CreateFishManager] 掉落道具推送(跳过击杀处理):fishId={killFish.fishId} " +
NetLog.Log($"[CreateFishManager] 掉落道具推送:fishId={killFish.fishId} " +
$"prop={killFish.drop.propNumber}×{killFish.drop.count}");
HandleDropProp(killFish);
return;
}
@@ -2467,6 +2474,92 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
}
#endregion
// ================================================================
// 道具掉落(服务端驱动:RoomKillNotice.drop 下发掉落数据)
// ================================================================
/// <summary>
/// 处理服务端下发的道具掉落推送(RoomKillNotice.drop 不为空、param 为空的消息)。
/// 与金币流程对称:EffectPool 取预制体→转 Canvas 坐标→InitProp 设置编号和座位终点→
/// SetCoinCount→FlyToSeat 飞到座位回收。
/// 道具图标按 /uploads/{propNumber}.png 从服务端下载(由 FishKillProp 内部完成)。
/// </summary>
private void HandleDropProp(RoomKillNotice killFish)
{
if (propDropPrefab == null || killFish.drop == null) return;
if (string.IsNullOrEmpty(killFish.drop.propNumber)) return;
if (currGameType == null) return;
// 找击杀者座位
SitPosData sitData = null;
foreach (var item in currGameType.list_Sits)
{
if (item.user == killFish.userId)
{
sitData = item;
break;
}
}
if (sitData == null)
{
NetLog.Log($"[CreateFishManager] 掉落道具找不到座位:userId={killFish.userId}");
return;
}
// 找鱼位置(鱼可能已回收,找不到就用座位位置兜底)
Vector3 dropPos = sitData.transform.position;
if (fishDatas_hashCodekey.TryGetValue(killFish.fishId, out var monster) && monster != null)
dropPos = monster.transform.position;
// 创建道具特效(和金币一样 autoReturn=false,由 FlyToSeat 手动回池)
var propObj = EffectPool.Instance.Get(
propDropPrefab,
dropPos, Quaternion.identity,
currGameType.subPanelParent.parent,
autoReturn: false);
if (propObj == null) return;
// 坐标转换(和金币一致:世界坐标转 UI Canvas 坐标)
try
{
propObj.GetComponent<RectTransform>().anchoredPosition =
CoordinateTransformation.WorldPos_ToCanvas(dropPos, GetUiCanvas());
}
catch
{
propObj.transform.position = dropPos;
}
// 初始化道具:编号、数量、起点、终点(座位)、玩家信息
var fishKillProp = propObj.GetComponent<FishKillProp>();
if (fishKillProp == null)
{
// 预制体没挂 FishKillProp 脚本 → 回收并退出
EffectPool.Instance.Return(propObj);
return;
}
fishKillProp.InitProp(
killFish.drop.propNumber,
killFish.drop.count,
dropPos,
sitData.transform.position,
killFish.userId,
sitData.posID,
null);
fishKillProp.JiaFen = (audioID) =>
{
AudioManager.Instance.PlayerFX(audioID);
// TODO: 道具拾取后的业务逻辑(如刷新背包、弹获得面板等)
};
fishKillProp.SetCoinCount();
fishKillProp.FlyToSeat();
GameManager.LogFishFlow("DROP_PROP", killFish.fishId,
$"prop={killFish.drop.propName}×{killFish.drop.count} 座位={sitData.posID} 击杀者={killFish.userId}",
propObj.GetInstanceID());
}
+2 -1
View File
@@ -40,7 +40,8 @@ namespace Assets.MyGame.Scripts.Net
dic_API[APIType.ActivityConfig_DiamondShopList] = "/api/ActivityConfig/diamondShopList";
dic_API[APIType.ActivityConfig_GoldShopList] = "/api/ActivityConfig/goldShopList";
dic_API[APIType.ActivityConfig_ReliefConfig] = "/api/ActivityConfig/reliefConfig";
dic_API[APIType.ActivityConfig_SignConfigs] = "/api/ActivityConfig/signConfigs";
dic_API[APIType.ActivityConfig_SignConfig] = "/api/ActivityConfig/signConfig";
dic_API[APIType.Hall_GetSignStatus] = "/api/Hall/GetSignStatus";
dic_API[APIType.ActivityConfig_RegisterConfig] = "/api/ActivityConfig/registerConfig";
dic_API[APIType.ActivityConfig_BattleOrderConfig] = "/api/ActivityConfig/battleOrderConfig";
dic_API[APIType.ActivityConfig_FishingReturnConfig] = "/api/ActivityConfig/fishingReturnConfig";
@@ -43,11 +43,11 @@ namespace Assets.MyGame.Scripts.Net
}
/// <summary>
/// 获取签到池配置(GET)
/// 获取签到活动配置(GET /api/ActivityConfig/signConfig,响应 SignActivityConfig:isOpen/cannotInterrupt/days)
/// </summary>
public static void GetSignConfigs(Action<List<SignConfig>> callback)
public static void GetSignConfig(Action<SignActivityConfig> callback)
{
APIManager.Response<List<SignConfig>>(APIType.ActivityConfig_SignConfigs, callback);
APIManager.Response<SignActivityConfig>(APIType.ActivityConfig_SignConfig, callback);
}
/// <summary>
@@ -123,13 +123,21 @@ namespace Assets.MyGame.Scripts.Net
}
/// <summary>
/// 签到(GET)
/// 参数:用户ID
/// 返回:long(data为本次签到奖励的金币数,后端直接返回数字,不是SignInfo对象)
/// 签到(GET /api/Hall/SignIn)
/// 返回:SignRewardResult(day=第几天,gold/diamond/product=本次奖励,服务端已含VIP加成)
/// </summary>
public static void SignIn(Action<long> callback)
public static void SignIn(Action<SignRewardResult> callback)
{
APIManager.Response<long>(APIType.Hall_SignIn, callback);
APIManager.Response<SignRewardResult>(APIType.Hall_SignIn, callback);
}
/// <summary>
/// 获取签到状态(GET /api/Hall/GetSignStatus)
/// 返回:SignStatusResult(当前签到进度与可领取状态)
/// </summary>
public static void GetSignStatus(Action<SignStatusResult> callback)
{
APIManager.Response<SignStatusResult>(APIType.Hall_GetSignStatus, callback);
}
/// <summary>
@@ -38,9 +38,9 @@ namespace Assets.MyGame.Scripts.Net.API
public ReliefConfig reliefConfig;
/// <summary>
/// 签到池配置
/// 签到活动配置
/// </summary>
public List<SignConfig> signConfigs;
public SignActivityConfig signActivityConfig;
/// <summary>
/// 金币商城配置
@@ -1,22 +1,40 @@
using System;
using System.Collections.Generic;
namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass
{
/// <summary>
/// 签到奖励配置
/// 签到活动配置(Swagger: SignActivityConfig,ActivityConfig.signActivityConfig)
/// </summary>
[Serializable]
public class SignActivityConfig
{
/// <summary>
/// 是否开启
/// </summary>
public bool isOpen;
/// <summary>
/// 不可中断:true=漏签不回退(按累计次数循环);false=可中断(漏签重置回第1天)
/// </summary>
public bool cannotInterrupt;
/// <summary>
/// 7 天奖励配置(按 day 1~7)
/// </summary>
public List<SignConfig> days;
}
/// <summary>
/// 单日签到奖励配置(Swagger: SignConfig)
/// </summary>
[Serializable]
public class SignConfig
{
/// <summary>
/// 排序
/// 第几天(1~7)
/// </summary>
public int order;
/// <summary>
/// 金币
/// </summary>
public long gold;
public int day;
/// <summary>
/// 名称
@@ -29,8 +47,8 @@ namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass
public string icon;
/// <summary>
/// 概率
/// 奖励列表(GiftConfig.type: 1=金币 2=钻石 3=道具)
/// </summary>
public double probability;
public List<GiftConfig> giftList;
}
}
@@ -85,18 +85,6 @@ namespace Assets.MyGame.Scripts.Net.APIResponses
public int addDiamond; // 本次增加钻石
}
/// <summary>
/// 签到响应
/// </summary>
[Serializable]
public class SignInfo
{
public int day; // 第几天签到
public bool isToday; // 今日是否已签到
public long rewardGold; // 奖励金币(改用long防溢出)
public long rewardDiamond;// 奖励钻石(改用long防溢出)
}
/// <summary>
/// 用户信息(切换炮台、翅膀、战神)
/// </summary>
@@ -0,0 +1,43 @@
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
using Assets.MyGame.Scripts.Net.API.UserData_SubClass;
using System;
using System.Collections.Generic;
namespace Assets.MyGame.Scripts.Net.APIResponses
{
/// <summary>
/// 签到奖励结果(GET /api/Hall/SignIn 响应)
/// </summary>
[Serializable]
public class SignRewardResult
{
/// <summary>本次签到对应第几天(1~7)</summary>
public int day;
/// <summary>本次奖励金币</summary>
public long gold;
/// <summary>本次奖励钻石</summary>
public int diamond;
/// <summary>本次奖励道具列表</summary>
public List<BaseProductCount> product;
}
/// <summary>
/// 签到状态(GET /api/Hall/GetSignStatus 响应):当前签到进度与可领取状态
/// </summary>
[Serializable]
public class SignStatusResult
{
/// <summary>签到活动是否开启</summary>
public bool isOpen;
/// <summary>不可中断:true=漏签不回退(按累计次数循环);false=可中断(漏签重置)</summary>
public bool cannotInterrupt;
/// <summary>当前周期已完成到第几天(0~7)</summary>
public int signDay;
/// <summary>今天签到对应的天数(已签=已签天数;未签=下一次目标天数)</summary>
public int todayDay;
/// <summary>今天是否已签到</summary>
public bool isTodaySigned;
/// <summary>7 天奖励配置</summary>
public List<SignConfig> days;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f11d508fb63c9084ab3948ee61ea44a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -22,6 +22,11 @@ namespace Assets.MyGame.Scripts.Net.API.UserData_SubClass
/// 签到时间
/// </summary>
public long signTime;
/// <summary>
/// 当前已签到的周期天数(0=未开始,1~7)
/// </summary>
public int signDay;
}
}
+5 -2
View File
@@ -31,8 +31,11 @@ namespace Assets.MyGame.Scripts.Net
/// <summary>救济金配置(GET)</summary>
ActivityConfig_ReliefConfig,
/// <summary>签到池配置(GET)</summary>
ActivityConfig_SignConfigs,
/// <summary>签到活动配置(GET)</summary>
ActivityConfig_SignConfig,
/// <summary>获取签到状态(GET)</summary>
Hall_GetSignStatus,
/// <summary>注册配置(GET)</summary>
ActivityConfig_RegisterConfig,
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 17115695c4c201946b804b772100142e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,137 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &8222732459988217625
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8802702133347106559}
- component: {fileID: 2822715846151478221}
m_Layer: 5
m_Name: Prop
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8802702133347106559
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8222732459988217625}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.4, y: 0.4, z: 0.4}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 6250418086618249309}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2822715846151478221
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8222732459988217625}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dcf86da74e3bba947934891ba2a69902, type: 3}
m_Name:
m_EditorClassIdentifier:
tMP_CoinCount: {fileID: 0}
gold: {fileID: 0}
silver: {fileID: 0}
endPoint: {x: 0, y: 0, z: 0}
isBoss: 0
isLocalCoin: 0
coinCount: 0
userID:
sitID: 0
startPoint: {x: 0, y: 0, z: 0}
propIco: {fileID: 8869491853869306038}
--- !u!1 &8236831357060171625
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6250418086618249309}
- component: {fileID: 4330466387219907929}
- component: {fileID: 8869491853869306038}
m_Layer: 5
m_Name: Coin
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6250418086618249309
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8236831357060171625}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8802702133347106559}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 49.999985, y: -50}
m_SizeDelta: {x: 80, y: 80}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4330466387219907929
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8236831357060171625}
m_CullTransparentMesh: 1
--- !u!114 &8869491853869306038
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8236831357060171625}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 4f84a65ffddb6cc43a280cab2ed65ca2, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 742f2b00809071748adce9b6c5b9d1f7
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,92 @@
using Assets.Framework.Tool;
using Assets.MyGame.Scripts.Net;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 鱼死亡掉落道具脚本(与 CoinScript 同级,均继承 BJBase)。
/// 行为类似金币:原地掉落 → 飞向击杀者座位 → 回收。
/// 掉落数据由服务端 RoomKillNotice.drop 下发(propNumber/propName/count),
/// 道具图标按 /uploads/{propNumber}.png 从服务端下载。
/// </summary>
public class FishKillProp : BJBase
{
/// <summary>道具图标(Inspector 绑定)</summary>
public Image propIco;
/// <summary>
/// 重写基类方法:道具不显示金币数字,而是下载并设置道具图标。
/// 和 CoinScript.SetCoinCount() 对称,供外部统一调用。
/// </summary>
public override void SetCoinCount()
{
// 修正 z 轴(和 CoinScript 一致,防止 UI 层级下 z 不为 0 导致被遮挡)
var v3 = transform.position;
v3.z = 0;
transform.position = v3;
}
/// <summary>
/// 初始化道具(扩展 BJBase.Init,增加道具编号)。
/// 由 CreateFishManager 在收到服务端掉落推送时调用。
/// </summary>
/// <param name="propNumber">道具编号(用于下载图标 /uploads/编号.png)</param>
/// <param name="propCount">掉落数量</param>
/// <param name="startPoint">掉落起点(鱼死亡位置)</param>
/// <param name="endPoint">飞行终点(击杀者座位位置)</param>
/// <param name="userID">击杀者 ID</param>
/// <param name="sitID">座位 ID</param>
/// <param name="jobj">附加数据(透传,回调时原样返回)</param>
public void InitProp(string propNumber, int propCount, Vector3 startPoint, Vector3 endPoint,
string userID = "", int sitID = 999, object jobj = null)
{
this.startPoint = startPoint;
// 复用基类 Init 设置通用字段(道具不涉及金币数量,coinCount 传 0)
Init(0, endPoint, userID, sitID, jobj);
// 下载道具图标:固定格式 /uploads/{propNumber}.png
DownloadPropIcon(propNumber);
}
/// <summary>
/// 从服务端下载道具图标并设置到 propIco。
/// 失败时 propIco 保留预制体上配置好的默认图(不强制兜底重试)。
/// </summary>
private void DownloadPropIcon(string propNumber)
{
if (string.IsNullOrEmpty(propNumber) || propIco == null) return;
string url = APIManager.baseURL + "/uploads/" + propNumber + ".png";
WebTool.DownImage(url, tex =>
{
// 回调时本物体可能已被销毁(飞到座位后回池/切场景),需判空
if (this == null || propIco == null) return;
if (tex != null)
{
propIco.sprite = Sprite.Create(tex,
new Rect(0, 0, tex.width, tex.height),
Vector2.one * 0.5f);
propIco.SetNativeSize();
propIco.gameObject.SetActive(true);
}
});
}
/// <summary>
/// 飞到座位位置并回收(效果同 coinEvent.HuiShou)。
/// 可由动画事件调用(预制体有掉落动画时),也可直接调用实现原地掉落后立即飞向座位。
/// </summary>
public void FlyToSeat()
{
transform.DOMove(endPoint, 0.5f).OnComplete(() =>
{
// 拾取回调(由 CreateFishManager 设置 JiaFen)
JiaFen?.Invoke(0);
// 拾取音效(复用金币拾取音效)
AudioManager.Instance.PlayerFX(61009);
// 池化回收
EffectPool.Instance.Return(gameObject);
});
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcf86da74e3bba947934891ba2a69902
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+12 -12
View File
@@ -61420,7 +61420,7 @@ PrefabInstance:
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
@@ -61430,7 +61430,7 @@ PrefabInstance:
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
propertyPath: m_AnchorMin.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
@@ -61480,12 +61480,12 @@ PrefabInstance:
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
value: 1140.2
objectReference: {fileID: 0}
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
value: -366.5
objectReference: {fileID: 0}
- target: {fileID: 6597416787623719195, guid: 0627c90518880634a948686e845a5161,
type: 3}
@@ -62533,7 +62533,7 @@ PrefabInstance:
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
@@ -62543,7 +62543,7 @@ PrefabInstance:
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
propertyPath: m_AnchorMin.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
@@ -62593,12 +62593,12 @@ PrefabInstance:
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
value: 242
objectReference: {fileID: 0}
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
value: -366.5
objectReference: {fileID: 0}
- target: {fileID: 3618039173474018553, guid: 76edf10e1a3669a4cbe017114b08c6a2,
type: 3}
@@ -63329,7 +63329,7 @@ PrefabInstance:
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
@@ -63339,7 +63339,7 @@ PrefabInstance:
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
propertyPath: m_AnchorMin.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
@@ -63389,12 +63389,12 @@ PrefabInstance:
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
value: 691.1
objectReference: {fileID: 0}
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
value: -366.5
objectReference: {fileID: 0}
- target: {fileID: 2455563011306354547, guid: f3b4b23ca2531734fa3699e11040b988,
type: 3}
@@ -11,10 +11,9 @@ using UnityEngine.UI;
public class UIHome_BY_UISignViewPanel : SubUIBase
{
//text_CoinNums是转盘各个位置的详细值 他从GameManager的activityConfig里面读取signConfigs后端配置的个数和text_CoinNums的个数是对应的 这里赋值的时候需要加上其他的数据公式是 值+值*GameManager的gameConfig的vip的signInAdd签到加成
//text_CoinNums是转盘各个位置的详细值 他从GameManager的activityConfig里面读取signActivityConfig.days后端配置的个数和text_CoinNums的个数是对应的 这里赋值的时候需要加上其他的数据公式是 值+值*GameManager的gameConfig的vip的signInAdd签到加成
/*签到API已封装在APIRequestList.SignIn(GET)
实测后端返回 {"data":440,"code":0,"info":null}——data直接是本次奖励的金币数字(不是SignInfo对象)
转盘停靠位置用奖励金额反查signConfigs得到(见FindSlotByReward)*/
响应为SignRewardResult:{day, gold, diamond, product}——day=本次签到天数(1~7),转盘直接按day停靠格子(不再金额反查)*/
//SLZPDieEvent这个脚本有之前写好的多层转盘的逻辑 签到这里只有单层 你可以直接看一下里面正确的
public List<Text> text_CoinNums;
public List<TextMeshProUGUI> text_CoinNums_TMP;
@@ -23,7 +22,7 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
public TextMeshProUGUI tmp_EndCoin; // 显示最终奖励金币数
public Button btn_ChouJiang;
public GameObject zhuanPan;
Dictionary<int, SignConfig> order_singConfig;
Dictionary<int, SignConfig> day_singConfig; // key=第几天(1~7)
[Header("转盘参数(参照SLZPDieEvent)")]
public int spinRounds = 5; // 到达目标前转的圈数
@@ -47,7 +46,7 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
private void RefreshPanel()
{
BuildOrderSignConfig();
BuildDaySignConfig();
UpdateSlotTexts();
UpdateBtnState();
UpdateVipAddText();
@@ -61,16 +60,16 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
tmp_VipJiaCheng.text = $"+{add * 100:0}%";
}
/// <summary>从GameManager的activityConfig读取signConfigs,按order建立字典</summary>
private void BuildOrderSignConfig()
/// <summary>从GameManager的activityConfig读取signActivityConfig.days,按day建立字典</summary>
private void BuildDaySignConfig()
{
order_singConfig = new Dictionary<int, SignConfig>();
var signConfigs = GameManager.Instance.activityConfig?.signConfigs;
if (signConfigs == null) return;
foreach (var cfg in signConfigs)
day_singConfig = new Dictionary<int, SignConfig>();
var days = GameManager.Instance.activityConfig?.signActivityConfig?.days;
if (days == null) return;
foreach (var cfg in days)
{
if (cfg == null) continue;
order_singConfig[cfg.order] = cfg;
day_singConfig[cfg.day] = cfg;
}
}
@@ -101,25 +100,33 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
text_CoinNums_TMP[index].text = value;
}
/// <summary>转盘格子赋值:值+值*signInAdd(VIP签到加成),按order排序后与text_CoinNums一一对应</summary>
/// <summary>转盘格子赋值:金币值+值*signInAdd(VIP签到加成),按day排序后与text_CoinNums一一对应</summary>
private void UpdateSlotTexts()
{
int slotCount = SlotCount();
if (order_singConfig == null || order_singConfig.Count == 0 || slotCount == 0) return;
if (day_singConfig == null || day_singConfig.Count == 0 || slotCount == 0) return;
var list = new List<SignConfig>(order_singConfig.Values);
list.Sort((a, b) => a.order - b.order);
var list = new List<SignConfig>(day_singConfig.Values);
list.Sort((a, b) => a.day - b.day);
double add = GetSignInAdd();
int count = Mathf.Min(list.Count, slotCount);
for (int i = 0; i < count; i++)
{
long gold = list[i].gold;
long gold = GetGiftGold(list[i]);
long showGold = gold + (long)Math.Round(gold * add);
SetSlotText(i, FormatGold(showGold));
}
}
/// <summary>取单日配置中的金币奖励数(giftList 中 type=1 金币项;无金币项返回0)</summary>
private static long GetGiftGold(SignConfig cfg)
{
if (cfg?.giftList == null) return 0;
var goldGift = cfg.giftList.Find(g => g != null && g.type == 1);
return goldGift != null ? goldGift.count : 0;
}
/// <summary>今日是否已签到</summary>
private bool IsSignedToday()
{
@@ -137,19 +144,24 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
if (isSpinning) return;
btn_ChouJiang.interactable = false;
// 后端SignIn的data直接返回本次奖励的金币数(数字)
APIRequestList.SignIn(reward =>
// SignIn 返回 SignRewardResult:day=本次签到天数,gold/diamond/product=本次奖励(服务端已含VIP加成)
APIRequestList.SignIn(res =>
{
int slot = FindSlotByReward(reward);
if (slot < 0)
if (res == null)
{
// 奖励金额匹配不到任何格子:不转转盘直接提示,避免停错位置
Debug.LogWarning($"[签到] 奖励{reward}在signConfigs里匹配不到对应格子");
OnSignInDone(reward);
OnSignInDone(0);
return;
}
Debug.Log($"[签到] 奖励{reward} → 转盘格子{slot}");
RotateToSlot(slot, () => OnSignInDone(reward));
int slot = SlotIndexByDay(res.day);
if (slot < 0)
{
// day 匹配不到格子:不转转盘直接提示,避免停错位置
Debug.LogWarning($"[签到] 第{res.day}天在签到配置里匹配不到对应格子");
OnSignInDone(res.gold);
return;
}
Debug.Log($"[签到] 第{res.day}天奖励{res.gold}金币 → 转盘格子{slot}");
RotateToSlot(slot, () => OnSignInDone(res.gold));
});
}
@@ -168,25 +180,20 @@ public class UIHome_BY_UISignViewPanel : SubUIBase
});
}
/// <summary>按奖励金额反查转盘格子:优先匹配加了VIP加成后的值,其次匹配原始配置值;匹配不到返回-1</summary>
private int FindSlotByReward(long reward)
/// <summary>按签到天数定位转盘格子:days按day升序后的下标;匹配不到返回-1</summary>
private int SlotIndexByDay(int day)
{
if (order_singConfig == null || order_singConfig.Count == 0 || SlotCount() == 0) return -1;
if (day_singConfig == null || day_singConfig.Count == 0 || SlotCount() == 0) return -1;
var list = new List<SignConfig>(order_singConfig.Values);
list.Sort((a, b) => a.order - b.order);
var list = new List<SignConfig>(day_singConfig.Values);
list.Sort((a, b) => a.day - b.day);
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;
if (list[i].day == day) return i;
}
return fallback;
return -1;
}
/// <summary>单层转盘旋转停靠(参照SLZPDieEvent:LocalAxisAdd增量旋转+OutCubic缓动+完成强制校正)</summary>