Solved Mid-air command cancel while maintaining velocity

Question that is answered or resolved.
use a keyscript to maintain velocity in air,


these are hop forward freespecials and then I use a keyscript in air for attacks

In model header add keyscript

animationscript data/scripts/script2.c
keyscript data/chars/zcody/zcodkey.c

com f f freespecial7 #hop forward

heres an example of keyscript


C:
void main()
{

    int iPlIndex = getlocalvar("player"); //Get calling player
    void vSelf = getplayerproperty(iPlIndex , "entity"); //Get calling entity
    void vAniID = getentityproperty(vSelf,"animationID"); //Get current animation ID
    void animpos = getentityproperty(vSelf, "animpos"); //Get current animation frame
    int iDir = getentityproperty(vSelf, "direction");  //Get current facing direction
    void vName = getentityproperty(vSelf,"name"); //Get name of calling entity
 
    void iUp = playerkeys(iPlIndex, 1, "moveup"); // New key status of "Up"
    void iDown = playerkeys(iPlIndex, 1, "movedown"); // New key status of "Down"
    void iLeft = playerkeys(iPlIndex, 1, "moveleft"); // New key status of "Left"
    void iRight = playerkeys(iPlIndex, 1, "moveright"); // New key status of "Right"
    void iJump = playerkeys(iPlIndex, 1, "jump"); //New key status of "Jump"
    void iSpecial = playerkeys(iPlIndex, 1, "Special"); //New key status of "Special"
    void iAttack = playerkeys(iPlIndex, 1, "attack"); //New key status of "Attack"
    void iAttack2 = playerkeys(iPlIndex, 1, "attack2"); // New key of "Attack 2"



if (vAniID == openborconstant("ANI_freespecial7") && animpos >= 1 && animpos <= 3){ //hop forward
    if(iAttack){ //attack pressed
    performattack(vSelf, openborconstant("ANI_freespecial13"));  // hop forawrd attack 1
  }    
 }
}

I use animpos so the hop forward attack does not perform while in the wrong animation frame like the animations land frame for example.
 
Last edited:
Thanks danno for the tip! But I actually need to cancel with an output like D F A, not just by pressing one button. How is it possible in the keyscript?
 
Which ever freespecial you are using you could add button held/released to your keyscripts

void iLeftH = playerkeys(iPlIndex, 0, "moveleft"); // key held
void iRightH = playerkeys(iPlIndex, 0, "moveright"); // key held
void iLeftR = playerkeys(iPlIndex, 2, "moveleft"); // Release status of "Left"
void iRightR = playerkeys(iPlIndex, 2, "moveright"); // Release status of "Right"

and something like

C:
     if(vAniID == openborconstant("ANI_FREESPECIAL7") ){ //cancel freespecial?
        if(iLeftR || iRightR){ //Left or Right released?
          if(!iLeftH && !iRightH){ //Left and Right not pressed?
            changeentityproperty(vSelf, "velocity", 0);
          }
        } else if(iLeft){ //Left pressed?
          changeentityproperty(vSelf, "velocity", -2);
        } else if(iRight){ //Right pressed?
          changeentityproperty(vSelf, "velocity", 2);
        }
    }
}

or If you don't want it to be key press related you can just use a simple jumpframe in your animation that matches your current momentum when the freespecial is activated like jumpframe 0 0.5 2

or a velocity script in the cancelling freespecial

C:
void velo001(float fX, float fZ, float fY){
  
    //velo001
    //Damon Vaughn Caskey
    //05/06/2007
    //
    //Replicates movement effect.
    //
    //fX: X axis speed.
    //fZ: Z axis speed.
    //fY: Y axis speed.
  
    void vSelf = getlocalvar("self"); //Get calling entity.
  
    if (getentityproperty(vSelf, "direction")==0){ //Is entity facing right?               
  
         fX = -fX; ////Reverse X direction to match facing.
      
    }       
  
    changeentityproperty(vSelf, "velocity", fX, fZ, fY); //Apply movement.

}

