eyes.prefabface.prefabhair.prefabpants.prefabshoes.prefabtop.prefab
以一个prefab为例
prefab只有 Transform 和 SkinnedMeshRenderer 组件,对于SkinnedMeshRenderer组件,内部Mesh,Root Bone, Materials全部置空,资源通过动态加载
female_skeleton.prefab
骨骼的prefab只保存骨架信息
bonenames.asset
[Serializable]
public class TransInfo
{
public Vector3 localPos;
public Quaternion localRot;
public Vector3 localScale;
}
[Serializable]
public struct Info
{
public string partName;
public string rootBoneName;
public string[] boneNames;
public Bounds bounds;
public TransInfo trans;
}记录 partName, rootBoneName, boneNames, bounds, trans信息, 然后通过查询的方式,填充进入相应的prefab中
public void ChangePart(Mesh mesh, List<Material> matList)
{
if (m_Character == null || m_SkinMesh == null)
return;
var partAsset = m_Character.partAsset;
if (partAsset == null)
return;
var partName = mesh.name;
// 设置Mesh
m_SkinMesh.sharedMesh = mesh;
// 设置材质 (真实项目里面,这里动态加载材质路径列表,然后填充进入列表)
// 例如 mat1path|mat2path|mat3path, 加载这些资源,然后加入列表里面
// 例如眼睛等部件,可能会有多个材质,所以这里需要是材质列表
m_MatList.Clear();
m_MatList.AddRange(matList);
m_SkinMesh.sharedMaterials = m_MatList.ToArray();
// 设置transform
var trans = partAsset.GetTransInfo(partName);
if (trans != null)
{
m_SkinMesh.transform.localPosition = trans.localPos;
m_SkinMesh.transform.localRotation = trans.localRot;
m_SkinMesh.transform.localScale = trans.localScale;
}
// 设置bounds
m_SkinMesh.localBounds = partAsset.GetBounds(partName);
// 设置骨骼列表和根骨骼
m_SkinMesh.bones = m_Character.GetBones(partName, out Transform rootBone);
m_SkinMesh.rootBone = rootBone;
}贴花矩阵计算代码
public static Matrix4x4 TransformUV(float rotation, Vector2 translate, float scale)
{
Matrix4x4 matrix = Matrix4x4.identity;
// rotate
float radian = rotation * Mathf.Deg2Rad;
float cosRadian = Mathf.Cos(radian);
float sinRadian = Mathf.Sin(radian);
Matrix4x4 rotateM = new Matrix4x4(new Vector4(cosRadian, sinRadian, 0, 0),
new Vector4(-sinRadian, cosRadian, 0, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1));
if (scale == 0)
{
scale = 1;
}
float invScale = 1 / scale;
float offestX = -translate.x * invScale - 0.5f * invScale;
float offsetY = -translate.y * invScale - 0.5f * invScale;
// anchor translate
Matrix4x4 preTranslateM = new Matrix4x4(new Vector4(1, 0, 0.5f, 0),
new Vector4(0, 1, 0.5f, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1));
// translate
Matrix4x4 translateM = new Matrix4x4(new Vector4(1, 0, offestX, 0),
new Vector4(0, 1, offsetY, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1));
// scale
Matrix4x4 scaleM = new Matrix4x4(new Vector4(invScale, 0, 0, 0),
new Vector4(0, invScale, 0, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1));
matrix = scaleM * translateM * rotateM * preTranslateM;
return matrix;
}贴花shader计算
half4 CalcDecal(float2 uv)
{
half3x3 calMatrix = half3x3(_FaceDecalUVMatrixM0.xyz,
_FaceDecalUVMatrixM1.xyz,
_FaceDecalUVMatrixM2.xyz);
uv = saturate(mul(calMatrix, half3(uv, 1)).xy);
half4 decalColor = tex2D(_FaceDecalTex, uv) * _FaceDecalColor;
return decalColor;
}原始 CreateAllAvatarRes 在 WebGL 打包后资源全部加载失败,原因有三:
| 问题 | 原因 |
|---|---|
DirectoryInfo / System.IO |
WebGL 运行在浏览器沙箱,无本地文件系统访问 |
FunctionUtil.CollectAll<T> |
方法体被 #if UNITY_EDITOR 包裹,打包后返回空列表 |
| Mesh / Material / Anim 不在 Resources 下 | 原路径在 Assets/Meshs/、Assets/Materials/、Assets/Anims/,打包时不会被包含 |
1. 移除 System.IO 依赖
// 删除
using System;
using System.IO;2. AvatarRes 路径常量改为 Resources 子路径
// 修改前
public const string MESH_PATH = "Assets/Meshs/";
public const string ANIM_PATH = "Assets/Anims/";
public const string Prefab_PATH = "Assets/Resources/";
public const string MAT_PATH = "Assets/Materials/";
// 修改后
public const string MESH_SUBPATH = "Meshs";
public const string ANIM_SUBPATH = "Anims";
public const string MAT_SUBPATH = "Materials";
public static readonly string[] CHAR_NAMES = { "Female", "Male" };3. CreateAllAvatarRes 改用 Resources.LoadAll
// 修改前:使用 DirectoryInfo 扫描文件系统 + FunctionUtil.CollectAll(Editor Only)
void CreateAllAvatarRes()
{
DirectoryInfo dir = new DirectoryInfo(AvatarRes.Prefab_PATH);
foreach(var subdir in dir.GetDirectories())
{
string dirname = subdir.Name;
List<Mesh> meshArray = FunctionUtil.CollectAll<Mesh>(AvatarRes.MESH_PATH + dirname + "/");
List<Material> matArray = FunctionUtil.CollectAll<Material>(AvatarRes.MAT_PATH + dirname + "/");
List<AnimationClip> clips = FunctionUtil.CollectAll<AnimationClip>(AvatarRes.ANIM_PATH + dirname + "/");
// ...
}
}
// 修改后:使用静态角色列表 + Resources.LoadAll(全平台可用)
void CreateAllAvatarRes()
{
foreach (string dirname in AvatarRes.CHAR_NAMES)
{
List<Mesh> meshArray = new List<Mesh>(Resources.LoadAll<Mesh>($"{dirname}/{AvatarRes.MESH_SUBPATH}"));
List<Material> matArray = new List<Material>(Resources.LoadAll<Material>($"{dirname}/{AvatarRes.MAT_SUBPATH}"));
List<AnimationClip> clips = new List<AnimationClip>(Resources.LoadAll<AnimationClip>($"{dirname}/{AvatarRes.ANIM_SUBPATH}"));
// ...
}
}将 Mesh、Material、AnimationClip 移入 Assets/Resources/ 对应子目录:
迁移前:
Assets/Meshs/Female/ → Assets/Resources/Female/Meshs/
Assets/Materials/Female/ → Assets/Resources/Female/Materials/
Assets/Anims/Female/ → Assets/Resources/Female/Anims/
Assets/Meshs/Male/ → Assets/Resources/Male/Meshs/
Assets/Materials/Male/ → Assets/Resources/Male/Materials/
Assets/Anims/Male/ → Assets/Resources/Male/Anims/
迁移后完整结构:
Assets/Resources/
├── Female/
│ ├── bonenames.asset
│ ├── female_skeleton.prefab
│ ├── eyes/face/hair/pants/shoes/top .prefab
│ ├── Meshs/ ← Mesh 资源
│ ├── Materials/ ← Material 资源
│ └── Anims/ ← AnimationClip 资源
└── Male/
└── ...(同上)
新增角色时:在
AvatarRes.CHAR_NAMES数组中追加角色目录名,并按上述结构放置资源即可。