NLDClient-yudde/ProjectNLD/Assets/Editor/CharacterBuilder/BaseRoleBuildData.cs

593 lines
23 KiB
C#
Raw Normal View History

2026-01-13 13:20:29 +08:00
using System.Collections.Generic;
2023-12-29 17:00:23 +08:00
using System.IO;
using cfg.WeaponCfg;
using Gameplay.Character;
2023-12-29 17:00:23 +08:00
using Gameplay.Utils;
2026-01-12 18:43:17 +08:00
using PhxhSDK;
2024-01-04 13:10:44 +08:00
using PhxhSDK.Phxh.AutoCombine;
using SkillEditor;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
namespace CharacterBuilder
{
public abstract class BaseRoleBuildData
{
public List<DataWeapon> weaponConfigList = new List<DataWeapon>();
public string weaponIdsStr
{
get
{
var result = "";
for (int j = 0; j < weaponConfigList.Count; j++)
{
var weaponConfig = weaponConfigList[j];
result += weaponConfig.ID;
if (j < weaponConfigList.Count - 1)
{
result += ", ";
}
}
return result;
}
}
2024-11-11 16:48:56 +08:00
public bool noSearchSkill = false;
2023-12-29 17:00:23 +08:00
public EmojiExtInfo emojiExt;
public string errorInfo;
public bool isChecked;
protected List<string> _keepAnims = new List<string>()
{
"ani_common_crawl_idle001",
"ani_common_crawl_moveforward001",
"ani_common_crawl_raise001",
"ani_common_dead001",
"ani_common_dizziness001",
"ani_common_retreat001",
"ani_common_stand_moveback001",
"ani_common_stand_moveforward001",
"ani_common_stand_moveleft001",
"ani_common_stand_moveright001",
"ani_common_move001",
2025-02-18 15:23:01 +08:00
"ani_common_ready001",
2025-06-26 13:27:36 +08:00
"ani_common_walk001",
2025-07-18 15:59:41 +08:00
"ani_common_surrender001",
2025-11-17 12:37:17 +08:00
"ani_common_crouchwalk001",
"ani_common_build001_attack",
"ani_common_build001_idle",
"ani_common_build001_reload",
"ani_common_build002_attack",
"ani_common_build002_idle",
"ani_common_build002_reload",
"ani_common_build003_attack",
"ani_common_build003_idle",
"ani_common_build003_reload",
"ani_common_build004_attack",
"ani_common_build004_idle",
"ani_common_build004_reload",
"ani_common_build005_attack",
"ani_common_build005_idle",
"ani_common_build005_reload"
};
protected string _GetNewSkillAnimName(string idName)
{
return $"ani_{idName}_skill01";
}
protected string _GetNewAnimName(string oldAnimName,
string idName, string weaponModName)
{
2024-01-30 15:59:14 +08:00
var splits = oldAnimName.Split('_');
if (splits.Length != 2)
2024-01-30 15:59:14 +08:00
{
Debug.LogError("动画名字不符合规范:" + oldAnimName);
return "";
2024-01-30 16:07:34 +08:00
}
var action1 = splits[0];
var action2 = splits[1];
2024-01-30 16:07:34 +08:00
return $"ani_{weaponModName}_{action1}_{action2}";
}
protected string _CheckAnims(Dictionary<string, AnimationClip> dict,
string nameId, string defaultSkinNameId)
{
/***
ani_common_dizziness001
ani_common_retreat001
ani_common_dead001
ani_g01_stand_ready001 使 ani_g04_stand_idle001
skill_01 使 ani_a00005_skill01
使 ani_g05_stand_idle001
*/
var loadAnimator = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/Art/Animations/animator/animator_pure_n3.controller");
var allClips = loadAnimator.animationClips;
for (int i = 0; i < allClips.Length; i++)
{
var oldClip = allClips[i];
var oldClipName = oldClip.name;
if (_keepAnims.Contains(oldClipName))
{
continue;
}
if (oldClipName == "skill01")
2024-11-11 16:48:56 +08:00
{
if (noSearchSkill)
{
continue;
}
var newClipName = _GetNewSkillAnimName(nameId);
if (!dict.ContainsKey(newClipName))
{
newClipName = _GetNewSkillAnimName(defaultSkinNameId);
if (!dict.ContainsKey(newClipName))
{
return $"动画{oldClipName}找不到对应的{newClipName}";
}
}
2024-11-11 16:48:56 +08:00
continue;
}
for (int j = 0; j < weaponConfigList.Count; j++)
{
var weaponConfig = weaponConfigList[j];
var newClipName = _GetNewAnimName(oldClipName, nameId, weaponConfig.MotionGroupName);
if (!dict.ContainsKey(newClipName))
{
return $"动画{oldClipName}找不到对应的{newClipName}";
}
}
}
return null;
}
2023-12-29 17:00:23 +08:00
private AnimatorOverrideController _AutoBuildAnimator(Dictionary<string, AnimationClip> clipDict,
string nameId, DataWeapon weaponConfig, string defaultSkinNameId)
{
// 创建overrideController
var overrideController = new AnimatorOverrideController();
// 建立动画关系
2025-07-18 15:59:41 +08:00
var loadAnimator = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/Art/Animations/animator/animator_pure_n3.controller");
overrideController.runtimeAnimatorController = loadAnimator;
var oList = new List<KeyValuePair<AnimationClip, AnimationClip>>();
var allClips = loadAnimator.animationClips;
var reCacheDict = new Dictionary<string, AnimationClip>();
for (int i = 0; i < allClips.Length; i++)
{
var oldClip = allClips[i];
var oldClipName = oldClip.name;
// 保持不变的
if (_keepAnims.Contains(oldClipName))
{
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, oldClip);
oList.Add(pairOld);
reCacheDict.TryAdd(oldClipName, oldClip);
continue;
}
if (oldClipName == "skill01")
{
if (noSearchSkill)
{
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, oldClip);
oList.Add(pairOld);
reCacheDict.TryAdd(oldClipName, oldClip);
continue;
}
var newClipName = _GetNewSkillAnimName(nameId);
if (!clipDict.TryGetValue(newClipName, out var newClip))
{
newClipName = _GetNewSkillAnimName(defaultSkinNameId);
if (!clipDict.TryGetValue(newClipName, out newClip))
{
DebugUtil.LogError($"找不到动画{newClipName}");
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, oldClip);
oList.Add(pairOld);
reCacheDict.TryAdd(oldClipName, oldClip);
continue;
}
}
var pair = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, newClip);
oList.Add(pair);
reCacheDict.TryAdd(oldClipName, newClip);
}
else
{
// 需要替换的
var newClipName = _GetNewAnimName(oldClipName, nameId, weaponConfig.MotionGroupName);
if (clipDict.TryGetValue(newClipName, out var newClip))
{
var pair = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, newClip);
oList.Add(pair);
reCacheDict.TryAdd(oldClipName, newClip);
}
else
{
// 找不到的
DebugUtil.LogError($"找不到动画{newClipName}");
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, oldClip);
oList.Add(pairOld);
reCacheDict.TryAdd(oldClipName, oldClip);
}
}
2023-12-29 17:00:23 +08:00
}
overrideController.ApplyOverrides(oList);
_CompressOverrideController(overrideController);
// 导出动画数据
AnimatorInfoExporter.ExportAnimatorInfo(loadAnimator, reCacheDict, nameId, weaponConfig.ID);
2023-12-29 17:00:23 +08:00
return overrideController;
}
private void _CompressOverrideController(AnimatorOverrideController overrideController)
{
var overrideClips = new List<KeyValuePair<AnimationClip, AnimationClip>>();
overrideController.GetOverrides(overrideClips);
var newClips = new List<KeyValuePair<AnimationClip, AnimationClip>>();
foreach (var oldKV in overrideClips)
{
var srcClip = oldKV.Key;
var dstClip = oldKV.Value;
if (dstClip == null)
{
dstClip = srcClip;
}
var newClip = LTCompressor.AutoCompress(dstClip);
newClips.Add(new KeyValuePair<AnimationClip, AnimationClip>(srcClip, newClip));
}
overrideController.ApplyOverrides(newClips);
}
private bool _CombineEmoji(GameObject prefab, int forceSize)
2023-12-29 17:00:23 +08:00
{
var emojiObj = _DeadSearchTrans(prefab.transform, "F_emoji");
if (emojiObj != null)
{
Object.DestroyImmediate(emojiObj.gameObject);
}
emojiObj = _DeadSearchTrans(prefab.transform, "G_emoji");
if (emojiObj != null)
{
Object.DestroyImmediate(emojiObj.gameObject);
}
if (emojiExt.emojiId == 0)
{
DebugUtil.LogError("没有表情id,取消创建默认表情:" + prefab.name);
return false;
}
// 找到头部
var playerObj = prefab.transform.GetChild(0);
var headObj = playerObj.GetChild(1);
if (!headObj.name.EndsWith("_face"))
{
DebugUtil.LogError("找不到头部模型,取消创建默认表情:" + playerObj.name);
return false;
}
var skinMeshRender = headObj.GetComponent<SkinnedMeshRenderer>();
if (skinMeshRender == null)
{
DebugUtil.LogError("找不到头部模型skninmeshrenderer,取消创建默认表情:" + playerObj.name);
return false;
}
var oldMat = skinMeshRender.sharedMaterial;
var mainTex = oldMat.GetTexture("_MainTex") as Texture2D;
var newMat = new Material(oldMat);
mainTex = _DrawFace(mainTex, forceSize);
2023-12-29 17:00:23 +08:00
newMat.SetTexture("_MainTex", mainTex);
// 设置材质
skinMeshRender.sharedMaterial = newMat;
return true;
}
private Texture2D _DrawFace(Texture2D t2d, int forceSize)
2023-12-29 17:00:23 +08:00
{
// Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01.png
// Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01_mask.png
var numStr = emojiExt.emojiId.ToString("D2");
var texturePath = $"Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_{numStr}.png";
var emojiTex = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);
if (emojiTex == null)
{
DebugUtil.LogError("找不到表情贴图:" + texturePath);
return t2d;
}
var maskTexPath = texturePath.Replace(".png", "_mask.png");
var maskTex = AssetDatabase.LoadAssetAtPath<Texture2D>(maskTexPath);
// 画到主贴图上
var newTex = _DrawEmojiToMainTex(t2d, emojiTex, maskTex, forceSize);
2023-12-29 17:00:23 +08:00
return newTex;
}
private Texture2D _DrawEmojiToMainTex(Texture2D mainTex,
Texture2D emojiTex,
Texture2D maskTex,
int forceSize)
2023-12-29 17:00:23 +08:00
{
2026-01-13 13:20:29 +08:00
var eyebowsColor = ColorUtils.FromHex(emojiExt.eyebowsColor);
2024-01-29 18:05:04 +08:00
if (string.IsNullOrEmpty(emojiExt.eyebowsColor))
{
2026-01-13 13:20:29 +08:00
eyebowsColor = ColorUtils.Random();
2024-01-29 18:05:04 +08:00
}
2026-01-13 13:20:29 +08:00
var eyesColor = ColorUtils.FromHex(emojiExt.eyesColor);
2024-01-29 17:59:45 +08:00
if (string.IsNullOrEmpty(emojiExt.eyesColor))
{
2026-01-13 13:20:29 +08:00
eyesColor = ColorUtils.Random();
2024-01-29 17:59:45 +08:00
}
2023-12-29 17:00:23 +08:00
var shader = Shader.Find("Unlit/DrawFace");
var material = new Material(shader);
material.SetTexture("_MainTex", mainTex);
material.SetTexture("_FaceTex", emojiTex);
material.SetTexture("_FaceMask", maskTex);
material.SetColor("_EyebowsColor", eyebowsColor);
material.SetColor("_EyeColor", eyesColor);
// 创建一个新的贴图
var newTex = new Texture2D(forceSize, forceSize, TextureFormat.RGBA32, false);
2023-12-29 17:00:23 +08:00
// 画到新的贴图上
var rt = RenderTexture.GetTemporary(forceSize, forceSize, 0, RenderTextureFormat.ARGB32);
2023-12-29 17:00:23 +08:00
Graphics.Blit(mainTex, rt, material);
RenderTexture.active = rt;
newTex.ReadPixels(new Rect(0, 0, forceSize, forceSize), 0, 0);
2023-12-29 17:00:23 +08:00
newTex.Apply();
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(rt);
return newTex;
}
private Transform _DeadSearchTrans(Transform rootTrans, string findName)
{
var childCount = rootTrans.childCount;
for (int i = 0; i < childCount; i++)
{
var child = rootTrans.GetChild(i);
if (child.name == findName)
{
return child;
}
var result = _DeadSearchTrans(child, findName);
if (result != null)
{
return result;
}
}
return null;
}
private void _SetHitEffectPoints(GameObject animatorObj)
{
var oldCmp = animatorObj.GetComponent<CmpCharacterEffectPoints>();
if (oldCmp == null)
{
oldCmp = animatorObj.AddComponent<CmpCharacterEffectPoints>();
}
oldCmp._AutoSet();
}
private GameObject _CreateTempPrefab(string srcPrefabPath, string nameId, DataWeapon weaponConfig)
2023-12-29 17:00:23 +08:00
{
var loadPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(srcPrefabPath);
2024-01-03 12:54:44 +08:00
if (loadPrefab == null)
{
Debug.LogError($"{nameId}找不到原始prefab:{srcPrefabPath},取消导出");
return null;
2024-01-03 12:54:44 +08:00
}
// 加载新的武器模型
var weaponModelPath = $"Assets/Art/Character/Prefabs/Guns/{weaponConfig.ModelPath}.prefab";
var weaponModel = AssetDatabase.LoadAssetAtPath<GameObject>(weaponModelPath);
if (weaponModel == null)
{
Debug.LogError($"{nameId}找不到武器模型:{weaponModelPath},取消导出");
return null;
}
var instPrefab = Object.Instantiate(loadPrefab);
// 找到武器挂点
var weaponParentPoint = instPrefab.transform.SearchChild("Grip_point01");
if (weaponParentPoint == null)
{
Debug.LogError($"{srcPrefabPath}找不到武器挂点,取消导出");
return null;
}
// 清空节点
weaponParentPoint.ClearChilds();
// 创建武器到挂点上
var weaponInst = Object.Instantiate(weaponModel, weaponParentPoint);
// 设置武器的位置
weaponInst.transform.ResetLocals();
// 获取原始路径父文件夹
var parentPath = Path.GetDirectoryName(srcPrefabPath);
var tempPath = $"{parentPath}/P_{nameId}_{weaponConfig.ID}.prefab";
var tempPrefab = PrefabUtility.SaveAsPrefabAsset(instPrefab, tempPath);
// 删除临时对象
Object.DestroyImmediate(instPrefab);
return tempPrefab;
}
private bool _RebuildAnimator(Dictionary<string, AnimationClip> clipDict,
GameObject loadPrefab,
string nameId,
DataWeapon weaponConfig,
string defaultSkinNameId)
{
2023-12-29 17:00:23 +08:00
// 找到animator
2024-02-02 16:03:58 +08:00
var animator = loadPrefab.GetComponentInChildren<Animator>(true);
2024-01-03 12:54:44 +08:00
if (animator == null)
{
Debug.LogError($"{nameId}原始prefab没有animator,取消导出");
return false;
}
2024-02-02 16:02:31 +08:00
animator.enabled = true;
var overrideController = _AutoBuildAnimator(clipDict, nameId, weaponConfig, defaultSkinNameId);
2023-12-29 17:00:23 +08:00
// Assets/Art_Out/AutoGen/Characters/character_{id}
var parentPath = $"Assets/Art_Out/AutoGen/Characters/character_{nameId}_{weaponConfig.ID}";
2023-12-29 17:00:23 +08:00
if (Directory.Exists(parentPath))
{
Directory.Delete(parentPath, true);
}
Directory.CreateDirectory(parentPath);
// 保存overrideController
2023-12-29 17:00:23 +08:00
// {parentPath}/character_{id}_{weapon}.overrideController
var savePath = $"{parentPath}/ov_{nameId}_{weaponConfig.MotionGroupName}.overrideController";
AssetDatabase.CreateAsset(overrideController, savePath);
_SetHitEffectPoints(animator.gameObject);
2023-12-29 17:00:23 +08:00
// 原来的prefab也需要保存
// 设置overrideController到animator上
animator.runtimeAnimatorController = overrideController;
2023-12-29 17:00:23 +08:00
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
EditorUtility.SetDirty(loadPrefab);
AssetDatabase.SaveAssets();
2024-01-03 12:54:44 +08:00
return true;
2023-12-29 17:00:23 +08:00
}
private string[] _PreSetAll(GameObject prefab)
{
var allSkin = prefab.GetComponentsInChildren<SkinnedMeshRenderer>();
string[] allTexNames = new string[] { "_MainTex", "_MaskTex" };
foreach (SkinnedMeshRenderer skin in allSkin)
{
var material = skin.sharedMaterial;
if (material.shader.name != "NLD_URP/NLD_Charactor")
{
// 修改为NLD_URP/NLD_Charactor
material.shader = Shader.Find("NLD_URP/NLD_Charactor");
}
// 判断所有的贴图是否已开启read/write
foreach (var texName in allTexNames)
{
var tex = material.GetTexture(texName);
if (tex != null)
{
var texPath = AssetDatabase.GetAssetPath(tex);
var importer = AssetImporter.GetAtPath(texPath) as TextureImporter;
if (importer.isReadable == false)
{
importer.isReadable = true;
importer.SaveAndReimport();
}
}
}
}
return allTexNames;
}
public static int FORCE_TEXTURE_SIZE = 512;
private string _CombineSkins(GameObject loadPrefab,
string nameId, DataWeapon weaponConfig)
2023-12-29 17:00:23 +08:00
{
var parentPath = $"Assets/Art_Out/AutoGen/Characters/character_{nameId}_{weaponConfig.ID}";
2023-12-29 17:00:23 +08:00
var instPrefab = Object.Instantiate(loadPrefab);
var allTexNames = _PreSetAll(instPrefab);
AssetDatabase.Refresh();
// 将表情画到头上
var faceImgSize = FORCE_TEXTURE_SIZE / 2;
var result = _CombineEmoji(instPrefab, faceImgSize);
2023-12-29 17:00:23 +08:00
if (!result)
{
Debug.LogError("合并表情失败,该角色进入战场后将没有表情");
}
AutoCombine.ReSetAllTextureSize(instPrefab, allTexNames, faceImgSize);
AutoCombine.CombineSkinMesh(instPrefab, allTexNames, FORCE_TEXTURE_SIZE);
2023-12-29 17:00:23 +08:00
// 合并完了将中间生成的素材进行保存,这里合并了之后理论上只有一个
var skinMeshRenderer = instPrefab.GetComponentInChildren<SkinnedMeshRenderer>();
var material = skinMeshRenderer.sharedMaterial;
// textures
foreach (var texName in allTexNames)
{
var tex = material.GetTexture(texName);
var texSavePath = $"{parentPath}/INFIGHT_{texName}.png";
2023-12-29 17:00:23 +08:00
var newTex = _SaveAndReloadTexture2D(tex as Texture2D, texSavePath);
material.SetTexture(texName, newTex);
}
// material
var matSavePath = $"{parentPath}/mat_{nameId}.mat";
AssetDatabase.CreateAsset(material, matSavePath);
// mesh
var mesh = skinMeshRenderer.sharedMesh;
var meshSavePath = $"{parentPath}/mesh_{nameId}.asset";
AssetDatabase.CreateAsset(mesh, meshSavePath);
// 保存新的prefab
// {parentPath}/character_{id}_{weapon}.prefab
var prefabSavePath = $"{parentPath}/character_{nameId}.prefab";
PrefabUtility.SaveAsPrefabAsset(instPrefab, prefabSavePath);
// 删除临时对象
Object.DestroyImmediate(instPrefab);
return parentPath;
2023-12-29 17:00:23 +08:00
}
private Texture2D _SaveAndReloadTexture2D(Texture2D t2d, string savePath)
{
var bytes = t2d.EncodeToPNG();
File.WriteAllBytes(savePath, bytes);
AssetDatabase.Refresh();
var newT2d = AssetDatabase.LoadAssetAtPath<Texture2D>(savePath);
return newT2d;
}
protected void _Rebuild(Dictionary<string, AnimationClip> clipDict,
string prefabPath,
string nameId,
string defaultSkinNameId)
2023-12-29 17:00:23 +08:00
{
for (int i = 0; i < weaponConfigList.Count; i++)
2024-01-03 12:54:44 +08:00
{
var weaponConfig = weaponConfigList[i];
var tempPrefab = _CreateTempPrefab(prefabPath, nameId, weaponConfig);
if (tempPrefab == null)
{
continue;
}
var result = _RebuildAnimator(clipDict, tempPrefab, nameId, weaponConfig, defaultSkinNameId);
if (!result)
{
continue;
}
_CombineSkins(tempPrefab, nameId, weaponConfig);
2024-01-03 12:54:44 +08:00
}
2023-12-29 17:00:23 +08:00
// 打印日志
Debug.Log($"重建{prefabPath}成功");
}
public abstract string CheckAnims(Dictionary<string, AnimationClip> dict);
public abstract void RebuildAll(Dictionary<string, AnimationClip> clipDict);
}
}