美术项目提交

This commit is contained in:
zhang hongbo 2026-01-27 15:41:53 +08:00
parent dde7071cf4
commit dc4acce758
12 changed files with 2873 additions and 1 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 56ee0b40ae212264598820ef1d8b3532
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef92907a06b50994a8029a98c05989f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
using UnityEngine;
using UnityEditor;
using ArtResource;
using System.Diagnostics;
using Debug = UnityEngine.Debug;
namespace CrazyMaple.Editor
{
/// <summary>
/// 测试工具验证SO加载时是否会连带加载引用资源
///
/// 测试方法:
/// 1. 在加载SO前后检查内存中的资源数量
/// 2. 分别测试带引用和不带引用的SO
/// 3. 分析加载耗时差异
/// </summary>
public class ArtResourceLoadTest : EditorWindow
{
[MenuItem("美术工具/测试SO加载行为")]
public static void ShowWindow()
{
GetWindow<ArtResourceLoadTest>("SO加载测试");
}
private void OnGUI()
{
GUILayout.Label("SO加载行为测试", EditorStyles.boldLabel);
GUILayout.Space(10);
if (GUILayout.Button("测试1: 检查SO序列化大小", GUILayout.Height(40)))
{
TestSerializationSize();
}
GUILayout.Space(5);
if (GUILayout.Button("测试2: 对比加载时间(带引用 vs 纯路径)", GUILayout.Height(40)))
{
TestLoadingTime();
}
GUILayout.Space(5);
if (GUILayout.Button("测试3: 检查内存占用(加载前后)", GUILayout.Height(40)))
{
TestMemoryUsage();
}
GUILayout.Space(10);
EditorGUILayout.HelpBox(
"这些测试将帮助确定:\n" +
"1. SO是否会连带加载引用资源\n" +
"2. 引用字段对加载速度的影响\n" +
"3. 内存占用差异",
MessageType.Info);
}
/// <summary>
/// 测试1: 检查SO序列化后的文件大小
/// </summary>
private void TestSerializationSize()
{
Debug.Log("========== 测试1: SO序列化大小 ==========");
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
if (guids.Length == 0)
{
Debug.LogWarning("未找到任何ArtTableSO");
return;
}
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
if (table != null)
{
// 获取文件大小
var fileInfo = new System.IO.FileInfo(path);
long fileSize = fileInfo.Length;
// 统计引用数量
int spriteRefCount = 0;
int spineRefCount = 0;
int pathCount = 0;
foreach (var item in table.Items)
{
if (item.Sprite != null) spriteRefCount++;
if (item.SpineAsset != null) spineRefCount++;
if (!string.IsNullOrEmpty(item.SpritePath) || !string.IsNullOrEmpty(item.SpineAssetPath))
pathCount++;
}
Debug.Log($"SO: {table.TableName}\n" +
$" 文件大小: {fileSize / 1024f:F2} KB\n" +
$" 资源项数: {table.Items.Count}\n" +
$" Sprite引用: {spriteRefCount}\n" +
$" Spine引用: {spineRefCount}\n" +
$" 路径字段: {pathCount}");
}
}
Debug.Log("========== 测试1 完成 ==========");
EditorUtility.DisplayDialog("测试完成", "请查看Console日志了解SO文件大小和引用情况", "确定");
}
/// <summary>
/// 测试2: 对比加载时间
/// </summary>
private void TestLoadingTime()
{
Debug.Log("========== 测试2: 加载时间对比 ==========");
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
if (guids.Length == 0)
{
Debug.LogWarning("未找到任何ArtTableSO");
return;
}
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
// 卸载以确保测试准确
Resources.UnloadUnusedAssets();
System.GC.Collect();
// 测试加载时间
var sw = Stopwatch.StartNew();
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
sw.Stop();
if (table != null)
{
// 统计引用情况
int refCount = 0;
foreach (var item in table.Items)
{
if (item.Sprite != null || item.SpineAsset != null)
refCount++;
}
Debug.Log($"SO: {table.TableName}\n" +
$" 加载耗时: {sw.Elapsed.TotalMilliseconds:F2} ms\n" +
$" 资源项数: {table.Items.Count}\n" +
$" 含引用项: {refCount}\n" +
$" 平均耗时: {sw.Elapsed.TotalMilliseconds / table.Items.Count:F3} ms/项");
// 尝试访问一个引用,看看是否触发额外加载
if (table.Items.Count > 0 && table.Items[0].Sprite != null)
{
var sw2 = Stopwatch.StartNew();
var sprite = table.Items[0].Sprite; // 访问引用
var name = sprite.name; // 访问属性
sw2.Stop();
Debug.Log($" 访问第1个Sprite引用: {sw2.Elapsed.TotalMilliseconds:F2} ms");
}
}
}
Debug.Log("========== 测试2 完成 ==========");
EditorUtility.DisplayDialog("测试完成", "请查看Console日志了解加载时间差异", "确定");
}
/// <summary>
/// 测试3: 检查内存占用
/// </summary>
private void TestMemoryUsage()
{
Debug.Log("========== 测试3: 内存占用测试 ==========");
// 清理内存
Resources.UnloadUnusedAssets();
System.GC.Collect();
long memBefore = System.GC.GetTotalMemory(true);
int textureBefore = Resources.FindObjectsOfTypeAll<Texture2D>().Length;
int spriteBefore = Resources.FindObjectsOfTypeAll<Sprite>().Length;
Debug.Log($"加载前:\n" +
$" 托管内存: {memBefore / 1024f / 1024f:F2} MB\n" +
$" Texture2D数量: {textureBefore}\n" +
$" Sprite数量: {spriteBefore}");
// 加载所有SO
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
var tables = new System.Collections.Generic.List<ArtTableSO>();
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
if (table != null)
{
tables.Add(table);
}
}
long memAfter = System.GC.GetTotalMemory(false);
int textureAfter = Resources.FindObjectsOfTypeAll<Texture2D>().Length;
int spriteAfter = Resources.FindObjectsOfTypeAll<Sprite>().Length;
Debug.Log($"加载后:\n" +
$" 托管内存: {memAfter / 1024f / 1024f:F2} MB (+{(memAfter - memBefore) / 1024f / 1024f:F2} MB)\n" +
$" Texture2D数量: {textureAfter} (+{textureAfter - textureBefore})\n" +
$" Sprite数量: {spriteAfter} (+{spriteAfter - spriteBefore})");
Debug.Log($"共加载 {tables.Count} 个SO");
Debug.Log("========== 测试3 完成 ==========");
EditorUtility.DisplayDialog(
"测试完成",
$"内存增加: {(memAfter - memBefore) / 1024f / 1024f:F2} MB\n" +
$"Texture2D增加: {textureAfter - textureBefore}\n" +
$"Sprite增加: {spriteAfter - spriteBefore}\n\n" +
$"如果纹理/Sprite数量大幅增加说明确实连带加载了引用资源",
"确定");
}
}
}

