Moonwalking

From Game Editor

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

Moonwalking is an annoying 'glitch' that occurs in movement codes. The same concept can occur in many other forms, but is most common and blatant in movement. It occurs due to openings in the code, as not all possible routines are programmed. Examples of moonwalking are, walking backwards, standing while walking, essentially doing any wrong animation. There are many ways to solve moonwalking, because there are many forms of this problem. One way is to implement all scenarios into the code, this is the most common fix. Others arguably are the same, but they determine what they can't do differently. Using state notifications so that the actor can only be doing one thing, depending on the last set of state. Another method is setting up a check to see if the actor is currently busy with an action, preventing further actions.

Simplest Method

Create a variable integer named canjump; When the jump button is pressed, use this script:

 yvelocity -= 10 * canjump;
 canjump = 0;

Then, on player collision with ground, use

 canjump = 1;

In this way you only get one jump per collision with ground. If you press the button after that, canjump will be 0 and yvelocity won't change.

All Possible

This uses a function, but the code does not need to go solely in a function, it can also be within the draw script (inserting code into function instead of calling function from draw). To use this method, insert this code in global code. Then in the actor's draw script, call movement.

void
movement()
{
    char * key = GetKeyState();
    if(key[KEY_RIGHT]==1 && key[KEY_LEFT]==0)
    {
        x+=5;
        dir=1;
        ChangeAnimation("Event Actor", "player_r_run", NO_CHANGE);
    }
    if(key[KEY_LEFT]==1 && key[KEY_RIGHT]==0)
    {
        x-=5;
        dir=-1;
        ChangeAnimation("Event Actor", "player_l_run", NO_CHANGE);
    }
 
    if(key[KEY_RIGHT]==0 && key[KEY_LEFT]==0 || key[KEY_RIGHT]==1 && key[KEY_LEFT]==1)
    {
        switch(dir)
        {
            case -1:
            ChangeAnimation("Event Actor", "player_r_stand", NO_CHANGE);
            break;
 
            case 1:
            ChangeAnimation("Event Actor", "player_r_stand", NO_CHANGE);
            break;
        }
    }
}

This is only for simple movement, left and right. It does not prevent moonwalking for jumps or any other actions. What this code does is pose all possible circumstances for the combination of left and right. This way there are no gaps in logic that the computer will slip on. To utilize this method, make sure to determine most, if not all combinations of trigger events, keydown and keyup.

Changing to 4 keys of directional input will increase the complexity of the code, and if you allow for diagonal movement, even more so. Here is an example using mainly all combinations for the keydown events. 8-Way Movement

States

State Method This is very similar to the previous method, except it is regulated through a single variable. Using the one variable will resemble the natural flow of events. An int STATE can be used to know what the actor is doing at any given time, like a keydown event. The idea being that the actor will only do one event at a time, and if you have a stable regulation of this variable, then there should be no moonwalking. This method does is more useful in more complex code. When you start to add jumping, attacking, and other animations, then the benefits of states would show. This method must be thought out in advance or have a method of adding new states, lest it get extremely convoluted. Know that this method may involve more coding depending on how thoroughly you regulate the states.


A small example lay out of STATE. This method will have each possible event, ie. keydown right, left, down. In this, depending on what STATE is, the actor will do the right event. To add more, just add new left right STATE values to the end.

0 = face right

1 = face left

2 = run right

3 = run left

4 = duck right

5 = duck left

6 = crawl right

7 = crawl left


player -> KeyDown event -> Right (Disabled) -> Script Editor

switch(STATE)
{
    case 0:
    ChangeAnimation("Event Actor", "player_r_run", FORWARD);
    STATE = 2;
    break;
    case 1:
    ChangeAnimation("Event Actor", "player_r_run", FORWARD);
    STATE = 2;
    break;
    case 2:
    break;
    case 3:
    ChangeAnimation("Event Actor", "player_r_stand", FORWARD);
    STATE=0;
    break;
    case 4:
    ChangeAnimation("Event Actor", "player_r_crawl", FORWARD);
    STATE=6;
    break;
    case 5:
    ChangeAnimation("Event Actor", "player_r_crawl", FORWARD);
    STATE=6;
    break;
    case 6:
    break;
    case 7:
    ChangeAnimation("Event Actor", "player_r_duck", FORWARD);
    STATE=4;
    break;
}

Notice how each possible event is layed out, but unlike the first way, it is using a variable to represent all possible. This in some ways is easier to show, the only disadvantage is the many different events needed, though it is possible to do all of this through one event, like the previous.


player -> KeyDown event -> Left (Disabled) -> Script Editor

switch(STATE)
{
    case 0:
    ChangeAnimation("Event Actor", "player_l_run", FORWARD);
    STATE = 3;
    break;
    case 1:
    ChangeAnimation("Event Actor", "player_l_run", FORWARD);
    STATE = 3;
    break;
    case 2:
    ChangeAnimation("Event Actor", "player_l_stand", FORWARD);
    STATE=1;
    break;
    case 3:
    break;
    case 4:
    ChangeAnimation("Event Actor", "player_l_crawl", FORWARD);
    STATE=7;
    break;
    case 5:
    ChangeAnimation("Event Actor", "player_l_crawl", FORWARD);
    STATE=7;
    break;
    case 6:
    ChangeAnimation("Event Actor", "player_l_duck", FORWARD);
    STATE=5;
    break;
    case 7:
    break;
}


player -> KeyDown event -> Down (Disabled) -> Script Editor

