2026-01-13 13:20:29 +08:00
|
|
|
using System.Collections.Generic;
|
2023-12-29 17:00:23 +08:00
|
|
|
using System.IO;
|
2023-12-20 13:47:28 +08:00
|
|
|
using cfg.WeaponCfg;
|
2024-04-11 16:28:16 +08:00
|
|
|
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;
|
2023-12-20 13:47:28 +08:00
|
|
|
using SkillEditor;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEditor.Animations;
|
|
|
|
|
using UnityEngine;
|
2024-11-04 13:41:33 +08:00
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
namespace CharacterBuilder
|
|
|
|
|
{
|
|
|
|
|
public abstract class BaseRoleBuildData
|
|
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
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;
|
2024-11-11 16:27:33 +08:00
|
|
|
|
2023-12-29 17:00:23 +08:00
|
|
|
public EmojiExtInfo emojiExt;
|
|
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
public string errorInfo;
|
|
|
|
|
public bool isChecked;
|
|
|
|
|
|
|
|
|
|
protected List<string> _keepAnims = new List<string>()
|
|
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
"ani_common_crawl_idle001",
|
|
|
|
|
"ani_common_crawl_moveforward001",
|
|
|
|
|
"ani_common_crawl_raise001",
|
|
|
|
|
"ani_common_dead001",
|
2023-12-20 13:47:28 +08:00
|
|
|
"ani_common_dizziness001",
|
2024-11-11 16:27:33 +08:00
|
|
|
"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",
|
2025-11-17 13:17:27 +08:00
|
|
|
"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"
|
2023-12-20 13:47:28 +08:00
|
|
|
};
|
|
|
|
|
|
2024-11-12 12:30:55 +08:00
|
|
|
protected string _GetNewSkillAnimName(string idName)
|
|
|
|
|
{
|
|
|
|
|
return $"ani_{idName}_skill01";
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
protected string _GetNewAnimName(string oldAnimName,
|
2024-11-11 16:27:33 +08:00
|
|
|
string idName, string weaponModName)
|
2023-12-20 13:47:28 +08:00
|
|
|
{
|
2024-01-30 15:59:14 +08:00
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
var splits = oldAnimName.Split('_');
|
2024-11-11 16:27:33 +08:00
|
|
|
if (splits.Length != 2)
|
2024-01-30 15:59:14 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
Debug.LogError("动画名字不符合规范:" + oldAnimName);
|
|
|
|
|
return "";
|
2024-01-30 16:07:34 +08:00
|
|
|
}
|
2024-11-11 16:27:33 +08:00
|
|
|
var action1 = splits[0];
|
|
|
|
|
var action2 = splits[1];
|
2024-01-30 16:07:34 +08:00
|
|
|
|
2024-11-11 16:27:33 +08:00
|
|
|
return $"ani_{weaponModName}_{action1}_{action2}";
|
2023-12-20 13:47:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected string _CheckAnims(Dictionary<string, AnimationClip> dict,
|
2024-11-12 12:30:55 +08:00
|
|
|
string nameId, string defaultSkinNameId)
|
2023-12-20 13:47:28 +08:00
|
|
|
{
|
|
|
|
|
/***
|
|
|
|
|
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
|
|
|
|
|
*/
|
2025-07-16 13:34:19 +08:00
|
|
|
var loadAnimator = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/Art/Animations/animator/animator_pure_n3.controller");
|
2023-12-20 13:47:28 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2024-11-12 12:30:55 +08:00
|
|
|
if (oldClipName == "skill01")
|
2024-11-11 16:48:56 +08:00
|
|
|
{
|
2024-11-12 12:30:55 +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;
|
|
|
|
|
}
|
2024-11-11 16:27:33 +08:00
|
|
|
for (int j = 0; j < weaponConfigList.Count; j++)
|
2023-12-20 13:47:28 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
var weaponConfig = weaponConfigList[j];
|
|
|
|
|
var newClipName = _GetNewAnimName(oldClipName, nameId, weaponConfig.MotionGroupName);
|
|
|
|
|
|
|
|
|
|
if (!dict.ContainsKey(newClipName))
|
|
|
|
|
{
|
|
|
|
|
return $"动画{oldClipName}找不到对应的{newClipName}";
|
|
|
|
|
}
|
2023-12-20 13:47:28 +08:00
|
|
|
}
|
2024-11-11 16:27:33 +08:00
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 17:00:23 +08:00
|
|
|
private AnimatorOverrideController _AutoBuildAnimator(Dictionary<string, AnimationClip> clipDict,
|
2024-11-12 12:30:55 +08:00
|
|
|
string nameId, DataWeapon weaponConfig, string defaultSkinNameId)
|
2023-12-20 13:47:28 +08:00
|
|
|
{
|
|
|
|
|
// 创建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");
|
2023-12-20 13:47:28 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2024-11-12 12:30:55 +08:00
|
|
|
if (oldClipName == "skill01")
|
2023-12-20 13:47:28 +08:00
|
|
|
{
|
2024-11-12 12:30:55 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-20 13:47:28 +08:00
|
|
|
var pair = new KeyValuePair<AnimationClip, AnimationClip>(oldClip, newClip);
|
|
|
|
|
oList.Add(pair);
|
|
|
|
|
reCacheDict.TryAdd(oldClipName, newClip);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-11-12 12:30:55 +08:00
|
|
|
// 需要替换的
|
|
|
|
|
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-20 13:47:28 +08:00
|
|
|
}
|
2024-11-12 12:30:55 +08:00
|
|
|
|
2023-12-29 17:00:23 +08:00
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
}
|
|
|
|
|
overrideController.ApplyOverrides(oList);
|
|
|
|
|
|
2024-11-07 14:24:54 +08:00
|
|
|
_CompressOverrideController(overrideController);
|
|
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
// 导出动画数据
|
2025-01-15 17:58:32 +08:00
|
|
|
AnimatorInfoExporter.ExportAnimatorInfo(loadAnimator, reCacheDict, nameId, weaponConfig.ID);
|
2023-12-20 13:47:28 +08:00
|
|
|
|
2023-12-29 17:00:23 +08:00
|
|
|
return overrideController;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 14:24:54 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:12:39 +08:00
|
|
|
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);
|
2025-07-31 15:12:39 +08:00
|
|
|
mainTex = _DrawFace(mainTex, forceSize);
|
2023-12-29 17:00:23 +08:00
|
|
|
newMat.SetTexture("_MainTex", mainTex);
|
|
|
|
|
// 设置材质
|
|
|
|
|
skinMeshRender.sharedMaterial = newMat;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:12:39 +08:00
|
|
|
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);
|
|
|
|
|
// 画到主贴图上
|
2025-07-31 15:12:39 +08:00
|
|
|
var newTex = _DrawEmojiToMainTex(t2d, emojiTex, maskTex, forceSize);
|
2023-12-29 17:00:23 +08:00
|
|
|
return newTex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Texture2D _DrawEmojiToMainTex(Texture2D mainTex,
|
|
|
|
|
Texture2D emojiTex,
|
2025-07-31 15:12:39 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
// 创建一个新的贴图
|
2025-07-31 15:12:39 +08:00
|
|
|
var newTex = new Texture2D(forceSize, forceSize, TextureFormat.RGBA32, false);
|
2023-12-29 17:00:23 +08:00
|
|
|
|
|
|
|
|
// 画到新的贴图上
|
2025-07-31 15:12:39 +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;
|
2025-07-31 15:12:39 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-11 16:27:33 +08:00
|
|
|
private void _SetHitEffectPoints(GameObject animatorObj)
|
2024-04-11 16:28:16 +08:00
|
|
|
{
|
|
|
|
|
var oldCmp = animatorObj.GetComponent<CmpCharacterEffectPoints>();
|
|
|
|
|
if (oldCmp == null)
|
|
|
|
|
{
|
|
|
|
|
oldCmp = animatorObj.AddComponent<CmpCharacterEffectPoints>();
|
|
|
|
|
}
|
|
|
|
|
oldCmp._AutoSet();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-11 16:27:33 +08:00
|
|
|
private GameObject _CreateTempPrefab(string srcPrefabPath, string nameId, DataWeapon weaponConfig)
|
2023-12-29 17:00:23 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
var loadPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(srcPrefabPath);
|
2024-01-03 12:54:44 +08:00
|
|
|
if (loadPrefab == null)
|
|
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
Debug.LogError($"{nameId}找不到原始prefab:{srcPrefabPath},取消导出");
|
|
|
|
|
return null;
|
2024-01-03 12:54:44 +08:00
|
|
|
}
|
2024-11-11 16:27:33 +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,
|
2024-11-12 12:30:55 +08:00
|
|
|
DataWeapon weaponConfig,
|
|
|
|
|
string defaultSkinNameId)
|
2024-11-11 16:27:33 +08:00
|
|
|
{
|
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-11-11 16:27:33 +08:00
|
|
|
|
2024-02-02 16:02:31 +08:00
|
|
|
animator.enabled = true;
|
2024-11-12 12:30:55 +08:00
|
|
|
var overrideController = _AutoBuildAnimator(clipDict, nameId, weaponConfig, defaultSkinNameId);
|
2023-12-29 17:00:23 +08:00
|
|
|
|
2024-11-04 13:41:33 +08:00
|
|
|
// Assets/Art_Out/AutoGen/Characters/character_{id}
|
2024-11-11 16:27:33 +08:00
|
|
|
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);
|
|
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
// 保存overrideController
|
2023-12-29 17:00:23 +08:00
|
|
|
// {parentPath}/character_{id}_{weapon}.overrideController
|
2024-11-11 16:27:33 +08:00
|
|
|
var savePath = $"{parentPath}/ov_{nameId}_{weaponConfig.MotionGroupName}.overrideController";
|
2023-12-20 13:47:28 +08:00
|
|
|
AssetDatabase.CreateAsset(overrideController, savePath);
|
2024-04-11 16:28:16 +08:00
|
|
|
|
2024-11-11 16:27:33 +08:00
|
|
|
_SetHitEffectPoints(animator.gameObject);
|
2023-12-29 17:00:23 +08:00
|
|
|
|
|
|
|
|
// 原来的prefab也需要保存
|
2023-12-20 13:47:28 +08:00
|
|
|
// 设置overrideController到animator上
|
|
|
|
|
animator.runtimeAnimatorController = overrideController;
|
2023-12-29 17:00:23 +08:00
|
|
|
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
|
2023-12-20 13:47:28 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:12:39 +08:00
|
|
|
public static int FORCE_TEXTURE_SIZE = 512;
|
|
|
|
|
|
2024-11-11 16:27:33 +08:00
|
|
|
private string _CombineSkins(GameObject loadPrefab,
|
|
|
|
|
string nameId, DataWeapon weaponConfig)
|
2023-12-29 17:00:23 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +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();
|
|
|
|
|
|
|
|
|
|
// 将表情画到头上
|
2025-07-31 15:12:39 +08:00
|
|
|
var faceImgSize = FORCE_TEXTURE_SIZE / 2;
|
|
|
|
|
var result = _CombineEmoji(instPrefab, faceImgSize);
|
2023-12-29 17:00:23 +08:00
|
|
|
if (!result)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("合并表情失败,该角色进入战场后将没有表情");
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:12:39 +08:00
|
|
|
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);
|
2025-03-24 13:24:10 +08:00
|
|
|
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);
|
2024-11-11 16:27:33 +08:00
|
|
|
|
2024-01-04 16:21:26 +08:00
|
|
|
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,
|
2024-11-12 12:30:55 +08:00
|
|
|
string nameId,
|
|
|
|
|
string defaultSkinNameId)
|
2023-12-29 17:00:23 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
for (int i = 0; i < weaponConfigList.Count; i++)
|
2024-01-03 12:54:44 +08:00
|
|
|
{
|
2024-11-11 16:27:33 +08:00
|
|
|
var weaponConfig = weaponConfigList[i];
|
|
|
|
|
var tempPrefab = _CreateTempPrefab(prefabPath, nameId, weaponConfig);
|
|
|
|
|
if (tempPrefab == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-11-12 12:30:55 +08:00
|
|
|
var result = _RebuildAnimator(clipDict, tempPrefab, nameId, weaponConfig, defaultSkinNameId);
|
2024-11-11 16:27:33 +08:00
|
|
|
if (!result)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
_CombineSkins(tempPrefab, nameId, weaponConfig);
|
2024-01-03 12:54:44 +08:00
|
|
|
}
|
2023-12-29 17:00:23 +08:00
|
|
|
|
2023-12-20 13:47:28 +08:00
|
|
|
// 打印日志
|
|
|
|
|
Debug.Log($"重建{prefabPath}成功");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public abstract string CheckAnims(Dictionary<string, AnimationClip> dict);
|
|
|
|
|
|
|
|
|
|
public abstract void RebuildAll(Dictionary<string, AnimationClip> clipDict);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|