here's some examples of command air cancels I use

 
Last edited:
Thanks for telling me all these ways!
I don't guess how to implement a sequence of inputs in keyscript even through your script example though.

Which ever freespecial you are using you could add button held/released to your keyscripts

void iLeftH = playerkeys(iPlIndex, 0, "moveleft"); // key held
void iRightH = playerkeys(iPlIndex, 0, "moveright"); // key held
void iLeftR = playerkeys(iPlIndex, 2, "moveleft"); // Release status of "Left"
void iRightR = playerkeys(iPlIndex, 2, "moveright"); // Release status of "Right"

and something like

C:
     if(vAniID == openborconstant("ANI_FREESPECIAL7") ){ //cancel freespecial?
        if(iLeftR || iRightR){ //Left or Right released?
          if(!iLeftH && !iRightH){ //Left and Right not pressed?
            changeentityproperty(vSelf, "velocity", 0);
          }
        } else if(iLeft){ //Left pressed?
          changeentityproperty(vSelf, "velocity", -2);
        } else if(iRight){ //Right pressed?
          changeentityproperty(vSelf, "velocity", 2);
        }
    }
}
 
You don't need to implement a sequence of inputs using keyscript to maintain velocity during cancel freespecial.

Inside your cancel freespecial use jumpframe or a velocity script to match previous animation like forwardjumps velocity/momentum

If you want to control the velocity/momentum during your cancel freespecial use keyscript so when you press left or right you move left or right.

in this video look how I move the player model in air with jumps, double jumps, dodges with some cancelling into freespecials while maintaining previous velocity or completely changing velocity while pressing a direction. all situation depending.

 
Last edited:
You don't need to implement a sequence of inputs using keyscript to maintain velocity during cancel freespecial.
I'd like this sequence of inputs in keyscript and I don't want to drive the velocity. Here is a sketch to be more understandable:
 

Attachments

  • Cancelling air move with a keyscript sequence of inputs.jpg
    Cancelling air move with a keyscript sequence of inputs.jpg
    185.6 KB · Views: 11
  • Haha
Reactions: NED
Firstly I wouldn't use a command attack to cancel the engines default jumpforward animation, using attack in succession this way causes some over ride issues even if you turned jumpforward into a freespecial via keyscript

a simple solution would be to use D F J cancel and use jumpframe to match velocity so you don't fall like an anvil during cancel.


anim jumpforward
loop 0
delay 8
offset 59 120
bbox 45 25 35 35
landframe 3
cancel 0 2 0 d f j freespecial17
sound data/sounds/gyal.wav
frame data/chars/zguy/ja4.png
delay -1000
hitfx data/sounds/beat7.wav
attack 60 30 58 38 20 1 0 0 20
frame data/chars/zguy/ja5.png
attack 0 0 0 0 0 0 0 0 0 0
delay 10
frame data/chars/zguy/ja4.png
frame data/chars/zguy/l.png

anim freespecial17
jumpframe 0 0 0.8
landframe 1
loop 0
delay -1000
offset 59 120
bbox 47 31 27 72
frame data/chars/zguy/j1.png
delay 10
frame data/chars/zguy/l.png


There are more complicated work arounds but I'm not sure if it's necessary.

If you wanted to use keyscript then I wouldn't use a command attack, I would just use attack otherwise again you'll be over complicating it.

