State Method

From Game Editor

Revision as of 01:52, 10 August 2011 by Skydereign (Talk | contribs)
(diff) ← Older revision | Current revision (diff) | Newer revision → (diff)
Jump to: navigation, search

When making games with gameEditor, new users will often run into problems managing an actors animations. This manifests itself in a bug called moonwalking. The reason for this particular bug is that the actor is being told to do different things at the same time. So in short, by trying to do both it will mess up. And of course, the most obvious display of this is through animations.


There are many ways of fixing this problem, and all of them use some form of variables. In this, we'll be going through the state method. This uses a single variable to hold exactly what the actor is doing, and uses that to determine what it will do.


If not already familiar with variables, they are a way for the computer to store bits of data, such as numbers, characters, and similar. Chances are you have already learned to use some variables that already exist in gE, such as x and y. Variables can be changed in Script Editor, as shown in some of the starting tutorials. In this method, we'll need to create a single int variable, named state.


CREATING VARIABLES


Before we start creating the core of our player actor, we should first get an idea of what the state method is. The state method uses a single integer (state) to hold what the actor is currently doing. Since an int variable holds an integer value (whole numbers), we can use it to do exactly that, store the current state of the actor. So, when state equals zero, it means the actor is doing one thing, and when it equals one, it is doing another. Usually when you use this method, you want to map out what values of state will hold what actions the actor is taking. For example:

state = 0 standing facing right state = 1 standing facing left state = 2 running facing right state = 3 running facing left

That is a pretty straight forward state breakdown for a simple platformer (sadly no jump yet). Anyway, you'll notice that there is a value for right, and a value for left. It is a handy trick to keep any animations or actions where the actor faces right to be even, while left facing actions are odd. The reason may become more obvious later. Anyway, just to clarify, we have an integer variable called state, that holds any number we want it to. In this case we will be having it equal any integer 0-3. When we set it to 0, that means the actor is standing facing right, while if we set it to 2, that means the actor is running facing left.


With that in mind, we can now use this variable in script to determine what the actor is doing, and tell gameEditor exactly what we want the actor to do (given the current state of the actor). This is accomplished with a switch statement. A switch statement takes an integer, and allows you to run blocks of code depending on what the value of the integer was. For example:

switch(var)
{
   case 0:
   r=255;
   g=0;
   b=0;
   break;

   case 1:
   r=255;
   g=255;
   b=255;
   break;
}

So what the above code does is takes the integer var, and runs certain bits of the code, depending on what var equals. If var equals zero, it will turn the actor red, but if it equals one, it will set it to its normal color. Of course, if it is neither, for instance 5, it will do nothing. The switch statements use cases to determine what code to run, and break statements to tell it to stop. In short, the switch statement checks the value of var, finds the case equal to that value, and executes code until it reaches a break point. So in the above example, if var equals one, then it jumps to the line "case 1:" and runs the code until it reaches break. As soon as it hits the break, it gets out of the switch statement. If you are familiar with if statements, a switch acts sort of like a bunch of if/else if statements chained together.


Two other little tricks when using a switch statement worth mentioning include, you can have multiple case statements per break statement. For example:

switch(var)
{
   case 0:
   case 2:
   case 4:
   r=255;
   g=0;
   b=0;
   break;

   case 1:
   case 3:
   case 5:
   r=255;
   g=255;
   b=255;
   break;
}

Unlike the first example, this one will turn the actor red if var is equal to zero, two, or four. And similarly, it will turn it back to normal if var is equal to one, three, or five. This technique allows you to not have to duplicate blocks of code when you want certain cases to do the same thing. You can think of it kind of like using or (||) in an if statement.

The other little trick is the keyword default. If you want some code to trigger only when var is equal to zero, but want some other code to trigger otherwise (like an if/else) you can use default. Normally you would have to write out each case statement, which would be impossible. Luckily you can use default. For example:

switch(var)
{
   case 0:
   r=255;
   g=0;
   b=0;
   break;

   default:
   r=255;
   g=255;
   b=255;
   break;
}

So, as stated above, this will turn the actor red when var is equal to zero, but set the colors to normal if var is any other color. This can be very handy when dealing with lots of possible values of the var (or soon to be, state).

Anyway, by using a switch statement on our state variable, we can execute bits of code depending on what the actor's current state is. Using our previous state values (0=stand right, 1=stand left, 2=run right, 3=run left), we can show a quick example of how it might be set up.

player -> Key Down Right (repeat enabled) -> Script Editor