View File

@ -0,0 +1,296 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using ArtResource;
using System.Linq;
namespace ArtTools
{
/// <summary>
/// 美术资源路径自动填充工具
///
/// 功能:
/// 1. 在保存SO时自动填充SpritePath和SpineAssetPath
/// 2. 提供手动批量更新所有SO的工具
///
/// 使用方法:
/// - 自动在ArtTableSO的OnValidate或保存时调用FillResourcePaths
/// - 手动:菜单 -> 美术工具 -> 更新所有美术资源路径
/// </summary>
public static class ArtResourcePathFiller
{
/// <summary>
/// 自动填充单个SO的资源路径
/// 应该在ArtTableSO保存时调用
/// </summary>
public static void FillResourcePaths(ArtTableSO table)
{
if (table == null || table.Items == null)
return;
bool hasChanges = false;
foreach (var item in table.Items)
{
// 填充Sprite路径
if (item.Sprite != null)
{
string spritePath = AssetDatabase.GetAssetPath(item.Sprite);
if (!string.IsNullOrEmpty(spritePath) && item.SpritePath != spritePath)
{
item.SpritePath = spritePath;
hasChanges = true;
}
}
else
{
// 引用为空时,清空路径
if (!string.IsNullOrEmpty(item.SpritePath))
{
item.SpritePath = "";
hasChanges = true;
}
}
// 填充Spine路径
if (item.SpineAsset != null)
{
string spinePath = AssetDatabase.GetAssetPath(item.SpineAsset);
if (!string.IsNullOrEmpty(spinePath) && item.SpineAssetPath != spinePath)
{
item.SpineAssetPath = spinePath;
hasChanges = true;
}
}
else
{
// 引用为空时,清空路径
if (!string.IsNullOrEmpty(item.SpineAssetPath))
{
item.SpineAssetPath = "";
hasChanges = true;
}
}
}
if (hasChanges)
{
EditorUtility.SetDirty(table);
Debug.Log($"[ArtResourcePathFiller] 已更新资源路径: {table.TableName} ({table.TableId})");
}
}
/// <summary>
/// 手动批量更新所有ArtTableSO的资源路径
/// </summary>
[MenuItem("美术工具/更新所有美术资源路径")]
public static void UpdateAllArtTablePaths()
{
// 查找所有ArtTableSO
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
if (guids.Length == 0)
{
Debug.LogWarning("[ArtResourcePathFiller] 未找到任何ArtTableSO文件");
return;
}
int updatedCount = 0;
int totalCount = guids.Length;
EditorUtility.DisplayProgressBar("更新美术资源路径", "正在扫描...", 0);
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
if (table != null)
{
EditorUtility.DisplayProgressBar(
"更新美术资源路径",
$"处理: {table.TableName} ({i + 1}/{totalCount})",
(float)i / totalCount
);
FillResourcePaths(table);
updatedCount++;
}
}
EditorUtility.ClearProgressBar();
// 保存所有修改
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[ArtResourcePathFiller] ✓ 批量更新完成: 处理了 {updatedCount} 个ArtTableSO");
EditorUtility.DisplayDialog(
"更新完成",
$"已更新 {updatedCount} 个美术资源表的路径信息\n\n现在可以在Runtime模式下正常加载资源了",
"确定"
);
}
/// <summary>
/// 验证所有SO的路径完整性
/// </summary>
[MenuItem("美术工具/验证美术资源路径完整性")]
public static void ValidateAllPaths()
{
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
if (guids.Length == 0)
{
Debug.LogWarning("[ArtResourcePathFiller] 未找到任何ArtTableSO文件");
return;
}
int missingPathCount = 0;
int totalItemCount = 0;
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
if (table == null || table.Items == null)
continue;
foreach (var item in table.Items)
{
totalItemCount++;
// 检查Sprite
if (item.Sprite != null && string.IsNullOrEmpty(item.SpritePath))
{
Debug.LogWarning($"[{table.TableName}] 资源项 '{item.Name}' 有Sprite引用但缺少路径");
missingPathCount++;
}
// 检查Spine
if (item.SpineAsset != null && string.IsNullOrEmpty(item.SpineAssetPath))
{
Debug.LogWarning($"[{table.TableName}] 资源项 '{item.Name}' 有Spine引用但缺少路径");
missingPathCount++;
}
}
}
if (missingPathCount == 0)
{
Debug.Log($"[ArtResourcePathFiller] ✓ 验证通过: 所有 {totalItemCount} 个资源项的路径都完整");
EditorUtility.DisplayDialog(
"验证通过",
$"所有 {totalItemCount} 个资源项的路径都完整\n\n可以正常使用Runtime模式",
"确定"
);
}
else
{
Debug.LogError($"[ArtResourcePathFiller] ✗ 发现 {missingPathCount} 个缺少路径的资源项");
EditorUtility.DisplayDialog(
"发现问题",
$"发现 {missingPathCount} 个缺少路径的资源项\n\n建议运行【美术工具 -> 更新所有美术资源路径】修复",
"确定"
);
}
}
}
/// <summary>
/// ArtTableSO的自定义Inspector
/// 在Inspector底部添加"更新路径"按钮
/// </summary>
[CustomEditor(typeof(ArtTableSO))]
public class ArtTableSOInspector : Editor
{
public override void OnInspectorGUI()
{
// 绘制默认Inspector
DrawDefaultInspector();
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
EditorGUILayout.Space(5);
var table = target as ArtTableSO;
if (table == null)
return;
// 统计信息
int spriteCount = table.Items.Count(item => item.Sprite != null);
int spineCount = table.Items.Count(item => item.SpineAsset != null);
int missingPathCount = table.Items.Count(item =>
(item.Sprite != null && string.IsNullOrEmpty(item.SpritePath)) ||
(item.SpineAsset != null && string.IsNullOrEmpty(item.SpineAssetPath))
);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("资源路径状态", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"Sprite数量: {spriteCount}");
EditorGUILayout.LabelField($"Spine数量: {spineCount}");
if (missingPathCount > 0)
{
var oldColor = GUI.contentColor;
GUI.contentColor = Color.red;
EditorGUILayout.LabelField($"⚠️ 缺少路径: {missingPathCount}");
GUI.contentColor = oldColor;
}
else if (spriteCount > 0 || spineCount > 0)
{
var oldColor = GUI.contentColor;
GUI.contentColor = Color.green;
EditorGUILayout.LabelField("✓ 路径完整");
GUI.contentColor = oldColor;
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(5);
// 更新路径按钮
if (GUILayout.Button("🔄 更新此表的资源路径", GUILayout.Height(30)))
{
ArtResourcePathFiller.FillResourcePaths(table);
EditorUtility.SetDirty(table);
AssetDatabase.SaveAssets();
EditorUtility.DisplayDialog("更新完成", $"已更新 {table.TableName} 的资源路径", "确定");
}
EditorGUILayout.Space(5);
EditorGUILayout.HelpBox(
"提示保存SO时会自动更新路径手动点击按钮可强制更新",
MessageType.Info
);
}
}
/// <summary>
/// 资源保存时自动填充路径
/// </summary>
public class ArtTableSOAssetPostprocessor : AssetPostprocessor
{
static void OnPostprocessAllAssets(
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
foreach (string assetPath in importedAssets)
{
// 只处理ArtTableSO
if (!assetPath.EndsWith(".asset"))
continue;
var table = AssetDatabase.LoadAssetAtPath<ArtTableSO>(assetPath);
if (table != null)
{
ArtResourcePathFiller.FillResourcePaths(table);
}
}
}
}
}
#endif