C:
void main()
{
 
 
    int iPlIndex = getlocalvar("player"); //Get calling player
    void vSelf = getplayerproperty(iPlIndex , "entity"); //Get calling entity
    void vAniID = getentityproperty(vSelf,"animationID"); //Get current animation ID
    void animpos = getentityproperty(vSelf, "animpos"); //Get current animation frame
    int iDir = getentityproperty(vSelf, "direction");  //Get current facing direction
    int MPower = getentityproperty(vSelf,"mp");
    void vName = getentityproperty(vSelf,"name"); //Get name of calling entity
 
    void iUp = playerkeys(iPlIndex, 1, "moveup"); // New key status of "Up"
    void iDown = playerkeys(iPlIndex, 1, "movedown"); // New key status of "Down"
    void iLeft = playerkeys(iPlIndex, 1, "moveleft"); // New key status of "Left"
    void iRight = playerkeys(iPlIndex, 1, "moveright"); // New key status of "Right"
    void iJump = playerkeys(iPlIndex, 1, "jump"); //New key status of "Jump"
    void iSpecial = playerkeys(iPlIndex, 1, "Special"); //New key status of "Special"
    void iAttack = playerkeys(iPlIndex, 1, "attack"); //New key status of "Attack"
    void iAttack2 = playerkeys(iPlIndex, 1, "attack2"); // New key of "Attack 2"
    void iAttack3 = playerkeys(iPlIndex, 1, "attack3"); // New key of "Attack 3"
    void iAttack4 = playerkeys(iPlIndex, 1, "attack4"); // New key of "Attack 4"


    void iUpR = playerkeys(iPlIndex, 2, "moveup"); // Release status of "UP"
    void iLeftR = playerkeys(iPlIndex, 2, "moveleft"); // Release status of "Left"
    void iRightR = playerkeys(iPlIndex, 2, "moveright"); // Release status of "Right"
    void iDownR = playerkeys(iPlIndex, 2, "movedown"); //Release status of "Down"
    void iAttackR = playerkeys(iPlIndex, 2, "attack"); //Release status of "Attack"
    void iAttack2R = playerkeys(iPlIndex, 2, "attack2"); //Release status of "Attack"

    void iUpH = playerkeys(iPlIndex, 0, "moveup");
    void iDownH = playerkeys(iPlIndex, 0, "movedown");
    void iLeftH = playerkeys(iPlIndex, 0, "moveleft");
    void iRightH = playerkeys(iPlIndex, 0, "moveright");
    void iAttackH = playerkeys(iPlIndex, 0, "attack");
    void iAttack2H = playerkeys(iPlIndex, 0, "attack2");

if (vAniID == openborconstant("ANI_FORWARDJUMP") && animpos >= 1 && animpos <= 6){ //forward jump
    if(iAttack){ //attack pressed
    performattack(vSelf, openborconstant("ANI_FREESPECIAL16"));  // jump forward attack
  }    
 }


if (vAniID == openborconstant("ANI_FREESPECIAL16") && animpos >= 0 && animpos <= 2){ //forward jump attack
    if(iAttack){ //attack pressed
    performattack(vSelf, openborconstant("ANI_FREESPECIAL17"));  // jump forward attack cancel
  }    
 }
}

If you're absolutely certain you need command air cancel then perhaps somebody more skilled than me could chime in, I originally thought you just wanted to maintain velocity.
 
Last edited:
Thank you, in this case, you're right, this way is the easiest one and it's useless to complicate the things. Nonetheless I may come back in this thread for other cases about mid-air cancels.
 
