using System; using UnityEngine; using UnityEngine.Playables; using Cysharp.Threading.Tasks; using System.Collections.Generic; using System.Linq; using Framework; using Random = UnityEngine.Random; using GameWrapper.Team; using PhxhSDK; using Constants = Framework.Constants; public class TeamPreviewNode : MonoBehaviour { private const string VehicleBatteryPointName = "Battery_point01"; private const string DEFAULT_SKINID_KEY = "DefaultSkinID"; private const int DEFAULT_SKIN_ID = 1; public Transform[] characterPos; public Transform[] vehiclesPos; public Transform cRoot; public Transform vRoot; public RenderTexture RTexture => _rTexture; private Dictionary _vehicleDic; private Dictionary _characterDic; private uint[] _vehicleIDs; private uint[] _charIDs; private bool _isLoading; private int _teamIdx; private bool _isReleasing; private RenderTexture _rTexture; private Dictionary _assetsLoadDic = new Dictionary(); private List _emojiMgrList = new List(); #region Static public static async UniTask LoadNode() { var prefab = await AssetManager.Instance.LoadAssetAsync(Framework.Constants.TEAM_EDIT_PREFAB_PATH); var go = Instantiate(prefab, Vector3.up, Quaternion.identity); var component = go.GetComponent(); return component; } public static void ReleaseNode(TeamPreviewNode node) { node.ReleaseRt(); node.RestoreOriginalShaders(); Destroy(node.gameObject); } private void ReleaseRt() { if (_rTexture) { RenderTexturePool.Instance.Destroy(_rTexture); _rTexture = null; } } #endregion #region MainLife private void Awake() { _characterDic = new Dictionary(); _vehicleDic = new Dictionary(); EventManager.Instance.Register(EventManager.EventName.TeamEditCharacterModelCreate, (Action)OnCharModelCreate); EventManager.Instance.Register(EventManager.EventName.TeamEditCharacterModelClear, (Action)OnCharModelClear); EventManager.Instance.Register(EventManager.EventName.TeamEditVehicleModelCreate, (Action)OnVehicleModelCreate); EventManager.Instance.Register(EventManager.EventName.TeamEditVehicleModelClear, (Action)OnVehicleModelClear); EventManager.Instance.Register(EventManager.EventName.TeamEditAllModelClear, ClearAllGo); EventManager.Instance.Register>(EventManager.EventName.TeamEditModelUpdate, RefreshNode); EventManager.Instance.Register(EventManager.EventName.TeamCameraSet, _OnTeamEditCameraSet); } private void OnDestroy() { EventManager.Instance.Unregister(EventManager.EventName.TeamEditCharacterModelCreate, (Action)OnCharModelCreate); EventManager.Instance.Unregister(EventManager.EventName.TeamEditCharacterModelClear, (Action)OnCharModelClear); EventManager.Instance.Unregister(EventManager.EventName.TeamEditVehicleModelCreate, (Action)OnVehicleModelCreate); EventManager.Instance.Unregister(EventManager.EventName.TeamEditVehicleModelClear, (Action)OnVehicleModelClear); EventManager.Instance.Unregister(EventManager.EventName.TeamEditAllModelClear, ClearAllGo); EventManager.Instance.Unregister>(EventManager.EventName.TeamEditModelUpdate, RefreshNode); EventManager.Instance.Unregister(EventManager.EventName.TeamCameraSet, _OnTeamEditCameraSet); UnLoad(); AssetManager.Instance.Unload(Constants.TEAM_EDIT_PREFAB_PATH); } private void UnLoad() { foreach (var kv in _assetsLoadDic) { var path = kv.Key; AssetManager.Instance.Unload(path); } foreach (var emojiMgr in _emojiMgrList) { TeamServiceHandle.CharacterService.DisposeEmojiManager(emojiMgr); } _emojiMgrList.Clear(); _characterDic.Clear(); _vehicleDic.Clear(); } private void ClearObjs() { foreach (Transform child in cRoot) { Destroy(child.gameObject); } foreach (Transform child in vRoot) { Destroy(child.gameObject); } _characterDic.Clear(); _vehicleDic.Clear(); } #endregion #region 对外方法 /// /// 刷新节点 /// public async UniTask Refresh() { ClearObjs(); var teamCamera = TeamServiceHandle.TeamEditCameraObj.teamEditCamera; teamCamera.targetTexture = _rTexture; for (var i = 0; i < _charIDs.Length; i++) { var id = _charIDs[i]; var isValid = TeamServiceHandle.CharacterService.CharacterDataDicContainsKey(id); if (isValid) { var trans = characterPos[i]; if (!_isLoading) { _isLoading = true; await AddCharacterGo(trans, id, i); } } } for (var i = 0; i < _vehicleIDs.Length; i++) { var id = _vehicleIDs[i]; var isValid = TeamServiceHandle.VehicleService.VehicleDataDicContainsKey(id); if (isValid) { var trans = vehiclesPos[i]; if (!_isLoading) { _isLoading = true; await AddVehicleGo(trans, id, i); } } } _isLoading = false; } public void SetData(int teamIdx, uint[] charIDs, uint[] vehicleIDs) { _teamIdx = teamIdx; _charIDs = (uint[])charIDs.Clone(); _vehicleIDs = vehicleIDs; } public void CreateRt() { if (!_rTexture) { _rTexture = RenderTexturePool.Instance.Create("TeamPreviewNode", Screen.width, Screen.height); } } public void Clear() { ClearAllGo(); _rTexture.Release(); } #endregion #region 加载模型 /// /// 添加角色模型 /// async UniTask AddCharacterGo(Transform trans, uint id, int idx) { var charData = TeamServiceHandle.CharacterService.GetCharacterData(id); // 获取角色配置信息 var charCfg = GetField(charData, "Cfg"); var skinID = GetField(charData, "SkinID") > 0 ? (int)GetField(charData, "SkinID") : GetDefaultSkinId(); var roleID = GetField(charCfg, "RoleID"); var data = TeamServiceHandle.TableService.GetSkinConfig(skinID, roleID); if (data != null) { var nameID = GetField(data, "NameID"); var prefabPath = TeamServiceHandle.CharacterService.GetCharacterDisplayModelPath(nameID); var prefab = await AssetManager.Instance.LoadAssetAsync(prefabPath); _assetsLoadDic.TryAdd(prefabPath, (int)id); if (!trans) { return; } var go = Instantiate(prefab, trans.position, trans.rotation, cRoot.transform); if (go) { _characterDic[idx] = go; // TODO 层级优化 go.SetLayerEx(9); var anim = go.GetComponentInChildren(); var playableDir = go.GetComponent(); if (playableDir) { playableDir.enabled = false; } if (anim) { int randomIdx = Random.Range(0, 3); switch (randomIdx) { case 1: { anim.Play("standready001"); break; } case 2: { anim.Play("standready001"); break; } default: { anim.Play("squatready001"); break; } } } // 处理角色表情 object charSingleData = null; if (TeamServiceHandle.TableService.TryGetCharacterAttriData((int)id, out var data2)) { charSingleData = data2; var eyesColor = GetField(charSingleData, "EmojiEyesColor"); var eyeBrowsType = GetField(charSingleData, "EmojiEyeBowsType"); var eyeBrowsColor = GetField(charSingleData, "EmojiEyeBowsColor"); var defaultEmojiId = GetField(charSingleData, "DefaultDisplayEmojiId"); var emojiMgr = TeamServiceHandle.CharacterService.CreateEmojiManager( go, eyesColor, eyeBrowsType, eyeBrowsColor, true); TeamServiceHandle.CharacterService.SwitchEmoji(emojiMgr, defaultEmojiId); _emojiMgrList.Add(emojiMgr); } } } _isLoading = false; } /// /// 添加载具模型 /// async UniTask AddVehicleGo(Transform trans, uint id, int idx) { var vehicleData = TeamServiceHandle.VehicleService.GetVehicleData(id); if (vehicleData != null) { var vehicleCfg = GetField(vehicleData, "Cfg"); var vehicleId = GetField(vehicleCfg, "ID"); var prefabPath = TeamServiceHandle.VehicleService.GetVehicleGameModelPath(vehicleId); var prefab = await AssetManager.Instance.LoadAssetAsync(prefabPath); ApplyVehicleShader(prefab); _assetsLoadDic.TryAdd(prefabPath, (int)id); if (!trans) return; var go = Instantiate(prefab, trans.position, trans.rotation, vRoot.transform); if (go) { _vehicleDic[idx] = go; // TODO 层级优化 go.SetLayerEx(9); go.transform.localScale = new Vector3(1.5f, 1.5f, 1.5f); // 炮台部分 var config = TeamServiceHandle.VehicleService.CombineConfigData(vehicleData); // 获取武器ID var weaponId = GetField(config, "weaponId"); // 炮台挂点 var allTrans = go.GetComponentsInChildren(); var fireTrans = allTrans.First(t => t.name == VehicleBatteryPointName); var rawConfig = TeamServiceHandle.TableService.GetBatteryConfig(weaponId); var modelPath = GetField(rawConfig, "ModelPath"); var batteryPath = TeamServiceHandle.VehicleService.GetBatteryModelPath(modelPath); var sampleObj = await AssetManager.Instance.LoadAssetAsync(batteryPath); if (sampleObj != null) { var rootObj = UnityEngine.Object.Instantiate(sampleObj, fireTrans); // TODO 层级优化 go.SetLayerEx(9); rootObj.transform.localPosition = Vector3.zero; rootObj.transform.localRotation = Quaternion.identity; rootObj.transform.localScale = Vector3.one; } else { Debug.LogError($"加载炮台失败: {batteryPath}"); } //设置涂装 var vehicleDev = TeamServiceHandle.VehicleService.GetVehicleDevelopment(id); var paintCfg = GetField(vehicleDev, "paint_set"); if (paintCfg != null) { for (int i = 1; i < (int)TeamPaintType.Max; i++) { var paintID = paintCfg[i]; if (paintID == 0) continue; var paintPath = TeamServiceHandle.VehicleService.GetVehiclePaintPath((int)id, paintID); //加载涂装材质球 var material = await TeamServiceHandle.VehicleService.GetVehiclePaintMaterial(paintPath); var root = TeamServiceHandle.VehicleService.FindVehiclePartRoot(go, (int)id, (TeamPaintType)i); if (material != null && root != null) { var instanceMaterial = new Material(material); var renderers = root.GetComponent(); renderers.materials = new Material[] { instanceMaterial }; } } } else Debug.LogError("载具涂装配置为空!"); } } _isLoading = false; } /// /// 设置游戏对象及其子对象的层级 /// private void SetLayerRecursively(GameObject obj, int layer) { obj.layer = layer; foreach (Transform child in obj.transform) { SetLayerRecursively(child.gameObject, layer); } } /// /// 更换模型shader /// private void ChangeModelShader(GameObject prefab) { var vehicleShader = TeamServiceHandle.TeamService.GetVehicleShader(); var vehicleRenderers = prefab.GetComponentsInChildren(true); if (vehicleShader != null) { foreach (var vehicleRenderer in vehicleRenderers) { foreach (var material in vehicleRenderer.sharedMaterials) { material.shader = vehicleShader; } } } } private Dictionary originalShaderDic = new Dictionary(); /// /// 替换Shader,并缓存原始Shader 用于解决场景切换后模型蒙上未知来源阴影 /// private void ApplyVehicleShader(GameObject prefab) { var vehicleShader = TeamServiceHandle.TeamService.GetVehicleShader(); if (vehicleShader == null) return; var vehicleRenderers = prefab.GetComponentsInChildren(true); foreach (var vehicleRenderer in vehicleRenderers) { if (!originalShaderDic.ContainsKey(vehicleRenderer)) { var originalShaders = new Shader[vehicleRenderer.sharedMaterials.Length]; for (int i = 0; i < vehicleRenderer.sharedMaterials.Length; i++) { originalShaders[i] = vehicleRenderer.sharedMaterials[i].shader; } originalShaderDic[vehicleRenderer] = originalShaders; } // 替换 Shader foreach (var mat in vehicleRenderer.sharedMaterials) { mat.shader = vehicleShader; } } } /// /// 还原Shader /// private void RestoreOriginalShaders() { foreach (var kvp in originalShaderDic) { var origRenderer = kvp.Key; var originalShaders = kvp.Value; var materials = origRenderer.sharedMaterials; for (int i = 0; i < materials.Length && i < originalShaders.Length; i++) { materials[i].shader = originalShaders[i]; } } originalShaderDic.Clear(); } /// /// 根据索引加载角色模型 /// private async UniTask LoadCharacterModel(uint id, int idx) { if (idx < 0) return; if (_isLoading) return; if (_charIDs[idx] != id) { ClearCharacterGo(idx); _charIDs[idx] = id; _isLoading = true; await AddCharacterGo(characterPos[idx], id, idx); } } /// /// 根据索引加载载具模型 /// private async UniTask LoadVehicleModel(uint id, int idx) { if (idx < 0) return; if (_isLoading) return; if (_vehicleIDs[idx] != id) { ClearVehicleGo(idx); _vehicleIDs[idx] = id; _isLoading = true; await AddVehicleGo(vehiclesPos[idx], id, idx); } } #endregion #region 清除模型 /// /// 指挥官清空后使用 /// private void ClearAllGo() { for (int i = 0; i < characterPos.Length; i++) { ClearCharacterGo(i); } for (int i = 0; i < vehiclesPos.Length; i++) { ClearVehicleGo(i); } } private void ClearCharacterGo(int idx) { if (_isLoading) { return; } _isLoading = true; if (_characterDic.TryGetValue(idx, out var go)) { if (go) { DestroyImmediate(go); _charIDs[idx] = TeamServiceHandle.TeamService.GetTeamNoneID(); _characterDic.Remove(idx); } } _isLoading = false; } private void ClearVehicleGo(int idx) { if (_isLoading) { return; } _isLoading = true; if (_vehicleDic.TryGetValue(idx, out var go)) { if (go) { DestroyImmediate(go); _vehicleIDs[idx] = TeamServiceHandle.TeamService.GetTeamNoneID(); _vehicleDic.Remove(idx); } } _isLoading = false; } #endregion #region 事件监听 private async void RefreshNode(ValueTuple data) { var team = TeamServiceHandle.TeamService.GetTeamByIndex(data.Item1, data.Item2); if (team == null) return; var charIDs = TeamServiceHandle.TeamService.GetAllCharacters(team); var vehicleIDs = TeamServiceHandle.TeamService.GetVehicleIDs(team); SetData(data.Item1, charIDs, vehicleIDs); await Refresh(); } private async void OnCharModelCreate(CharAddData charAddData) { await LoadCharacterModel(charAddData.ID, charAddData.Idx); } private void OnCharModelClear(int idx) { if (idx >= 0) { ClearCharacterGo(idx); } } private async void OnVehicleModelCreate(VehicleAddData vehicleAddData) { await LoadVehicleModel(vehicleAddData.ID, vehicleAddData.Idx); } private void OnVehicleModelClear(int idx) { if (idx >= 0) { ClearVehicleGo(idx); } } private void _OnTeamEditCameraSet() { if (TeamServiceHandle.TeamEditCameraObj != null && TeamServiceHandle.TeamEditCameraObj.teamEditCamera != null) { TeamServiceHandle.TeamEditCameraObj.SetCameraAntialiasing( TeamServiceHandle.TeamEditCameraObj.teamEditCamera); } } #endregion #region Helpers private int GetDefaultSkinId() { return DEFAULT_SKIN_ID; } // 通过反射获取对象字段值 // 通过反射获取对象字段值,添加更健壮的类型转换 private T GetField(object obj, string fieldName) { if (obj == null) return default; try { var field = obj.GetType().GetField(fieldName); if (field != null) { var value = field.GetValue(obj); if (value == null) return default; // 尝试直接转换 if (value is T typedValue) return typedValue; // 尝试从其他类型转换 try { return (T)Convert.ChangeType(value, typeof(T)); } catch { Debug.LogWarning($"无法将类型 {value.GetType()} 转换为 {typeof(T)} - 字段名: {fieldName}"); return default; } } var property = obj.GetType().GetProperty(fieldName); if (property != null) { var value = property.GetValue(obj); if (value == null) return default; // 尝试直接转换 if (value is T typedValue) return typedValue; // 尝试从其他类型转换 try { return (T)Convert.ChangeType(value, typeof(T)); } catch { Debug.LogWarning($"无法将类型 {value.GetType()} 转换为 {typeof(T)} - 属性名: {fieldName}"); return default; } } Debug.LogWarning($"对象 {obj.GetType()} 中未找到字段或属性: {fieldName}"); return default; } catch (Exception ex) { Debug.LogError($"从对象 {obj.GetType()} 获取字段 {fieldName} 时发生异常: {ex.Message}"); return default; } } #endregion }