View File

@ -0,0 +1,131 @@
using UnityEngine;
using UnityEditor;
using ArtResource;
using System.Diagnostics;
using GameFramework.Resource;
using UnityGameFramework.Runtime;
using Debug = UnityEngine.Debug;
namespace CrazyMaple.Editor
{
/// <summary>
/// 对比测试AssetDatabase vs GameEntry.Resource.LoadAsset
/// </summary>
public class LoadMethodComparisonTest : EditorWindow
{
[MenuItem("美术工具/对比加载方式性能")]
public static void ShowWindow()
{
GetWindow<LoadMethodComparisonTest>("加载方式对比");
}
private void OnGUI()
{
GUILayout.Label("对比测试", EditorStyles.boldLabel);
GUILayout.Space(10);
EditorGUILayout.HelpBox(
"对比两种加载方式的性能差异:\n" +
"1. AssetDatabase.LoadAssetAtPathEditor专用\n" +
"2. GameEntry.Resource.LoadAsset框架统一接口",
MessageType.Info);
GUILayout.Space(10);
if (GUILayout.Button("运行对比测试", GUILayout.Height(40)))
{
RunComparisonTest();
}
}
private void RunComparisonTest()
{
Debug.Log("========== 加载方式性能对比 ==========");
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO");
if (guids.Length == 0)
{
Debug.LogWarning("未找到任何ArtTableSO");
return;
}
// 清理缓存
Resources.UnloadUnusedAssets();
System.GC.Collect();
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Debug.Log($"\n--- 测试文件: {path} ---");
// 方法1: AssetDatabaseEditor专用最直接
Resources.UnloadUnusedAssets();
var sw1 = Stopwatch.StartNew();
var table1 = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
sw1.Stop();
Debug.Log($"[AssetDatabase] 加载耗时: {sw1.Elapsed.TotalMilliseconds:F2} ms");
// 方法2: GameEntry.Resource框架接口通过EditorResourceComponent
Resources.UnloadUnusedAssets();
// 检查 GameEntry 是否已初始化
if (GameEntry.Resource == null)
{
Debug.LogWarning("[GameEntry.Resource] GameEntry未初始化请先运行游戏或在Play模式下测试");
Debug.LogWarning("跳过GameEntry.Resource测试只显示AssetDatabase结果");
continue;
}
var sw2 = Stopwatch.StartNew();
bool loadCompleted = false;
ArtTableSO table2 = null;
var callbacks = new LoadAssetCallbacks(
(assetName, asset, duration, userData) =>
{
sw2.Stop();
table2 = asset as ArtTableSO;
loadCompleted = true;
Debug.Log($"[GameEntry.Resource] 加载耗时: {sw2.Elapsed.TotalMilliseconds:F2} ms");
Debug.Log($" - Framework报告的duration: {duration:F4} 秒 ({duration * 1000:F2} ms)");
},
(assetName, status, errorMessage, userData) =>
{
sw2.Stop();
loadCompleted = true;
Debug.LogError($"[GameEntry.Resource] 加载失败: {errorMessage}");
}
);
GameEntry.Resource.LoadAsset(path, typeof(ArtTableSO), callbacks);
// 等待异步加载完成Editor模式下实际是同步的但需要等待回调
int timeout = 0;
while (!loadCompleted && timeout < 1000)
{
System.Threading.Thread.Sleep(1);
timeout++;
}
if (!loadCompleted)
{
Debug.LogError("[GameEntry.Resource] 加载超时!");
}
// 对比
if (table1 != null && table2 != null)
{
float ratio = (float)sw2.Elapsed.TotalMilliseconds / (float)sw1.Elapsed.TotalMilliseconds;
Debug.Log($"性能差异: GameEntry.Resource 比 AssetDatabase 慢 {ratio:F1}x");
}
}
Debug.Log("\n========== 测试完成 ==========");
Debug.Log("结论: 如果GameEntry.Resource明显更慢说明EditorResourceComponent有额外开销");
Debug.Log("建议: 真机/打包后测试Runtime的AssetBundleResourceComponent性能可能完全不同");
}
}
}

