Solved How to make a brake animation after run animation?

Question that is answered or resolved.

hbdhl

Active member
I'd like to create a character with a lot of inertia:
The character runs fast, and when the player releases the arrow keys, the character slides forward slightly, like skating on ice.
 
Solution
I liked how it turns out. Question: do we need to use a freespecial for that? Can't we use the native run?
I don't want to use many cancels to use runattack, run jump, etc.
Now the code is using the native run but with a follow animation for the brake frames.
Basically the script will preserve the current speed moment for both X/Z axis during the brake animation and allow it to be gradually reduced.

Animations
Code:
anim idle
    loop    1
    delay    16
    offset    127 179
    bbox    117 109 19 72
    @cmd animVar "runFlag" 1 openborconstant("ANI_FOLLOW15") #CHANGE TO THE BRAKE ANIMATION IF THE RUNFLAG IS 1
    frame    data/chars/heroes/blaze/sor2/idle00.png
    frame    data/chars/heroes/blaze/sor2/idle01.png
    frame...
After referring to your project, I feel like doing this:
1. A keyloop script.
2. The Run animation has running frames and braking frames, when the keyloop script is executing, only loop play the running frames.
I think the Run animation will not work because the character will go to Idle as soon as you release the directional keys. You can maybe try a keyscript to detect a directional key released and force a transition between the Run animation and possibly a Follow animation that contains the brake frames.

Then, you can insert a @cmd dasher in every frame of the Follow animation which gradually reduces the character's speed, I'm using it in some dash attacks.

Code:
    @cmd dasher 1 0 0
    frame    data/chars/bosses/bongo/bkm/fire02.png
    @cmd dasher 0.5 0 0
    frame    data/chars/bosses/bongo/bkm/fire03.png
    @cmd dasher 0.1 0 0
    frame    data/chars/bosses/bongo/bkm/fire04.png

Code:
//DETECT LEFT/RIGHT KEYS RELEASED
if(playerkeys(iPIndex, 2, "moveleft") || playerkeys(iPIndex, 2, "moveright")){
    doSomething();
}

Code:
void dasher(float Vx, float Vy, float Vz)
{//Dash with desired speed!
    void self    = getlocalvar("self");
    int dir        = getentityproperty(self,"direction");

    if(dir == 0){Vx = -Vx ;}
    changeentityproperty(self, "velocity", Vx, Vz, Vy);
}
 
I think the Run animation will not work because the character will go to Idle as soon as you release the directional keys. You can maybe try a keyscript to detect a directional key released and force a transition between the Run animation and possibly a Follow animation that contains the brake frames.

Then, you can insert a @cmd dasher in every frame of the Follow animation which gradually reduces the character's speed, I'm using it in some dash attacks.

Code:
    @cmd dasher 1 0 0
    frame    data/chars/bosses/bongo/bkm/fire02.png
    @cmd dasher 0.5 0 0
    frame    data/chars/bosses/bongo/bkm/fire03.png
    @cmd dasher 0.1 0 0
    frame    data/chars/bosses/bongo/bkm/fire04.png

Code:
//DETECT LEFT/RIGHT KEYS RELEASED
if(playerkeys(iPIndex, 2, "moveleft") || playerkeys(iPIndex, 2, "moveright")){
    doSomething();
}

Code:
void dasher(float Vx, float Vy, float Vz)
{//Dash with desired speed!
    void self    = getlocalvar("self");
    int dir        = getentityproperty(self,"direction");

    if(dir == 0){Vx = -Vx ;}
    changeentityproperty(self, "velocity", Vx, Vz, Vy);
}
I was just thinking about this:After detecting the release of the arrow left/right keys, jump to the animation follow{xx}.
But “the character will go to Idle as soon as you release the directional keys”... Mr Kratus, thank you!

I have another idea:
Instead of using ‘anim run’, add a ‘com f f freespecial1’. then make this ‘anim freespecial1’play running & braking frames.
 
Yeah, it worked better than I thought! Very very close to the native Run feature, you had a good idea.
Here's all the codes I used in the video.

Code:
com f f    freespecial15