switch(state)
{
   case 0:
   ChangeAnimation("Event Actor", "run_right", FORWARD);
   state=2;
   break;

   case 1:
   ChangeAnimation("Event Actor", "stand_right", FORWARD);
   state=0;
   break;

   case 2:
   x+=10;
   break;

   case 3:
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;
}

While the above code might intimidate some beginners, it is amazingly straight forward. What it does is it checks what state is equal to do, and runs code according to what state equals. Remember, we have determined what each value of state should mean, so we can use it to determine what the player should do. This particular event is the keydown right event, normally used for movement. The first case is case 0. This code executes when state is equal to zero, more importantly, when the player is standing facing right. And so, if the actor is standing facing right, we would want the actor to start running. This is accomplished by changing the player's animation so at least it looks like it is moving. But, one of the most important parts is the next line, "state=2;". If you remember, the state variable is supposed to hold a value according to what the actor is currently doing. So whenever the state of the actor changes (in this case from standing to running), we need to change the state to reflect the new state. From our chart in the beginning, we said that running right would be symbolized by state equaling 2. The next line is a break statement, telling gameEditor that it is done with the switch statement.


The next case (case 1) is run if at the time the actor is standing facing left (since that is the value we decided to use). Since the event is still the keydown right event, if the actor is facing the other direction, it'd make sense for it to turn you around. Because of that, we call ChangeAnimation to set the actor face standing right. Now, again one of the most essential parts to the state method, since we changed the state of the actor, we need to update the state variable (in this case from standing left to standing right). That means state should equal to zero, as we are treating that as the actor is standing right.


case 2 is triggered when state is equal to 2, which in our case means the actor is running facing right. Since this is keydown right (the event that makes the actor run), we need to actually move the actor. Now, you'll notice that this case doesn't have a ChangeAnimation. This is because if state is equal to 2, then the player is already running. Therefore we can skip setting its animation to run, and we can skip changing the state, as the player's state hasn't changed (it is still running). Now this is why all we have in case 2 is increasing x by 10 (or whatever speed your player actor moves at).


The last case (case 3) is pretty self-explanatory if you understand what is happening in the other three cases. But just for recap, case 3 is the value of state that means the player is running left. Again this code is the keydown right event, so if you were running and the other directional key was pressed, normally the player would stop. In this case, the player does exactly that. It changes the animation to stand left, and of course changes the state to equal 1 (which we know to be the stand left). And with that, our simple script is done. The following is the above code with some comments to explain things if you are still having trouble.

player -> Key Down Right (repeat enabled) -> Script Editor

switch(state) // execute bits of code depending on what state equals
{
   case 0: // run code if state is equal to 0 (if the actor is standing facing right)
   ChangeAnimation("Event Actor", "run_right", FORWARD); // change the animation to run right
   state=2; // set state so it knows the player is running right
   break; // get out of the switch statement

   case 1: // run code if state is equal to 1 (if the actor is facing left)
   ChangeAnimation("Event Actor", "stand_right", FORWARD); // change the animation to stand right
   state=0; // set state so it knows the player is now standing facing right.
   break; // get out of the switch statement

   case 2: // if the actor is running right
   x+=10; // move the actor
   break; // get out of the switch statement

   case 3: // if the actor is running left
   ChangeAnimation("Event Actor", "stand_left", FORWARD); // stop running and stand facing left
   state=1; // set state so it knows that the actor is now standing left.
   break; // get out of the switch statement
}


By now you should have picked up that making sure to change state whenever you change the animation is pretty important. Since it is good policy (for the player actor) to have a single value of state set aside for each animation the player has, whenever you change the animation of the player, you should also be changing the state. In the above example, each time we changed the animation we changed the value of state to reflect the new animation. If we didn't do this, then every time we use that code, it will think that the actor is standing right (as state will always equal zero).


Now that we've covered the basics, we'll begin with implementing the state method on a simple platformer player actor. As always, you should write out what states the actor will be in, and map them to certain values of state.

state = 0 standing facing right state = 1 standing facing left state = 2 running facing right state = 3 running facing left state = 4 jumping facing right state = 5 jumping facing left

This is very similar to the state breakdown we used above, but this time we added jump states. When state is equal to four, that means the player actor is jumping facing right, and if it equals five, it is jumping facing left. To recap state is an integer variable, which can only hold one value at a time. Depending on what value state is holding, gameEditor will know what the player is doing, and in turn you can tell it exactly what to do given what the player is already doing.

It is usually a good idea to start with the movement events. When you have created your player actor, add a keydown right event (with repeat enabled). In it, add the following code.

player -> Key Down Right (repeat enabled) -> Script Editor