View File

@ -0,0 +1,83 @@
================================================================================
操作类型: 提交并推送
开始时间: 2026-01-20 17:23:48
项目路径: E:/WorkSpace/MeowmentArt
================================================================================
[2026-01-20 17:23:48] [INFO] 日志: E:\WorkSpace\MeowmentArt\GitToolLog\提交并推送_20260120_172348.log
[2026-01-20 17:23:48] [INFO] 提交信息: 美术仓库初始化
[2026-01-20 17:23:48] [INFO] ============================================================
[2026-01-20 17:23:48] [INFO] 开始提交Art_SubModule
[2026-01-20 17:23:48] [INFO] ============================================================
[2026-01-20 17:23:48] [INFO]
步骤1: 检查改动
[2026-01-20 17:23:48] [INFO] 执行: git status --short
[2026-01-20 17:23:48] [INFO] D README.md
D README.md.meta
D "\346\265\213\350\257\2251.txt"
D "\346\265\213\350\257\2251.txt.meta"
?? HeadResource.prefab
?? HeadResource.prefab.meta
?? Scripts.meta
?? Scripts/
?? UISprites.meta
[2026-01-20 17:23:48] [INFO] 改动:
D README.md
D README.md.meta
D "\346\265\213\350\257\2251.txt"
D "\346\265\213\350\257\2251.txt.meta"
?? HeadResource.prefab
?? HeadResource.prefab.meta
?? Scripts.meta
?? Scripts/
?? UISprites.meta
[2026-01-20 17:23:48] [INFO]
步骤2: 添加改动
[2026-01-20 17:23:48] [INFO] 执行: git add .
[2026-01-20 17:23:48] [WARNING] warning: in the working copy of 'HeadResource.prefab', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'HeadResource.prefab.meta', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'Scripts.meta', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'Scripts/HeadResource.cs.meta', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'UISprites.meta', LF will be replaced by CRLF the next time Git touches it
[2026-01-20 17:23:48] [INFO]
步骤3: 提交
[2026-01-20 17:23:48] [INFO] 执行: git commit -m "美术仓库初始化"
[2026-01-20 17:23:48] [INFO] [detached HEAD dcd133c] 美术仓库初始化
8 files changed, 75 insertions(+), 4 deletions(-)
create mode 100644 HeadResource.prefab
rename README.md.meta => HeadResource.prefab.meta (62%)
delete mode 100644 README.md
rename "\346\265\213\350\257\2251.txt.meta" => Scripts.meta (57%)
create mode 100644 Scripts/HeadResource.cs
create mode 100644 Scripts/HeadResource.cs.meta
create mode 100644 UISprites.meta
delete mode 100644 "\346\265\213\350\257\2251.txt"
[2026-01-20 17:23:48] [INFO]
步骤4: Push前Pull
[2026-01-20 17:23:48] [INFO] 执行: git pull
[2026-01-20 17:23:49] [WARNING] You are not currently on a branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
[2026-01-20 17:23:49] [ERROR] Pull失败-存在冲突!
[2026-01-20 17:23:49] [ERROR] 解决方法:
1.打开Art_SubModule目录
2.解决冲突
3.git add .
4.git commit
5.重新提交
================================================================================
结束时间: 2026-01-20 17:24:05
执行结果: 失败
================================================================================
!!! 操作失败 !!!
日志文件: E:\WorkSpace\MeowmentArt\GitToolLog\提交并推送_20260120_172348.log

