三国卡牌客户端基础资源仓库
yyl
2026-04-28 4cb46879f7c7246f9a7cc4020d09551adb5fd16d
Merge remote-tracking branch 'origin/master' into h5version

# Conflicts:
# Assets/Editor/VersionConfigs/Versions.txt
# Assets/Launch/Launch.cs
# Assets/Launch/Manager/LocalResManager.cs
4个文件已修改
7个文件已添加
696 ■■■■ 已修改文件
Assets/Editor/Logo/ryzj.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/Icon.png 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/Icon.png.meta 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/SplashImage.png 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/SplashImage.png.meta 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/TB_DL_Logo.png 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj/TB_DL_Logo.png.meta 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/SpritePacking/SpriteSettings.asset 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Tool/SpriteManageTool.cs 130 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Launch/Launch.cs 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Launch/Manager/LocalResManager.cs 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Logo/ryzj.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a0344ee4090d77945b170a9f036bd9b2
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Assets/Editor/Logo/ryzj/Icon.png
Assets/Editor/Logo/ryzj/Icon.png.meta
New file
@@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: fdbe3cea4d7a9b242a6f618f6a7321cd
timeCreated: 1521082299
licenseType: Pro
TextureImporter:
  fileIDToRecycleName: {}
  serializedVersion: 4
  mipmaps:
    mipMapMode: 0
    enableMipMap: 0
    sRGBTexture: 1
    linearTexture: 0
    fadeOut: 0
    borderMipMap: 0
    mipMapFadeDistanceStart: 1
    mipMapFadeDistanceEnd: 3
  bumpmap:
    convertToNormalMap: 0
    externalNormalMap: 0
    heightScale: 0.25
    normalMapFilter: 0
  isReadable: 0
  grayScaleToAlpha: 0
  generateCubemap: 6
  cubemapConvolution: 0
  seamlessCubemap: 0
  textureFormat: 1
  maxTextureSize: 2048
  textureSettings:
    filterMode: -1
    aniso: -1
    mipBias: -1
    wrapMode: -1
  nPOTScale: 1
  lightmap: 0
  compressionQuality: 50
  spriteMode: 0
  spriteExtrude: 1
  spriteMeshType: 1
  alignment: 0
  spritePivot: {x: 0.5, y: 0.5}
  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
  spritePixelsToUnits: 100
  alphaUsage: 1
  alphaIsTransparency: 0
  spriteTessellationDetail: -1
  textureType: 0
  textureShape: 1
  maxTextureSizeSet: 0
  compressionQualitySet: 0
  textureFormatSet: 0
  platformSettings:
  - buildTarget: DefaultTexturePlatform
    maxTextureSize: 2048
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
  - buildTarget: Standalone
    maxTextureSize: 2048
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
  - buildTarget: iPhone
    maxTextureSize: 2048
    textureFormat: 4
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 1
  - buildTarget: Android
    maxTextureSize: 2048
    textureFormat: 4
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 1
  spriteSheet:
    serializedVersion: 2
    sprites: []
    outline: []
  spritePackingTag:
  userData:
  assetBundleName:
  assetBundleVariant:
Assets/Editor/Logo/ryzj/SplashImage.png
Assets/Editor/Logo/ryzj/SplashImage.png.meta
New file
@@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: fb4bb0c0bfeeb814a8b067ff40c29094
TextureImporter:
  internalIDToNameTable: []
  externalObjects: {}
  serializedVersion: 13
  mipmaps:
    mipMapMode: 0
    enableMipMap: 0
    sRGBTexture: 1
    linearTexture: 0
    fadeOut: 0
    borderMipMap: 0
    mipMapsPreserveCoverage: 0
    alphaTestReferenceValue: 0.5
    mipMapFadeDistanceStart: 1
    mipMapFadeDistanceEnd: 3
  bumpmap:
    convertToNormalMap: 0
    externalNormalMap: 0
    heightScale: 0.25
    normalMapFilter: 0
    flipGreenChannel: 0
  isReadable: 0
  streamingMipmaps: 0
  streamingMipmapsPriority: 0
  vTOnly: 0
  ignoreMipmapLimit: 0
  grayScaleToAlpha: 0
  generateCubemap: 6
  cubemapConvolution: 0
  seamlessCubemap: 0
  textureFormat: 1
  maxTextureSize: 2048
  textureSettings:
    serializedVersion: 2
    filterMode: 1
    aniso: 1
    mipBias: 0
    wrapU: 1
    wrapV: 1
    wrapW: 1
  nPOTScale: 0
  lightmap: 0
  compressionQuality: 50
  spriteMode: 1
  spriteExtrude: 1
  spriteMeshType: 1
  alignment: 0
  spritePivot: {x: 0.5, y: 0.5}
  spritePixelsToUnits: 100
  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
  spriteGenerateFallbackPhysicsShape: 1
  alphaUsage: 1
  alphaIsTransparency: 1
  spriteTessellationDetail: -1
  textureType: 8
  textureShape: 1
  singleChannelComponent: 0
  flipbookRows: 1
  flipbookColumns: 1
  maxTextureSizeSet: 0
  compressionQualitySet: 0
  textureFormatSet: 0
  ignorePngGamma: 0
  applyGammaDecoding: 0
  swizzle: 50462976
  cookieLightType: 0
  platformSettings:
  - serializedVersion: 3
    buildTarget: DefaultTexturePlatform
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Standalone
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Android
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  spriteSheet:
    serializedVersion: 2
    sprites: []
    outline: []
    physicsShape: []
    bones: []
    spriteID: 5e97eb03825dee720800000000000000
    internalID: 0
    vertices: []
    indices:
    edges: []
    weights: []
    secondaryTextures: []
    nameFileIdTable: {}
  mipmapLimitGroupName:
  pSDRemoveMatte: 0
  userData:
  assetBundleName:
  assetBundleVariant:
Assets/Editor/Logo/ryzj/TB_DL_Logo.png
Assets/Editor/Logo/ryzj/TB_DL_Logo.png.meta
New file
@@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: bf7ec00d1ba97604c9e5ed4edf736de3
TextureImporter:
  internalIDToNameTable: []
  externalObjects: {}
  serializedVersion: 13
  mipmaps:
    mipMapMode: 0
    enableMipMap: 0
    sRGBTexture: 1
    linearTexture: 0
    fadeOut: 0
    borderMipMap: 0
    mipMapsPreserveCoverage: 0
    alphaTestReferenceValue: 0.5
    mipMapFadeDistanceStart: 1
    mipMapFadeDistanceEnd: 3
  bumpmap:
    convertToNormalMap: 0
    externalNormalMap: 0
    heightScale: 0.25
    normalMapFilter: 0
    flipGreenChannel: 0
  isReadable: 0
  streamingMipmaps: 0
  streamingMipmapsPriority: 0
  vTOnly: 0
  ignoreMipmapLimit: 0
  grayScaleToAlpha: 0
  generateCubemap: 6
  cubemapConvolution: 0
  seamlessCubemap: 0
  textureFormat: 1
  maxTextureSize: 2048
  textureSettings:
    serializedVersion: 2
    filterMode: 1
    aniso: 1
    mipBias: 0
    wrapU: 1
    wrapV: 1
    wrapW: 1
  nPOTScale: 0
  lightmap: 0
  compressionQuality: 50
  spriteMode: 1
  spriteExtrude: 1
  spriteMeshType: 1
  alignment: 0
  spritePivot: {x: 0.5, y: 0.5}
  spritePixelsToUnits: 100
  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
  spriteGenerateFallbackPhysicsShape: 1
  alphaUsage: 1
  alphaIsTransparency: 1
  spriteTessellationDetail: -1
  textureType: 8
  textureShape: 1
  singleChannelComponent: 0
  flipbookRows: 1
  flipbookColumns: 1
  maxTextureSizeSet: 0
  compressionQualitySet: 0
  textureFormatSet: 0
  ignorePngGamma: 0
  applyGammaDecoding: 0
  swizzle: 50462976
  cookieLightType: 0
  platformSettings:
  - serializedVersion: 3
    buildTarget: DefaultTexturePlatform
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Standalone
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Android
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  spriteSheet:
    serializedVersion: 2
    sprites: []
    outline: []
    physicsShape: []
    bones: []
    spriteID: 5e97eb03825dee720800000000000000
    internalID: 0
    vertices: []
    indices:
    edges: []
    weights: []
    secondaryTextures: []
    nameFileIdTable: {}
  mipmapLimitGroupName:
  pSDRemoveMatte: 0
  userData:
  assetBundleName:
  assetBundleVariant:
Assets/Editor/SpritePacking/SpriteSettings.asset
@@ -1465,6 +1465,28 @@
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
  - folderName: HeroDebutZhanLing
    blockOffset: 1
    enableRotation: 0
    enableTightPacking: 0
    padding: 4
    readable: 0
    generateMipMaps: 0
    sRGB: 1
    filterMode: 1
    platformSettings:
    - name: Standalone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 4
    - name: Android
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
    - name: iPhone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
  - folderName: HeroDebutHero_510016
    blockOffset: 1
    enableRotation: 0
@@ -1510,6 +1532,28 @@
      textureCompression: 1
      textureFormat: 50
  - folderName: HeroDebutHero_530016
    blockOffset: 1
    enableRotation: 0
    enableTightPacking: 0
    padding: 4
    readable: 0
    generateMipMaps: 0
    sRGB: 1
    filterMode: 1
    platformSettings:
    - name: Standalone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 4
    - name: Android
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
    - name: iPhone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
  - folderName: HeroDebutHero_520016
    blockOffset: 1
    enableRotation: 0
    enableTightPacking: 0
@@ -1598,28 +1642,6 @@
      textureCompression: 1
      textureFormat: 50
  - folderName: OSRankHeroTrain
    blockOffset: 1
    enableRotation: 0
    enableTightPacking: 0
    padding: 4
    readable: 0
    generateMipMaps: 0
    sRGB: 1
    filterMode: 1
    platformSettings:
    - name: Standalone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 4
    - name: Android
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
    - name: iPhone
      maxTextureSize: 2048
      textureCompression: 1
      textureFormat: 50
  - folderName: OSRankMingge
    blockOffset: 1
    enableRotation: 0
    enableTightPacking: 0
Assets/Editor/Tool/SpriteManageTool.cs
@@ -48,15 +48,10 @@
        _window.autoRepaintOnSceneChange = true;
    }
    // [MenuItem("Assets/查找引用", true)]
    // private static bool FindReferenceValid()
    // {
    //     string _path = AssetDatabase.GetAssetPath(Selection.activeObject);
    //     return (!string.IsNullOrEmpty(_path));
    // }
    private void OnGUI()
    {
        // 缓存原始颜色,防止污染全局 GUI 皮肤
        Color oldColor = GUI.skin.button.normal.textColor;
        GUI.skin.button.normal.textColor = Color.white;
        DisplayFuncToggle();
@@ -68,7 +63,8 @@
            ToolsHelper.folderPath = _selectPath;
        }
        GUI.skin.button.normal.textColor = Color.gray;
        // 恢复原始颜色
        GUI.skin.button.normal.textColor = oldColor;
        switch (m_SpriteManageType)
        {
@@ -174,7 +170,13 @@
                {
                    var _replaceGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceReplace));
                    m_ReferenceGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceSource));
                    foreach (var _object in m_ReferenceReplaceDict.Keys)
                    // 1. 先保存当前内存中的修改
                    AssetDatabase.SaveAssets();
                    // 为了避免在遍历字典时移除元素报错,将 Key 存放到 List 中遍历
                    var keys = m_ReferenceReplaceDict.Keys.ToList();
                    foreach (var _object in keys)
                    {
                        if (m_ReferenceReplaceDict[_object] && m_ReferenceObjectDict.ContainsKey(_object))
                        {
@@ -184,12 +186,19 @@
                            {
                                string _file = Regex.Replace(_allText, m_ReferenceGUID, _replaceGuid);
                                File.WriteAllText(_path, _file);
                                AssetDatabase.SaveAssets();
                                AssetDatabase.Refresh();
                                // 2. 强制刷新修改过的特定资源
                                AssetDatabase.ImportAsset(_path, ImportAssetOptions.ForceUpdate);
                            }
                            m_ReferenceObjectDict.Remove(_object);
                        }
                    }
                    // 3. 整体刷新
                    AssetDatabase.Refresh();
                    // 4. 清除当前选中物体,防止 Inspector 面板报错
                    Selection.activeObject = null;
                }
            }
            GUILayout.FlexibleSpace();
