Monday, 16 March 2015

Creating animations with Unity - complete noobs only!

While we're still finding our way around Unity, we're stumbling about falling into all kinds of little gotchas and not quite understanding how it all works. But today we managed to create our own custom animation, entirely from scratch. Something which has been a really headache for days now, as screens appear and disappear, previously seen windows no longer available and so on.

Here's how we made some sci-fi sliding doors for our Starship Raiders board game. It may not be the best way to do it. It may not even be the right way to do it! But this is how we did it, and it works, so better get it written down before we forget!

This is the animation we created:


It's simply a door frame with two door sections "inside" it. When a function call is made, the doors slide apart. Another function call brings the two back together. A simple spot effect is played as the doors open/close.

To begin with we're using the Top Down Sci Fi (mobile) environment from Manufactura K4. We just placed a couple of their source models into our scene (to keep things simple).


The doors are originally designed to slide horizontally. But in our game, we're going to be putting a floor and ceiling in place, and sliding them vertically. If we stuck with horizontal, then we'd have to keep at least one blank panel alongside each edge of the door, so that the doors aren't visible after we've pushed them open. By turning them to operate vertically, it doesn't matter if they protrude above the ceiling, or below the floor, as they will be hidden by the floor and ceiling tiles anyway.

After rotating the doors, we placed them inside the frame - lining them up "by eye". We also made sure that each door was made a child of the doorframe. This is important, should we want to make copies of the sliding door to use again in the game. By simply cloning the parent (frame) we don't have to mess about setting up the doors again in future.


To keep things nice and tidy (it's not really necessary, but after using Unity for half a dozen times or so, you quickly learn it really does make things easier in the long run!) we created a folder for our animations, selected one of the doors and selected "Animation" (not animator) from the menu


At first we really struggled with even this basic premise. We tried creating an empty animation file, then tried to find some way of telling it which object to apply the animation(s) to. It may be possible to do it this way, be we just got confused. So this is our method - select the item you want to move, then bring up the animation window.

When you click "add property" you'll be prompted to save your animation to a file. Enter a suitable filename here. We called our first animation "door1_open".


Gotcha number two - if you don't see the "add property" button, it's probably because you've nothing selected that can be animated. It took is ages to work this one out. By selecting an object before bringing up the animation window, you should always have an "add property" button, because Unity already knows which object you want to animate.


There are a few things to note here:
Firstly, we selected the "position" property of our door, and the animator immediately displayed two sets of keyframes. Since we want our door to start off in the closed position, we left everything alone and moved the playhead (indicated by the arrow) to the second set of keyframes.

At this point, the record button, playbar at the top, and the x/y/z properties were all lit up in red. This tells us that we're in "record mode". Anything that we move around here will be recorded in the animation.

Making sure that the playhead was in the last set of keyframes, we lifted the door upwards, to it's final open position (that it's popping out of the frame doesn't really matter - when the ceiling tiles are in place, it'll just look like it has slid inside the frame). Click off the record button and the changes are committed to the animation (the door drops back to its original position in the scene as well).

Here's gotcha number three: we want to create a second animation, moving the door from the open to the closed position. It's quite simple, once you know what you're doing, but it took us ages to work this one out!


Animations are applied to objects - so you must have an object selected in order to animate it. We spent ages creating second, empty animation clips, then wondering how to get to this bit again, where we could add keyframes and move things around. The answer is, with the object selected, click the drop down in the top of the animation window and create a new clip.

This will throw up the save dialogue window, and allow you to create your second, separate animation file. Because the door object is selected, it already knows which object to animate, and so, once again, you're presented with the "add property" button.

As before, we selected transform - position, and this time on the first frame moved the door to the same Y co-ordinates as at the end of the previous animation (you can just type into the co-ordinate boxes in the inspection panel). Because we're closing the door - moving it from up in the air back to it's resting place, we left the last set of keyframe values as they were; you can always hit play to preview the animation.

With our "open" and "close" animations for door 1 complete, we repeated the process for door 2, until we had a total of four animations

[edit: it has been pointed out, that had we selected the door frame and started our animations, we could have set the y-co-ordinates of both door1 and door2 in a single animation, since both are child objects of the door frame. This would have meant having just two animations - one that animated both door1 and door2 to the open position at the same time, and a second which brought them both to the closed position. In future we'll use this method, but for now we're leaving this instruction post as-is, because this is the method that worked at the time!]


With our four animations in place, it's time to create an animation controller and bung some script in, to make the door open and close!