Code:
anim freespecial15
    loop    0
    delay    10
    offset    127 179
    bbox    125 113 17 68
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/heroes/blaze/sor2/run00.png
    @cmd keyMove 2 1 6
    @cmd keyFlip
    @cmd sound "data/sounds/sorx_run.wav"
    frame    data/chars/heroes/blaze/sor2/run01.png
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/heroes/blaze/sor2/run02.png
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/heroes/blaze/sor2/run03.png
    @cmd keyMove 2 1 6
    @cmd keyFlip
    @cmd sound "data/sounds/sorx_run2.wav"
    frame    data/chars/heroes/blaze/sor2/run04.png
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/heroes/blaze/sor2/run05.png
    @cmd keyLoop "moveleft" 0
    @cmd keyLoop "moveright" 0
    @cmd keyFlip
    @cmd dasher 2 0 0
    frame    data/chars/heroes/blaze/sor2/idle00.png
    @cmd dasher 1 0 0
    frame    data/chars/heroes/blaze/sor2/idle01.png
    @cmd dasher 0.5 0 0
    frame    data/chars/heroes/blaze/sor2/idle02.png
    @cmd dasher 0.1 0 0
    frame    data/chars/heroes/blaze/sor2/idle03.png

Code:
void keyMove(float xVel, float zVel, int brakeFrame)
{//Move entity if direction button is pressed
 //Use the brakeFrame to define to what frame the entity will jump when all keys are released in order to start the brake
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex");
    float xdir    = 0;
    float zdir    = 0;

    if(xVel != 0){
        if(playerkeys(iPIndex, 0, "moveleft")){
            xdir = -xVel;
        }
        else
        if(playerkeys(iPIndex, 0, "moveright")){
            xdir = xVel;
        }
        else
        {
            updateframe(self, brakeFrame);
        }
    }
    else
    {
        xdir = NULL();
    }
 
    if(zVel != 0){
        if(playerkeys(iPIndex, 0, "moveup")){
            zdir = -zVel;
        }
        else
        if(playerkeys(iPIndex, 0, "movedown")){
            zdir = zVel;
        }
    }
    else
    {
        zdir = NULL();
    }

    changeentityproperty(self, "velocity", xdir, zdir);
}

void keyFlip(int opposite)
{//Change entity's facing direction if left or right is pressed
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex"); //Get player index

    if(opposite == NULL()){
        if(playerkeys(iPIndex, 0, "moveleft")){changeentityproperty(self, "direction", 0);}else
        if(playerkeys(iPIndex, 0, "moveright")){changeentityproperty(self, "direction", 1);}
    }
    else
    {
        if(playerkeys(iPIndex, 0, "moveleft")){changeentityproperty(self, "direction", 1);}else
        if(playerkeys(iPIndex, 0, "moveright")){changeentityproperty(self, "direction", 0);}
    }
}

void keyLoop(void key, int frame, void var, void value)
{//Loops defined frame if defined key is held
 //Use "var" if you want to define a variable requirement, otherwise let it empty
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex");

    if(var != NULL()){
        if(getglobalvar(var) == value){
            if(playerkeys(iPIndex, 0, key)){updateframe(self, frame);}
        }
    }
    else
    {
        if(playerkeys(iPIndex, 0, key)){updateframe(self, frame);}
    }
}


EDIT: I made a minor improvement in the brakeFrame variable, now defined directly in the animation.
 
Don't worry buddy. Please, make a try with the latest script I posted, I'm curious to know if it will work fine in your game.
Perfect! I'm not at liberty to record a video to show you right now, I'm tweaking the values and I'll get the brake attack animation that goes with it done as soon as I can and show you.
 
Great :) Glad the code works
Hello Sir, I modified your Mario project for testing and the results were not good, I must have missed something - this is my process:

1, created a new folder called run in the Data\scripts\ folder, edited a default.c and pasted what you provided:
Code:
void keyMove(float xVel, float zVel, int brakeFrame)
{//Move entity if direction button is pressed
 //Use the brakeFrame to define to what frame the entity will jump when all keys are released in order to start the brake
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex");
    float xdir    = 0;
    float zdir    = 0;

    if(xVel != 0){
        if(playerkeys(iPIndex, 0, "moveleft")){
            xdir = -xVel;
        }
        else
        if(playerkeys(iPIndex, 0, "moveright")){
            xdir = xVel;
        }
        else
        {
            updateframe(self, brakeFrame);
        }
    }
    else
    {
        xdir = NULL();
    }
 
    if(zVel != 0){
        if(playerkeys(iPIndex, 0, "moveup")){
            zdir = -zVel;
        }
        else
        if(playerkeys(iPIndex, 0, "movedown")){
            zdir = zVel;
        }
    }
    else
    {
        zdir = NULL();
    }

    changeentityproperty(self, "velocity", xdir, zdir);
}