switch(STATE)
{
    case 0:
    ChangeAnimation("Event Actor", "player_r_duck", FORWARD);
    STATE = 4;
    break;
    case 1:
    ChangeAnimation("Event Actor", "player_l_duck", FORWARD);
    STATE = 5;
    break;
    case 2:
    ChangeAnimation("Event Actor", "player_r_crawl", FORWARD);
    STATE=6;
    break;
    case 3:
    ChangeAnimation("Event Actor", "player_l_crawl", FORWARD);
    STATE=7;
    break;
    case 4:
    break;
    case 5:
    break;
    case 6:
    break;
    case 7:
    break;
}


These established any incoming events that direct movement, now we need reset events, the keyups.

player -> KeyUp event -> Right -> Script Editor

switch(STATE)
{
    case 0:
    break;
    case 1:
    break;
    case 2:
    ChangeAnimation("Event Actor", "player_r_stand", FORWARD);
    STATE=0;
    break;
    case 3:
    break;
    case 4:
    break;
    case 5:
    break;
    case 6:
    ChangeAnimation("Event Actor", "player_r_duck", FORWARD);
    STATE=4;
    break;
    case 7:
    break;
}

player -> KeyUp event -> Left -> Script Editor

switch(STATE)
{
    case 0:
    break;
    case 1:
    break;
    case 2:
    break;
    case 3:
    ChangeAnimation("Event Actor", "player_l_stand", FORWARD);
    STATE=1;
    break;
    case 4:
    break;
    case 5:
    break;
    case 6:
    break;
    case 7:
    ChangeAnimation("Event Actor", "player_l_duck", FORWARD);
    STATE=5;
    break;
}


player -> KeyUp event -> Down -> Script Editor

switch(STATE)
{
    case 0:
    break;
    case 1:
    break;
    case 2:
    break;
    case 3:
    break;
    case 4:
    ChangeAnimation("Event Actor", "player_r_stand", FORWARD);
    STATE=0;
    break;
    case 5:
    ChangeAnimation("Event Actor", "player_l_stand", FORWARD);
    STATE=1;
    break;
    case 6:
    ChangeAnimation("Event Actor", "player_r_run", FORWARD);
    STATE=2;
    break;
    case 7:
    ChangeAnimation("Event Actor", "player_l_run", FORWARD);
    STATE=3;
    break;
}


Many of the cases are empty, this means that nothing should change. A good example is the key up event of down. When down is pressed, you should still be ducked, but when you let go, you should stand, so the cases are only filled when the state is some form of duck state, 4-7. The last part is actually determining movement, in Draw. This will also use the states to determine what type of movement.


player -> DrawActor -> Script Editor

switch(STATE)
{
    case 0:
    break;
    case 1:
    break;
    case 2:
    x+=5;
    break;
    case 3:
    x-=5;
    break;
    case 4:
    break;
    case 5:
    break;
    case 6:
    x+=5;
    break;
    case 7:
    x-=5;
    break;
}


States can be directed partly by which animation is currently being run, as most actions will change an actor's animation. To utilize this, instead of a variable STATE, you can use animindex. Using this in combination with getAnimIndex will allow for easy reference to the current state of the actor. This code checks what the current animation is and checks if it is the same as your desired animation.


if(animindex == getAnimIndex("AnimationName"))
{
    // Code
}

animindex could be used instead of the STATE within the switch, but this requires stable animindex, and familiarity with every animation. Using this if statement will allow for easy regulation. This method is similar to the next way of preventing moonwalking, and as stated previous, they are usually combined to run as smoothly as possible. To see this method combined with others in a more complex moving system, 8-Way Movement.

Busy

This style utilizes concepts from the first, but uses a sub state system. This is not necessarily the best, but it takes ideas from each. It uses the constant flow of events like in the first, mapping out most possiblities, unlike the first it does not need every possiblity mapped. This style, depending on extremity, would need barely any events mapped. The events would actually just call for a single conditional. It does 'check' all possiblities, by setting states of each event. So each trigger event would check all sub states. Examples of this would be jump. A variable would be used to tell when an action is being done. You could say that this is STATES, but with multiple variables, but it does differ slightly. A common form of antimoonwalking uses animationfinish. It is possible to use this variable to determine the current state of affairs, only allowing a new action after the current animation finishes.

The main idea is that when you trigger jump, the jump variables turns on, so if you try to duck, assuming that is only possible when standing or running on the ground, it would check if the actor is jumping, and forbid the action. This method can be less organized and still work pretty well. This is a short example just using jump. A problem with this method is the resulting need of regulation for the given states. This can be a problem, but the more common states would reset on AnimationFinish, hence the stand alone method, and collision with the ground.

player -> KeyDown event -> z (Enabled) -> Script Editor

if(jump==0 && attack==0)
{
    ChangeAnimation("Event Actor", "player_attack", FORWARD);
    attack=1;
}

player -> AnimationFinish -> player_attack -> Script Editor

attack=0;


Now, this will prevent repeat attacking, and attacking while in air. You can use these states to determine combination events, but at that point it would be better to switch to STATES. Now to prevent jumping while attacking;


player -> KeyDown event -> Space (Enabled) -> Script Editor

if(jump==0 && attack==0)
{
    ChangeAnimation("Event Actor", "player_jump", FORWARD);
    yvelocity-=10;
    jump=1;
}


player -> Collision -> ground -> Script Editor

jump=0;


This demonstrates the main idea of sub states, to telling the actor when it can change events. Again, all of these forms of antimoonwalk code have the same idea, but are implemented slightly differently. By allowing different numbers for attack, or any other sub state, you can alter what happens during reset, allowing for combos and other events. Understanding how you are preventing moonwalking, or any code implemented within your game will allow you to change, and improve your game. Mixing all of these or implementing them in ways not described here can allow for many more possibilities.