This commit is contained in:
2026-07-29 22:59:26 +08:00
parent 61f490c4b4
commit b5ff32c7e1
19 changed files with 535 additions and 209 deletions
@@ -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:
+16 -7
View File
@@ -16,6 +16,15 @@ using UnityEngine;
/// </summary>
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
+12 -3
View File
@@ -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}");
}
}
}
+13 -4
View File
@@ -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}");
}
}
}
+2 -1
View File
@@ -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
@@ -1389,10 +1389,36 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
UIManager.Instance.uiRoot.GetComponent<Canvas>());
var coinScript = award.GetComponent<BJBase>();
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<DieEffect_TSY>();
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<CreateFishManager>
// 4. 设置 CoinScript 的终点为 targetWorldPos
var coinScript = award.GetComponent<CoinScript>();
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<CreateFishManager>
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<int> onScoreAdd = (audioID) =>
@@ -1614,13 +1640,40 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
var coinScript = award.GetComponent<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.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<DieEffect_TSY>();
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 鱼:显示浮动分数文本
+103 -25
View File
@@ -23,6 +23,10 @@ public class GameManager : MonoSingleton<GameManager>
[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<string, FishTypeConfig> dic_fishTypeConfigs = new Dictionary<string, FishTypeConfig>();
@@ -209,8 +213,12 @@ public class GameManager : MonoSingleton<GameManager>
// ⭐ 防止手机屏幕自动黑屏休眠
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<GameManager>
}
// ⭐ 日志文件写入器:把 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<GameManager>
Application.logMessageReceived += OnLogMessageReceived;
Debug.Log($"[GameManager] 日志文件已创建: {logPath}");
// 同时初始化金币流水日志文件
InitGoldFlowWriter(logDir);
}
catch (Exception e)
@@ -266,17 +280,23 @@ public class GameManager : MonoSingleton<GameManager>
}
}
// ⭐ 金币流水日志写入器:单独写入"金币流水"文件,格式对齐后端日志
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>
}
}
/// <summary>
/// 记录金币流水(供全项目调用)
/// 用法: GameManager.LogGoldFlow("KillFish", $"击中鱼原有金币={oldGold} 增加金币=+{addGold} 剩余金币={newGold} 房间={roomId}");
/// </summary>
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<GameManager>
_logFileWriter.WriteLine($" Stack: {stackTrace}");
}
}
catch { /* 写日志失败不影响游戏运行 */ }
catch { }
}
private void OnDestroy()
@@ -340,6 +342,82 @@ public class GameManager : MonoSingleton<GameManager>
_goldFlowWriter = null;
}
}
#endif
// ============================ 金币流水日志(与后端逐条对比) ============================
// 拆成4类,方便和后端日志逐行对比:
// - 收到扣钱信息(推送): 后端发来扣钱通知,客户端尚未执行
// - 已扣钱(本地执行): 客户端本地已扣完金币
// - 收到加钱信息(推送): 后端发来加钱通知,客户端尚未执行
// - 已加钱(本地执行): 客户端本地已加完金币
//
// 控制方式:
// 1. 编译时: 未定义 ENABLE_LOG_FILE 符号时,所有调用被编译器自动移除([Conditional]特性),零开销
// 2. 运行时: 即使编译了,Inspector 中 enableLogFile=false 也不会写入文件
//
// 通用记录(无关扣/加,如退出房间)继续用 LogGoldFlow
/// <summary>记录"收到扣钱信息"(后端推送,尚未执行)。如:后端推送 SyncPlayerGold 差值为负</summary>
[System.Diagnostics.Conditional("ENABLE_LOG_FILE")]
public static void LogGoldFlowReceiveCost(string eventName, string description)
{
#if ENABLE_LOG_FILE
WriteGoldFlow("收到扣钱", eventName, description);
#endif
}
/// <summary>记录"已扣钱"(本地已执行扣金币)。如:开火 ConsumeGold 扣完</summary>
[System.Diagnostics.Conditional("ENABLE_LOG_FILE")]
public static void LogGoldFlowAppliedCost(string eventName, string description)
{
#if ENABLE_LOG_FILE
WriteGoldFlow("已扣钱", eventName, description);
#endif
}
/// <summary>记录"收到加钱信息"(后端推送,尚未执行)。如:RoomJoinNotice/RoomInfo 推送金币</summary>
[System.Diagnostics.Conditional("ENABLE_LOG_FILE")]
public static void LogGoldFlowReceiveGain(string eventName, string description)
{
#if ENABLE_LOG_FILE
WriteGoldFlow("收到加钱", eventName, description);
#endif
}
/// <summary>记录"已加钱"(本地已执行加金币)。如:杀鱼 KillFish 加完、进房 JoinGame 设置初始金币</summary>
[System.Diagnostics.Conditional("ENABLE_LOG_FILE")]
public static void LogGoldFlowAppliedGain(string eventName, string description)
{
#if ENABLE_LOG_FILE
WriteGoldFlow("已加钱", eventName, description);
#endif
}
/// <summary>通用金币流水记录(无关扣/加,如退出房间)。需要明确分类时请用上面4个方法</summary>
[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
/// <summary>金币流水核心写入(私有)。所有公开 LogGoldFlow* 方法最终都走这里</summary>
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()
{
+4 -1
View File
@@ -16,7 +16,10 @@ namespace Assets.MyGame.Scripts.Net
{
public static Dictionary<APIType, string> dic_API = new Dictionary<APIType, string>();
//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";
@@ -29,9 +29,11 @@ namespace Assets.MyGame.Scripts.Net
private readonly HashSet<SocketType> 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";
@@ -476,62 +476,79 @@ namespace Assets.MyGame.Scripts.Net
break;
case "Exception":
{
var va = JsonConvert.DeserializeObject<WebSocketResponseBase<string>>(msg).arguments[0];
UIManager.Instance.ShowBoardError(va);
switch (va)
var ex = JsonConvert.DeserializeObject<WebSocketResponseBase<WebSocketReponseList.FishGameExceptionData>>(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;
}
}
@@ -57,11 +57,15 @@ namespace Assets.MyGame.Scripts.Net
/// <param name="msg"></param>
public static void RoomJoinNotice(string msg)
{
// ⭐ 记录金币流水:收到 RoomJoinNotice 推送
// ⭐ 记录金币流水:收到加钱信息(RoomJoinNotice 推送,尚未执行)—— 只记录自己
try
{
var notice = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomJoinNotice>>(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<GameMainPanel_FishBase>(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<WebSocketResponseBase<RoomChangeResponse>>(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;
}
/// <summary>
/// FishGameHub Exception 数据({code, message})
/// code: 0=未知错误
/// 1001=房间已满 1002=金币不足 1003=房间不存在 1004=玩家不在房间
/// 1005=签名错误 1006=请求已过期 1007=鱼配置不存在 1008=子弹参数无效
/// 1009=金币参数超出范围 1011=用户不存在 1012=道具配置不存在
/// 1013=道具数量不足 1014=钻石不足 1015=炮倍参数无效
/// </summary>
[Serializable]
public class FishGameExceptionData
{
public int code;
public string message;
}
}
}
+2 -2
View File
@@ -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;
}
@@ -1,90 +1,188 @@
using Assets.MyGame.Scripts.Net.APIResponses.Enum;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 特殊鱼爆奖基类(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() —— 子类自身流程结束后调用,按模式分发爆奖。
/// </summary>
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<BJEndUIEntry> endUIEntries = new List<BJEndUIEntry>();
[Header("爆奖类型")]
public TSBJ effectType = TSBJ.转盘; // 死亡动画的爆奖类型,默认转盘
[Header("====== 通用 ======")]
public MonsterContral monster; // 宿主鱼(由调用方赋值)
public Vector3 startPoint; // 爆奖起点(特效/闪电起始位置)
public long coinCount; // 爆奖总额
public bool isLocalCoin; // 是否本地玩家的金币(金/银区分用)
public Action<int> 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
/// <summary>
/// 运行时切换爆奖模式(特殊鱼↔Boss 互通入口)。
/// </summary>
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()
/// <summary>
/// Boss 爆奖初始化。模式 = Boss爆奖 时由调用方先调本方法传齐参数,再调 SetCoinCount/Play。
/// </summary>
public void InitBossAward(int goldCount, int posID, int paobei, int fishScore, int beishu, Action<int> 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()
/// <summary>结算 UI 枚举 → prefab:写入基类 bjEndUI,OnScoreEnd 时由基类实例化。</summary>
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");
}
}
/// <summary>金币/爆奖启动入口(由 CreateFishManager 杀鱼流程调用,子类覆写)。</summary>
public virtual void SetCoinCount() { }
/// <summary>死亡特效入口(由 MonsterDieList.SpecialDieFish 调用,子类覆写)。</summary>
public virtual void PlayEffect(ParticleSystem effect)
{
if (effect != null) effect.Play();
}
public void CreateBaoJiangEffect()
/// <summary>爆奖总入口:按 baoJiangMode 分发(子类自身流程结束后调用)。</summary>
public virtual void CreateBaoJiangEffect()
{
if (baoJiangMode == TSYBaoJiangMode.Boss爆奖)
{
PlayBossAward();
}
else
{
PlaySmallFishAward();
}
}
/// <summary>
/// 小鱼爆奖:实例化 BaoJiangTSSmallFish 拓展,按 effectType 播 爆奖/转盘。
/// (移植自 EffectZuanTouDie 旧版 CreateBaoJiangEffect 的可用逻辑)
/// </summary>
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<BaoJiangTSSmallFish>();
baoJiang.JiaFen = JiaFen;
if (baoJiang != null)
var baoJiang = effectObj.GetComponent<BaoJiangTSSmallFish>();
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);
}
}
/// <summary>
/// Boss 爆奖:走 BJEventBase 完整流程。
/// CallBJ → 协模拟金币逐个到达累分 → 自动 EffEnd → 滚分 → MoveToSit → BJEndUI → JiaFen → die。
/// </summary>
protected virtual void PlayBossAward()
{
if (!_bossInited)
{
Debug.LogWarning("[DieEffect_TSY] Boss爆奖模式需先调用 InitBossAward 传入爆奖参数!");
return;
}
ResolveEndUI();
CallBJ(minBJCount, maxBJCount);
}
/// <summary>兼容旧子类的协程占位(EffectZuanTouDie 等历史代码)。</summary>
public virtual IEnumerator enumerator()
{
yield return null;
}
}
@@ -0,0 +1,31 @@
using System;
/// <summary>
/// 特殊鱼爆奖模式 —— 特殊鱼与 Boss 爆奖互通的核心开关。
/// 策划后续"特殊鱼变 Boss / Boss 变特殊鱼"只改这一项(或运行时 SetBaoJiangMode 切换)。
/// </summary>
public enum TSYBaoJiangMode
{
小鱼爆奖 = 0, // 轻量流程:BaoJiangTSSmallFish 拓展(爆奖/转盘样式)
Boss爆奖 = 1, // 完整流程:BJEventBase(红包 → 滚分 → BJEndUI 结算)
}
/// <summary>
/// Boss 爆奖结算 UI 类型(枚举选项,策划下拉选;prefab 映射配在 DieEffect_TSY.endUIEntries)。
/// 后续有新结算样式:加枚举值 + 配一条映射即可,不改代码结构。
/// </summary>
public enum BJEndUIType
{
无 = 0,
默认Boss结算 = 1,
大奖结算 = 2,
特殊Boss结算 = 3,
}
/// <summary>BJEndUIType → BJEndUI prefab 映射项。</summary>
[Serializable]
public class BJEndUIEntry
{
public BJEndUIType type;
public BJEndUI prefab;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de97264872500254bb686452a3b5deb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
@@ -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);
@@ -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);
}
}
/// <summary>
/// 修复战神 prefab 实例化后相机 targetTexture 丢失的问题。
/// 打包后从 AA 加载 prefab 时,相机上序列化的 RenderTexture 引用会变成 null,
/// 这里从面板的 RawImage.texture 取出 RenderTexture 重新赋值给战神相机。
/// </summary>
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<Camera>(true) : null;
rt = panelCam != null ? panelCam.targetTexture : null;
}
if (rt == null) return;
// 战神 prefab 自带的相机(打包后 targetTexture 为 null)
var cams = zhanShenInstance.GetComponentsInChildren<Camera>(true);
foreach (var cam in cams)
{
if (cam.targetTexture == null)
{
cam.targetTexture = rt;
Debug.Log($"[UIHome_BY_ItemListPanels] 修复战神相机 targetTexture: {cam.name} → {rt.name}");
}
}
}