@danno
Cancelling by example a slide animation including a jumpframe seems to not work with the way you described. If there is no jumpframe in the following freespecial that cancels the slide animation, the player falls like an anvil. But if there is a jumpframe in this freespecial (the same as slide animation's), the player makes a jump in mid-air.
Am I understood or I post the text of animations to be clearer?
 
post the text animation, I'll understand better seeing the text because I'm not sure why the character falls during a slide.
 
Sorry, what I said can be confusing since my slide animation is a back dash like in Final Fight 3, I guess.

anim slide
cancel 0 1 0 D F A FREESPECIAL10
loop 0
offset 84 132
delay 99
jumpframe 0 2 -2
landframe 1
bbox 64 32 36 74
frame data/chars/andy'/bdash01.png
delay 7
bbox 59 40 40 92
frame data/chars/andy'/crouch01.png
anim freespecial10
offset 84 132
forcedirection -1
bbox 63 31 36 100
landframe 1
delay 999
Energycost 100 1 0
hitflash flash3
frame data/chars/andy'/jump04.png
delay 10
frame data/chars/andy'/crouch01.png
frame data/chars/andy'/fspec101.png
frame data/chars/andy'/fspec102.png
delay 8
attack 79 42 98 39 35 1 0 0 0 17 //
dropv 2 3.5
frame data/chars/andy'/fspec103.png
frame data/chars/andy'/fspec104.png
attack 139 42 53 39 35 1 0 0 0 17 //
dropv 2 3.5
frame data/chars/andy'/fspec105.png
frame data/chars/andy'/fspec106.png
delay 6
attack 0 0 0 0 0
frame data/chars/andy'/fspec107.png
frame data/chars/andy'/fspec108.png
frame data/chars/andy'/fspec108.png
In the freespecial10 above the player falls like an anvil. But if I add "jumpframe 0 2 -2" in it, the player double-jumps.
 
@16-bit Fighter when you cancel using key script there is no stop to the velocity that only happens if you set a cancel command in the animation. Here is a example on how to perform a D F A cancel into Freespecial2 when in the animation jump or jumpattack also it checkers the players name as it is a global keyscript so it will work for both player 1 and 2. Just realised if in 2 player the "dfa_input" should be using setentityvar so the input is relevant to the player. Added the code and a example demo when ryu performs a kick or just jump D F A will cancel into a hurricane kick without loss of velocity. make sure in models.txt you have "noaircancel 1" and make sure there are no other cancels set when you press the inputs in your characters animation data. this code does not use timing you can press D F A as slow or as fast as you like.


keyall.c

C-like:
void main()
{//All players Keyscripts

    int playerIndex = getlocalvar("player"); //Get calling player
    void self = getplayerproperty(playerIndex ,"entity"); //Get calling entity

    void exists = getentityproperty(self, "health")>0?1:NULL(); // Is caller alive
     if (openborvariant("in_level") && exists)
    {
   void ani_id = getentityproperty(self,"animationID"); //Get current animation ID
    char name = getentityproperty(self,"name"); //Get current name
   int dir = getentityproperty(self, "direction");  //Get current facing direction
    dfa_input;

   //Press Keys
   void attack = playerkeys(playerIndex, 1, "attack"); //New key status of "Attack"
    void down = playerkeys(playerIndex, 1, "movedown"); //New key status of "MoveDown"
    void forward = playerkeys(playerIndex, 1, dir==0?"moveleft":"moveright"); //Check if pressed forward based on direction
    //void backward = playerkeys(playerIndex, 1, dir==0?"moveright":"moveleft"); //Check if pressed backward based on direction


        if( name == "Ryu" && ani_id == openborconstant("ani_jump") || ani_id == openborconstant("ani_jumpattack") ) //check right player and animation
        {
            if(down){ dfa_input=1;} // Input step 1
            if(forward && dfa_input==1){ dfa_input=2;} // Input step 2
            if(attack && dfa_input==2){ performattack(self, openborconstant("ANI_freespecial2")); dfa_input=0;} // Input step 3 and reset step counter
        }
    }
}


example demo
 
Last edited:
In the freespecial10 above the player falls like an anvil. But if I add "jumpframe 0 2 -2" in it, the player double-jumps.

To answer this, if you use the same jumpframe properties in both anim slide and freespecial10 then you will do a double jump, in freespecial10 use different jumpframe properties like jumpframe 0 0 -0.8

However msmalik's solution is much better as it allows 'mid-air command cancel while maintaining velocity' which I also think you should change this thread's title to.
 
@msmalik681
Thank you very much! For now I won't use your script because I have a simpler solution but I keep it in my pocket for learning and a possible future.

To answer this, if you use the same jumpframe properties in both anim slide and freespecial10 then you will do a double jump, in freespecial10 use different jumpframe properties like jumpframe 0 0 -0.8

However msmalik's solution is much better as it allows 'mid-air command cancel while maintaining velocity' which I also think you should change this thread's title to.
I realized that jumpframe 0 0 -2 allow to keep the velocity exactly like I want.
About your suggestion to change the thread's title, I don't see a difference but since I'm not fluent in english I trust you. ^^
 
C-like:
void main()
{//All players Keyscripts

    int playerIndex = getlocalvar("player"); //Get calling player
    void self = getplayerproperty(playerIndex ,"entity"); //Get calling entity

    void exists = getentityproperty(self, "health")>0?1:NULL(); // Is caller alive
     if (openborvariant("in_level") && exists)
    {
   void ani_id = getentityproperty(self,"animationID"); //Get current animation ID
    char name = getentityproperty(self,"name"); //Get current name
   int dir = getentityproperty(self, "direction");  //Get current facing direction
    dfa_input;

   //Press Keys
   void attack = playerkeys(playerIndex, 1, "attack"); //New key status of "Attack"
    void down = playerkeys(playerIndex, 1, "movedown"); //New key status of "MoveDown"
    void forward = playerkeys(playerIndex, 1, dir==0?"moveleft":"moveright"); //Check if pressed forward based on direction
    //void backward = playerkeys(playerIndex, 1, dir==0?"moveright":"moveleft"); //Check if pressed backward based on direction


        if( name == "Ryu" && ani_id == openborconstant("ani_jump") || ani_id == openborconstant("ani_jumpattack") ) //check right player and animation
        {
            if(down){ dfa_input=1;} // Input step 1
            if(forward && dfa_input==1){ dfa_input=2;} // Input step 2
            if(attack && dfa_input==2){ performattack(self, openborconstant("ANI_freespecial2")); dfa_input=0;} // Input step 3 and reset step counter
        }
    }
}
At last I tried your code (without any modification, just to test) but OB shut down and OBLog says "Script compile error in 'entitykeyscript': dfa_input line 24, column 22".
 
woops my bad try this one.


C-like:
void main()
{//All players Keyscripts

    int playerIndex = getlocalvar("player"); //Get calling player
    void self = getplayerproperty(playerIndex ,"entity"); //Get calling entity

    void exists = getentityproperty(self, "health")>0?1:NULL(); // Is caller alive
     if (openborvariant("in_level") && exists)
    {
   void ani_id = getentityproperty(self,"animationID"); //Get current animation ID
    char name = getentityproperty(self,"name"); //Get current name
   int dir = getentityproperty(self, "direction");  //Get current facing direction
   int dfa_input;

   //Press Keys
   void attack = playerkeys(playerIndex, 1, "attack"); //New key status of "Attack"
    void down = playerkeys(playerIndex, 1, "movedown"); //New key status of "MoveDown"
    void forward = playerkeys(playerIndex, 1, dir==0?"moveleft":"moveright"); //Check if pressed forward based on direction
    //void backward = playerkeys(playerIndex, 1, dir==0?"moveright":"moveleft"); //Check if pressed backward based on direction


        if( name == "Ryu" && ani_id == openborconstant("ani_jump") || ani_id == openborconstant("ani_jumpattack") ) //check right player and animation
        {
            if(down){ dfa_input=1;} // Input step 1
            if(forward && dfa_input==1){ dfa_input=2;} // Input step 2
            if(attack && dfa_input==2){ performattack(self, openborconstant("ANI_freespecial")); dfa_input=0;} // Input step 3 and reset step counter
        }
    }
}
 
@msmalik681 : Thanks!

Well the result isn't the expected one, though. Actually for a split second the freespecial is performed (I can't see due to the short time but I know it because I heard the sound of the attack activated during the first frame) but it immediately turns into the jump attack animation.
It perfectly works when I delete the jump attack animation. Which I don't want.
 
I'd like this sequence of inputs in keyscript and I don't want to drive the velocity. Here is a sketch to be more understandable:
The picture explain it perfectly even with stick men.
I might use the same process as well.

I used to add a jumpframe. But this is the lazy way to do it (with bugs etc)
 
Back
Top Bottom