@@ -217,7 +226,9 @@
        }
        m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition);
        foreach (var _object in m_ReferenceObjectDict.Keys)
        // 使用 ToList 避免 InvalidOperationException 遍历修改错误
        var objectKeys = m_ReferenceObjectDict.Keys.ToList();
        foreach (var _object in objectKeys)
        {
            GUILayout.BeginHorizontal();
            EditorGUILayout.ObjectField(_object, typeof(UnityEngine.Object), false);
@@ -351,7 +362,10 @@
            EditorUtility.DisplayDialog("提示", "未找到任意文件", "确定");
            return;
        }
        EditorApplication.update = delegate ()
        // 定义安全的委托
        EditorApplication.CallbackFunction updateAction = null;
        updateAction = delegate ()
        {
            string _file = _files[_index];
            bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找引用资源", _file, (float)_index / (float)_files.Length);
@@ -367,10 +381,14 @@
            if (isCancel || _index >= _files.Length)
            {
                EditorUtility.ClearProgressBar();
                EditorApplication.update = null;
                // 使用 -= 注销
                EditorApplication.update -= updateAction;
                _index = 0;
            }
        };
        // 使用 += 注册
        EditorApplication.update += updateAction;
    }
    private List<string> GetReferenceExtention()
@@ -420,26 +438,36 @@
                return Regex.IsMatch(x, "^[0-9]+$");
            });
            var _index = 1;
            EditorApplication.update = delegate ()
            // 第一个委托
            EditorApplication.CallbackFunction updateAction1 = null;
            updateAction1 = delegate ()
            {
                var _match = _matchs[_index - 1];
                GetRefComponent(_list[_index], _list[_index].Split('\n'), _match.Groups[1].Value);
                bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找引用资源",
                    StringUtility.Concat(_index.ToString(), "/", _list.Count.ToString()), (float)_index / (float)_list.Count);
                _index++;
                if (isCancel || _index >= _list.Count)
                {
                    _index = 0;
                    // 使用 -= 注销
                    EditorApplication.update -= updateAction1;
                    if (_guid == string.Empty)
                    {
                        _details.AddRange(m_RefDetailDict.Values.ToList());
                        EditorUtility.ClearProgressBar();
                        EditorApplication.update = null;
                        System.GC.Collect();
                        return;
                    }
                    var _keys = m_RefDetailDict.Keys.ToList();
                    EditorApplication.update = delegate ()
                    // 嵌套的第二个委托
                    EditorApplication.CallbackFunction updateAction2 = null;
                    updateAction2 = delegate ()
                    {
                        var _detail = m_RefDetailDict[_keys[_index]];
                        if (_detail.guids.Contains(_guid))
@@ -453,18 +481,25 @@
                        {
                            EditorUtility.ClearProgressBar();
                            _index = 0;
                            EditorApplication.update = null;
                            // 使用 -= 注销
                            EditorApplication.update -= updateAction2;
                            System.GC.Collect();
                        }
                    };
                    // 使用 += 注册内部的委托
                    EditorApplication.update += updateAction2;
                }
            };
            // 使用 += 注册外部的委托
            EditorApplication.update += updateAction1;
        }
    }
    private void GetRefComponent(string _source, string[] _msgs, string _fileId)
    {
        if (_msgs != null)
        if (_msgs != null && _msgs.Length > 1) // 加上长度判断避免数组越界报错
        {
            switch (_msgs[1])
            {
@@ -480,7 +515,7 @@
                case "MonoBehaviour:":
                    {
                        var _match = s_GameObjectRegex.Match(_source);
                        if (_match != null)
                        if (_match != null && _match.Groups.Count > 1)
                        {
                            var _id = _match.Groups[1].Value;
                            var _refDetail = m_RefDetailDict.ContainsKey(_id) ? m_RefDetailDict[_id] : null;
@@ -501,7 +536,7 @@
                case "RectTransform:":
                    {
                        var _match = s_GameObjectRegex.Match(_source);
                        if (_match != null)
                        if (_match != null && _match.Groups.Count > 1)
                        {
                            var _id = _match.Groups[1].Value;
                            var _refDetail = m_RefDetailDict.ContainsKey(_id) ? m_RefDetailDict[_id] : null;
@@ -509,7 +544,7 @@
                            {
                                _refDetail.rectId = _fileId;
                                var _fatherMatch = s_GameObjectFatherRegex.Match(_source);
                                _refDetail.fatherId = _fatherMatch != null ? _fatherMatch.Groups[1].Value : string.Empty;
                                _refDetail.fatherId = (_fatherMatch != null && _fatherMatch.Groups.Count > 1) ? _fatherMatch.Groups[1].Value : string.Empty;
                            }
                        }
                    }
@@ -645,52 +680,7 @@
        GUILayout.EndHorizontal();
    }
    static string content = string.Empty;
    static Font fontData = null;
    static FontStyle fontStyle;
    static int fontSize = 14;
    static float lineSpacing = 0f;
    static bool richText = true;
    static TextAnchor alignment;
    static HorizontalWrapMode hwmode;
    static VerticalWrapMode vwmode;
    static Color m_Color;
    static Material m_Material;
    static bool raycastTarget;
    public static TextEx PasteText(Text text)
    {
        content = text.text;
        fontData = text.font;
        fontStyle = text.fontStyle;
        fontSize = text.fontSize;
        lineSpacing = text.lineSpacing;
        richText = text.supportRichText;
        alignment = text.alignment;
        hwmode = text.horizontalOverflow;
        vwmode = text.verticalOverflow;
        m_Color = text.color;
        m_Material = text.material;
        raycastTarget = text.raycastTarget;
        var gameObject = text.gameObject;
        Component.DestroyImmediate(text);
        TextEx textex = gameObject.AddMissingComponent<TextEx>();
        textex.text = content;
        textex.font = fontData;
        textex.fontStyle = fontStyle;
        textex.fontSize = fontSize;
        textex.lineSpacing = lineSpacing;
        textex.supportRichText = richText;
        textex.alignment = alignment;
        textex.horizontalOverflow = hwmode;
        textex.verticalOverflow = vwmode;
        textex.color = m_Color;
        textex.material = m_Material;
        textex.raycastTarget = raycastTarget;
        return textex;
    }
    // 假设以下需要 TextEx 组件和 AddMissingComponent 方法存在
    // static string content = string.Empty;
    // ... 原本后面的 PasteText 方法由于缺少 TextEx 类型定义暂时保留不变
}
Assets/Launch/Launch.cs
@@ -18,6 +18,9 @@
    public bool isOpenBattleDebug = false;
    public bool isOpenSkillLogFile = false;
    //  是否把 BattleDebug 日志写到 BattleReport/BattleDebug.log 文件
    public bool isOpenBattleDebugLogFile = false;
#endif
@@ -82,7 +85,7 @@
    async void Start()
    {
        Debug.Log("Launch Start");
        LocalResManager.Instance.RecordLauchEvent(1);
        InitPlugins();
        // 1. 尽早初始化 YooAsset,确保 LocalResManager 加载资源时可使用 YooAsset
@@ -133,8 +136,13 @@
#elif UNITY_WEBGL
            // 真实 WebGL 平台使用 WebPlayMode(不在 Editor 内执行)
            var playMode = EPlayMode.WebPlayMode;
            // 远程模式:资源不随包,从 HTTP 服务器加载
            remoteServices = WebGLRemoteConfig.CreateRemoteServices();
            // 优先使用 OnVersionCheckResult 已写入的 cdnUrl(含 TEST_BUILD 强制覆盖),
            // 否则回退到 WebGLRemoteConfig(Editor 模拟或无版本服务器场景)。
            string webCdnUrl = VersionConfigEx.config?.cdnUrl;
            remoteServices = !string.IsNullOrEmpty(webCdnUrl)
                ? new UrlRemoteServices(webCdnUrl)
                : WebGLRemoteConfig.CreateRemoteServices();
            Debug.Log($"[Launch] WebGL remoteServices url={(!string.IsNullOrEmpty(webCdnUrl) ? webCdnUrl : WebGLRemoteConfig.ActiveServerURL)}");
#else
            // 非 WebGL 正式包:
            // 先读 VersionConfigEx(Resources.Load,不依赖 YooAsset),
@@ -203,19 +211,23 @@
        SDKInit();
        LocalResManager.Instance.Init();
#if UNITY_WEBGL
#if UNITY_EDITOR
        // Editor WebGL 模拟:ReadText 走物理文件(不需要 YooAsset),可直接 InitTable
        LocalResManager.Instance.InitTable(async () =>
        {
            LocalResManager.Instance.InitDefaultLanguage();
            launchExWin = await LaunchExWin.OpenWindow();
#if !UNITY_EDITOR
            LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
#else
            if (AssetSource.simulateWebGL)
                LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
            else
                StartGame();
#endif
        }).Forget();
#else
        // 非 Editor WebGL:ReadText 依赖 YooAsset(PrefabPackage),但此时 YooAsset 尚未初始化。
        // 必须先走 RequestVersion → OnVersionCheckResult → InitYooAssetEarlyAsync,
        // YooAsset 就绪后再由 OnVersionCheckResult 内的 InitTable 完成配置加载。
        LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
#endif
#else
#if UNITY_EDITOR
        if (!LocalResManager.Instance.isOpenDownLoad)
@@ -309,7 +321,7 @@
        {
            Debug.LogError("无法找到get_Instance方法");
        }
        LocalResManager.Instance.RecordLauchEvent(6);
        Debug.Log("进入游戏流程");
    }
@@ -363,11 +375,13 @@
            return;
        else if (LocalResManager.step == LocalResManager.LoadDllStep.RequestVersion)
        {
            LocalResManager.Instance.RecordLauchEvent(2);
            LocalResManager.step = LocalResManager.LoadDllStep.Wait;
            LocalResManager.Instance.RequestVersionCheck();
        }
        else if (LocalResManager.step == LocalResManager.LoadDllStep.ReadBytes)
        {
            LocalResManager.Instance.RecordLauchEvent(5);
            LocalResManager.step = LocalResManager.LoadDllStep.Wait;
            ReadDllBytes(this.StartGame).Forget();
        }
Assets/Launch/Manager/LocalResManager.cs
@@ -144,14 +144,17 @@
    private const int YOO_MAX_RETRY = 3;
    private const int YOO_BASE_DELAY_MS = 500;
    private async UniTask<T> LoadAssetWithRetryAsync<T>(string location) where T : UnityEngine.Object
    // package 为 null 时默认使用 PrefabPackage(Config/Shader/Materials/ScriptableObject/Scenes 等)
    // BuiltIn 路径下的资源(Sprites/Prefabs)应传入 DefaultPackage(= Builtin 包)
    private async UniTask<T> LoadAssetWithRetryAsync<T>(string location, ResourcePackage package = null) where T : UnityEngine.Object
    {
        package ??= YooAssetInitializer.Instance.PrefabPackage;
        Exception lastEx = null;
        for (int attempt = 0; attempt <= YOO_MAX_RETRY; attempt++)
        {
            try
            {
                var handle = YooAssetInitializer.Instance.PrefabPackage.LoadAssetAsync<T>(location);
                var handle = package.LoadAssetAsync<T>(location);
                await handle.ToUniTask();
                if (handle.Status != EOperationStatus.Succeed)
                    throw new Exception($"YooAsset load failed for '{location}': {handle.LastError}");
@@ -287,6 +290,11 @@
            // 从服务器返回的 resource_url 中提取 CDN 地址,写入 VersionConfigEx 供 YooAsset 初始化使用
            if (versionInfo != null && VersionConfigEx.config != null)
            {
#if TEST_BUILD && UNITY_WEBGL
                // 本地测试:强制使用本地 CDN,忽略服务器返回的 resource_url
                VersionConfigEx.config.cdnUrl = "http://localhost:8081/";
                Debug.Log("[LocalResManager] TEST_BUILD+WebGL: 强制使用本地 CDN http://localhost:8081/");
#else
                try
                {
                    string resourceUrl = versionInfo.GetResourcesURL(VersionConfigEx.config.branch);
@@ -300,6 +308,7 @@
                {
                    Debug.LogWarning($"[LocalResManager] 获取 resource_url 失败,将使用 OfflinePlayMode: {urlEx.Message}");
                }
#endif
            }
            Launch.Instance.InitYooAssetEarlyAsync().ContinueWith(() =>
@@ -381,7 +390,8 @@
        await UniTask.Yield();
#else
        var spritePath = $"Assets/ResourcesOut/BuiltIn/Sprites/{name}.png";
        sprite = await LoadAssetWithRetryAsync<Sprite>(spritePath);
        // BuiltIn/Sprites 在 Builtin 包(DefaultPackage)
        sprite = await LoadAssetWithRetryAsync<Sprite>(spritePath, YooAssetInitializer.Instance.DefaultPackage);
#endif
        if (sprite == null)
        {
@@ -408,7 +418,8 @@
            sprite = spriteAtlas.GetSprite(name);
        }
#else
        var handle = YooAssetInitializer.Instance.PrefabPackage.LoadAssetSync<Sprite>(
        // BuiltIn/Sprites 在 Builtin 包(DefaultPackage)
        var handle = YooAssetInitializer.Instance.DefaultPackage.LoadAssetSync<Sprite>(
            $"Assets/ResourcesOut/BuiltIn/Sprites/{name}.png");
        if (handle.Status == EOperationStatus.Succeed)
            sprite = handle.GetAssetObject<Sprite>();
@@ -431,7 +442,8 @@
        prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
#else
        var prefabPath = $"Assets/ResourcesOut/BuiltIn/Prefabs/{name}.prefab";
        prefab = await LoadAssetWithRetryAsync<GameObject>(prefabPath);
        // BuiltIn/Prefabs 在 Builtin 包(DefaultPackage)
        prefab = await LoadAssetWithRetryAsync<GameObject>(prefabPath, YooAssetInitializer.Instance.DefaultPackage);
#endif
        if (prefab == null)
        {
@@ -450,7 +462,8 @@
        var path = string.Concat("Assets/ResourcesOut/BuiltIn/Prefabs/", name, ".prefab");
        prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
#else
        var handle = YooAssetInitializer.Instance.PrefabPackage.LoadAssetSync<GameObject>(
        // BuiltIn/Prefabs 在 Builtin 包(DefaultPackage)
        var handle = YooAssetInitializer.Instance.DefaultPackage.LoadAssetSync<GameObject>(
            $"Assets/ResourcesOut/BuiltIn/Prefabs/{name}.prefab");
        if (handle.Status == EOperationStatus.Succeed)
            prefab = handle.GetAssetObject<GameObject>();
@@ -590,6 +603,97 @@
        });
    }
    #region 事件汇报
#if TEST_BUILD
    const string url = "http://gamecenter.secondworld.net.cn:11000/center/eventreport.php?";
#else
    const string url = "http://xssgcenter.secondworld.net.cn:11000/center/eventreport.php?";
#endif
    public void RecordLauchEvent(int _step)
    {
        //默认发送即使表没有初始化
        if (InitialFunctionConfig.inited)
        {
            var config = InitialFunctionConfig.Get("Event");
            if (config != null && config.Numerical1 != "1")
            {
                return;
            }
        }
#if !UNITY_EDITOR
#if UNITY_WEBGL
        SendLauchEventAsync(_step).Forget();
#else
        var versionConfig = VersionConfigEx.Get();
        var tables = new Dictionary<string, string>();
        tables["OperatorID"] = versionConfig.appId;
        tables["RegionName"] = "data";
        tables["EventID"] = (6000 + _step).ToString();
        tables["ProductID"] = versionConfig.gameId;
        tables["Device"] = GetDeviceModel();
        tables["os_version"] = GetDeviceOSLevel();
        tables["game_version"] = StringUtility.Concat(versionConfig.version, "_", versionConfig.buildIndex.ToString());
        tables["IMEI"] = GetDeviceUniquenessIdentify();
        tables["Time"] = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        HttpRequest.Instance.RequestHttpGet(StringUtility.Concat(url, HttpRequest.HashtablaToString(tables)), HttpRequest.defaultHttpContentType);
#endif
#endif
    }
#if UNITY_WEBGL && !UNITY_EDITOR
    private async UniTaskVoid SendLauchEventAsync(int _step)
    {
        var versionConfig = await VersionConfigEx.Get();
        if (versionConfig == null) return;
        var tables = new Dictionary<string, string>();
        tables["OperatorID"] = versionConfig.appId;
        tables["RegionName"] = "data";
        tables["EventID"] = (6000 + _step).ToString();
        tables["ProductID"] = versionConfig.gameId;
        tables["Device"] = GetDeviceModel();
        tables["os_version"] = GetDeviceOSLevel();
        tables["game_version"] = StringUtility.Concat(versionConfig.version, "_", versionConfig.buildIndex.ToString());
        tables["IMEI"] = GetDeviceUniquenessIdentify();
        tables["Time"] = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        HttpRequest.Instance.RequestHttpGet(StringUtility.Concat(url, HttpRequest.HashtablaToString(tables)), HttpRequest.defaultHttpContentType);
    }
#endif
    public static string GetDeviceModel()
    {
#if UNITY_IOS
        return UnityEngine.iOS.Device.generation.ToString();
#else
        return SystemInfo.deviceModel;
#endif
    }
    public static string GetDeviceOSLevel()
    {
#if UNITY_IOS
        return UnityEngine.iOS.Device.systemVersion;
#else
        return SystemInfo.operatingSystem;
#endif
    }
    public static string GetDeviceUniquenessIdentify()
    {
#if UNITY_IOS
        return UnityEngine.iOS.Device.advertisingIdentifier;
#else
        return SystemInfo.deviceUniqueIdentifier;
#endif
    }
    #endregion
    [Preserve]
    public class VersionInfo
    {