switch(state)
{
   case 0: // if standing right
   ChangeAnimation("Event Actor", "run_right", FORWARD);
   state=2;
   break;

   case 1: // if standing left
   ChangeAnimation("Event Actor", "stand_right", FORWARD);
   state=0;
   break;

   case 3: // if running left
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;

   case 2: // if running right
   case 4: // or jumping right
   case 5: // or jumping left
   x+=10;
   break;
}

As you can see I've inserted comments after each case statement. This is helpful to do as it tells you what state the actor is in if the following code is triggered. To summarize a bit of what the code is doing, if the player is standing facing right, and you press the key right, then it will make the player run, and set its state to run. Now if you are facing left when you press right, it will make the player change direction (so change animation to stand_right and set its state to stand right). Now, if the player is running to the left, when you press the opposite direction (right), you should stop. case 3 does exactly that. Now, if the player is running right, jumping right, or jumping left, when you press right, the actor should move.

A lot of problems beginners run into is that when their player actor jumps and then they try to move, it will change the actor's animation to a running animation, even though they are in the air. Our keydown right event above automatically fixes this. If you note, the jumping states (and if that isn't clear by now, that means when the player is jumping), it does not change the animation to run. All pressing right down does during those states is move the actor.

Anyway, you'll notice that the keydown right event we just wrote, covers all possible states the player actor can be in. Because we tell the actor exactly what to do in ever case, this will fix many of the things that cause moonwalking. As was said before, moonwalking is caused by multiple events happening at the same time, mixing together for example jump and run. Or, even run in one direction, and running in the other (which would usually have the actor have the running animation while not moving).

Onto the keydown left event. The following code is essentially the same thing as the keydown right code except it flips the directions. So when you are standing right, instead of making you run, it flips your direction, and when you are running right, it stops you. That's just because this key event is the opposite of the other key event.

player -> Key Down Left (repeat enabled) -> Script Editor

switch(state)
{
   case 0: // if standing right
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;

   case 1: // if standing left
   ChangeAnimation("Event Actor", "run_left", FORWARD);
   state=3;
   break;

   case 2: // if running right
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;

   case 3: // if running left
   case 4: // or jumping right
   case 5: // or jumping left
   x-=10;
   break;
}

At this point you can try it out, and you should be able to move back and forth. You'll quickly notice though that when you let go of a key, you'll not stop running. This problem is due to us not adding a keyup event that stops the actor from running. Here's what the keyup right will look like.

player -> Keyup Right -> Script Editor

switch(state)
{
   case 2: // if running right
   ChangeAnimation("Event Actor", "stand_right", FORWARD);
   state=0;
   break;
}

Very straight forward. Now the reason we only have one case, is that really releasing the right key (keyup right) only really should do one thing. And that is, if you were running, stop running. We can tell it to do this, as that code will only trigger if state is equal to 2, and when it does, it changes the player's animation to stand_right, and then adjusts the state to stand right. Now just to clarify, releasing the key right should not do anything except reset the player's animation if it was running. If you are running left, releasing right shouldn't do anything. Same for the jump states, and the two stand states (in the case of stand right, you are already standing right).

By now you probably already know what to do for keyup left, but in case you don't, here it is.

player -> Keyup Left -> Script Editor

switch(state)
{
   case 3: // if running left
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;
}

This works exactly like the keyup right event, so as before, the only time releasing left will do anything, and should do anything, is when you are running left. In that case, the player stops running left. Very simple.

Testing in game mode should reveal that we fixed the problem we experienced last time. You can try tripping up the game by moving back in forth in weird ways to see if it will cause the player actor to moonwalk, but it won't. So in essence we have fixed a couple of major moonwalking bugs. But, onto making this an actual platformer.

To do this, we need to create a ground actor. In this case I'd create a tile actor.

ADDING A TILE ACTOR

Now that we've set up a tiled actor to be the floor and walls of our game, we should add gravity. To do this, add a draw event to the player actor. All it needs to do is increase yvelocity by one, so just the following. player -> Draw Actor -> Script Editor yvelocity++;

Before testing again, we should add a collision event with the ground. Of course, when dealing with collisions, there are several bugs if you just use a single collision event, so we'll bypass these by adding several collision events. To start, we'll add the top side collision. This collision will have a PhysicalResponse call to prevent the player from falling through, and it will also reset the player's animation when they collide.

player -> Collision with top of tiles (repeat enabled) -> Script Editor PhysicalRespons(MOVE_EVENT_ACTOR_ONLY, USE_CALCULATED_MASS, 1.00, 1.00, 0.00, 0.00);

