Unit 高效开发系列 - 第一章 代码自动化创建动画预制体
前言
Uinty高效开发系列主要包含一系列的Unity开发实例,主要来源于笔者的一个3D游戏项目和一个手机APP项目,适合利用Unity开发游戏或手机APP场景。
一、代码自动创建动画预制体实现
1.加载场景和角色模型,设置模型比例
static Dictionary<string, Dictionary<string, float>> g_RoleModeScal = new Dictionary<string, Dictionary<string, float>>()
{
{
"Role",new Dictionary<string, float>()
{
{ "R_LT_F_01",120f },
{ "R_FT_M_01",125f },
{ "R_JT_M_01",116f },
{ "R_YT_F_01",63f },
{ "R_HT_M_01",3.7f },
}
},
};
ModifyModelInfo(typeName, AssetsPath, g_RoleModeScal[typeName][objName] * g_Scale);
g_RoleModeScal用于定义模型个性化的比例,g_Scale用于全局调整模型大小比例。
2.切分模型动画
读取帧数分布.txt文件,切分模型动画
帧数分布.txt文件示例:
静止帧数 0
run: 5 - 33
standby: 35 - 125
attack: 135 -180
danti: 185 -240
qunti: 240 -315
fuzhu: 320 -375
hit: 380 -402
die: 402 -430
injury sart:435-455
injury stand:455-605
injury end: 605-625
sit start :625 -700
sit stand: 700 -820
meditation start:820-860
meditation stand:860-980
meditation end: 980-1020
sit end : 1020 -1095 (动作结束,法杖消失)
collect :1100 -1175
mining :1185 -1235 (动作开始,锄头出现)
idle1: 1240-1415 (动作开始锄头消失,法杖出现)
idle2: 1415-1590
idle3: 1590-1760
defense:1760-1800
public static void ModifyModelInfo(string typeName,string AssetsPath, float ModeScale)
{
ModelImporter importer = AssetImporter.GetAtPath(AssetsPath) as ModelImporter;
if (importer != null)
{
importer.globalScale = ModeScale;
importer.materialLocation = ModelImporterMaterialLocation.External;
importer.clipAnimations = (ModelImporterClipAnimation[])SplitAnimationClip(typeName, AssetsPath).ToArray(typeof(ModelImporterClipAnimation));
AssetDatabase.ImportAsset(AssetsPath); //这句不加,面板上数字会变,但是实际大小不会变
}
}
public static ArrayList SplitAnimationClip(string typeName,string AssetsPath)
{
ArrayList retList = new ArrayList();
string ClipText = GetZhenFenBu(AssetsPath);
if (ClipText == "")
{
return retList;
}
//Debug.Log("ClipText:" + ClipText);
StreamReader file = new StreamReader(ClipText, Encoding.GetEncoding("gb2312"));
string sAnimList = file.ReadToEnd();
file.Close();
string Name = AssetsPath.Substring(0, AssetsPath.LastIndexOf("/"));
Name = Name.Substring(Name.LastIndexOf("/") + 1);
ParseAnimFile(sAnimList, Name, ref retList);
return retList;
}
3.设置模型Shader
List<CreatePrefabBase.ShaderPrep> lstShaderPrep = new List<CreatePrefabBase.ShaderPrep>();
lstShaderPrep.Add(new CreatePrefabBase.ShaderPrep("_Brightness", 1.1f, CreatePrefabBase.ShaderPropertyType.SPT_Float));
lstShaderPrep.Add(new CreatePrefabBase.ShaderPrep("_Saturation", 1.3f, CreatePrefabBase.ShaderPropertyType.SPT_Float));
lstShaderPrep.Add(new CreatePrefabBase.ShaderPrep("_Contrast", 1.3f, CreatePrefabBase.ShaderPropertyType.SPT_Float));
CreatePrefabBase.ModifyShader(cloneObj, AssetsPath, Shader, lstShaderPrep);
示例程序中附带一个非常好的带透明贴图的Shader,可以通过_Brightness、_Saturation、_Contrast三个参数分别设置明亮度、饱和度、对比度。两张图展示一下该Shader的效果:
4.添加动画对象属性
static Dictionary<string, List<SimperProperty>> AnimaPropertyDic = new Dictionary<string, List<SimperProperty>>()
{
{"Role",new List<SimperProperty>(){
new SimperProperty(typeof(Rigidbody).AssemblyQualifiedName,new Dictionary<string, string>{{"mass","200"},{ "useGravity", "true" } }),
new SimperProperty(typeof(BaseCharacterController).AssemblyQualifiedName,null),
new SimperProperty(typeof(CharacterMovement).AssemblyQualifiedName,null),
new SimperProperty(typeof(GroundDetection).AssemblyQualifiedName,null),
new SimperProperty(typeof(MeshCollider).AssemblyQualifiedName,null),
new SimperProperty(typeof(NavMeshAgent).AssemblyQualifiedName,new Dictionary<string, string>(){ {"radius","0.01"}, { "enabled", "false"}}),
new SimperProperty(typeof(AnimationEventPro).AssemblyQualifiedName,null),
} },
};
foreach (Si
mperProperty sp in AnimaPropertyDic[typeName])
{
Component c = rooyObj.AddComponent(Type.GetType(sp.ScriptName));
if (sp.PropertyValue != null)
{
foreach (string key in sp.PropertyValue.Keys)
{
switch (key)
{
case "enabled":
{
PropertyInfo propertyInfo;
propertyInfo = c.GetType().GetProperty("enabled");
if (sp.PropertyValue[key] == "false")
{
propertyInfo.SetValue(c, false, null);
}
else
{
propertyInfo.SetValue(c, true, null);
}
}
break;
default:
{
PropertyInfo propertyInfo;
propertyInfo = c.GetType().GetProperty(key);
propertyInfo.SetValue(c, CreatePrefabBase.Parse(sp.PropertyValue[key], key), null);
}
break;
}
}
}
}
5.添加动画状态机
Animator An = cloneObj.GetComponent<Animator>();
AnimatorController _animatorController = AddStateTransitionForRole(strName, AssetsPath);
An.runtimeAnimatorController = _animatorController;
动画状态机通过如下结构定义:
public struct AnimaInfoStrc
{
public int def;
public List<StateTransitionStrc> lstSt;
public Dictionary<int,AnimaStateStuc> lstAs;
public AnimaInfoStrc(int _def,List<StateTransitionStrc> _lstSt, Dictionary<int, AnimaStateStuc> _lstAs)
{
lstSt = _lstSt;
def = _def;
lstAs = _lstAs;
}
}
public struct AnimaStateStuc
{
public float pre;
public float dur;
public float skill;
//public float post; //默认带后处理函数
public AnimaStateStuc( float _pre = -1, float _dur = -1, float _skill = -1)
{
pre = _pre;
dur = _dur;
skill = _skill;
//post = _post;
}
}
static Dictionary<string,AnimaInfoStrc> g_AnimaInfo = new Dictionary<string, AnimaInfoStrc>()
{
{
"Role", new AnimaInfoStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2,new List<StateTransitionStrc>()
{ new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_run_1 ,(int)RoleAnima.RoleAnimaEnum.AS_run_1, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_run_1),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_run_1 ,(int)RoleAnima.RoleAnimaEnum.AS_standby_2, AnimatorConditionMode.NotEqual,(int)RoleAnima.RoleAnimaEnum.AS_run_1,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_attack_3, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_attack_3,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_danti_4, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_danti_4,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_run_1, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_run_1,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_hit_7, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_hit_7,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_standby_2 ,(int)RoleAnima.RoleAnimaEnum.AS_collect_18, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_collect_18,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_attack_3, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_attack_3,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_danti_4, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_danti_4,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_run_1, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_run_1,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_hit_7, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_hit_7,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle1_20 ,(int)RoleAnima.RoleAnimaEnum.AS_collect_18, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_collect_18,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_attack_3, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_attack_3,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_danti_4, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_danti_4,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_run_1, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_run_1,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_hit_7, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_hit_7,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle2_21 ,(int)RoleAnima.RoleAnimaEnum.AS_collect_18, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_collect_18,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_attack_3, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_attack_3,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_danti_4, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_danti_4,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_qunti_5,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_fuzhu_6,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_run_1, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_run_1,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_hit_7, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_hit_7,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_start_12,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_idle3_22 ,(int)RoleAnima.RoleAnimaEnum.AS_collect_18, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_collect_18,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_sit_start_12 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13,true),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_sit_stand_13 ,(int)RoleAnima.RoleAnimaEnum.AS_sit_end_17, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_sit_end_17,false),
new StateTransitionStrc((int)RoleAnima.RoleAnimaEnum.AS_sit_end_17 ,(int)RoleAnima.RoleAnimaEnum.AS_standby_2, AnimatorConditionMode.Equals,(int)RoleAnima.RoleAnimaEnum.AS_standby_2,false),
},
new Dictionary<int,AnimaStateStuc> ()
{
{ (int)RoleAnima.RoleAnimaEnum.AS_standby_2,new AnimaStateStuc(0,-1)},
{ (int)RoleAnima.RoleAnimaEnum.AS_run_1,new AnimaStateStuc(0,-1)}
})
},
};
6.隐藏一些不需要显示的对象
- DisableNoNeedObj(rooyObj);
7.设置对象旋转、中心点、添加碰撞体
- 设置对象旋转
public static Dictionary<string, Dictionary<string, Vector3>> g_ModeRoate = new Dictionary<string, Dictionary<string, Vector3>>()
{
{
“Role”,new Dictionary<string, Vector3>()
{
{ “R_LT_F_01”,new Vector3(0.0f,0.0f,0f)},
{ “R_FT_M_01”,new Vector3(0.0f,0.0f,0f) },
{ “R_JT_M_01”,new Vector3(0.0f,0.0f,0f) },
{ “R_YT_F_01”,new Vector3(0.0f,0.0f,0f) },
{ “R_HT_M_01”,new Vector3(0.0f,0.0f,0f) },
}
},
};
cloneObj.transform.rotation = Quaternion.Euler(CreateAnimaPrefab.g_ModeRoate[typeName][objName]);
- 设置对象中心点
public static Dictionary<string, Dictionary<string, Vector3>> g_ModePivotOffset = new Dictionary<string, Dictionary<string, Vector3>>()
{
{
"Role",new Dictionary<string, Vector3>()
{
{ "R_LT_F_01",new Vector3(0.0f,0.01f,0f)},
{ "R_FT_M_01",new Vector3(0.02f,0.07f,0f) },
{ "R_JT_M_01",new Vector3(0.13f,0.03f,-0.185f) },
{ "R_YT_F_01",new Vector3(0f,0.04f,0f) },
{ "R_HT_M_01",new Vector3(0.148f,0.05f,-0.42f) },
}
},
};
PivotPos = g_ModePivotOffset[typeName][objName] + new Vector3(0, Bs.center.y - Bs.extents.y, 0);
API.SetPivot(rooyObj.transform, PivotPos, API.Space.Global);
- 添加碰撞体
AddMeshCollider(rooyObj);
8.保存动画预制体
GameObject _prefab = PrefabUtility.SaveAsPrefabAsset(rooyObj, PrefabPath + objName + ".prefab", out result);
GameObject.DestroyImmediate(rooyObj);
二、示例工程编译及运行
- 解压CreateAnimatPrefab.zip文件,可得到如下目录结构:
- 使用unity 2019.2.2f1版本打开该文件夹:
- 打开Login场景:
代码如下(示例):
- 点击菜单栏“Tool”->"CreateChuChangPrefab"菜单,启动生成动画预制体,完成后可启动游戏。
三、资源下载
-
获取示例工程资源
链接:https://pan.baidu.com/s/1PK660pxlmI8tc9qoDAf0Tg?pwd=9uo8
提取码:9uo8