Jump to content

[RESOLVED] How to Play Individual Animation Clips from an FBX Imported Animation.


Recommended Posts

Apparently, nobody at the Unity forums (including a couple Unity engine developers) knows how to do this or has never done it themselves. So, if you can answer this, you need to go work at Unity.

I have a single, baked, FBX animation that I imported from Blender with 110 frames. The animation contains two separate actions: Deploy (frames 1-70) and Scan(70-110). When the user clicks the context-menu button that says Deploy, I want to play the first 70 frames of the animation. I would think this would be defined with AddClip(). However, when I display the length of the AddClip() result, it shows 1. when I attempt to play it, it does not play.

Here's the code. the "anim.Play("Deploy");" fails to play. However, "anim.Play();" works, but that plays the entire 110 frames, of course.


private Animation anim;
private AnimationClip clipDeploy;
private AnimationClip clipScan;

public override void OnStart(PartModule.StartState state)
{
anim = part.FindModelAnimators("Scene")[0];
clipDeploy = new AnimationClip();
anim.AddClip(clipDeploy, "Deploy", 0, 70);
clipScan = new AnimationClip();
anim.AddClip(clipScan, "Scan", 70, 109);
Debug.Log("clipDeploy=" + clipDeploy.length);
Debug.Log("clipScan=" + clipScan.length);
base.OnStart(state);
}

//toggles deployment
[KSPEvent(active = true, guiActive = true, guiActiveEditor = true, guiName = "Deploy")]
public void ToggleDeploy()
{
Deploy(!isDeployed);
}

private void Deploy(bool status)
{
isDeployed = status;

if(isDeployed) // deploy
{
anim["Scene"].speed = 1f;
anim["Scene"].normalizedTime = 0f;
}
else // retract
{
anim["Scene"].speed = -1f;
anim["Scene"].normalizedTime = 1f;
}
anim.Play("Deploy");
Events["ToggleScan"].active = isDeployed;
Events["ToggleScan"].guiActive = isDeployed;
Events["ToggleDeploy"].guiName = (isDeployed ? "Retract" : "Deploy");
return;
}

What am I doing wrong? I've also tried defining clips in the Unity Animations Inspector using the same names and frame numbers. No joy.

Does nobody do this? If not, how do you play individual pieces of an animation? Or, do you somehow bring multiple animations into Unity and magically select them to play? Is there a tutroial you know of? I've googled until my fingers bled. Do you know a github piece of code you can point me to?

I've spent several days on this simple, very frustrating task. Your help is appreciated. And, if you hav a solution, please teach the developers on the Unity forum.

Edited by Apollo13
Link to comment
Share on other sites

didn't I answer this allready?

http://forum.kerbalspaceprogram.com/threads/94011-RESOLVED-Unity-animation-Play-Problem?p=1422843&viewfull=1#post1422843

I assume, you try to play animation from the wrong gameobject/model..

you still need to loop through them as I said back then..