switch(state)
{
   case 4:
   ChangeAnimation("Event Actor", "stand_right", FORWARD);
   state=0;
   break;

   case 5:
   ChangeAnimation("Event Actor", "stand_left", FORWARD);
   state=1;
   break;
}

The first line will prevent the player from falling through the ground, and the rest of the code is devoted to setting the player's stand animation. One thing to notice is that in this event I did not put the comments that told us what the case statement was acting from (// if jumping right). While it is always a good idea to add those to your switch statements (and comment your code in general), you should still remember that those comments are not necessary and are not what tell gE what each state is tied to each value. What determines what each state stands for, is you and your planning. All gE does is follow your instructions.

Anyway, you can now test it out, and it should work and you now have gravity. Of course you still can't jump, so we'll add that (otherwise collisions don't really mean much). One thing to note is that you can't jump while already jumping, but with this state breakdown, you can jump in pretty much any other state. So if you are feeling confident about what to do go ahead and try it without looking at the following event, but if you need to check or are still figuring it out, here's the jump event.

player -> Keydown Space (repeat disabled) -> Script Editor

switch(state)
{
   case 0: // if standing right
   case 2: // if running right
   yvelocity=-15;
   ChangeAnimation("Event Actor", "jump_right", FORWARD);
   state=4;
   break;

   case 1: // if standing left
   case 3: // if running left
   yvelocity=-15;
   ChangeAnimation("Event Actor", "jump_left", FORWARD);
   state=5;
   break;
}

What the above event does is check if the player is currently standing or running, and if so makes the player jump. The player automatically knows what direction to make the actor jump in because cases 0 and 2 are the right facing states, and therefore if you jump you should be facing right. While, 1 and 3 are the left facing states, and of course you should jump facing left.

Now that we've added the jump event, we're pretty much done. All that is really left are the other collisions. These ones are pretty simple as well, and don't have any impact on the state method, since they don't change the player's state (all they do is prevent the player from flying through them). But, if you've worked on platformers before, you'll know that a simple PhysicalResponse event won't work with walls. If all you do is put that one function, your player will be able to stick to walls. As to make our game as top-notch as possible, we cannot let bugs like that go. And without further ado, here is the fix.

player -> Collision with left or right side of tiles (repeat enabled) -> Script Editor

double yvel = yvelocity;
PhysicalResponse(MOVE_EVENT_ACTOR_ONLY, USE_CALCULATED_MASS, 1.00, 1.00, 0.00, 0.00);
yvelocity=yvel;

If you are new to variables the first line might not make sense to you. But, all it does is declare a new variable (one that can hold a number with a decimal) and set it equal to yvelocity. So, that line of code creates a variable to remember what the player's yvelocity is before the collision. That way, we can set yvelocity back to what it was after the collision. If you didn't know, the PhysicalResponse line bounces the player back slightly and zeros its xvelocity and yvelocity. So the reason the player actor would stick to the wall, is that when you collide with it, the PhysicalResponse sets your yvelocity to 0. With no yvelocity, the player won't fall, and there is your bug. This event however stops the actor from passing through the wall, and lets it continue falling at the same speed it was before.

And the simplest event for last. All the collision the player needs with the bottom of tiles is to stop it from passing through it. So, the simple PhysicalResponse code will work here.

player -> Collision with bottom of tiles (repeat enabled) -> Script Editor PhysicalResponse(MOVE_EVENT_ACTOR_ONLY, USE_CALCULATED_MASS, 1.00, 1.00, 0.00, 0.00);


And there you have a simple platformer using the state method. The game has no moonwalking and works quite nicely.

A general recap. The state method uses a single variable to track what the actor is currently doing. So if you're actor does several things, your state variable will have different possible values, each one representing a different action the actor can do. By using this, you can combine it with a switch statement to have complete control over an actor. When you go to implement the state method, you should make a list of all the possible states the actor can be in, that way you have a quick reference. If you are unsure if something is actually another state, it doesn't hurt to have more states, though the less states there are, the easier it is for you.

As a rule of thumb, any event that can change the actor's state should have a switch statement. That way you can tell gE exactly what the actor should do in ANY of the possible states that the actor can be in.

Another rule, this one far more important, is whenever you change the actor's animation, you will also be changing the actor's state. While there are exceptions to this rule, rarely do you break this, and when you do you should know exactly what you are doing. A more general way of saying this is, if the event is supposed to change the actor's state, then make sure to change the state. With the case of the player actor, every state had a different animation, but some actors you might use the state method on don't have any animation changes. It all depends.