Want to skip all the explanations and see an example of a simple program made with the Expression class? Scroll all the way to the bottom
Just as with the Expansion class, the Expression class assumes you have your character divided into parts, each of which is a MovieClip. Some good parts to always have are: eyes, mouth, arms and legs.
Each of the parts of the character has these properties: A set of animation frames, an X and Y position on the screen and a rotation property, that ranges between 0 and 360 degrees.
The parts of a character can be divided into sets of frames, each set of frames represents a looping animation.
Each part of the character can have one of these two types: Animated or Movable. Animated parts are ones that will jump directly to whichever set of frames corresponds to the animation you want to play. Movable parts are ones that will play a transitioning animation between one looping animation and another.
Animated parts are best used for things that change quickly, like a character's eyes and mouth. The frame loops can represent things like "open", "closed", "happy" and "sad".
Movable parts are best used for a character's arms or legs. A character's arm normally stays in one place, which we can consider a frame loop. Then, when the arm moves to a different place, we would use a transition animation between that frame loop and the next one. Frame loops would represent positions, like "up" or "down". Transition animations would represent motion, like "move up" or "move down".
In order for the program to properly animate Animated parts, each animated part is going to have to follow these rules:
this.gotoAndPlay(this.currentLabel);As you may have noticed, the code in the end of the loop just makes the MovieClip go back to the beginning of the loop.
In order for the program to properly animate Movable parts, each movable part is going to have to follow these rules:
this.gotoAndPlay(this.currentLabel);In this image, you can see an example of 3 frame loops, labeled "a", "b" and "c". Loop "a" goes from frame 1 to frame 5. Loop "b" goes from frame 6 to frame 10. "c" goes from frame 10 to frame 11.
"<name1>2<name2>", where "name1" and "name2" are the begining labels of the two frame loops you're trying to connect.this.gotoAndPlay(<name2>), where "name2" is the beginning label of the second frame loop.In this image, you can see one example of a part with 3 frame loops. They are labeled "a", "b" and "c". For each possible pair of frame loops, there's a transition animation. So we have 6 transitions in total: "a2b", "b2a", "a2c", "c2a", "b2c", "c2b".
At the end of each transition loop is a frame with the the "gotoAndPlay(<name>)" code. Here's a list of the values of "name" for each transition.
To use as examples, we'll have a character made of 4 MovieClips: eye1, eye2, mouth, arm1. The character has more parts, but they won't be animated, so they don't need to be named.
The following image shows the timeline of each MovieClip. eye1 and eye2 both have 2 frame loops: "closed" and "sad". The mouth has two frame loops: "happy" and "sad". The arm has two frame loops: "up" and "down". The arm also has two transition animations, labeled "up2down" and "down2up". Remember that the transition animations are necessary for movable items.
Having that ready, it's time to start writing code
The first thing to do it load the library using import expansion.*. Then, create a new Expression object, giving it the stage as the first parameter, just like with the Expansion object.
import expansion.*;
var character:Expression = new Expression(stage);
Now you have to insert the parts of the character into the Expression object. You can do this with the function addPart(). This function has 3 parameters.
The first parameter is the reference to the MovieClip to add.
The second one is a number indicating the type of part it is: Animated or Movable. The Expression class has two global constants you can use: Expression.ANIMATED and Expression.MOVABLE
The third parameter is a Boolean value named "eye". This last parameter determines that the part you're inserting is an eye. It will be explained later.
By default, if you use only the first parameter, it will consider it an ANIMATED part and not an eye. addPart() returns a value which is the ID number of the part you inserted. This number will be used to access the part in other functions.
Let's insert our 4 MovieClips
character.addPart(mouth); //Default parameters work in this case
character.addPart(eye1,Expression.ANIMATED,true);
character.addPart(eye2,Expression.ANIMATED,true);
character.addPart(arm1,Expression.MOVABLE);
You can get the ID number of a part by calling the getPartID() function, giving the MovieClip as a parameter. If you had the ID and not the MovieClip, you can use getPartAt() to get the MovieClip from the ID number.
var mouthID:int = character.getPartID(mouth);
var test:MovieClip = character.getPartAt(mouthID);
trace(test == mouth); //Output: true
You can remove a part from the Expression object by calling the removePart() function giving the MovieClip as a parameter, or you can call removePartAt() and give the ID instead.
var mouthID:int = character.getPartID(mouth);
character.removePart(eye1);
character.removePartAt(mouthID);
A state is the list of values all the parts of the character should have at one moment. Here's an example of one state: Her arm should be down, her eyes should be closed and her mouth smiling.
The Expression class uses an Array to represent each state. You can create each state entirely in your own, but the Expression class comes with two functions that can help you build your state.
The first function is addStateAttributes(). If you give it an empty Array, it will fill the Array with the values you gave it in the parameters. This function has 4 parameters.
The first parameter is the reference to the state you want to edit. The second parameter is "canBlink", and it tells you if the character will be able to blink in this state. In normal states, this should be true, but if she has her eyes closed, you wouldn't want that.
The third and fourth parameters are "frequency" and "offset". These two values are numbers that control her breathing rhythm. Her breathing follows a sine wave pattern. The "frequency" parameter indicates how many radians of the sine wave are completed in one frame. The "offset" number of a value between 0 and 1, which indicate how heavily she's breathing.
To make a new state and give it the global values, you can do this
var state:Array = new Array();
//The character can blink. She breathes slowly and deeply
character.addStateAttributes(state,true,0.1,1);
//The state now contains the canBlink, frequency and offset values.
If you use NaN as values for "frequency" and "offset", this state will not change the way the character is breathes
The second function to edit states is addPartToState(). This function will let you set all the properties of one of the parts for this state. It has seven parameters.
The first parameter is the reference to the state you want to edit. The second parameter is the ID number of the part want to control.
The third parameter is the name of the frame loop you want the part to go to. The fourth parameter is the rotation the MovieClip should have when this state is activated. The fifth parameter is the rotation speed: how fast the part will spin to get to that rotation. The last two parameters are the X and Y position relative to where the MovieClip was when it was added to the Expression object.
Here's an example of how to use both of these function together to create a state for the character
var state1:Array = new Array();
character.addStateAttributes(state1,false,0.1,1);
character.addPartToState(state1,character.getPartID(eye1),"closed",0,1,0,0);
//Actually, "0,1,0,0" are the default parameters.
//We don't have to put them in this function.
character.addPartToState(state1,character.getPartID(eye2),"closed");
character.addPartToState(state1,character.getPartID(mouth),"happy");
character.addPartToState(state1,character.getPartID(arm1),"down");
If you use NaN as values for the rotation, rotation speed, x position and y position, those values will not be affected when the character uses this state.
Once you have created a state, you can insert it into the Expression object. You can do that with the addState() function. This function has two parameters: The name of the state and the state Array. You can add as many states as you want.
//Continuing from the previous code
var state2:Array = new Array();
character.addStateAttributes(state2,true,0.3,1);
character.addPartToState(state2,character.getPartID(eye1),"sad");
character.addPartToState(state2,character.getPartID(eye2),"sad");
character.addPartToState(state2,character.getPartID(mouth),"sad");
character.addPartToState(state2,character.getPartID(arm1),"up");
//Add states
character.addState("expanding",state1);
character.addState("reverting",state2);
You can remove a state by calling removeState() and giving the state's name as a parameter:
character.removeState("reverting");
You can get a state from the Expansion object by calling getStateByName() and giving the name of the state. You can edit the returned state without having to insert it again with addState().
var eye1ID:int = character.getPartID(eye1);
var state:Array = character.getStateByName("expanding");
state[eye1ID][Expression.FRAME] = "sad";
//No need to re-insert it into the Expression object
//The change is already there
As seen in the code before, you can access the state of every single part by using its ID in the [] operator. What's more, you can then access each property of the part using a second [] operator. Each property has its own ID number. There are 5 in total:
Expansion.FRAME |
The name of the frame loop the part should be in. |
Expansion.ROTATION |
The rotation of the part. |
Expansion.ROTATION_SPEED |
The rotation speed of the part. |
Expansion.X_POS |
The horizontal position of the part compared to its starting point. |
Expansion.Y_POS |
The vertical position of the part compared to its starting point. |
You can also access the "canBlink", "frequency" and "offset" properties from the state Array like this:
state.frequency = 0.2;
state.offset = 0.5;
state.canBlink = true;
Just in case that was a bit confusing, in this diagram you can see the structure of each state Array:
At this point you have inserted all of the parts into the Expression object. Then you created the states and inserted them into the Expression object. You're ready to start controlling the states.
First call activate() to "start time". Just like with the Expansion object, you can start and stop time with this function anytime. Unlike the Expansion object, animated objects will continue to animate even when time is stopped. The flow of time only affects things like breathing, position and rotation.
Use setState() to make a character go to a specific state. Give the name of the state as a parameter.
character.setState("expanding");
And that's it. When you call this function, all of the character's parts will go to the positions given by the state. The animated objects will go to the frame loops, the movable objects will transition towards their frame loops. Any of the parts that were not specified in the state will not change.
You can use the state property to get the name of the state the character is currently in. You can use the curState property to get an Array representing the state the character is in.
trace(character.state);
var state:Array = character.curState;
Other than states, there are a few other values you can get from the Expression object:
state:String[read-only] |
Represents the character's current state. |
curState:Object[read-only] |
A copy of the character's current state Array. |
defaultState:String[read-only] |
Returns the name of the default state of the character. |
offset:Number[read-write] |
Represents how heavily the character is breathing. Recommended values are between 0 and 1. |
frequency:Number[read-write] |
Represents how fast the character is breathing |
angle:Number[read-only] |
The current angle of the sine wave that represents the character's breathing. |
position:Number[read-only] |
The sine of the angle mentioned above. |
canBlink:Boolean[read-write] |
Indicates whether the character is allowed to blink. |
blinking:Boolean[read-only] |
Indicates that the character is blinking. |
timer:int[read-only] |
Tells how many frames are left before a character stops reacting. |
reacting:Boolean[read-only] |
Indicates whether a character is reacting or not. |
interruptible:Boolean[read-only] |
Indicates whether a character can override reacting or not. |
So you've seen the "eye" parameter in the addPart function, and the "canBlink" property in the state object. You may already be guess what that means.
When you add a part to the Expression object with addPart() function, you have the option of declaring it as an "eye" by setting the last parameter to "true". Yes, a character can have anywhere between 0 and countless eyes. There's no limit.
character.addPart(eye1,Expression.ANIMATED,true);
Every time a frame passes, there is a 1/100 chance that the character decides to blink. When this happens, all of the character's eyes will blink at once. And they will all stop blinking when the first eye you inserted reaches the end of its blinking animation.
This means you have to set up the frames in each of your eyes according to a few rules:
this.gotoAndPlay("endblink").If all of your eyes are set up like that, congratulations. The character can blink any time! No more work needed.
You may have noticed that there are a lot of variables controlling the breathing of the character, but none of these variables are affecting any of the parts of the character. You may be wondering: What's it for?
Really, it's up to you what you want to do with the breathing. The Expression class gives you the raw data of a breathing character. Here's an example of one thing you can do with it:
Create a MovieClip named "chest" and animate it so that it's smallest on frame 1 and biggest on frame 30. This would be the character's chest. You can create an "enterFrame" function that runs this code:
var character:Expression; //We have this thing already set up
var chest:MovieClip; //The animated clip of the character's chest
stage.addEventListener("enterFrame",frame);
function frame(E:Event){
//character.position is a number between -1 and 1
//Divide it by 2 and shift it upwards by 1/2
var position:Number = character.position*0.5+0.5;
//Multiply it by offset to control how heavily the character breathes
//Multiply it by 30 so that it can get up to 30 frames big
var toScale:Number = position*character.offset*30;
//Get its integer value
var chestFrame:Number = 1+Math.floor(toScale);
//Animate a breathing chest
chest.gotoAndStop(chestFrame);
}
Think of other ways to use it. Maybe you can make an animated mouth just like with the chest. Or you can make the breasts of a female character move up and down by changing their X and Y positions based on the "position" and "offset" values
So far you have a character made of parts. And your character can have different states that control the parts. You could work with that already, but this library lets you do even more! Your character can react to different events without you having to manually program them.
The first thing you can do is call the react() function to make the character react to something. This function saves the character's current state, then it gives the character a new state for a short while. When the time is up, the character will take the old state it had before.
character.react("surprised!",30);
The first parameter of this function is the name of the state you want to switch to. The second parameter is the "timer" value, the number of frames you want to wait before returning to the old state. If you set the timer to -1, the character will stay with this reaction forever. You can stop the reaction at any time by calling the endReact() function.
character.react("surprised!",-1);
//Maybe you want to stop the reaction only after
//a special event has ocurred
belly.addEventListener("mouseDown",stopReacting);
function stopReacting(E:Event){
//This character only stops being surprised
//when you click her belly
character.endReact();
}
There are a few side effects of using reactions. While a character is reacting, setState() will not have any effect and the character will never blink. Both of these side effects were made to prevent conflicts between the current state and the stored state.
Besides being able to make the character react at any time, you can also make the character react to any event at all, by calling the addReaction() function.
This function takes any EventDispatcher at all as the first parameter, and the name of any event as the second parameter. The third parameter is the name of the state to go to when the event happens to the EventDispatcher object. The last parameter is how long the character should spend reacting after the event happened.
Here's how to make the character react to her belly being poked:
var belly:MovieClip; //This is the character's belly
//The character gets surprised for 30 frames
//when the user clicks her belly
character.addReaction(belly,"mouseDown","surprised",30);
You can even use it to monitor events completely unrelated to the character, like if a key was pressed on the keyboard
//The character gets happy when you press the keys
character.addReaction(stage,"keyDown","happy",30);
If you want to find out whether you have a certain reaction for a specific event, you can call getReaction(). The first parameter is the EventDispatcher object you want to check on, and the second parameter is the name of the event you want to get.
var reaction:Object = character.getReaction(stage,"keyDown");
This function returns an object containing these properties:
hit:EventDispatcher |
The EventDispatcher object waiting for an event. |
type:String |
The name of the event the EventDispatcher is going to trigger. |
state:String |
The name of the state the character will go to when the event is triggered. |
timer:String |
The time the character will spend reacting after the event has been triggered. |