void keyFlip(int opposite)
{//Change entity's facing direction if left or right is pressed
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex"); //Get player index

    if(opposite == NULL()){
        if(playerkeys(iPIndex, 0, "moveleft")){changeentityproperty(self, "direction", 0);}else
        if(playerkeys(iPIndex, 0, "moveright")){changeentityproperty(self, "direction", 1);}
    }
    else
    {
        if(playerkeys(iPIndex, 0, "moveleft")){changeentityproperty(self, "direction", 1);}else
        if(playerkeys(iPIndex, 0, "moveright")){changeentityproperty(self, "direction", 0);}
    }
}

void keyLoop(void key, int frame, void var, void value)
{//Loops defined frame if defined key is held
 //Use "var" if you want to define a variable requirement, otherwise let it empty
    void self    = getlocalvar("self");
    int iPIndex    = getentityproperty(self,"playerindex");

    if(var != NULL()){
        if(getglobalvar(var) == value){
            if(playerkeys(iPIndex, 0, key)){updateframe(self, frame);}
        }
    }
    else
    {
        if(playerkeys(iPIndex, 0, key)){updateframe(self, frame);}
    }
}

void dasher(float Vx, float Vy, float Vz)
{//Dash with desired speed!
    void self    = getlocalvar("self");
    int dir        = getentityproperty(self,"direction");

    if(dir == 0){Vx = -Vx ;}
    changeentityproperty(self, "velocity", Vx, Vz, Vy);
}


2. Modify mario.txt and add these:
Code:
runscript data/scripts/run/default.c

com f f freespecial15

anim freespecial15
    loop    0
    delay    10
    offset    24 39
    bbox    125 113 17 68
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/mario/run00.gif
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/mario/run01.gif
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/mario/run00.gif
    @cmd keyMove 2 1 6
    @cmd keyFlip
    frame    data/chars/mario/run02.gif
    @cmd keyLoop "moveleft" 0
    @cmd keyLoop "moveright" 0
    @cmd keyFlip
    @cmd dasher 2 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 1 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 0.5 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 0.1 0 0
    frame    data/chars/mario/turn00.gif


After running, report error:
Code:
Can't compile script ‘Mario’ data/chars/mario/mario.txt


I tried to troubleshoot the error, and after gradually adding ‘#’ in front of ‘ @cmd’ in mario.txt, I found the problem here:
Code:
anim freespecial15
    loop    0
    delay    10
    offset    24 39
    bbox    125 113 17 68
#---------- ------wrong ↓--- ---
#    @cmd keyMove 2 1 6
#    @cmd keyFlip
#    frame    data/chars/mario/run00.gif
#    @cmd keyMove 2 1 6
#    @cmd keyFlip
#    frame    data/chars/mario/run01.gif
#    @cmd keyMove 2 1 6
#    @cmd keyFlip
#    frame    data/chars/mario/run00.gif
#    @cmd keyMove 2 1 6
#    @cmd keyFlip
#    frame    data/chars/mario/run02.gif
#    @cmd keyLoop "moveleft" 0
#    @cmd keyLoop "moveright" 0
#    @cmd keyFlip
#---------- ------wrong ↑--- ---
    @cmd dasher 2 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 1 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 0.5 0 0
    frame    data/chars/mario/turn00.gif
    @cmd dasher 0.1 0 0
    frame    data/chars/mario/turn00.gif


When one of these appears in the txt:
Code:
    @cmd keyMove 2 1 6
    @cmd keyFlip
    @cmd keyLoop ‘moveleft’ 0
    @cmd keyLoop ‘moveright’ 0
I open the game, it will just appear as ‘load...’ and exit.
If I put ‘#’ in front of these 4 ‘@cmd’, the game executes - Mario can't fastrun by pressing the arrow keys in a row, and Mario brakes after any movement.

My guess is that I missed this line when I copied the script to cause this:
Code:
//DETECT LEFT/RIGHT KEYS RELEASED
if(playerkeys(iPIndex, 2, ‘moveleft’) || playerkeys(iPIndex, 2, ‘moveright’)){
    doSomething();
}

If the error is due to missing this line, could you please tell me where this should be pasted?

This is the mario.txt and default.c used for testing, which I've also put here.
 

Attachments

Last edited:
Hello Sir, I modified your Mario project for testing and the results were not good, I must have missed something - this is my process:

1, created a new folder called run in the Data\scripts\ folder, edited a default.c and pasted what you provided:
void keyMove(float xVel, float zVel, int brakeFrame)
......

void keyFlip(int opposite)
......

void keyLoop(void key, int frame, void var, void value)
......

void dasher(float Vx, float Vy, float Vz)
......


2. Modify mario.txt and add these:
runscript data/scripts/run/default.c