View File

@ -0,0 +1,27 @@
================================================================================
操作类型: 更新
开始时间: 2026-01-20 17:12:17
项目路径: E:/WorkSpace/MeowmentArt
================================================================================
[2026-01-20 17:12:17] [INFO] 日志: E:\WorkSpace\MeowmentArt\GitToolLog\更新_20260120_171217.log
[2026-01-20 17:12:17] [INFO] ============================================================
[2026-01-20 17:12:17] [INFO] 开始更新子模块
[2026-01-20 17:12:17] [INFO] ============================================================
[2026-01-20 17:12:17] [INFO] 执行: git submodule update --init --recursive --remote --merge
[2026-01-20 17:12:19] [INFO] Updating 54a32bf..10480ba
Fast-forward
README.md.meta | 7 +++++++
"\346\265\213\350\257\2251.txt.meta" | 7 +++++++
2 files changed, 14 insertions(+)
create mode 100644 README.md.meta
create mode 100644 "\346\265\213\350\257\2251.txt.meta"
Submodule path 'Assets/Art_SubModule': merged in '10480ba573cacd75c1496737614d2d0d26147533'
[2026-01-20 17:12:19] [INFO]
更新完成!
================================================================================
结束时间: 2026-01-20 17:12:20
执行结果: 成功
================================================================================

