Unity Model resource usage process; unity Model usage process
Yesterday I studied how to automatically generate the animation controller in Unity. Today, the operation process is improved a little and explained in detail how to use model resources (AssetBundle) in MMO. This method is required only when model resources such as MMO are very large. If it is relatively small, plug it directly into the Resources directory and load Prefab. You don't have to worry about these issues.
First, attach the final modified script. It creates a menu option, selects a folder (or file), right-click and select CreateAnimation to automatically traverse all model resources, and then creates an AnimatorController and Prefab file. After Prefab is generated (or at the same time), you can use the AssetBundle creation function to create AssetBundle in any directory on the disk.
Using UnityEngine; using UnityEditor; using UnityEditorInternal; using System. IO; using System. collections; using System. collections. generic; public class CreateAnimation: Editor {// The generated Prefab path private static readonly string PREFAB_PATH = "Assets/Prefab/"; private static readonly string PATH_TAG = "Assets /"; private static bool _ ingnoreExist = true; // These animations are played cyclically (manually created for other loop animations). private static reado Nly string [] LOOP_TAG = new string [] {"idle", "walk", "run"}; [MenuItem ("Assets/CreateAnimation")] static void DoCreateAnimation () {UnityEngine. object obj = Selection. activeObject; string path = AssetDatabase. getAssetPath (Selection. activeObject); if (Directory. exists (path) {// if it is a path, Walk (path, (string fbxPath) =>{ fbxPath = fbxPath. replace ("\\","/"). replace (". FBX ",". fbx "); string relativeP Ath = fbxPath. Substring (fbxPath. IndexOf (PATH_TAG); if (! RelativePath. endsWith (". fbx ") {return;} DoCreateController (relativePath); DoCreatePrefab (relativePath) ;});} else if (File. exists (path) {// string fbxPath = path if it is a file. replace ("\\","/"). replace (". FBX ",". fbx "); string relativePath = fbxPath. substring (fbxPath. indexOf (PATH_TAG); if (! RelativePath. endsWith (". fbx ") {return;} DoCreateController (relativePath); DoCreatePrefab (relativePath);} else {// The path does not exist in Debug. logError (string. format ("path does not exist: {0}", path);} AssetDatabase. refresh (); Debug. log (string. format ("converted: {0}", path) ;}// traverse the folder delegate void PathCallBack (string path); static void Walk (string dir, PathCallBack callback) {DirectoryInfo d = new DirectoryInfo (dir); if (d. GetDirectories ()! = Null) {FileInfo [] allf = d. GetFiles (); for (int I = 0; I <allf. Length; I ++) {if (callback! = Null) {callback (allf [I]. FullName) ;}} DirectoryInfo [] alldr = d. GetDirectories (); if (alldr! = Null) {for (int I = 0; I <alldr. length; I ++) {Walk (alldr [I]. fullName, callback) ;}}} static void DoCreateController (string assetPath) {// obtain the animationclip List <AnimationClip> clips = new List <AnimationClip> (); UnityEngine. object [] objects = AssetDatabase. loadAllAssetsAtPath (assetPath); foreach (var obj in objects) {AnimationClip clip = obj as AnimationClip; if (clip! = Null & clip. name. indexOf ("_ preview _") =-1) {clips. add (clip) ;}} if (clips. count <= 0) {return;} // The controller file can be put together with fbx string acPath = assetPath. replace (". fbx ",". controller "); // create an animation controller AnimatorController animatorController = null; // The controller cannot be updated in a timely manner. Therefore, the controller cannot be created but cannot be prefab separately. Otherwise, the associated animatorController = AnimatorController will be lost. createAnimatorControllerAtPath (acPath); AnimatorControl LerLayer layer = animatorController. getLayer (0); UnityEditorInternal. stateMachine sm = layer. stateMachine; Vector3 anyStatePosition = sm. anyStatePosition; float OFFSET_X = 220; float OFFSET_Y = 60; float ITEM_PER_LINE = 4; float originX position = anyStatePosition. x-OFFSET_X * (ITEM_PER_LINE/2.5f); float originY = anyStatePosition. y + OFFSET_Y; float x = originX; float y = originY; State defaultState = Null; foreach (AnimationClip newClip in clips) {string clipName = newClip. name. toLower (); State state = sm. addState (clipName); // sets defaultState. The idle animation if (defaultState = null) {if (clipName. indexOf ("idle ")! =-1) {sm. defaultState = state; defaultState = state ;}} else {if (clipName = "idle") {sm. defaultState = state;} foreach (var tag in LOOP_TAG) {// some animations I want to be born with an animation loop if (clipName. indexOf (tag )! =-1) {SerializedObject serializedClip = new SerializedObject (newClip); AnimationClipSettings clipSettings = new AnimationClipSettings (serializedClip. findProperty ("m_AnimationClipSettings"); clipSettings. loopTime = true; clipSettings. loopBlend = true; serializedClip. applyModifiedProperties (); break;} state. setAnimationClip (newClip, layer); state. position = new Vector3 (x, y, 0); x + = OFFSET_X; If (x> = originX + OFFSET_X * ITEM_PER_LINE) {x = originX; y + = OFFSET_Y ;}} AssetDatabase. saveAssets ();} static void DoCreatePrefab (string assetPath) {// create the corresponding folder string destPath = assetPath; if (! DestPath. StartsWith (PREFAB_PATH) {destPath = assetPath. Replace ("Assets/", PREFAB_PATH);} string dirPath = System. IO. Path. GetDirectoryName (destPath); if (! System. IO. directory. exists (dirPath) {System. IO. directory. createDirectory (dirPath);} string acPath = assetPath. replace (". fbx ",". controller "); string prefabPath = destPath. replace (". fbx ",". prefab "); string name = assetPath. substring (assetPath. lastIndexOf ("/") + 1 ). replace (". fbx "," "); // create prefab GameObject fbx = AssetDatabase. loadAssetAtPath (assetPath, typeof (GameObject) as GameObject; GameObject go = Instantiate (fbx) as GameObject; go. name = name; // if the controller file exists, the corresponding animator if (System. IO. file. exists (acPath) {AnimatorController animatorController = AssetDatabase. loadAssetAtPath (acPath, typeof (AnimatorController) as AnimatorController; Animator animator = go. getComponent <Animator> (); if (animator = null) {animator = go. addComponent <Animator> ();} animator. applyRootMotion = false; animator. runtimeAnimatorController = animatorController;} // create prefab if (System. IO. file. exists (prefabPath) {// replace the original prefab (each prefab has a corresponding udid and cannot be used directly, otherwise all references will be invalid) GameObject prefab = AssetDatabase. loadAssetAtPath (prefabPath, typeof (GameObject) as GameObject; PrefabUtility. replacePrefab (go, prefab);} else {// create a new prefab PrefabUtility. createPrefab (prefabPath, go, ReplacePrefabOptions. connectToPrefab);} GameObject. destroyImmediate (go, true); AssetDatabase. saveAssets ();} class AnimationClipSettings {SerializedProperty m_Property; private SerializedProperty Get (string property) {return attributes (property);} public AnimationClipSettings (SerializedProperty prop) {m_Property = prop ;} public float startTime {get {return Get ("m_StartTime "). floatValue;} set {Get ("m_StartTime "). floatValue = value ;}} public float stopTime {get {return Get ("m_StopTime "). floatValue;} set {Get ("m_StopTime "). floatValue = value ;}} public float orientationOffsetY {get {return Get ("m_OrientationOffsetY "). floatValue;} set {Get ("m_OrientationOffsetY "). floatValue = value ;}} public float level {get {return Get ("m_Level "). floatValue;} set {Get ("m_Level "). floatValue = value ;}} public float cycleOffset {get {return Get ("m_CycleOffset "). floatValue;} set {Get ("m_CycleOffset "). floatValue = value ;}} public bool loopTime {get {return Get ("m_LoopTime "). boolValue;} set {Get ("m_LoopTime "). boolValue = value ;}} public bool loopBlend {get {return Get ("m_LoopBlend "). boolValue;} set {Get ("m_LoopBlend "). boolValue = value ;}} public bool loopBlendOrientation {get {return Get ("m_LoopBlendOrientation "). boolValue;} set {Get ("m_LoopBlendOrientation "). boolValue = value ;}} public bool loopBlendPositionY {get {return Get ("m_LoopBlendPositionY "). boolValue;} set {Get ("m_LoopBlendPositionY "). boolValue = value ;}} public bool loopBlendPositionXZ {get {return Get ("m_LoopBlendPositionXZ "). boolValue;} set {Get ("m_LoopBlendPositionXZ "). boolValue = value ;}} public bool keepOriginalOrientation {get {return Get ("m_KeepOriginalOrientation "). boolValue;} set {Get ("m_KeepOriginalOrientation "). boolValue = value ;}} public bool keepOriginalPositionY {get {return Get ("m_KeepOriginalPositionY "). boolValue;} set {Get ("m_KeepOriginalPositionY "). boolValue = value ;}} public bool keepOriginalPositionXZ {get {return Get ("m_KeepOriginalPositionXZ "). boolValue;} set {Get ("m_KeepOriginalPositionXZ "). boolValue = value ;}} public bool heightFromFeet {get {return Get ("m_HeightFromFeet "). boolValue;} set {Get ("m_HeightFromFeet "). boolValue = value ;}} public bool mirror {get {return Get ("m_Mirror "). boolValue;} set {Get ("m_Mirror "). boolValue = value ;}}}}
Here are a few notes:
1. The time for generating the AnimatorController and Prefab is not fast, so there should be no too many models for one conversion.
2. It would be better to update the content of the AnimatorController, but it was not successful. Therefore, the Prefab should be updated after the controller is created, so that the association information will not be lost (miss animator)
3. If the Prefab already exists when the Prefab is created, update the Prefab. This ensures that information is not lost in other places where Prefab is used. Because a new Prefab is created, a new uuid is generated, so that the original reference does not exist.
4. When the controller is generated, it determines the animationclip. If it is a casual or running action, it is automatically set as the loop mode. If this is not processed in the script, you need to modify a model. If the animation does not belong to the LOOP_TAG, you can set the loop to modify it in Unity. I originally wanted to modify it through ModelImporter, but the correct method was not found. Therefore, you can directly modify the attributes of the AnimationClip by referring to the method of Yusong.
The use process of such resources (AssetBundle) has the following advantages:
1. the development project is not very large. Load resources as programs. Otherwise, a large MMO may have several GB of resources. It takes several minutes to run Unity.
2. AssetBundle can be done by Art. The program only cares about resource loading and usage.
3. Projects that create AssetBundle can be classified. For example, a scenario art project and a role art project. This can further reduce the project and reduce the chance of conflict.