com f f freespecial15

anim freespecial15
......


After running, report error:
Can't compile script ‘Mario’ data/chars/mario/mario.txt


I tried to troubleshoot the error, and after gradually adding ‘#’ in front of ‘ @cmd’ in mario.txt, I found the problem here:
anim freespecial15
loop 0
delay 10
offset 24 39
bbox 125 113 17 68
---------- ------wrong ↓--- ---
@cmd keyMove 2 1 6
@cmd keyFlip
frame data/chars/mario/run00.gif
@cmd keyMove 2 1 6
@cmd keyFlip
frame data/chars/mario/run01.gif
@cmd keyMove 2 1 6
@cmd keyFlip
frame data/chars/mario/run02.gif
@cmd keyMove 2 1 6
@cmd keyFlip
frame data/chars/mario/run00.gif
@cmd keyLoop ‘moveleft’ 0
@cmd keyLoop ‘moveleft’ 0
@cmd keyFlip
---------- ------wrong ↑--- ---
@cmd dasher 2 0 0
frame data/chars/mario/turn00.gif
@cmd dasher 1 0 0
frame data/chars/mario/turn00.gif
@cmd dasher 0.5 0 0 0 frame data/chars/mario/turn00.gif
frame data/chars/mario/turn00.gif
@cmd dasher 0.1 0 0 frame data/chars/mario/turn00.gif
frame data/chars/mario/turn00.gif


When one of these appears in the txt:
@cmd keyMove 2 1 6
@cmd keyFlip
@cmd keyLoop ‘moveleft’ 0
@cmd keyLoop ‘moveright’ 0
I open the game, it will just appear as ‘load...’ and exit.
If I put ‘#’ in front of these 4 ‘@cmd’, the game executes - Mario can't fastrun by pressing the arrow keys in a row, and Mario brakes after any movement.

My guess is that I missed this line when I copied the script to cause this:
//DETECT LEFT/RIGHT KEYS RELEASED
if(playerkeys(iPIndex, 2, ‘moveleft’) || playerkeys(iPIndex, 2, ‘moveright’)){
doSomething();
}

If the error is due to missing this line, could you please tell me where this should be pasted?

This is the mario.txt and default.c used for testing, which I've also put here.

@hbdhl

Please don't paste raw code directly to your posts. Use code tags.


DC
 
Hello Sir, I modified your Mario project for testing and the results were not good, I must have missed something - this is my process:
The keyscripts I posted (keyMove, keyFlip, keyLoop) were not developed to be used in the super mario template, I made them to be applied directly in your game (or in an empty BOR mod). The super mario template already has other scripts managing the Run feature and will conflict with the keyscripts.
 
I liked how it turns out. Question: do we need to use a freespecial for that? Can't we use the native run?
I don't want to use many cancels to use runattack, run jump, etc.
The problem is that the native run changes to idle instantly as soon as you release the directional keys. However, I'm testing some workarounds right now and found a way to make it work using native run, I only need to finish some adjustments.
 
I liked how it turns out. Question: do we need to use a freespecial for that? Can't we use the native run?
I don't want to use many cancels to use runattack, run jump, etc.
Now the code is using the native run but with a follow animation for the brake frames.
Basically the script will preserve the current speed moment for both X/Z axis during the brake animation and allow it to be gradually reduced.

Animations
Code:
anim idle
    loop    1
    delay    16
    offset    127 179
    bbox    117 109 19 72
    @cmd animVar "runFlag" 1 openborconstant("ANI_FOLLOW15") #CHANGE TO THE BRAKE ANIMATION IF THE RUNFLAG IS 1
    frame    data/chars/heroes/blaze/sor2/idle00.png
    frame    data/chars/heroes/blaze/sor2/idle01.png
    frame    data/chars/heroes/blaze/sor2/idle02.png
    frame    data/chars/heroes/blaze/sor2/idle03.png
    frame    data/chars/heroes/blaze/sor2/idle00.png
    frame    data/chars/heroes/blaze/sor2/idle01.png
    frame    data/chars/heroes/blaze/sor2/idle02.png
    frame    data/chars/heroes/blaze/sor2/idle03.png

