diff --git a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs index 71aa4acea..03a0b7f34 100644 --- a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs +++ b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs @@ -79,9 +79,18 @@ public class CreateFishManager : MonoSingleton 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 // ⭐ 掉落道具推送去重:服务端把"击杀爆奖"和"掉落道具"拆成两条 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 } #endregion + // ================================================================ + // 道具掉落(服务端驱动:RoomKillNotice.drop 下发掉落数据) + // ================================================================ + + /// + /// 处理服务端下发的道具掉落推送(RoomKillNotice.drop 不为空、param 为空的消息)。 + /// 与金币流程对称:EffectPool 取预制体→转 Canvas 坐标→InitProp 设置编号和座位终点→ + /// SetCoinCount→FlyToSeat 飞到座位回收。 + /// 道具图标按 /uploads/{propNumber}.png 从服务端下载(由 FishKillProp 内部完成)。 + /// + 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().anchoredPosition = + CoordinateTransformation.WorldPos_ToCanvas(dropPos, GetUiCanvas()); + } + catch + { + propObj.transform.position = dropPos; + } + + // 初始化道具:编号、数量、起点、终点(座位)、玩家信息 + var fishKillProp = propObj.GetComponent(); + 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()); + } + diff --git a/Assets/MyGame/Scripts/Net/APIManager.cs b/Assets/MyGame/Scripts/Net/APIManager.cs index 3a20bcf21..39735a68b 100644 --- a/Assets/MyGame/Scripts/Net/APIManager.cs +++ b/Assets/MyGame/Scripts/Net/APIManager.cs @@ -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"; diff --git a/Assets/MyGame/Scripts/Net/APIRequestList_Activity.cs b/Assets/MyGame/Scripts/Net/APIRequestList_Activity.cs index 902e4b4ff..6860a7cba 100644 --- a/Assets/MyGame/Scripts/Net/APIRequestList_Activity.cs +++ b/Assets/MyGame/Scripts/Net/APIRequestList_Activity.cs @@ -43,11 +43,11 @@ namespace Assets.MyGame.Scripts.Net } /// - /// 获取签到池配置(GET) + /// 获取签到活动配置(GET /api/ActivityConfig/signConfig,响应 SignActivityConfig:isOpen/cannotInterrupt/days) /// - public static void GetSignConfigs(Action> callback) + public static void GetSignConfig(Action callback) { - APIManager.Response>(APIType.ActivityConfig_SignConfigs, callback); + APIManager.Response(APIType.ActivityConfig_SignConfig, callback); } /// diff --git a/Assets/MyGame/Scripts/Net/APIRequestList_Hall.cs b/Assets/MyGame/Scripts/Net/APIRequestList_Hall.cs index 9920f1a99..95899cf90 100644 --- a/Assets/MyGame/Scripts/Net/APIRequestList_Hall.cs +++ b/Assets/MyGame/Scripts/Net/APIRequestList_Hall.cs @@ -123,13 +123,21 @@ namespace Assets.MyGame.Scripts.Net } /// - /// 签到(GET) - /// 参数:用户ID - /// 返回:long(data为本次签到奖励的金币数,后端直接返回数字,不是SignInfo对象) + /// 签到(GET /api/Hall/SignIn) + /// 返回:SignRewardResult(day=第几天,gold/diamond/product=本次奖励,服务端已含VIP加成) /// - public static void SignIn(Action callback) + public static void SignIn(Action callback) { - APIManager.Response(APIType.Hall_SignIn, callback); + APIManager.Response(APIType.Hall_SignIn, callback); + } + + /// + /// 获取签到状态(GET /api/Hall/GetSignStatus) + /// 返回:SignStatusResult(当前签到进度与可领取状态) + /// + public static void GetSignStatus(Action callback) + { + APIManager.Response(APIType.Hall_GetSignStatus, callback); } /// diff --git a/Assets/MyGame/Scripts/Net/APIResponses/ActivityConfig.cs b/Assets/MyGame/Scripts/Net/APIResponses/ActivityConfig.cs index b5c0d546f..2dbeedf3a 100644 --- a/Assets/MyGame/Scripts/Net/APIResponses/ActivityConfig.cs +++ b/Assets/MyGame/Scripts/Net/APIResponses/ActivityConfig.cs @@ -38,9 +38,9 @@ namespace Assets.MyGame.Scripts.Net.API public ReliefConfig reliefConfig; /// - /// 签到池配置 + /// 签到活动配置 /// - public List signConfigs; + public SignActivityConfig signActivityConfig; /// /// 金币商城配置 diff --git a/Assets/MyGame/Scripts/Net/APIResponses/GameConfig_SubClass/SignConfig.cs b/Assets/MyGame/Scripts/Net/APIResponses/GameConfig_SubClass/SignConfig.cs index e2822ac45..754113430 100644 --- a/Assets/MyGame/Scripts/Net/APIResponses/GameConfig_SubClass/SignConfig.cs +++ b/Assets/MyGame/Scripts/Net/APIResponses/GameConfig_SubClass/SignConfig.cs @@ -1,22 +1,40 @@ using System; +using System.Collections.Generic; namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass { /// - /// 签到奖励配置 + /// 签到活动配置(Swagger: SignActivityConfig,ActivityConfig.signActivityConfig) + /// + [Serializable] + public class SignActivityConfig + { + /// + /// 是否开启 + /// + public bool isOpen; + + /// + /// 不可中断:true=漏签不回退(按累计次数循环);false=可中断(漏签重置回第1天) + /// + public bool cannotInterrupt; + + /// + /// 7 天奖励配置(按 day 1~7) + /// + public List days; + } + + /// + /// 单日签到奖励配置(Swagger: SignConfig) /// [Serializable] public class SignConfig { /// - /// 排序 + /// 第几天(1~7) /// - public int order; - - /// - /// 金币 - /// - public long gold; + public int day; /// /// 名称 @@ -29,8 +47,8 @@ namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass public string icon; /// - /// 概率 + /// 奖励列表(GiftConfig.type: 1=金币 2=钻石 3=道具) /// - public double probability; + public List giftList; } } diff --git a/Assets/MyGame/Scripts/Net/APIResponses/HallResponses.cs b/Assets/MyGame/Scripts/Net/APIResponses/HallResponses.cs index 0e0646198..a9e04c5d0 100644 --- a/Assets/MyGame/Scripts/Net/APIResponses/HallResponses.cs +++ b/Assets/MyGame/Scripts/Net/APIResponses/HallResponses.cs @@ -85,18 +85,6 @@ namespace Assets.MyGame.Scripts.Net.APIResponses public int addDiamond; // 本次增加钻石 } - /// - /// 签到响应 - /// - [Serializable] - public class SignInfo - { - public int day; // 第几天签到 - public bool isToday; // 今日是否已签到 - public long rewardGold; // 奖励金币(改用long防溢出) - public long rewardDiamond;// 奖励钻石(改用long防溢出) - } - /// /// 用户信息(切换炮台、翅膀、战神) /// diff --git a/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs b/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs new file mode 100644 index 000000000..92cb1e251 --- /dev/null +++ b/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs @@ -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 +{ + /// + /// 签到奖励结果(GET /api/Hall/SignIn 响应) + /// + [Serializable] + public class SignRewardResult + { + /// 本次签到对应第几天(1~7) + public int day; + /// 本次奖励金币 + public long gold; + /// 本次奖励钻石 + public int diamond; + /// 本次奖励道具列表 + public List product; + } + + /// + /// 签到状态(GET /api/Hall/GetSignStatus 响应):当前签到进度与可领取状态 + /// + [Serializable] + public class SignStatusResult + { + /// 签到活动是否开启 + public bool isOpen; + /// 不可中断:true=漏签不回退(按累计次数循环);false=可中断(漏签重置) + public bool cannotInterrupt; + /// 当前周期已完成到第几天(0~7) + public int signDay; + /// 今天签到对应的天数(已签=已签天数;未签=下一次目标天数) + public int todayDay; + /// 今天是否已签到 + public bool isTodaySigned; + /// 7 天奖励配置 + public List days; + } +} diff --git a/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs.meta b/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs.meta new file mode 100644 index 000000000..d5774ce27 --- /dev/null +++ b/Assets/MyGame/Scripts/Net/APIResponses/SignResponses.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f11d508fb63c9084ab3948ee61ea44a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/Scripts/Net/APIResponses/UserData_SubClass/SignInfo.cs b/Assets/MyGame/Scripts/Net/APIResponses/UserData_SubClass/SignInfo.cs index 952a8f9a0..daea7f8e0 100644 --- a/Assets/MyGame/Scripts/Net/APIResponses/UserData_SubClass/SignInfo.cs +++ b/Assets/MyGame/Scripts/Net/APIResponses/UserData_SubClass/SignInfo.cs @@ -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 /// 签到时间 /// public long signTime; + + /// + /// 当前已签到的周期天数(0=未开始,1~7) + /// + public int signDay; } } diff --git a/Assets/MyGame/Scripts/Net/APIType.cs b/Assets/MyGame/Scripts/Net/APIType.cs index 4e815da2d..93884009d 100644 --- a/Assets/MyGame/Scripts/Net/APIType.cs +++ b/Assets/MyGame/Scripts/Net/APIType.cs @@ -31,8 +31,11 @@ namespace Assets.MyGame.Scripts.Net /// 救济金配置(GET) ActivityConfig_ReliefConfig, - /// 签到池配置(GET) - ActivityConfig_SignConfigs, + /// 签到活动配置(GET) + ActivityConfig_SignConfig, + + /// 获取签到状态(GET) + Hall_GetSignStatus, /// 注册配置(GET) ActivityConfig_RegisterConfig, diff --git a/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.meta b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.meta new file mode 100644 index 000000000..8f92bf68d --- /dev/null +++ b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17115695c4c201946b804b772100142e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab new file mode 100644 index 000000000..7b5e3ce4b --- /dev/null +++ b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab @@ -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 diff --git a/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab.meta b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab.meta new file mode 100644 index 000000000..2f690f7ad --- /dev/null +++ b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 742f2b00809071748adce9b6c5b9d1f7 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs new file mode 100644 index 000000000..377c9a6aa --- /dev/null +++ b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs @@ -0,0 +1,92 @@ +using Assets.Framework.Tool; +using Assets.MyGame.Scripts.Net; +using DG.Tweening; +using UnityEngine; +using UnityEngine.UI; + +/// +/// 鱼死亡掉落道具脚本(与 CoinScript 同级,均继承 BJBase)。 +/// 行为类似金币:原地掉落 → 飞向击杀者座位 → 回收。 +/// 掉落数据由服务端 RoomKillNotice.drop 下发(propNumber/propName/count), +/// 道具图标按 /uploads/{propNumber}.png 从服务端下载。 +/// +public class FishKillProp : BJBase +{ + /// 道具图标(Inspector 绑定) + public Image propIco; + + /// + /// 重写基类方法:道具不显示金币数字,而是下载并设置道具图标。 + /// 和 CoinScript.SetCoinCount() 对称,供外部统一调用。 + /// + public override void SetCoinCount() + { + // 修正 z 轴(和 CoinScript 一致,防止 UI 层级下 z 不为 0 导致被遮挡) + var v3 = transform.position; + v3.z = 0; + transform.position = v3; + } + + /// + /// 初始化道具(扩展 BJBase.Init,增加道具编号)。 + /// 由 CreateFishManager 在收到服务端掉落推送时调用。 + /// + /// 道具编号(用于下载图标 /uploads/编号.png) + /// 掉落数量 + /// 掉落起点(鱼死亡位置) + /// 飞行终点(击杀者座位位置) + /// 击杀者 ID + /// 座位 ID + /// 附加数据(透传,回调时原样返回) + 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); + } + + /// + /// 从服务端下载道具图标并设置到 propIco。 + /// 失败时 propIco 保留预制体上配置好的默认图(不强制兜底重试)。 + /// + 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); + } + }); + } + + /// + /// 飞到座位位置并回收(效果同 coinEvent.HuiShou)。 + /// 可由动画事件调用(预制体有掉落动画时),也可直接调用实现原地掉落后立即飞向座位。 + /// + public void FlyToSeat() + { + transform.DOMove(endPoint, 0.5f).OnComplete(() => + { + // 拾取回调(由 CreateFishManager 设置 JiaFen) + JiaFen?.Invoke(0); + // 拾取音效(复用金币拾取音效) + AudioManager.Instance.PlayerFX(61009); + // 池化回收 + EffectPool.Instance.Return(gameObject); + }); + } +} diff --git a/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs.meta b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs.meta new file mode 100644 index 000000000..5a5e7fe5d --- /dev/null +++ b/Assets/MyGame/aa/Game/DefaultDieEffect/Prop/FishKillProp.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcf86da74e3bba947934891ba2a69902 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/UI/Home/MainPanel.prefab b/Assets/MyGame/aa/UI/Home/MainPanel.prefab index 66f19cb44..7ddd5fbf1 100644 --- a/Assets/MyGame/aa/UI/Home/MainPanel.prefab +++ b/Assets/MyGame/aa/UI/Home/MainPanel.prefab @@ -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} diff --git a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_UISignViewPanel.cs b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_UISignViewPanel.cs index e2a456914..d4f53883d 100644 --- a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_UISignViewPanel.cs +++ b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_UISignViewPanel.cs @@ -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_CoinNums; public List 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 order_singConfig; + Dictionary 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}%"; } - /// 从GameManager的activityConfig读取signConfigs,按order建立字典 - private void BuildOrderSignConfig() + /// 从GameManager的activityConfig读取signActivityConfig.days,按day建立字典 + private void BuildDaySignConfig() { - order_singConfig = new Dictionary(); - var signConfigs = GameManager.Instance.activityConfig?.signConfigs; - if (signConfigs == null) return; - foreach (var cfg in signConfigs) + day_singConfig = new Dictionary(); + 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; } - /// 转盘格子赋值:值+值*signInAdd(VIP签到加成),按order排序后与text_CoinNums一一对应 + /// 转盘格子赋值:金币值+值*signInAdd(VIP签到加成),按day排序后与text_CoinNums一一对应 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(order_singConfig.Values); - list.Sort((a, b) => a.order - b.order); + var list = new List(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)); } } + /// 取单日配置中的金币奖励数(giftList 中 type=1 金币项;无金币项返回0) + 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; + } + /// 今日是否已签到 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 }); } - /// 按奖励金额反查转盘格子:优先匹配加了VIP加成后的值,其次匹配原始配置值;匹配不到返回-1 - private int FindSlotByReward(long reward) + /// 按签到天数定位转盘格子:days按day升序后的下标;匹配不到返回-1 + 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(order_singConfig.Values); - list.Sort((a, b) => a.order - b.order); + var list = new List(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; } /// 单层转盘旋转停靠(参照SLZPDieEvent:LocalAxisAdd增量旋转+OutCubic缓动+完成强制校正)