From b5ff32c7e117cc103b32f5054e8bd7fad55ca6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A3=AE2003?= <2384017988@qq.com> Date: Wed, 29 Jul 2026 22:59:26 +0800 Subject: [PATCH] 1 --- .../AssetGroups/Default Local Group.asset | 5 + .../Windows/addressables_content_state.bin | Bin 3247 -> 4130 bytes Assets/Editor/AssetDependencyAnalyzer.cs | 23 +- Assets/Editor/TurretPrefabAALabeler.cs | 15 +- Assets/Editor/TurretSpriteAALabeler.cs | 17 +- Assets/MyGame/Scenes/UIScene.unity | 3 +- .../Scripts/Manager/CreateFishManager.cs | 81 +++++-- Assets/MyGame/Scripts/Manager/GameManager.cs | 128 ++++++++--- Assets/MyGame/Scripts/Net/APIManager.cs | 5 +- Assets/MyGame/Scripts/Net/WebSocketManager.cs | 4 +- .../Scripts/Net/WebSocketManager_Message.cs | 123 ++++++----- .../Scripts/Net/WebSocketReponseList.cs | 44 +++- Assets/MyGame/Scripts/Turret/SitPosData.cs | 4 +- .../aa/Game/HHWK/MonsterTSY/DieEffect_TSY.cs | 208 +++++++++++++----- .../Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs | 31 +++ .../HHWK/MonsterTSY/TSYBaoJiangEnums.cs.meta | 11 + .../HHWK/MonsterTSY/ZDX/GameObject.prefab | 4 +- .../aa/UI/Game/GameMainPanel_FishBase.cs | 4 +- .../UI/Home/Sub/UIHome_BY_ItemListPanels.cs | 34 --- 19 files changed, 535 insertions(+), 209 deletions(-) create mode 100644 Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs create mode 100644 Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs.meta diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset index 4639d650c..16aacc699 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -30,6 +30,11 @@ MonoBehaviour: m_SerializedLabels: - Default FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 88507e399a88aa744970855b92a576a8 + m_Address: Assets/MyGame/aa/UI/Home/Sub/Turret/ZhanShen.renderTexture + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 3d7ea06370502a34e82011fabf822b45, type: 2} m_SchemaSet: diff --git a/Assets/AddressableAssetsData/Windows/addressables_content_state.bin b/Assets/AddressableAssetsData/Windows/addressables_content_state.bin index 026b69309dc7f17a1b7ff929ff069f186cd5364a..994e12693960b906f9902f5d3095b93db7cbf513 100644 GIT binary patch delta 1666 zcmah}O=w(I6rPu6CYdC)lcuJv8hx5f6g9*Bd-uK<{Hw*Cwxv`wh7#}J%LK+r$s`54 zNmpIC5xkoUE<{k&g+`>iLO~G(p(v=`D&4e#;G&=_^_(Y@JXDy+ymQaId%o|SckcP# zr$^t5cYcqmW0gu}IP459201i4Sw8@O>kTxMU?vWR<)IIwsqpIX=~JuvHNUmC;d?8( z*YlgObUG`JB^hNrH;H!6dPS(VK1rFAd6p;6waix6b$tC0utjw+qj1;m2J=yPw|XMn z*>`qo8uze{V(&0GmvL`-b8BP6Z`$^n(cRSxZQbp)efMAaZ6=H}j3=C0uZi$NWl16( z5mHE(iGKbgm}9)3<0!n)OJR9mLs?>So_ZoU{G{-yPGqhn&mECW3fsrugZPO){$9Yp zIdvaC+uwgS1I`;*_Y0l%)wS+_I;9nLiswobYg8sA(Z)&XJS9pwBUwMw{m6P(pY;K3 z{qybh|7kr*WJdGEn~ZatTE=M#+__XHQ=AIfCybF0&$VdpgW;X(IhiSAi03|)gykwB zURs;ynp>eXGrL`T2%$I)1$4IP+S|2;qB`+Z<(v>pxe)@6+~n3-<`d1e=l#@kh{r)H z@Xhd3&9tmxoeR$6ZQA>BQy%Rz39jxsi6XeGvS56h*k!9tIN+n!A}R zC(lA$rScbCxYiy46SG8U%tk1ViB^*PgyzmCiWs4|tlog|r(W!?ZNC0o7f#W)PW#?^ z_;|GScm(*7^SZO;S5_*cSP5R!w~ygQ1x$o zo^QP8ZpDo= zBfIHifEVdA}as4(T z_Zt0VI6vN8`l7^s_3&^Jg6t@;g8gZ5;z6>!IDH8}S0(mm!X040R$||YzuY}VlwiUB iEI9EX;Vyh|@g$P()qXr|Of-*NEwL}~Uy2Z@ef)2TQ?YCS delta 1006 zcmaiz&ubGw6vwmCY>k_A)B3B`nr^j;VliZAcIQVC6si{sQUsBJPm!5aJnym=BqPy|7&hoT&w~4MoCZQT0A68BA81BSDq3;Y}DR2UUd)mpJ(_co)Oi5Oc!=`W*Rem!h z0q2o)DGy>wl_Du(5)s7|u^($)RP9?g2g{+%Q?V~1H)0|62=%BUVJwssf-|OKj(W%Xh7X)8m?>3=qDC4 z4(ZR9tbPRf=0C*OcPN3G@Su~)ozL;TR5t(j8+=&2k2e9hwdjOp^*vw7z7R-V*j0!RtuGV`kej-^tS`_@~3Is zXF^A76Z&xooM9ItDxQ9c%09iOTXv=KW`KV8>B9fBN7EB}9Rg?ARaq!Jx{vTa-PK!m RW#aV!{lbqf{@hr*^#?XP_R9bO diff --git a/Assets/Editor/AssetDependencyAnalyzer.cs b/Assets/Editor/AssetDependencyAnalyzer.cs index 328fc1c12..bd1a47815 100644 --- a/Assets/Editor/AssetDependencyAnalyzer.cs +++ b/Assets/Editor/AssetDependencyAnalyzer.cs @@ -16,6 +16,15 @@ using UnityEngine; /// public class AssetDependencyAnalyzer : EditorWindow { + // ============================ 日志开关 ============================ + // 启用: Player Settings → Scripting Define Symbols 添加 ENABLE_EDITOR_LOG + // 关闭: 删除该符号。未定义时所有 Log/LogError 调用被编译器自动移除,零开销 + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void Log(string msg) { Debug.Log(msg); } + + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void LogError(string msg) { Debug.LogError(msg); } + #region 内部类 [Serializable] @@ -438,7 +447,7 @@ public class AssetDependencyAnalyzer : EditorWindow if (o != null) objs.Add(o); } Selection.objects = objs.ToArray(); - Debug.Log($"[依赖分析器] 已选中 {objs.Count} 个非AA资源到 Project 视图"); + Log($"[依赖分析器] 已选中 {objs.Count} 个非AA资源到 Project 视图"); } private void DrawDependencyRow(DependencyInfo info) @@ -571,12 +580,12 @@ public class AssetDependencyAnalyzer : EditorWindow allTypes = dependencies.Select(d => d.typeName).Distinct().OrderBy(t => t).ToList(); EditorUtility.ClearProgressBar(); - Debug.Log($"[依赖分析器] 分析完成,共 {dependencies.Count} 个依赖资源"); + Log($"[依赖分析器] 分析完成,共 {dependencies.Count} 个依赖资源"); } catch (Exception e) { EditorUtility.ClearProgressBar(); - Debug.LogError($"[依赖分析器] 分析失败: {e}"); + LogError($"[依赖分析器] 分析失败: {e}"); } } @@ -753,13 +762,13 @@ public class AssetDependencyAnalyzer : EditorWindow } else { - Debug.Log(sb.ToString()); + Log(sb.ToString()); } } catch (Exception e) { EditorUtility.ClearProgressBar(); - Debug.LogError($"[依赖分析器] 反向查找失败: {e}"); + LogError($"[依赖分析器] 反向查找失败: {e}"); } } @@ -796,7 +805,7 @@ public class AssetDependencyAnalyzer : EditorWindow File.WriteAllText(savePath, sb.ToString(), Encoding.UTF8); EditorUtility.DisplayDialog("完成", $"报告已导出到:\n{savePath}", "确定"); - Debug.Log($"[依赖分析器] 报告已导出: {savePath}"); + Log($"[依赖分析器] 报告已导出: {savePath}"); } private string EscapeCsv(string s) @@ -833,7 +842,7 @@ public class AssetDependencyAnalyzer : EditorWindow sb.AppendLine(info.aaAddress ?? info.assetPath); } GUIUtility.systemCopyBuffer = sb.ToString(); - Debug.Log($"[依赖分析器] 已复制 {selected.Count} 条地址到剪贴板"); + Log($"[依赖分析器] 已复制 {selected.Count} 条地址到剪贴板"); } #endregion diff --git a/Assets/Editor/TurretPrefabAALabeler.cs b/Assets/Editor/TurretPrefabAALabeler.cs index e038250c3..17a969902 100644 --- a/Assets/Editor/TurretPrefabAALabeler.cs +++ b/Assets/Editor/TurretPrefabAALabeler.cs @@ -17,6 +17,15 @@ public class TurretPrefabAALabeler : EditorWindow private const string LABEL_PT = "PT"; private const string LABEL_DEFAULT = "Default"; + // ============================ 日志开关 ============================ + // 启用: Player Settings → Scripting Define Symbols 添加 ENABLE_EDITOR_LOG + // 关闭: 删除该符号。未定义时所有 Log/LogError 调用被编译器自动移除,零开销 + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void Log(string msg) { Debug.Log(msg); } + + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void LogError(string msg) { Debug.LogError(msg); } + [MenuItem("Tools/炮台/配置Prefab AA标签(PT+Default)", false, 102)] public static void SetupAddressableLabels() { @@ -40,7 +49,7 @@ public class TurretPrefabAALabeler : EditorWindow string absoluteDir = Path.Combine(Application.dataPath, "MyGame/aa/Game/Turrets"); string[] prefabFiles = Directory.GetFiles(absoluteDir, "paotai*.prefab"); - Debug.Log($"[TurretPrefabAA] 扫描到 {prefabFiles.Length} 个炮台 Prefab"); + Log($"[TurretPrefabAA] 扫描到 {prefabFiles.Length} 个炮台 Prefab"); AssetDatabase.Refresh(); @@ -74,7 +83,7 @@ public class TurretPrefabAALabeler : EditorWindow entry.SetLabel(LABEL_DEFAULT, true, true); count++; - Debug.Log($"[TurretPrefabAA] ✓ {fileName}"); + Log($"[TurretPrefabAA] ✓ {fileName}"); } settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, null, true); @@ -91,7 +100,7 @@ public class TurretPrefabAALabeler : EditorWindow if (!settings.GetLabels().Contains(label)) { settings.AddLabel(label); - Debug.Log($"[TurretPrefabAA] 新增标签: {label}"); + Log($"[TurretPrefabAA] 新增标签: {label}"); } } } diff --git a/Assets/Editor/TurretSpriteAALabeler.cs b/Assets/Editor/TurretSpriteAALabeler.cs index f628e5719..e3ce97a59 100644 --- a/Assets/Editor/TurretSpriteAALabeler.cs +++ b/Assets/Editor/TurretSpriteAALabeler.cs @@ -17,6 +17,15 @@ public class TurretSpriteAALabeler : EditorWindow private const string LABEL_PT = "PT"; private const string LABEL_DEFAULT = "Default"; + // ============================ 日志开关 ============================ + // 启用: Player Settings → Scripting Define Symbols 添加 ENABLE_EDITOR_LOG + // 关闭: 删除该符号。未定义时所有 Log/LogError 调用被编译器自动移除,零开销 + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void Log(string msg) { Debug.Log(msg); } + + [System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")] + private static void LogError(string msg) { Debug.LogError(msg); } + [MenuItem("Tools/炮台/配置AA标签(PT+Default)", false, 101)] public static void SetupAddressableLabels() { @@ -52,7 +61,7 @@ public class TurretSpriteAALabeler : EditorWindow } string[] pngFiles = Directory.GetFiles(absoluteSpritesDir, "paotai*.png"); - Debug.Log($"[TurretSpriteAA] 扫描到 {pngFiles.Length} 个炮台 PNG 文件"); + Log($"[TurretSpriteAA] 扫描到 {pngFiles.Length} 个炮台 PNG 文件"); // 强制刷新 AssetDatabase,确保所有 PNG 都被识别 AssetDatabase.Refresh(); @@ -72,7 +81,7 @@ public class TurretSpriteAALabeler : EditorWindow if (importer == null) { // 文件可能还没导入,强制导入 - Debug.Log($"[TurretSpriteAA] 强制导入: {assetPath}"); + Log($"[TurretSpriteAA] 强制导入: {assetPath}"); AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); importer = AssetImporter.GetAtPath(assetPath) as TextureImporter; } @@ -119,7 +128,7 @@ public class TurretSpriteAALabeler : EditorWindow entry.SetLabel(LABEL_DEFAULT, true, true); count++; - Debug.Log($"[TurretSpriteAA] ✓ {fileName}: address={entry.address}, labels=PT+Default"); + Log($"[TurretSpriteAA] ✓ {fileName}: address={entry.address}, labels=PT+Default"); } settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, null, true); @@ -137,7 +146,7 @@ public class TurretSpriteAALabeler : EditorWindow if (!settings.GetLabels().Contains(label)) { settings.AddLabel(label); - Debug.Log($"[TurretSpriteAA] 新增标签: {label}"); + Log($"[TurretSpriteAA] 新增标签: {label}"); } } } diff --git a/Assets/MyGame/Scenes/UIScene.unity b/Assets/MyGame/Scenes/UIScene.unity index 8ecee1ff8..94838dee7 100644 --- a/Assets/MyGame/Scenes/UIScene.unity +++ b/Assets/MyGame/Scenes/UIScene.unity @@ -908,7 +908,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 22400762, guid: 67117722a812a2e46ab8cb8eafbf5f5e, type: 3} propertyPath: m_AnchoredPosition.y - value: -0.000022888184 + value: -0.000015258789 objectReference: {fileID: 0} - target: {fileID: 22426080, guid: 67117722a812a2e46ab8cb8eafbf5f5e, type: 3} propertyPath: m_AnchorMax.x @@ -6005,6 +6005,7 @@ MonoBehaviour: sendLeaveRoom: 1 showWaitingAfterExit: 1 useRemoteBotConfig: 0 + enableLogFile: 1 gameConfig: id: isDelete: 0 diff --git a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs index 9284b7f27..956503d53 100644 --- a/Assets/MyGame/Scripts/Manager/CreateFishManager.cs +++ b/Assets/MyGame/Scripts/Manager/CreateFishManager.cs @@ -1389,10 +1389,36 @@ public class CreateFishManager : MonoSingleton UIManager.Instance.uiRoot.GetComponent()); var coinScript = award.GetComponent(); - coinScript.Init(coinNum.Amount, v3Point); - coinScript.isBoss = true; - coinScript.isLocalCoin = isLocal; - coinScript.SetCoinCount(); + if (coinScript != null) + { + // 普通爆奖(BJBase 体系) + coinScript.Init((int)coinNum.Amount, v3Point); + coinScript.isBoss = true; + coinScript.isLocalCoin = isLocal; + coinScript.SetCoinCount(); + } + else + { + // 特殊鱼爆奖(DieEffect_TSY 体系:小鱼/Boss 模式互通) + var tsy = award.GetComponent(); + if (tsy != null) + { + tsy.startPoint = award.transform.position; + tsy.isLocalCoin = isLocal; + tsy.monster = monster; + tsy.coinCount = coinNum.Amount; + + if (tsy.baoJiangMode == TSYBaoJiangMode.Boss爆奖) + { + var sit = currGameType.currSitData; + tsy.InitBossAward((int)coinNum.Amount, + sit != null ? sit.posID : 0, + sit != null ? (int)sit.consumeGoldNum : 1, + monster.monsterData.fishTypeConfig.fishScore, 1, null); + } + tsy.SetCoinCount(); + } + } } //MonsterDieList.Instance.SmallHuangYiHuang( @@ -1441,7 +1467,7 @@ public class CreateFishManager : MonoSingleton // 4. 设置 CoinScript 的终点为 targetWorldPos var coinScript = award.GetComponent(); - coinScript.Init(coinNum.Amount, targetWorldPos); + coinScript.Init((int)coinNum.Amount, targetWorldPos); coinScript.isBoss = true; coinScript.isLocalCoin = isLocal; coinScript.SetCoinCount(); @@ -1487,8 +1513,8 @@ public class CreateFishManager : MonoSingleton sitData.goldNum += goldToAdd; // 使用副本,不再依赖 killFish 对象 sitData.tMP_GoldNum.text = sitData.goldNum.ToString(); AudioManager.Instance.PlayerFX(audioID); - // ⭐ 记录金币流水:杀鱼获得金币 - GameManager.LogGoldFlow("KillFish", $"击中鱼原有金币={oldGold} 增加金币=+{goldToAdd} 剩余金币={sitData.goldNum} 鱼ID={fishId} 座位={sitData.posID} 房间={GameManager.Instance.gameData?.roomInfo?.roomId}"); + // ⭐ 记录金币流水:已加钱(杀鱼获得,本地已执行) + GameManager.LogGoldFlowAppliedGain("KillFish", $"击中鱼原有金币={oldGold} 增加金币=+{goldToAdd} 剩余金币={sitData.goldNum} 鱼ID={fishId} 座位={sitData.posID} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}"); }; //// 加分回调(在金币/加分面板飞完后执行) //Action onScoreAdd = (audioID) => @@ -1614,13 +1640,40 @@ public class CreateFishManager : MonoSingleton var coinScript = award.GetComponent(); - coinScript.startPoint = award.transform.position; - coinScript.Init(killFish.gold, sitData.transform.position, killFish.userId, sitData.posID, killFish.param); - coinScript.isLocalCoin = sitData.user == GameManager.Instance.user.id; - coinScript.startPoint = award.transform.position; - coinScript.isBoss = false; - coinScript.JiaFen = onScoreAdd; - coinScript.SetCoinCount(); + if (coinScript != null) + { + // 普通爆奖(BJBase 体系) + coinScript.startPoint = award.transform.position; + coinScript.Init(killFish.gold, sitData.transform.position, killFish.userId, sitData.posID, killFish.param); + coinScript.isLocalCoin = sitData.user == GameManager.Instance.user.id; + coinScript.isBoss = false; + coinScript.JiaFen = onScoreAdd; + coinScript.SetCoinCount(); + } + else + { + // 特殊鱼爆奖(DieEffect_TSY 体系:小鱼/Boss 模式互通) + var tsy = award.GetComponent(); + if (tsy != null) + { + tsy.startPoint = award.transform.position; + tsy.isLocalCoin = sitData.user == GameManager.Instance.user.id; + tsy.monster = monster; + tsy.coinCount = killFish.gold; + + if (tsy.baoJiangMode == TSYBaoJiangMode.Boss爆奖) + { + tsy.InitBossAward(killFish.gold, sitData.posID, + (int)sitData.consumeGoldNum, + monster.monsterData.fishTypeConfig.fishScore, 1, onScoreAdd); + } + else + { + tsy.JiaFenEx = onScoreAdd; + } + tsy.SetCoinCount(); + } + } // // Boss 鱼:显示浮动分数文本 diff --git a/Assets/MyGame/Scripts/Manager/GameManager.cs b/Assets/MyGame/Scripts/Manager/GameManager.cs index edf632c71..379ffb6af 100644 --- a/Assets/MyGame/Scripts/Manager/GameManager.cs +++ b/Assets/MyGame/Scripts/Manager/GameManager.cs @@ -23,6 +23,10 @@ public class GameManager : MonoSingleton [Tooltip("是否使用后端下发的 BotConfig。false=用 Inspector 本地配置(默认,方便测试);true=用后端 GameConfig 返回的 botConfig 覆盖本地")] public bool useRemoteBotConfig = false; + [Header("日志文件")] + [Tooltip("运行时开关:是否实际生成日志文件。需要同时启用 ENABLE_LOG_FILE 预编译指令才生效。\ntrue=生成日志文件;false=不生成(运行时关闭)")] + public bool enableLogFile = true; + public GameConfig gameConfig; public ActivityConfig activityConfig; public Dictionary dic_fishTypeConfigs = new Dictionary(); @@ -209,8 +213,12 @@ public class GameManager : MonoSingleton // ⭐ 防止手机屏幕自动黑屏休眠 Screen.sleepTimeout = SleepTimeout.NeverSleep; +#if ENABLE_LOG_FILE // ⭐ 启动日志文件自动保存:把所有 Debug.Log/Warning/Error 写入 persistentDataPath/Logs/ - InitLogFileWriter(); + // 需要在 Player Settings → Scripting Define Symbols 中添加 ENABLE_LOG_FILE 才会编译 + // 运行时可通过 Inspector 的 enableLogFile 勾选框关闭 + if (enableLogFile) InitLogFileWriter(); +#endif AudioResourcesData audioResourcesData = new AudioResourcesData(); InItLoad(); @@ -244,8 +252,15 @@ public class GameManager : MonoSingleton } - // ⭐ 日志文件写入器:把 Application.logMessageReceived 的日志实时写入文件 +#if ENABLE_LOG_FILE + // ============================ 日志文件系统 ============================ + // 启用方法: Player Settings → Other Settings → Scripting Define Symbols 添加 ENABLE_LOG_FILE + // 关闭方法: 删除该符号,或 Inspector 中 enableLogFile 取消勾选 + // 说明: LogGoldFlow 用 [Conditional] 标记,未定义 ENABLE_LOG_FILE 时所有调用点会被编译器自动移除,零开销 + private StreamWriter _logFileWriter; + private StreamWriter _goldFlowWriter; + private void InitLogFileWriter() { try @@ -257,7 +272,6 @@ public class GameManager : MonoSingleton Application.logMessageReceived += OnLogMessageReceived; Debug.Log($"[GameManager] 日志文件已创建: {logPath}"); - // 同时初始化金币流水日志文件 InitGoldFlowWriter(logDir); } catch (Exception e) @@ -266,17 +280,23 @@ public class GameManager : MonoSingleton } } - // ⭐ 金币流水日志写入器:单独写入"金币流水"文件,格式对齐后端日志 - private StreamWriter _goldFlowWriter; private void InitGoldFlowWriter(string logDir) { try { string goldLogPath = Path.Combine(logDir, "金币流水_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".log"); _goldFlowWriter = new StreamWriter(goldLogPath, true) { AutoFlush = true }; + + // ===== 文件头:只记录当前玩家自己(进游戏前可能 user 还没拉到,做 null 保护) ===== + string userName = user?.BaseInfo?.userName ?? "未登录"; + string userId = user?.id ?? "未登录"; _goldFlowWriter.WriteLine($"# 金币流水日志 - 启动于 {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); - _goldFlowWriter.WriteLine($"# 格式: 时间|service=client|INFO|GoldFlow|[金币流水] 玩家=名称(ID) 事件=xxx {描述}"); + _goldFlowWriter.WriteLine($"# 当前玩家: {userName}({userId})"); + _goldFlowWriter.WriteLine($"# 说明: 只记录当前玩家自己的金币变动,不记录其他玩家"); + _goldFlowWriter.WriteLine($"# 格式: 时间|service=client|INFO|GoldFlow|[分类] 玩家=名称(ID) 事件=target 描述"); + _goldFlowWriter.WriteLine($"# 分类: 收到加钱/已加钱/收到扣钱/已扣钱/金币流水(通用)"); _goldFlowWriter.WriteLine(new string('-', 80)); + Debug.Log($"[GameManager] 金币流水日志文件已创建: {goldLogPath}"); } catch (Exception e) @@ -285,24 +305,6 @@ public class GameManager : MonoSingleton } } - /// - /// 记录金币流水(供全项目调用) - /// 用法: GameManager.LogGoldFlow("KillFish", $"击中鱼原有金币={oldGold} 增加金币=+{addGold} 剩余金币={newGold} 房间={roomId}"); - /// - public static void LogGoldFlow(string eventName, string description) - { - var inst = Instance; - if (inst == null || inst._goldFlowWriter == null) return; - try - { - string userName = inst.user?.BaseInfo?.userName ?? "未知"; - string userId = inst.user?.id ?? "未知"; - string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); - inst._goldFlowWriter.WriteLine($"{timeStr}|service=client|INFO|GoldFlow|[金币流水] 玩家={userName}({userId}) 事件={eventName} {description}"); - } - catch { /* 写日志失败不影响游戏运行 */ } - } - private void OnLogMessageReceived(string condition, string stackTrace, LogType type) { if (_logFileWriter == null) return; @@ -323,7 +325,7 @@ public class GameManager : MonoSingleton _logFileWriter.WriteLine($" Stack: {stackTrace}"); } } - catch { /* 写日志失败不影响游戏运行 */ } + catch { } } private void OnDestroy() @@ -340,6 +342,82 @@ public class GameManager : MonoSingleton _goldFlowWriter = null; } } +#endif + + // ============================ 金币流水日志(与后端逐条对比) ============================ + // 拆成4类,方便和后端日志逐行对比: + // - 收到扣钱信息(推送): 后端发来扣钱通知,客户端尚未执行 + // - 已扣钱(本地执行): 客户端本地已扣完金币 + // - 收到加钱信息(推送): 后端发来加钱通知,客户端尚未执行 + // - 已加钱(本地执行): 客户端本地已加完金币 + // + // 控制方式: + // 1. 编译时: 未定义 ENABLE_LOG_FILE 符号时,所有调用被编译器自动移除([Conditional]特性),零开销 + // 2. 运行时: 即使编译了,Inspector 中 enableLogFile=false 也不会写入文件 + // + // 通用记录(无关扣/加,如退出房间)继续用 LogGoldFlow + + /// 记录"收到扣钱信息"(后端推送,尚未执行)。如:后端推送 SyncPlayerGold 差值为负 + [System.Diagnostics.Conditional("ENABLE_LOG_FILE")] + public static void LogGoldFlowReceiveCost(string eventName, string description) + { +#if ENABLE_LOG_FILE + WriteGoldFlow("收到扣钱", eventName, description); +#endif + } + + /// 记录"已扣钱"(本地已执行扣金币)。如:开火 ConsumeGold 扣完 + [System.Diagnostics.Conditional("ENABLE_LOG_FILE")] + public static void LogGoldFlowAppliedCost(string eventName, string description) + { +#if ENABLE_LOG_FILE + WriteGoldFlow("已扣钱", eventName, description); +#endif + } + + /// 记录"收到加钱信息"(后端推送,尚未执行)。如:RoomJoinNotice/RoomInfo 推送金币 + [System.Diagnostics.Conditional("ENABLE_LOG_FILE")] + public static void LogGoldFlowReceiveGain(string eventName, string description) + { +#if ENABLE_LOG_FILE + WriteGoldFlow("收到加钱", eventName, description); +#endif + } + + /// 记录"已加钱"(本地已执行加金币)。如:杀鱼 KillFish 加完、进房 JoinGame 设置初始金币 + [System.Diagnostics.Conditional("ENABLE_LOG_FILE")] + public static void LogGoldFlowAppliedGain(string eventName, string description) + { +#if ENABLE_LOG_FILE + WriteGoldFlow("已加钱", eventName, description); +#endif + } + + /// 通用金币流水记录(无关扣/加,如退出房间)。需要明确分类时请用上面4个方法 + [System.Diagnostics.Conditional("ENABLE_LOG_FILE")] + public static void LogGoldFlow(string eventName, string description) + { +#if ENABLE_LOG_FILE + WriteGoldFlow("金币流水", eventName, description); +#endif + } + +#if ENABLE_LOG_FILE + /// 金币流水核心写入(私有)。所有公开 LogGoldFlow* 方法最终都走这里 + private static void WriteGoldFlow(string category, string eventName, string description) + { + var inst = Instance; + if (inst == null || !inst.enableLogFile || inst._goldFlowWriter == null) return; + try + { + string userName = inst.user?.BaseInfo?.userName ?? "未知"; + string userId = inst.user?.id ?? "未知"; + string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); + inst._goldFlowWriter.WriteLine($"{timeStr}|service=client|INFO|GoldFlow|[{category}] 玩家={userName}({userId}) 事件={eventName} {description}"); + } + catch { } + } +#endif public async Task InItLoad() { diff --git a/Assets/MyGame/Scripts/Net/APIManager.cs b/Assets/MyGame/Scripts/Net/APIManager.cs index b5250ece2..bb8965b6e 100644 --- a/Assets/MyGame/Scripts/Net/APIManager.cs +++ b/Assets/MyGame/Scripts/Net/APIManager.cs @@ -16,7 +16,10 @@ namespace Assets.MyGame.Scripts.Net { public static Dictionary dic_API = new Dictionary(); //public const string baseURL = "http://game5241.wzaxr.top"; -#if UNITY_EDITOR +#if ENABLE_LOG_FILE + public const string baseURL = "http://47.109.132.12"; + +#elif UNITY_EDITOR //public const string baseURL = "http://8.137.163.153"; public const string baseURL = "http://47.109.132.12"; diff --git a/Assets/MyGame/Scripts/Net/WebSocketManager.cs b/Assets/MyGame/Scripts/Net/WebSocketManager.cs index 43241002d..de26b0808 100644 --- a/Assets/MyGame/Scripts/Net/WebSocketManager.cs +++ b/Assets/MyGame/Scripts/Net/WebSocketManager.cs @@ -29,9 +29,11 @@ namespace Assets.MyGame.Scripts.Net private readonly HashSet manualClosing = new(); private float nextHeartbeatTime; private const float HeartbeatIntervalSeconds = 20f; +#if ENABLE_LOG_FILE + private const string baseUrl = "ws://47.109.132.12"; //private const string baseUrl = "ws://game5241.wzaxr.top"; -#if UNITY_EDITOR +#elif UNITY_EDITOR //private const string baseUrl = "ws://8.137.163.153"; private const string baseUrl = "ws://47.109.132.12"; diff --git a/Assets/MyGame/Scripts/Net/WebSocketManager_Message.cs b/Assets/MyGame/Scripts/Net/WebSocketManager_Message.cs index 8261d14b7..27ebf8742 100644 --- a/Assets/MyGame/Scripts/Net/WebSocketManager_Message.cs +++ b/Assets/MyGame/Scripts/Net/WebSocketManager_Message.cs @@ -476,62 +476,79 @@ namespace Assets.MyGame.Scripts.Net break; case "Exception": { - var va = JsonConvert.DeserializeObject>(msg).arguments[0]; - UIManager.Instance.ShowBoardError(va); - switch (va) + var ex = JsonConvert.DeserializeObject>(msg).arguments[0]; + if (ex == null) break; + + // 根据错误码分类处理 + switch (ex.code) { - case "房间人数已满": - switch (GameManager.Instance.gameData.currGameType) + case 1001: // 房间已满 → 弹窗提示并跳转房间列表 + UIManager.Instance.ShowTiShi(ex.message, () => { - case GameType.JQQD: - break; - case GameType.defaultPanel: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelDefault, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - case GameType.FLZY: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelFLZY, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - case GameType.YYSC: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelYYSC, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - case GameType.ZGXQ: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelZGXQ, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - case GameType.XJMH: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelXJMH, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - case GameType.HHWK: - UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelHHWK, null, (panel) => - { - WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); - - }); - break; - default: - break; - } + switch (GameManager.Instance.gameData.currGameType) + { + case GameType.JQQD: + break; + case GameType.defaultPanel: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelDefault, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + case GameType.FLZY: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelFLZY, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + case GameType.YYSC: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelYYSC, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + case GameType.ZGXQ: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelZGXQ, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + case GameType.XJMH: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelXJMH, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + case GameType.HHWK: + UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, UIPanel.MainGamePanelHHWK, null, (panel) => + { + WebSocketSendList.GetRoomListFish(GameManager.Instance.gameData.currRoomData.roomLevel, 1); + }); + break; + default: + break; + } + }); break; - default: + // 资产/状态类错误 → 弹窗提示,玩家需明确知晓 + case 1002: // 金币不足 + case 1003: // 房间不存在 + case 1011: // 用户不存在 + case 1013: // 道具数量不足 + case 1014: // 钻石不足 + UIManager.Instance.ShowTiShi(ex.message); + break; + // 参数/配置/技术性错误 → Toast 轻提示 + case 1004: // 玩家不在房间 + case 1005: // 签名错误 + case 1006: // 请求已过期 + case 1007: // 鱼配置不存在 + case 1008: // 子弹参数无效 + case 1009: // 金币参数超出范围 + case 1012: // 道具配置不存在 + case 1015: // 炮倍参数无效 + default: // 0=未知错误 + UIManager.Instance.ShowBoardError(ex.message); break; } } diff --git a/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs b/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs index 390f462f5..f9a8dad8f 100644 --- a/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs +++ b/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs @@ -57,11 +57,15 @@ namespace Assets.MyGame.Scripts.Net /// public static void RoomJoinNotice(string msg) { - // ⭐ 记录金币流水:收到 RoomJoinNotice 推送 + // ⭐ 记录金币流水:收到加钱信息(RoomJoinNotice 推送,尚未执行)—— 只记录自己 try { var notice = JsonConvert.DeserializeObject>(msg).arguments[0]; - GameManager.LogGoldFlow("RoomJoinNotice", $"推送金币={notice.gold} 钻石={notice.diamond} 炮台消耗={notice.cannonMultiple} 座位={notice.position}"); + string myId = GameManager.Instance.user?.id; + if (!string.IsNullOrEmpty(myId) && notice.userId == myId) + { + GameManager.LogGoldFlowReceiveGain("RoomJoinNotice", $"推送金币={notice.gold} 钻石={notice.diamond} 炮台消耗={notice.cannonMultiple} 座位={notice.position}"); + } } catch { } @@ -226,22 +230,35 @@ namespace Assets.MyGame.Scripts.Net var panel = UIManager.Instance.Get(panelType); if (panel == null || panel.currSitData == null) return; long oldGold = panel.currSitData.goldNum; + long diff = gold - oldGold; + // ⭐ 记录金币流水:收到后端推送 SyncPlayerGold(尚未执行) + if (diff > 0) + GameManager.LogGoldFlowReceiveGain("SyncPlayerGold", $"推送金币={gold} 修正前金币={oldGold} 差值=+{diff} 座位={panel.currSitData.posID}"); + else if (diff < 0) + GameManager.LogGoldFlowReceiveCost("SyncPlayerGold", $"推送金币={gold} 修正前金币={oldGold} 差值={diff} 座位={panel.currSitData.posID}"); + panel.currSitData.goldNum = gold; if (panel.currSitData.tMP_GoldNum != null) panel.currSitData.tMP_GoldNum.text = gold.ToString(); - // ⭐ 记录金币流水:后端推送 SyncPlayerGold 修正 - GameManager.LogGoldFlow("SyncPlayerGold", $"推送金币={gold} 修正前金币={oldGold} 差值={gold - oldGold} 座位={panel.currSitData.posID}"); + + // ⭐ 记录金币流水:已执行修正(本地已写入) + if (diff > 0) + GameManager.LogGoldFlowAppliedGain("SyncPlayerGold", $"已修正金币={gold} 修正前金币={oldGold} 增加金币=+{diff} 座位={panel.currSitData.posID}"); + else if (diff < 0) + GameManager.LogGoldFlowAppliedCost("SyncPlayerGold", $"已修正金币={gold} 修正前金币={oldGold} 扣除金币={diff} 座位={panel.currSitData.posID}"); } public static void GetRoomInfoResult(string msg) { //GameManager.Instance.StartGame();RoomChangeResponse var roomData = JsonConvert.DeserializeObject>(msg).arguments[0]; - // ⭐ 记录金币流水:收到 RoomInfo 推送 + // ⭐ 记录金币流水:收到加钱信息(RoomInfo 推送玩家金币,尚未执行)—— 只记录自己 if (roomData?.players != null) { + string myId = GameManager.Instance.user?.id; foreach (var p in roomData.players) { - GameManager.LogGoldFlow("RoomInfo", $"推送金币={p.gold} 钻石={p.diamond} 炮台消耗={p.cannonMultiple} 座位={p.position} 房间={roomData.roomId}"); + if (string.IsNullOrEmpty(myId) || p.userId != myId) continue; + GameManager.LogGoldFlowReceiveGain("RoomInfo", $"推送金币={p.gold} 钻石={p.diamond} 炮台消耗={p.cannonMultiple} 座位={p.position} 房间={roomData.roomId}"); } } UIHome_BY_SelectRoomLevelPanel.StartGame(roomData); @@ -1074,6 +1091,21 @@ namespace Assets.MyGame.Scripts.Net public string title; public string emailId; } + + /// + /// FishGameHub Exception 数据({code, message}) + /// code: 0=未知错误 + /// 1001=房间已满 1002=金币不足 1003=房间不存在 1004=玩家不在房间 + /// 1005=签名错误 1006=请求已过期 1007=鱼配置不存在 1008=子弹参数无效 + /// 1009=金币参数超出范围 1011=用户不存在 1012=道具配置不存在 + /// 1013=道具数量不足 1014=钻石不足 1015=炮倍参数无效 + /// + [Serializable] + public class FishGameExceptionData + { + public int code; + public string message; + } } } diff --git a/Assets/MyGame/Scripts/Turret/SitPosData.cs b/Assets/MyGame/Scripts/Turret/SitPosData.cs index 34fa9bcd5..a992a4556 100644 --- a/Assets/MyGame/Scripts/Turret/SitPosData.cs +++ b/Assets/MyGame/Scripts/Turret/SitPosData.cs @@ -601,8 +601,8 @@ public class SitPosData : MonoBehaviour long oldGold = goldNum; goldNum = temp; tMP_GoldNum.text = goldNum.ToString(); - // ⭐ 记录金币流水:开火消耗金币 - GameManager.LogGoldFlow("Fire", $"开火消耗 原有金币={oldGold} 消耗金币=-{consumeGoldNum * mul} 剩余金币={goldNum} 座位={posID} 房间={GameManager.Instance.gameData?.roomInfo?.roomId}"); + // ⭐ 记录金币流水:已扣钱(开火消耗,本地已执行) + GameManager.LogGoldFlowAppliedCost("Fire", $"开火消耗 原有金币={oldGold} 消耗金币=-{consumeGoldNum * mul} 剩余金币={goldNum} 座位={posID} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}"); return true; } diff --git a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/DieEffect_TSY.cs b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/DieEffect_TSY.cs index 87e43e393..702f1048b 100644 --- a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/DieEffect_TSY.cs +++ b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/DieEffect_TSY.cs @@ -1,90 +1,188 @@ -using Assets.MyGame.Scripts.Net.APIResponses.Enum; +using System; using System.Collections; using System.Collections.Generic; using UnityEngine; +/// +/// 特殊鱼爆奖基类(Boss 爆奖体系)。 +/// 所有特殊鱼死亡事件(ZDX / SDY / 钻头 / 闪电…)的统一基类: +/// +/// 1. 继承 BJEventBase —— 特殊鱼可直接走 Boss 完整爆奖流程(红包 → 滚分 → BJEndUI 结算); +/// 2. baoJiangMode 枚举决定爆奖表现: +/// 小鱼爆奖 = BaoJiangTSSmallFish 拓展(保留,爆奖/转盘样式); +/// Boss爆奖 = BJEventBase 完整流程; +/// 特殊鱼升 Boss、Boss 降特殊鱼只改这一个枚举(SetBaoJiangMode 支持运行时切换); +/// 3. BJEndUI 用 endUIType 枚举选择,prefab 映射配在 endUIEntries; +/// 4. 子类分工: +/// SetCoinCount() —— 由 CreateFishManager 杀鱼流程调用(SDY 覆写为闪电链); +/// PlayEffect() —— 由 MonsterDieList.SpecialDieFish 调用(ZDX 覆写为宝箱金币); +/// CreateBaoJiangEffect() —— 子类自身流程结束后调用,按模式分发爆奖。 +/// public class DieEffect_TSY : BJEventBase { - public int radius; - public int count; - public float time = 0.5f; + [Header("====== 爆奖模式(特殊鱼/Boss 互通开关)======")] + [Tooltip("小鱼爆奖 = 走 BaoJiangTSSmallFish 拓展;Boss爆奖 = 走 BJEventBase 完整流程")] + public TSYBaoJiangMode baoJiangMode = TSYBaoJiangMode.小鱼爆奖; - [Header("爆奖特效")] - public GameObject baoJiangEffectPrefab; // 爆奖特效预制体(转盘特效) - public Transform parentTransform; // 父对象(在Inspector中指定) - public Vector3 offsetPosition = new Vector3(0, 2, 0); // 世界坐标偏移量 + [Header("====== Boss 结算 UI(枚举选择)======")] + public BJEndUIType endUIType = BJEndUIType.默认Boss结算; + [Tooltip("枚举 → BJEndUI prefab 映射表")] + public List endUIEntries = new List(); - [Header("爆奖类型")] - public TSBJ effectType = TSBJ.转盘; // 死亡动画的爆奖类型,默认转盘 + [Header("====== 通用 ======")] + public MonsterContral monster; // 宿主鱼(由调用方赋值) + public Vector3 startPoint; // 爆奖起点(特效/闪电起始位置) + public long coinCount; // 爆奖总额 + public bool isLocalCoin; // 是否本地玩家的金币(金/银区分用) + public Action JiaFenEx; // 小鱼爆奖路径的加分回调(透传 BaoJiangTSSmallFish) - private void Start() + [Header("====== 小鱼爆奖(BaoJiangTSSmallFish 拓展)======")] + public GameObject baoJiangEffectPrefab; // 爆奖特效预制体(上挂 BaoJiangTSSmallFish) + public Transform parentTransform; // 生成父节点(空则统一挂 bjEffectRoot) + public Vector3 offsetPosition; // 生成位置偏移 + public TSBJ effectType; // 爆奖样式(暴富/完美/转盘…) + + [Header("====== 时序表现 ======")] + public GameObject[] onTimeObjs; // 爆奖期间激活的表现物件 + public float[] hitTimes; // 各阶段时间点(秒) + public float time; // 通用等待时长 + public int dieEffectTesyNum; // 计数器(金币/链点完成数) + + [Header("====== Boss 爆奖参数 ======")] + public int minBJCount = 10; // 每次 CallBJ 最小红包数 + public int maxBJCount = 15; // 每次 CallBJ 最大红包数 + + protected bool _bossInited; // 是否已 InitBossAward + + /// + /// 运行时切换爆奖模式(特殊鱼↔Boss 互通入口)。 + /// + public void SetBaoJiangMode(TSYBaoJiangMode mode, BJEndUIType? endUI = null) { - paoBei = (int)CreateFishManager.Instance.currGameType.currSitData.consumeGoldNum; + baoJiangMode = mode; + if (endUI.HasValue) endUIType = endUI.Value; } - public void SetCoinCount() + /// + /// Boss 爆奖初始化。模式 = Boss爆奖 时由调用方先调本方法传齐参数,再调 SetCoinCount/Play。 + /// + public void InitBossAward(int goldCount, int posID, int paobei, int fishScore, int beishu, Action jiafen) { - StartCoroutine(enumerator()); + // totalBJCount 在 Start() 里同步自 scoreCount,保证 CallBJ 后能自动 EffEnd + if (scoreCount <= 0) scoreCount = 1; + totalBJCount = scoreCount; + + ResolveEndUI(); + _bossInited = true; + Init(goldCount, posID, paobei, fishScore, beishu, jiafen); } - public virtual IEnumerator enumerator() + /// 结算 UI 枚举 → prefab:写入基类 bjEndUI,OnScoreEnd 时由基类实例化。 + protected virtual void ResolveEndUI() { - yield return new WaitForSeconds(time); - - // 在销毁前创建爆奖特效 - CreateBaoJiangEffect(); - - Destroy(gameObject); + if (endUIType == BJEndUIType.无) + { + bjEndUI = null; + return; + } + var entry = endUIEntries.Find(e => e.type == endUIType); + if (entry != null && entry.prefab != null) + { + bjEndUI = entry.prefab; + } + else + { + Debug.LogWarning($"[DieEffect_TSY] 未配置 {endUIType} 对应的 BJEndUI prefab"); + } } + /// 金币/爆奖启动入口(由 CreateFishManager 杀鱼流程调用,子类覆写)。 + public virtual void SetCoinCount() { } + /// 死亡特效入口(由 MonsterDieList.SpecialDieFish 调用,子类覆写)。 + public virtual void PlayEffect(ParticleSystem effect) + { + if (effect != null) effect.Play(); + } - public void CreateBaoJiangEffect() + /// 爆奖总入口:按 baoJiangMode 分发(子类自身流程结束后调用)。 + public virtual void CreateBaoJiangEffect() + { + if (baoJiangMode == TSYBaoJiangMode.Boss爆奖) + { + PlayBossAward(); + } + else + { + PlaySmallFishAward(); + } + } + + /// + /// 小鱼爆奖:实例化 BaoJiangTSSmallFish 拓展,按 effectType 播 爆奖/转盘。 + /// (移植自 EffectZuanTouDie 旧版 CreateBaoJiangEffect 的可用逻辑) + /// + protected virtual void PlaySmallFishAward() { if (baoJiangEffectPrefab == null) { - Debug.LogWarning("爆奖特效预制体未设置!"); + Debug.LogWarning("[DieEffect_TSY] 小鱼爆奖模式但未配置 baoJiangEffectPrefab!"); return; } - // 确定父对象:优先用 Inspector 指定的 parentTransform → 否则用统一爆奖父节点 bjEffectRoot - Transform parent = parentTransform != null ? parentTransform : CreateFishManager.Instance.bjEffectRoot; + // 父对象:优先 Inspector 指定 → 否则统一爆奖父节点 bjEffectRoot + Transform parent = parentTransform != null + ? parentTransform + : CreateFishManager.Instance.bjEffectRoot; - // 获取源对象的世界坐标 - Vector3 sourcePosition = CreateFishManager.Instance.currGameType.currSitData.transform.position; + // 基准位置:座位坐标 + 手调偏移 + Vector3 basePos = CreateFishManager.Instance.currGameType != null + && CreateFishManager.Instance.currGameType.currSitData != null + ? CreateFishManager.Instance.currGameType.currSitData.transform.position + : transform.position; - // 计算生成位置(世界坐标 + 偏移量) - Vector3 spawnPosition = sourcePosition + offsetPosition; - - // 实例化爆奖特效,设置父对象 - GameObject effectObj = Instantiate(baoJiangEffectPrefab, parent); - - // 设置位置(世界坐标) - effectObj.transform.position = spawnPosition; - - // 重置缩放(避免父对象缩放影响) + var effectObj = Instantiate(baoJiangEffectPrefab, parent); + effectObj.transform.position = basePos + offsetPosition; effectObj.transform.localScale = Vector3.one; - // 获取BaoJiangTSSmallFish组件 - BaoJiangTSSmallFish baoJiang = effectObj.GetComponent(); - baoJiang.JiaFen = JiaFen; - - if (baoJiang != null) + var baoJiang = effectObj.GetComponent(); + if (baoJiang == null) { - // 计算总奖金 - long totalReward = count; // 可以根据需要修改 + Debug.LogWarning("[DieEffect_TSY] baoJiangEffectPrefab 上缺少 BaoJiangTSSmallFish 组件!"); + return; + } - // 根据枚举类型调用不同的方法 - if (effectType == TSBJ.转盘) - { - // 调用转盘特效(不带图片) - baoJiang.ZP(totalReward, true, null, true); - } - else - { - // 调用爆奖特效 - baoJiang.BJ(totalReward, effectType, true, true); - } + baoJiang.JiaFen = () => JiaFenEx?.Invoke(0); + + // 根据枚举类型调用不同爆奖方法 + if (effectType == TSBJ.转盘) + { + baoJiang.ZP(coinCount, true, null, true); + } + else + { + baoJiang.BJ(coinCount, effectType, true, true); } } + + /// + /// Boss 爆奖:走 BJEventBase 完整流程。 + /// CallBJ → 协模拟金币逐个到达累分 → 自动 EffEnd → 滚分 → MoveToSit → BJEndUI → JiaFen → die。 + /// + protected virtual void PlayBossAward() + { + if (!_bossInited) + { + Debug.LogWarning("[DieEffect_TSY] Boss爆奖模式需先调用 InitBossAward 传入爆奖参数!"); + return; + } + ResolveEndUI(); + CallBJ(minBJCount, maxBJCount); + } + + /// 兼容旧子类的协程占位(EffectZuanTouDie 等历史代码)。 + public virtual IEnumerator enumerator() + { + yield return null; + } } diff --git a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs new file mode 100644 index 000000000..4dd99e99c --- /dev/null +++ b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs @@ -0,0 +1,31 @@ +using System; + +/// +/// 特殊鱼爆奖模式 —— 特殊鱼与 Boss 爆奖互通的核心开关。 +/// 策划后续"特殊鱼变 Boss / Boss 变特殊鱼"只改这一项(或运行时 SetBaoJiangMode 切换)。 +/// +public enum TSYBaoJiangMode +{ + 小鱼爆奖 = 0, // 轻量流程:BaoJiangTSSmallFish 拓展(爆奖/转盘样式) + Boss爆奖 = 1, // 完整流程:BJEventBase(红包 → 滚分 → BJEndUI 结算) +} + +/// +/// Boss 爆奖结算 UI 类型(枚举选项,策划下拉选;prefab 映射配在 DieEffect_TSY.endUIEntries)。 +/// 后续有新结算样式:加枚举值 + 配一条映射即可,不改代码结构。 +/// +public enum BJEndUIType +{ + 无 = 0, + 默认Boss结算 = 1, + 大奖结算 = 2, + 特殊Boss结算 = 3, +} + +/// BJEndUIType → BJEndUI prefab 映射项。 +[Serializable] +public class BJEndUIEntry +{ + public BJEndUIType type; + public BJEndUI prefab; +} diff --git a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs.meta b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs.meta new file mode 100644 index 000000000..843a02289 --- /dev/null +++ b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/TSYBaoJiangEnums.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de97264872500254bb686452a3b5deb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/ZDX/GameObject.prefab b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/ZDX/GameObject.prefab index 05579110c..b8fcf1e24 100644 --- a/Assets/MyGame/aa/Game/HHWK/MonsterTSY/ZDX/GameObject.prefab +++ b/Assets/MyGame/aa/Game/HHWK/MonsterTSY/ZDX/GameObject.prefab @@ -49,6 +49,7 @@ MonoBehaviour: silver: {fileID: 0} endPoint: {x: 0, y: 0, z: 0} isBoss: 0 + isLocalCoin: 0 coinCount: 0 userID: sitID: 0 @@ -57,7 +58,8 @@ MonoBehaviour: count: 0 paoBei: 0 time: 0.5 - baoJiangEffectPrefab: {fileID: 0} + baoJiangEffectPrefab: {fileID: 136624701376015062, guid: 8d6e13708cafa5d448d52c8fd7f00f1a, + type: 3} parentTransform: {fileID: 0} offsetPosition: {x: 0, y: 2, z: 0} effectType: 5 diff --git a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs index cd6933a4a..51757129a 100644 --- a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs +++ b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs @@ -2389,8 +2389,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base sit.goldNum = goldNum; sit.consumeGoldNum = consumeGold; - // ⭐ 记录金币流水:进房初始金币 - GameManager.LogGoldFlow("JoinGame", $"座位={sitNum} 初始金币={goldNum} 钻石={diamondNum} 炮台消耗={consumeGold} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}"); + // ⭐ 记录金币流水:已加钱(进房设置初始金币,本地已执行) + GameManager.LogGoldFlowAppliedGain("JoinGame", $"座位={sitNum} 初始金币={goldNum} 钻石={diamondNum} 炮台消耗={consumeGold} 房间={GameManager.Instance.gameData?.currRoomData?.roomId}"); // ⭐ 同步 indexConsume 到后端下发的 cannonMultiple 对应档位,避免加减炮倍时从 0 档跳变 sit.SyncIndexByConsume(consumeGold); diff --git a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_ItemListPanels.cs b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_ItemListPanels.cs index 0e54fdda7..75d0e9d0e 100644 --- a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_ItemListPanels.cs +++ b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_ItemListPanels.cs @@ -705,40 +705,6 @@ public class UIHome_BY_ItemListPanels : SubUIBase { _currentPreviewObj = Instantiate(prefab, previewParent); _currentPreviewObj.SetActive(true); - - // 打包后战神 prefab 自带相机的 targetTexture 引用会丢失(AA bundle 加载导致), - // 需要从面板的 RawImage 取出 RenderTexture 重新赋给战神相机,否则投影不出来。 - FixZhanShenCameraTargetTexture(_currentPreviewObj); - } - } - - /// - /// 修复战神 prefab 实例化后相机 targetTexture 丢失的问题。 - /// 打包后从 AA 加载 prefab 时,相机上序列化的 RenderTexture 引用会变成 null, - /// 这里从面板的 RawImage.texture 取出 RenderTexture 重新赋值给战神相机。 - /// - private void FixZhanShenCameraTargetTexture(GameObject zhanShenInstance) - { - if (zhanShenInstance == null || rawImg_DetailPreview == null) return; - - RenderTexture rt = rawImg_DetailPreview.texture as RenderTexture; - if (rt == null) - { - // 兜底:从 previewParent 下的面板相机取 targetTexture - var panelCam = previewParent != null ? previewParent.GetComponentInChildren(true) : null; - rt = panelCam != null ? panelCam.targetTexture : null; - } - if (rt == null) return; - - // 战神 prefab 自带的相机(打包后 targetTexture 为 null) - var cams = zhanShenInstance.GetComponentsInChildren(true); - foreach (var cam in cams) - { - if (cam.targetTexture == null) - { - cam.targetTexture = rt; - Debug.Log($"[UIHome_BY_ItemListPanels] 修复战神相机 targetTexture: {cam.name} → {rt.name}"); - } } }