anim walk
    loop    1
    delay    14
    offset    127 179
    bbox    124 107 12 73
    @cmd animVar "runFlag" 1 openborconstant("ANI_FOLLOW15") #CHANGE TO THE BRAKE ANIMATION IF THE RUNFLAG IS 1
    frame    data/chars/heroes/blaze/sor2/walk00.png
    @cmd setEntityVar "runFlag" NULL() #CLEAR THE RUNFLAG IF THE PLAYER HOLD THE WALKING INSTANCE
    frame    data/chars/heroes/blaze/sor2/walk01.png
    frame    data/chars/heroes/blaze/sor2/walk02.png
    frame    data/chars/heroes/blaze/sor2/walk03.png
    frame    data/chars/heroes/blaze/sor2/walk04.png
    frame    data/chars/heroes/blaze/sor2/walk05.png

anim run
    loop    1
    delay    10
    offset    127 179
    bbox    125 113 17 68
    @cmd setEntityVar "runFlag" 1 #SET THE RUNFLAG VARIABLE
    @cmd saveSpeed #SAVE THE CURRENT SPEED DURING THE RUN ANIMATION
    frame    data/chars/heroes/blaze/sor2/run00.png
    @cmd saveSpeed
    @cmd sound "data/sounds/sorx_run.wav"
    frame    data/chars/heroes/blaze/sor2/run01.png
    @cmd saveSpeed
    frame    data/chars/heroes/blaze/sor2/run02.png
    @cmd saveSpeed
    frame    data/chars/heroes/blaze/sor2/run03.png
    @cmd saveSpeed
    @cmd sound "data/sounds/sorx_run2.wav"
    frame    data/chars/heroes/blaze/sor2/run04.png
    @cmd saveSpeed
    frame    data/chars/heroes/blaze/sor2/run05.png

anim follow15 #BRAKE ANIMATION
    loop    0
    delay    16
    offset    127 179
    bbox    117 109 19 72
    @cmd loadSpeed 2 2 #LOAD PREVIOUSLY SAVED SPEED IN THE RUN ANIMATION AND REDUCE GRADUALLY
    frame    data/chars/heroes/blaze/sor2/idle00.png
    @cmd loadSpeed 2 2
    frame    data/chars/heroes/blaze/sor2/idle01.png
    @cmd loadSpeed 2 2
    frame    data/chars/heroes/blaze/sor2/idle02.png
    @cmd dasher 0 0 0
    frame    data/chars/heroes/blaze/sor2/idle03.png
    @cmd setEntityVar "runFlag" NULL() #CLEAR THE RUNFLAG
    @cmd setIdle #SET ANIMATION TO IDLE
    frame    data/chars/heroes/blaze/sor2/idle00.png

anim runattack #BLITZ
    fastattack 1
    jugglecost 4
    forcedirection -1
    otg 1
    loop    0
    delay    10
    offset    127 179
    @cmd hitfx "data/sounds/sor3_hit5.wav"
    @cmd voice "data/voices/sor2_blaze_attack3.wav"
    @cmd setEntityVar "runFlag" NULL() #CLEAR THE RUNFLAG DURING RUNNING ATTACKS
    frame    data/chars/heroes/blaze/sor2/runatk00.png
    frame    data/chars/heroes/blaze/sor2/runatk01.png

Scripts
Code:
void animVar(void variable, float value, void ani)
{
    void self = getlocalvar("self");

    if(getentityvar(self, variable) == value){
        executeanimation(self, ani, 1);
        setentityvar(self, variable, NULL());
    }
}

void setEntityVar(void variable, void value)
{
    setentityvar(getlocalvar("self"), variable, value);
}

void dasher(float Vx, float Vy, float Vz)
{
    void self    = getlocalvar("self");
    int dir        = getentityproperty(self,"direction");

    if(dir == 0){Vx = -Vx ;}
    changeentityproperty(self, "velocity", Vx, Vz, Vy);
}

void saveSpeed()
{
    void self    = getlocalvar("self");
    float xDir    = getentityproperty(self,"xdir");
    float zDir    = getentityproperty(self,"zdir");

    setentityvar(self, "xdir", xDir);
    setentityvar(self, "zdir", zDir);
}

void loadSpeed(int reduceX, int reduceZ)
{
    void self    = getlocalvar("self");
    float Vx    = getentityvar(self, "xdir");
    float Vz    = getentityvar(self, "zdir");
    float Vy    = 0;
    
    if(reduceX){
        Vx = Vx/reduceX;
        setentityvar(self, "xdir", Vx);
    }
    if(reduceZ){
        Vz = Vz/reduceZ;
        setentityvar(self, "zdir", Vz);
    }

    changeentityproperty(self, "velocity", Vx, Vz, Vy);
}

void setIdle()
{//Reset to idle instance
    void self = getlocalvar("self");

    setidle(self, openborconstant("ANI_IDLE"));
}


 
Solution
Back
Top Bottom