View File

@ -287,7 +287,99 @@ PlayerSettings:
AndroidValidateAppBundleSize: 1
AndroidAppBundleSizeToValidate: 150
m_BuildTargetIcons: []
m_BuildTargetPlatformIcons: []
m_BuildTargetPlatformIcons:
- m_BuildTarget: Android
m_Icons:
- m_Textures: []
m_Width: 432
m_Height: 432
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 324
m_Height: 324
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 216
m_Height: 216
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 162
m_Height: 162
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 108
m_Height: 108
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 81
m_Height: 81
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 0
m_SubKind:
m_BuildTargetBatching:
- m_BuildTarget: Standalone
m_StaticBatching: 1

3
artist_config.json Normal file
View File

@ -0,0 +1,3 @@
{
"project_path": "E:/WorkSpace/MeowmentArt"
}

BIN
美术Git提交工具.exe Normal file

Binary file not shown.

352
美术Git操作指南.md Normal file
View File

@ -0,0 +1,352 @@
# 美术Git操作指南
> 💡 **提示**命令行操作最准确可靠。SourceTree因版本不同界面可能有差异如遇找不到功能的情况请使用命令行。
---
## 📋 目录
1. [首次拉取项目](#首次拉取项目)
2. [日常提交流程](#日常提交流程)
3. [避免冲突](#避免冲突)
4. [解决冲突](#解决冲突)
5. [自动同步SubModule](#自动同步submodule)
---
## 首次拉取项目
### 命令行方式 ✅
```powershell
# 1. 克隆项目自动包含SubModule
git clone --recursive git@gitea.bywaystudios.com:zhanghongbo/MeowmentArt.git
# 2. 进入项目
cd MeowmentArt
# 3. 配置自动同步SubModule
git config submodule.recurse true
# 4. 配置用户信息
git config user.name "LiBoyang"
git config user.email "zhangsan@company.com"
# 完成可以用Unity打开项目了
```
### SourceTree方式
```
1. File → Clone / New
2. 填写信息:
Source URL: git@gitea.bywaystudios.com:你的用户名/MeowmentArt.git
Destination Path: E:\WorkSpace\MeowmentArt
✓ Recurse submodules ← 勾选这个!
3. 点击 Clone
4. 克隆完成后Tools → Options → Git
找到 Submodules 部分
✓ 勾选 Update submodules when pulling
```
---
## 日常提交流程
### 命令行方式 ✅
```powershell
# === 第一步在SubModule内提交必须先做 ===
cd Assets/Art_SubModule
# 查看修改了哪些文件
git status
# 添加文件(可以选择性添加)
git add Characters/hero.png # 添加单个文件
git add Characters/ # 添加整个文件夹
git add . # 添加所有修改
# 提交前先拉取更新(避免冲突)
git pull
# 提交
git commit -m "添加英雄角色贴图"
# 推送到远程
git push
# === 第二步回到主项目提交记录SubModule更新 ===
cd ../..
# 查看状态会看到SubModule有变化
git status
# 添加SubModule的变化
git add Assets/Art_SubModule
# 提交主项目
git commit -m "更新美术资源"
# 推送
git push
```
### SourceTree方式
```
=== 第一步提交SubModule ===
1. 在左侧 SUBMODULES 区域双击 "Assets/Art_SubModule"
会打开新窗口显示SubModule
2. 在SubModule窗口
- 点击 File Status
- 勾选要提交的文件
- 点击 Stage Selected暂存所选
- 底部输入提交信息:"添加英雄角色贴图"
- 点击 Commit
- 点击 Push
=== 第二步:提交主项目 ===
3. 回到主项目窗口:
- File Status 会显示 "Assets/Art_SubModule" 有变化
- 勾选这个变化
- 点击 Stage Selected
- 输入提交信息:"更新美术资源"
- 点击 Commit
- 点击 Push
```
**⚠️ 重要提示:**
- 必须先push SubModule再push主项目
- 两个都要提交,缺一不可
---
## 避免冲突
### 规则1提交前先拉取 ⭐ 最重要
```powershell
# 养成习惯:每次提交前先执行
cd Assets/Art_SubModule
git pull # 先拉取别人的更新
# 如果有冲突,现在解决比稍后解决简单
# 然后再提交你的修改
git add .
git commit -m "..."
git push
```
### 规则2明确分工
```
建议分工方式:
美术A → Characters/ 文件夹
美术B → UI/ 文件夹
美术C → Effects/ 文件夹
各自只修改自己负责的文件夹,不会冲突!
```
### 规则3提交前沟通
```
在团队群里:
"我正在修改 characters/hero.png大家别动这个文件"
"收到!"
→ 避免多人同时修改同一文件
```
### 规则4经常提交
```
✅ 好习惯:完成一个小功能就提交
❌ 坏习惯:攒几天的修改一次性提交
经常提交 = 减少冲突范围 = 更容易解决
```
---
## 解决冲突
### 场景1push时提示冲突
#### 命令行方式 ✅
```powershell
cd Assets/Art_SubModule
# 推送失败
git push
# ❌ error: failed to push some refs
# 原因:远程有新提交
# 解决方法:
# 1. 先拉取远程更新
git pull
# 2. 如果自动合并成功
# Merge made by... ← 看到这个说明成功
git push # 再次推送即可
# 3. 如果提示冲突
# CONFLICT (content): Merge conflict in xxx.png
# Auto-merging xxx failed
# 4. 查看哪些文件冲突
git status
# both modified: Characters/hero.png ← 冲突的文件
# 5A. 如果是二进制文件(图片、模型等)
# 选择保留谁的版本:
# 保留自己的版本
git checkout --ours Characters/hero.png
git add Characters/hero.png
# 或者保留别人的版本
git checkout --theirs Characters/hero.png
git add Characters/hero.png
# 5B. 如果是文本文件(配置文件等)
# 打开文件手动编辑,删除冲突标记:
# <<<<<<< HEAD
# 你的内容
# =======
# 别人的内容
# >>>>>>> xxx
# 保留需要的内容,删除标记
# 6. 标记冲突已解决
git add .
# 7. 完成合并
git commit -m "解决冲突"
# 8. 推送
git push
```
#### SourceTree方式
```
1. Push失败后会提示需要Pull
2. 点击 Pull 按钮
3. 如果有冲突,会显示:
⚠️ Conflicted files:
- Characters/hero.png
4. 右键冲突文件:
对于图片/模型等二进制文件:
- Resolve Conflicts → Use Mine (保留你的)
- Resolve Conflicts → Use Theirs (保留别人的)
对于文本文件:
- Resolve Conflicts → Launch External Merge Tool
- 或直接编辑文件
5. 解决后:
- 右键文件 → Mark Resolved
- 点击 Commit
- 输入 "解决冲突"
- Push
```
### 场景2主项目SubModule引用冲突
#### 命令行方式 ✅
```powershell
cd MeowmentArt # 主项目根目录
git pull
# CONFLICT (submodule): Merge conflict in Assets/Art_SubModule
# 解决方法:
# 1. 进入SubModule拉取最新
cd Assets/Art_SubModule
git pull
cd ../..
# 2. 标记已解决
git add Assets/Art_SubModule
# 3. 完成合并
git commit -m "同步SubModule更新"
# 4. 推送
git push
```
---
## 自动同步SubModule
### 问题pull时SubModule没更新
**原因**:默认情况下 `git pull` 不会自动更新SubModule
**解决方法**
#### 命令行方式 ✅
```powershell
cd MeowmentArt # 主项目根目录
# 一次性配置,永久生效
git config submodule.recurse true
# 配置后git pull 会自动同步SubModule
git pull # 会自动更新SubModule了
# 验证配置
git config --get submodule.recurse
# 输出: true = 配置成功
```
#### SourceTree方式
```
方式1全局设置
1. Tools → Options → Git
2. 找到 Submodules 部分
3. ✓ 勾选 Update submodules when pulling
方式2Pull时手动勾选
1. 每次点击 Pull 时
2. 在弹出对话框中
3. ✓ 勾选 Update submodules
4. 点击 OK
```
### 手动更新SubModule到最新版本
有时需要把SubModule更新到远程最新版本
```powershell
# 更新SubModule到远程最新
git submodule update --remote
# 或在SourceTree中
# Repository → Update Submodules...
```
---
**遇到问题随时联系客户端程序!**