Selecting each  of the doors, we created an animation controller for them (we'll put the animations in place in a moment) then selecting the door frame (not the individual doors) we created a script and dropped this onto the (parent) door frame too.


Inside each door controller we repeated the same process. Select a door and then "Animator" (not animation) from the menu. Create a boolean parameter and call it isOpen. Create a blank, empty state, and make this the default

Next drag the appropriate open/closed animations into the animator window.
So if you've got door one selected in the scene, drop the door1_open and door1_close animations into the window. If it's door two you have selected, drop door2_open and door2_close in there.


Now our default state is "door is closed". So we want a transition from the default state to door1_open, when the boolean value isOpen is set to true. Click the default state (to select it) then right click, and select "make transition" before drawing a line to the door1_open state.

Click on the white arrow that appears between the two states, and from the properties panel, add a new condition - isOpen is true. This tells Unity that at any time we're in the default state, we can play the "door opening" animation whenever the boolean value is set to true (we'll do that later with a bit of scripting).


Now we need to create a transition from door1_open to door1_close. The condition for this is isOpen = false. This is because after we've set the isOpen value to true, the open door animation will play, and the "state machine" in the animator will remain in the "open" state. So when we've opened the door, Unity will keep monitoring the isOpen property and if it ever goes false (while the door is open) it will then play the door1_close animation.


Lastly, we make a transition from the _close back to the _open animation, any time the isOpen property ever goes true again. Once all this is in place, we repeat the whole lot all over again, for door2.

If you hit play at this point, nothing particularly exciting happens. In fact, nothing should happen at all. If your doors are opening and closing at this point, something has gone wrong (and you're probably feeling like we did for two days!) Let's write some code to make these things move!
In the door frame we placed a controller script. This needs editing now...

#pragma strict
var door1:GameObject;
var door2:GameObject;
var door1Anim:Animator;
var door2Anim:Animator;
var doorState:boolean;

function Start () {
   
     // loop through the children of this object (rather than just
     // use object.find which could return any matching name on the map!)
     // and get the two door components for this frame object.
     var allChildren = gameObject.GetComponentsInChildren(Transform);
     for (var child in allChildren) {
          var s:String=child.name;
          if(s=="Doors_02A"){ door1=child.gameObject; }
          if(s=="Doors_02B"){ door2=child.gameObject; }
     }

     doorState=false;   
     door1Anim = door1.GetComponent(Animator);
     door2Anim = door2.GetComponent(Animator);
}

function Update () {

}

function openDoor(b:boolean){
     door1Anim.SetBool("isOpen",b);
     door2Anim.SetBool("isOpen",b);   
     doorState=b;
}

This little script runs as soon as the door frame object is created at "runtime".
It basically gets a reference (pointer) to the child door objects, and the animator objects that control their animations.

The openDoor function is a publicly accessible function - it's going to be called by our main controller in a minute - and can accept either true or false; Whichever value is sent into this function is passed to the two door controller objects. If the door is in either its default position, or the closed position, we created a transition to play the open animation, whenever the isOpen parameter goes true.

Similarly if the door has played the open animation, it plays the closed animation whenever the isOpen parameter is true. Any other combination of true/false is ignored (so if the door is open and the function openDoor(true) is called, nothing happens - you've tried to set the door to open, and it's already open, so it is correct to ignore this request).

So now all we really need to do is to create a script to allow us to call the openDoor function on the doorframe...


There are probably a hundred ways you can do this. We like to create a new, blank gameObject and call it "mainController" and add a script to this. It just makes it easier to keep everything in the same sort of place, once the project gets a little larger (and a bit more unwieldy).

In our main controller script, we just place a couple of buttons on the screen so we can call the openDoor function. In reality, our game logic will be making all kinds of decisions and deciding which doors need to open and close. But for testing our animations, this will do for now.

#pragma strict

function Start () {

}

function Update () {

}

function OnGUI(){
     if (GUI.Button (Rect (10,10,150,30), "doors open")){
          var d:GameObject=GameObject.Find("Gate_02");
          d.GetComponent.<door_controller>().openDoor(true);
     }
   
     if (GUI.Button (Rect (10,50,150,30), "doors close")){
          var e:GameObject=GameObject.Find("Gate_02");
          e.GetComponent.<door_controller>().openDoor(false);
     }
}

And that's it!
Marvel at your amazing sliding doors


One last little gotcha - if your doors open then flip shut, then start opening again, make sure you haven't got the "loop" option ticked in the door1_open, door1_close, door2_open, door2_close animations.


For added authenticity, you can add in a "hydraulic swoosh" sound, as the doors open and close. But that's probably a bit much for one night. For now we're just thrilled that we managed to understand enough about the crazy visually-based Unity editor to get some doors to open and close!

Good luck.......