(with evil's code snippet, you can find out in what go/model)

The downside is the animation state will not be as expected..

so if you run the scan-animation, the deploy will allways be at "end"-state => deployed

Some one posted a plugin last week that shall fix that issue..

but I didn't follow up after initial post..

Link to comment
Share on other sites

@philotical: Thanks. I previously tried anim.Rewind() followed by anim.Play(). But, I'll tinker with that a bit more. At this point, I'LL TRY ANYTHING.

Every time the game enters a scene, does it not "rewind" the animation?

Edited by Apollo13
Link to comment
Share on other sites

The revised code didn't work:

            anim = part.FindModelAnimators("Deploy")[0];
anim.Rewind();
anim.Play("Deploy");

nor this:

            anim = part.FindModelAnimators("Deploy")[0];
anim.Rewind();
anim.PlayQueued("Deploy");

I use index = 0 because there is only one part on the entire vessel. In your example, you're searching for all the landing gears on a vessel.

Edited by Apollo13
Link to comment
Share on other sites

not quite - my example code is from the belerophon..

see my signature..

it's a one part vessel with 25 animations in it.. - also the landing legs are the same part.. - just an animation

what I do is loop through all of the animations and find the one with the "right" name - that one I play queued.

you can go with 0 as long as you like - it's a time waste - you most likely won't get around that loop - I tried quite some time..

Link to comment
Share on other sites

this didn't work and I get a NullReference Exception:

            foreach (Animation animation in this.vessel.rootPart.FindModelAnimators("Deploy"))
{
animation.Rewind();
animation.PlayQueued("Deploy");
}

In my Unity Animations Inspector, I've defined:

Element 0 : Scene (imported in FBX file)

Element 1 : Deploy 0-70

Element 2: Scan 70-110

How does yours look?

Finally, I don't know how to use xEvilreeper's snippet

Link to comment
Share on other sites

Mine has 25 lines and each one is a clip like your's in Element 1&2..

the scene I don't have - it's not needed..

Maybe you need it because it's fbx, I don't know, never used fbx - I usually import my blend directly through the assets forder and I get "default_clip" with the full length of all animations combined..

I simply overwrite that with my name and limit it to the needed frames..

than I add an other one and do the same..

but keep in mind, I get it to work, because I do what they said - but I don't know what I'm doing as much as evil would know..

So - don't blame me if I missed a difference here - haha

but I doubt it - you just stand on your own tube as I did for days..

A null ref is something to debug - find out what value/ref it is and fix it..

I can't debug your NullRef from the posted code..

Evil's snip:

Put this in your name space, just above your class definition..

    public delegate void GameObjectVisitor(GameObject go, int indent);

public static class DebugExtensions
{
private static void internal_PrintComponents(GameObject go, int indent)
{
Debug.Log((indent > 0 ? new string('-', indent) + ">" : "")+" "+go.name+" has components:");

var components = go.GetComponents<Component>();
foreach (var c in components)
Debug.Log(new string('.', indent + 3) + "c"+ ": "+c.GetType().FullName);
}

public static void PrintComponents(this UnityEngine.GameObject go)
{
go.TraverseHierarchy(internal_PrintComponents);
}

public static void TraverseHierarchy(this UnityEngine.GameObject go, GameObjectVisitor visitor, int indent = 0)
{
visitor(go, indent);

for (int i = 0; i < go.transform.childCount; ++i)
go.transform.GetChild(i).gameObject.TraverseHierarchy(visitor, indent + 3);
}
}

than put this, where ever you got the data to read out:


DebugExtensions.PrintComponents(v.gameObject);

v is a vessel in my case..

..should be in your's too..

It is a class with the static keyword in front of it, that means you must not use it with a instance but with it's classname..

DebugExtensions. - after that you just add the functionname you want to call..

that function has to be static too..

In this case, that one function does the whole job..

Link to comment
Share on other sites

here's the code I used

            try
{
foreach (Animation animx in this.vessel.rootPart.FindModelAnimators("Deploy"))
{
animx.Rewind();
animx.PlayQueued("Deploy");
}
}
catch(NullReferenceException e)
{
DebugExtensions.PrintComponents(vessel.gameObject);
}

I did not get the dump like xEvil shows.

Link to comment
Share on other sites

if evils snip wasn't executed, it's because you locked it out..

it only happens IF you have an exception and you obviousely didn't have a null ref.

do this:

DebugExtensions.PrintComponents(this.vessel.gameObject);

foreach (Animation animx in this.vessel.rootPart.FindModelAnimators("Deploy"))

{

animx.Rewind();

animx.PlayQueued("Deploy");

}

open the log and check where your animations are, than you will probbably see why you get a null ref

Are you sure it's the rootpart where your animations are?!?!

That could only be if your part was a command pod afaik

Is it?

Link to comment
Share on other sites

this is the statement causing the NullReferenceException:

foreach (Animation animx in this.vessel.rootPart.FindModelAnimators("Deploy"))

I tried:
foreach (Animation animx in part.FindModelAnimators("Deploy"))

I also tried:
foreach (Animation animx in this.part.FindModelAnimators("Deploy"))

i don't get the exception. But, I still get no animation.

I'm getting the "no animation" problem in the VAB, where I have only two parts: command probe, my part

BTW: I use FBX import, because when I tried importing the .BLEND file, I would get an error message that "Blender has stopped working" even when Blender wasn't running. That may have been a problem with Blender v 2.71

Just yesterday, I tried a direct .blend import again, and it worked. I'm using Blender v 2.72. So, I may try that instead of FBX.

EDIT: just tried importing .BLEND file. Import worked. I generated the .MU file and Unity did not complain. However, the file was not loaded into KSP. KSP.log says "model does not exist". Yet, the .MU file is there. in the parts directory.

Edited by Apollo13
Link to comment
Share on other sites

Do you have the part and config you could post? My snippet only prints which components are attached to a GameObject and its children, nothing more. If you're getting an animation to play at all then it won't help you and you're already past the "are we sure this part HAS an animation?" part of debugging

Link to comment
Share on other sites

I went back to a solution I used a month ago. I wanted to use AddClip() so I could set AnimationEvents. After much frustration, decided to drop that effort and go old school. BTW: this is still WIP. I'm using OnFixedUpdate because I have other changes planned that require that.


using System;
using System.Collections;
using UnityEngine;

namespace orbitalscanner2
{
public class orbitscan : PartModule
{
[KSPField(isPersistant = true, guiActive = false)]
public bool isDeployed = false;

[KSPField(isPersistant = true, guiActive = false)]
public bool isScanning = false;

private Animation anim;

public override void OnStart(PartModule.StartState state)
{
anim = part.FindModelAnimators("Scene")[0];
base.OnStart(state);
}

//toggles deployment
[KSPEvent(active = true, guiActive = true, guiActiveEditor = false, guiName = "Deploy")]
public void ToggleDeploy()
{
Deploy(!isDeployed);
return;
}

public void Deploy(bool status)
{
isDeployed = status;

if (isDeployed) // deploy, start scanning
{
anim.Rewind();
anim["Deploy"].speed = 1f;
anim["Deploy"].normalizedTime = 0f;
anim.Play("Deploy");
isScanning = true;
}
else // stop scanning, retract
{
isScanning = false;
anim.Stop();
anim.Rewind();
anim["Deploy"].speed = -1f;
anim["Deploy"].normalizedTime = 1f;
anim.Play("Deploy");
}

Events["ToggleDeploy"].guiName = (isDeployed ? "Retract" : "Deploy");
return;
}

// to test OnFixedUpdate, you MUST have at least one stage and Launch!!!
public override void OnFixedUpdate()
{
if (isScanning && !anim.isPlaying) //restart scanner if playing
{
anim.Rewind();
anim["Scan"].speed = 1f;
anim["Scan"].normalizedTime = 0f;
anim.Play("Scan");
}
base.OnFixedUpdate();
}
}
}

Edited by Apollo13
Link to comment
Share on other sites

You were pretty close, though. You built your new AnimationStates out of empty clips so naturally you won't be seeing any animation. It works as expected, with the single gotcha being that since your PartModule isn't attached to the same GO as the Animation you're messing with, the message sent from an AnimationEvent won't go to your script automatically.

Here's an example that tweaks the Communotron for fun:

class JellyListener : MonoBehaviour
{
private void MethodSentOnAnimationsGameObject()
{
// just forward it
GetComponentInParent<JellyfishDish>().SendMessage("JellyfishOpen");
}
}

class JellyfishDish : PartModule
{
new Animation animation;

public override void OnAwake()
{
base.OnAwake();
print("Jellyfish awake");

animation = part.FindModelAnimators().SingleOrDefault();
int frames = (int)(animation.clip.length * animation.clip.frameRate);

animation.AddClip(animation.clip, "Jellyfish", (int)(frames * 0.7) /* selected arbitrarily */, frames);

AnimationState jellyfish = animation["Jellyfish"];

jellyfish.normalizedSpeed *= 3f;
jellyfish.clip.AddEvent(new AnimationEvent()
{
time = 0f,
functionName = "MethodSentOnAnimationsGameObject"
});

jellyfish.wrapMode = WrapMode.PingPong;
animation.gameObject.AddComponent<JellyListener>(); // listens for our event
}

[KSPEvent(active = true, guiActive = true, guiActiveEditor = true, guiName = "Deploy the Jellyfish")]
public void JellyfishDeploy()
{
animation.Play("Jellyfish");
}

private void JellyfishOpen()
{
ScreenMessages.PostScreenMessage("Jellyfish!", .5f, (ScreenMessageStyle)UnityEngine.Random.Range(0, 3));
print("JellyfishOpen event");
}
}

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
class InsertPartModule : MonoBehaviour
{
private void Start()
{
PartLoader.getPartInfoByName("commDish").partPrefab.AddModule("JellyfishDish");
}
}

Link to comment
Share on other sites

@xEvilReeperx: thanks for that code. I'll use that in the future and ask you a few questions as well. Sent you a forum Rep point. I had asked for AddClip() help in the Unity forums. the only response I got back was some guy saying "You're creating an empty clip, and that just seems wrong." Three times. Mind you, he didn't offer any assistance to make it right. He sure as HEII didn't offer a specific coding example (I did ask). Nope, just told me I was doing it wrong.

@philotical: Thanks for your assistance and patience. Sent you a Rep point as well.

Edited by Apollo13
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...