Search Unity

Getting object as prefab (In my case, Animation)

Discussion in 'Scripting' started by arsnyan2002, Jul 2, 2019.

  1. arsnyan2002

    arsnyan2002

    Joined:
    Apr 30, 2019
    Posts:
    30
    So, as I still have a problem with Unity's imported FBX's rotation, I would love to at least know, how to rotate their animations through script.

    So, the first thing to do was duplicating animation files from prefab. You just expand it, find animation in prefab and click 'Ctrl+D'. That's how I extract animations from imported prefabs.

    Then I'm trying to use special script to make everything look a bit better:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. public class FixAnimationPostprocessor
    7. {
    8.     public Animator animator;
    9.  
    10.     [MenuItem("Assets/Transform Animations")]
    11.     public static bool TransformAnimations()
    12.     {
    13.         var selected = Selection.objects;
    14.  
    15.         foreach (Object selection in selected)
    16.         {
    17.             var animation = Resources.Load<Animation>(AssetDatabase.GetAssetPath(selection));
    18.             animation.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
    19.         }
    20.  
    21.         return Selection.activeObject.GetType() == typeof(Animation);
    22.     }
    23. }
    24.  
    And, this is a bad solution at least because I get this error:
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. FixAnimationPostprocessor.TransformAnimations () (at Assets/Editor/FixAnimationPostprocessor.cs:18)
    3.  
    I don't even know what to use in that case, so that I can access my asset as Animation.class
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You probably need to use Animator, AnimatorController, or AnimationClip, not the Animation class. The Animation class is an obsolete legacy component.
     
  3. arsnyan2002

    arsnyan2002

    Joined:
    Apr 30, 2019
    Posts:
    30
    Even though I should use it, I still wouldn't be able to access asset Object.class as Animator.class.

    P.S. I put '.class' to mark it as a C# script class.
    P.S.S. I've also thought that I should use Animator, because it's a little more precise in my case.
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I'm not sure I follow your problem. As far as I can tell, you don't ever have an "Object" type variable anywhere in your code. In line 17, Resources.Load<Animation> will return an object of type Animation, and because it's using the "var" keyword, the assignment will determine the type - therefore "animation" is of type Animation. The problem isn't that "animation" is an Object, the problem is that it's null. It's null because Resources.Load<Animation> didn't find anything of type Animation at the specified path, because Unity no longer imports things as type Animation.
     
  5. arsnyan2002

    arsnyan2002

    Joined:
    Apr 30, 2019
    Posts:
    30
    @StarManta mhm, I don't really understand why Resources.Load works only at import, but I've tried to use AssetDatabase.LoadAssetAtPath... And it doesn't work, still.
    The code now is:
    Code (CSharp):
    1. [MenuItem("Assets/Transform Animations")]
    2.     public static bool TransformAnimations()
    3.     {
    4.         var selected = Selection.objects;
    5.  
    6.         foreach (Object selection in selected)
    7.         {
    8.             var animation = AssetDatabase.LoadAssetAtPath<Animator>(AssetDatabase.GetAssetPath(selection));
    9.             animation.bodyRotation = Quaternion.Euler(90f, 0f, 0f);
    10.         }
    11.  
    12.         return Selection.activeObject.GetType() == typeof(Animator);
    13.     }
    Ah, it's so hard to understand :D
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Could you show a screenshot (including the project window and the inspector) of the object on which you're attempting to run this script?
     
  7. arsnyan2002

    arsnyan2002

    Joined:
    Apr 30, 2019
    Posts:
    30
    @StarManta sorry for being late. Here's the screenshots step-by-step:
    upload_2019-7-2_20-53-15.png
    upload_2019-7-2_20-53-26.png
    upload_2019-7-2_20-53-46.png
    upload_2019-7-2_20-54-3.png
    upload_2019-7-2_20-54-11.png
    And I should say - it's just horrible, that Unity, unlike UE4, does not support FBX's On-import settings. In UE4, I can easily set local rotation to 90*, in Unity I can change local rotation when importing things with code. However, it does not support animations.

    Actually, the rotation for static models works with this code by someone else on r/Unity3D, but I've forget the author:
    Code (CSharp):
    1. using System.IO;
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. public class FixFbxModelPostprocessor : AssetPostprocessor
    6. {
    7.     public void OnPostprocessModel(GameObject obj)
    8.     {
    9.         ModelImporter importer = assetImporter as ModelImporter;
    10.         if (Path.GetExtension(importer.assetPath) == ".fbx")
    11.         {
    12.             FixObject(obj.transform);
    13.             Debug.Log("[Fix FBX] Finished for " + Path.GetFileName(importer.assetPath));
    14.         }
    15.     }
    16.  
    17.     private void FixObject(Transform obj)
    18.     {
    19.         MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
    20.  
    21.         /*
    22.          * First we need to undo the stupid -90 dregree rotation on the X axis that Unity does
    23.          * on every game object with a mesh
    24.          */
    25.         if (meshFilter)
    26.         {
    27.             obj.localRotation *= Quaternion.Euler(90, 0, 0);
    28.         }
    29.  
    30.         /*
    31.          * Translate rotation into Unity's coordinate system
    32.          */
    33.         Quaternion lc = obj.transform.localRotation;
    34.         obj.transform.localRotation = Quaternion.Euler(-lc.x, lc.y, -lc.z); //lc.w);
    35.  
    36.         /*
    37.          * Translate position into Unity's coordinate system
    38.          */
    39.         Vector3 currentPos = obj.transform.localPosition;
    40.         obj.transform.localPosition = new Vector3(-currentPos.x, currentPos.y, -currentPos.z);
    41.  
    42.         /*
    43.          * Translate mesh into Unity's coordinate system
    44.          */
    45.         if (meshFilter)
    46.         {
    47.             FixMesh(meshFilter.sharedMesh);
    48.         }
    49.  
    50.         /*
    51.          * Repeat for all sub objects
    52.          */
    53.         foreach (Transform child in obj)
    54.         {
    55.             FixObject(child);
    56.         }
    57.     }
    58.  
    59.     private void FixMesh(Mesh mesh)
    60.     {
    61.         /*
    62.          * We fix the vertices by flipping Z axis with Y axis
    63.          * Odly enough X has to be inverted. When we store a positive X in Blender somehow it gets inverted in the *.fbx format O.o
    64.          */
    65.         Vector3[] vertices = mesh.vertices;
    66.         for (int i = 0; i < vertices.Length; i++)
    67.         {
    68.             vertices[i] = new Vector3(-vertices[i].x, vertices[i].z, vertices[i].y);
    69.         }
    70.         mesh.vertices = vertices;
    71.  
    72.         /*
    73.          * Same goes for normals
    74.          */
    75.         Vector3[] normals = mesh.normals;
    76.         for (int i = 0; i < normals.Length; i++)
    77.         {
    78.             normals[i] = new Vector3(-normals[i].x, normals[i].z, normals[i].y);
    79.         }
    80.         mesh.normals = normals;
    81.  
    82.         /*
    83.          * Vertex positions have changed, so recalc bounds
    84.          */
    85.         mesh.RecalculateBounds();
    86.     }
    87. }
    It works at runtime, so when any model is imported, it's being converted to Unity's orientation system.

    Hope this will help you to understand the problem.
     
  8. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Alright, those files will be of type AnimationClip.

    I may have to burst a bubble here though - the functionality you're going for may not be doable through normal Unity classes. AnimationClips (at least last time I tried to do something similar) have terrible script accessibility in terms of editing them.

    The general advice here (and actually the official answer) would be to make your animated object a child of another object. The parent object will be moved as the primary object, while the child (the model) is rotated at whatever angles are needed to make it upright.
     
    arsnyan2002 likes this.
  9. arsnyan2002

    arsnyan2002

    Joined:
    Apr 30, 2019
    Posts:
    30
    @StarManta well, that's a shame ._.

    Actually, it's not that bad solution, but I just keep answering myself why Unity doesn't make any better solution within Unity Editor.

    Okay, thanks for responding and help! At least I found the more or less suitable solution.

    Thanks again.