Skip to content
Animation and Stance

Animation and Stance

It takes about 15 minutes to read this article

This article provides an overview of what animation is, what stance is, the default stance for character , and how to use animation and stance.

Definition of animation and stance

Animation Definition

Animation definition: animation is making a character perform an independent action.

Stance Definition

Stance definition: stance is an action effect that a character can maintain indefinitely.

The stance includes two effects: [Basic stance] and [sub stance].

  • [Basic stance]: The basic stance is all the action performances in the state machine of the three mode: ground, flying and swimming. This includes our pre-made walking, running, jumping and other action performances. That is to say, when you enter the experience directly without making any modifications, the character's movements will be the effect of the basic stance.

  • [Sub stance]: It is an extended function of the animation system, which is independent of the complex animation logic outside the basic stance , such as the functional effects of holding a gun.

Animation

Asset

Asset description: We need to use loadAnimation to load the corresponding animation asset.

Sample script:

ts
//Get the Player character
let chara = Player.localPlayer.character
//Load character animation asset
let animation = chara.loadAnimation("14700");
//Get the Player character
let chara = Player.localPlayer.character
//Load character animation asset
let animation = chara.loadAnimation("14700");

Animation loop times

Function description: When a character plays an animation, it plays the animation asset repeatedly until the upper limit of the number of loops is reached, and then stops playing the animation.

Practical application: You can let the NPC play actions in an infinite loop, or you can let the NPC wait after playing the actions.

First we place the character object in the scene

8ab481a4-8c7f-41f4-bfb9-f0afdf3895c6

Then mount the following script

ts
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected async onStart(): Promise<void> {
        // Get the Player
        let chara = Player.localPlayer.character
        // Get three NPC object by GameObject ID . Replace the GameObject ID you just copy here.
        let npc1 = await GameObject.asyncFindGameObjectById("02186A6A") as Character
        let npc2 = await GameObject.asyncFindGameObjectById("3F58547B") as Character
        let npc3 = await GameObject.asyncFindGameObjectById("0DC8EEF6") as Character
        //Load character animation asset
        let danceAnimation = chara.loadAnimation("14700");
        //Load NPC1's animation asset
        let AA = npc1.loadAnimation("14700");
        // Set NPC1's animation loop times to infinite loop
        AA.loop = 0;
        //Load NPC2 animation asset
        let BB = npc2.loadAnimation("14700");        
        // Set NPC1's animation loop count to 1
        BB.loop = 1;
        // //Since NPC3 is a four-legged appearance, the animation mode needs to be changed to customize mode.
        // npc3.animationMode = AnimationMode.Custom;
        //Load NPC3 animation asset
        let CC = npc3.loadAnimation("181289");  
        // Set NPC3's animation loop count to 3 times
        CC.loop = 3;
        // press the "1" key to trigger the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Let the character animation play
            danceAnimation.play();            
            //Let NPC1 animation play
            AA.play();
            //Let NPC2 animation play
            BB.play();
            //Let NPC3 animation play
            CC.play();
        });
    }
}
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected async onStart(): Promise<void> {
        // Get the Player
        let chara = Player.localPlayer.character
        // Get three NPC object by GameObject ID . Replace the GameObject ID you just copy here.
        let npc1 = await GameObject.asyncFindGameObjectById("02186A6A") as Character
        let npc2 = await GameObject.asyncFindGameObjectById("3F58547B") as Character
        let npc3 = await GameObject.asyncFindGameObjectById("0DC8EEF6") as Character
        //Load character animation asset
        let danceAnimation = chara.loadAnimation("14700");
        //Load NPC1's animation asset
        let AA = npc1.loadAnimation("14700");
        // Set NPC1's animation loop times to infinite loop
        AA.loop = 0;
        //Load NPC2 animation asset
        let BB = npc2.loadAnimation("14700");        
        // Set NPC1's animation loop count to 1
        BB.loop = 1;
        // //Since NPC3 is a four-legged appearance, the animation mode needs to be changed to customize mode.
        // npc3.animationMode = AnimationMode.Custom;
        //Load NPC3 animation asset
        let CC = npc3.loadAnimation("181289");  
        // Set NPC3's animation loop count to 3 times
        CC.loop = 3;
        // press the "1" key to trigger the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Let the character animation play
            danceAnimation.play();            
            //Let NPC1 animation play
            AA.play();
            //Let NPC2 animation play
            BB.play();
            //Let NPC3 animation play
            CC.play();
        });
    }
}

Finally, copy the ID of the character object we placed in the scene , and replace the ID of the character object in the script to achieve the effect.

image-20240827133903202

Animation duration

Function description: The total duration of the character's animation playback.

Practical application: Currently we can only get the animation duration, the main purpose is to control the speed effect through the animation duration. For example, if the role of magician profession in the game, they will chant for x seconds to release the skill, then we need to judge whether the length of the animation meets the chanting time, if not, we need to adjust the animation playback speed to match the effect of the chanting action.

Sample script:

ts
//Get the Player character
let chara = Player.localPlayer.character
//Load a character animation
let danceAnimation = chara.loadAnimation("14700");
//Print the total duration of the animation
console.log(danceAnimation.length);
//Get the Player character
let chara = Player.localPlayer.character
//Load a character animation
let danceAnimation = chara.loadAnimation("14700");
//Print the total duration of the animation
console.log(danceAnimation.length);

Animation Play Slot

Function description: Which parts of the character play the animation. Currently, the parts that can play animation include: whole body, upper body, and lower body.

Practical application: We can use this animation part to control the character or NPC to only play the animation of the upper body or lower body, thereby achieving the effect of animation fusion. For example, the upper body can play a greeting animation, and the lower body can play a sitting stance, achieve the different animation requirements of the upper and lower body.

Sample script:

ts
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected async onStart(): Promise<void> {
        // Get the Player
        let chara = Player.localPlayer.character
        //Load a character animation
        let animation = chara.loadAnimation("14700");

        // press the "1" key to trigger the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Set the animation playback part to the whole body
            animation.slot = AnimSlot.FullyBody;
            //Let the character animation play
            animation.play();
        });
        // press the "2" key to trigger the following logic
        InputUtil.onKeyDown(Keys.Two, () => {
            //Set the animation playback part to the upper body
            animation.slot = AnimSlot.Upper;
            //Let the character animation play
            animation.play();
        });
        // press the "3" key to trigger the following logic
        InputUtil.onKeyDown(Keys.Three, () => {
            //Set the animation playback part to the lower body
            animation.slot = AnimSlot.Lower;
            //Let the character animation play
            animation.play();
        });
    }
}
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected async onStart(): Promise<void> {
        // Get the Player
        let chara = Player.localPlayer.character
        //Load a character animation
        let animation = chara.loadAnimation("14700");

        // press the "1" key to trigger the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Set the animation playback part to the whole body
            animation.slot = AnimSlot.FullyBody;
            //Let the character animation play
            animation.play();
        });
        // press the "2" key to trigger the following logic
        InputUtil.onKeyDown(Keys.Two, () => {
            //Set the animation playback part to the upper body
            animation.slot = AnimSlot.Upper;
            //Let the character animation play
            animation.play();
        });
        // press the "3" key to trigger the following logic
        InputUtil.onKeyDown(Keys.Three, () => {
            //Set the animation playback part to the lower body
            animation.slot = AnimSlot.Lower;
            //Let the character animation play
            animation.play();
        });
    }
}

Animation playback speed

Function description: control the playback speed of the animation. The speed here represents the playback speed multiple. When the speed is 1, it represents the initial playback speed. The larger the speed value, the faster the playback speed, and the smaller the speed value, the slower the playback speed.

Practical application: We can extend the character's animation playback time by modify the animation playback speed.

Sample script:

ts
//Get the Player character
let chara = Player.localPlayer.character
//Load a character animation
let animation = chara.loadAnimation("14700");
// press the "1" key to trigger the following logic
InputUtil.onKeyDown(Keys.One, () => {
    //Set the animation playback speed to 0.5
    animation.speed = 0.5
    //Let the character animation play
    animation.play();
});
//Get the Player character
let chara = Player.localPlayer.character
//Load a character animation
let animation = chara.loadAnimation("14700");
// press the "1" key to trigger the following logic
InputUtil.onKeyDown(Keys.One, () => {
    //Set the animation playback speed to 0.5
    animation.speed = 0.5
    //Let the character animation play
    animation.play();
});

Play Function

Function description : The function of making the specified character play the animation according to the animation's loop/speed and other property .

Sample script:

ts
// animation playback
animation.play();
// animation playback
animation.play();

Pause function

Function description : pause the character that is playing the animation and keep the animation posture when it is pause .

Sample script:

ts
// pause the animation
animation.pause();
// pause the animation
animation.pause();

Resume Function

Function description : redo the character whose animation has been pause and continue to play the subsequent animation.

Sample script:

ts
//Continue playing the animation
animation.resume();
//Continue playing the animation
animation.resume();

Stop function

Function description : Stop the character that is playing the animation and redo it to the default standby state.

Sample script:

ts
//Stop playing the animation
animation.stop();
//Stop playing the animation
animation.stop();

Is Playing

Function description: detect whether the animation is still playing.

Practical application: For example, we want to prevent the character from moving while playing an action. It is necessary to determine whether the character is playing an animation to shield the character's operating system.

Sample script:

ts
// Check if the animation is playing
if (animation.isPlaying == true) {
    //If the animation is playing, execute the logic
} else {
    //If the animation does not play, execute the logic            
}
// Check if the animation is playing
if (animation.isPlaying == true) {
    //If the animation is playing, execute the logic
} else {
    //If the animation does not play, execute the logic            
}

Animation end callback

Function description: After the animation is finished playing, the end event is thrown.

Practical application: After the character completes the animation, play the effect.

  • Sample script:
ts
// Add a function to the [animation Complete] delegate to play an upgrade effect
animation.onFinish.add(() => {
    // Play effect on the object ( effect ID, target object)
    EffectService.playOnGameObject("106699", Player.localPlayer.character, 
    {
        // slot position for effect playback
        slotType:HumanoidSlotType.Root,
        //Rotation angle of effect
        rotation:new Rotation(0,0,-90)
    });
});
// Add a function to the [animation Complete] delegate to play an upgrade effect
animation.onFinish.add(() => {
    // Play effect on the object ( effect ID, target object)
    EffectService.playOnGameObject("106699", Player.localPlayer.character, 
    {
        // slot position for effect playback
        slotType:HumanoidSlotType.Root,
        //Rotation angle of effect
        rotation:new Rotation(0,0,-90)
    });
});

Basic stance

Setting the basic stance

Function description: Change the basic stance asset

Practical application: At present, we provide several basic stance for selection according to the character's appearance setting, including Lowpoly basic female stance, Lowpoly basic male stance, realistic basic female stance, realistic basic male stance, European and American basic female stance, European and American basic male stance, etc. Then we can switch the character's basic stance to make the character with a female appearance play the male basic stance or other stance script:

ts
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        //Get the Player character
        let chara = Player.localPlayer.character
        //Load a realistic male basic stance asset
        let stance = chara.loadStance("119836");
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Play the basic stance of a realistic male
            stance.play();
        });
    }
}
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        //Get the Player character
        let chara = Player.localPlayer.character
        //Load a realistic male basic stance asset
        let stance = chara.loadStance("119836");
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Play the basic stance of a realistic male
            stance.play();
        });
    }
}

Play Function

Function description: Based on the loaded basic stance asset, the specified character can play the stance.

Sample script:

ts
//Play stance
stance.play();
//Play stance
stance.play();

Stop function

Function description: According to the loaded basic stance asset, make the specified character stop the stance being played.

Sample script:

ts
//Stop stance
stance.stop();
//Stop stance
stance.stop();

Aim offset function

Function description: After turning on the aim offset function, the character head will move in the direction of the camera and will not turn the body. When aim offset, camera movement will not affect the character effect.

Sample script:

ts
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        //Get the Player character
        let chara = Player.localPlayer.character
        // Load a anime female basic stance for the character
        let stance = chara.loadStance("30274");
        // aim offset is disabled by default
        stance.aimOffsetEnabled = false;
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            stance.play();
            // Enable aim offset
            stance.aimOffsetEnabled = true;
        });
    }
}
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        //Get the Player character
        let chara = Player.localPlayer.character
        // Load a anime female basic stance for the character
        let stance = chara.loadStance("30274");
        // aim offset is disabled by default
        stance.aimOffsetEnabled = false;
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            stance.play();
            // Enable aim offset
            stance.aimOffsetEnabled = true;
        });
    }
}

Sub stance

Setting the sub stance

Function description: Change the sub stance asset

Sample script:

ts
//Get the Player character
let chara = Player.localPlayer.character
//Load a basic stance asset
let subStance = chara.loadSubStance("94258");
//Get the Player character
let chara = Player.localPlayer.character
//Load a basic stance asset
let subStance = chara.loadSubStance("94258");

The usage of functions such as [Play] and [Stop] is the same as the basic stance.

Stance Mixing

Function description : According to the asset of the loaded sub stance , the character only plays the upper body effect, lower body effect or full body effect of the stance.

Practical application: When playing shoot experience, the character upper body is often required to maintain a gun-holding stance, and the lower body will be integrated according to the basic stance. achieve the effect that the character can stance while holding a gun.

Sample script:

ts
@Component
export default class stanceTest extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        // Get the Player character
        let chara = Player.localPlayer.character
        //Load a sub stance asset(holding a gun with both hands)
        let stance = chara.loadSubStance("94258");
        //Set the stance effect to full body stance
        //stance.blendMode = StanceBlendMode.WholeBody;
        //Set the stance effect to lower body stance
        //stance.blendMode = StanceBlendMode.BlendLower;
        //Set the stance effect to upper body effect
        stance.blendMode = StanceBlendMode.BlendUpper;
        
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Play stance
            stance.play();
        });        
    }
}
@Component
export default class stanceTest extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {
        // Get the Player character
        let chara = Player.localPlayer.character
        //Load a sub stance asset(holding a gun with both hands)
        let stance = chara.loadSubStance("94258");
        //Set the stance effect to full body stance
        //stance.blendMode = StanceBlendMode.WholeBody;
        //Set the stance effect to lower body stance
        //stance.blendMode = StanceBlendMode.BlendLower;
        //Set the stance effect to upper body effect
        stance.blendMode = StanceBlendMode.BlendUpper;
        
        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Play stance
            stance.play();
        });        
    }
}

Animation stance

Function description: The loadSubStance() method can be filled with stance asset or animation asset. If the asset type is identified as an animation asset, the character will automatically play the stance effect of its animation . This allows us to control action asset effects more flexibly.

Practical application: We can choose the desired animation to convert into stance to achieve the effect. For example, there are animation asset of sitting cross-legged in the asset library . We can use the StanceBlendMode function to keep only the lower body effect, so that the character achieve stance cross-legged. Then combine it with other animation requirements to achieve the effect of animation fusion, such as sitting cross-legged and drinking tea.

Sample script:

ts
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {

        // Get the Player
        let chara = Player.localPlayer.character
        //Convert the animation asset of sitting and greeting to the sub stance
        let stance = chara.loadSubStance("29753");
        //Load a waving character animation
        let animation = chara.loadAnimation("29775");

        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Set the stance effect to full body stance
            stance.blendMode = StanceBlendMode.WholeBody;
            //Play stance
            stance.play();
        });

        // Add a key method: press the keyboard "2" to execute the following logic
        InputUtil.onKeyDown(Keys.Two, () => {
            //Stop stance
            stance.stop();
            //Set the stance effect to lower body stance
            stance.blendMode = StanceBlendMode.BlendLower;
            //Play stance
            stance.play();
        });

        // Add a key method: press the keyboard "3" to execute the following logic
        InputUtil.onKeyDown(Keys.Three, () => {
            //Set the animation playback part to the upper body
            animation.slot = AnimSlot.Upper;
            //Play animation
            animation.play();            
        });
    }
}
@Component
export default class NewScript extends Script {

    /** When the script is instantiated, this function will be call before the first frame is update */
    protected onStart(): void {

        // Get the Player
        let chara = Player.localPlayer.character
        //Convert the animation asset of sitting and greeting to the sub stance
        let stance = chara.loadSubStance("29753");
        //Load a waving character animation
        let animation = chara.loadAnimation("29775");

        // Add a key method: press the keyboard "1" to execute the following logic
        InputUtil.onKeyDown(Keys.One, () => {
            //Set the stance effect to full body stance
            stance.blendMode = StanceBlendMode.WholeBody;
            //Play stance
            stance.play();
        });

        // Add a key method: press the keyboard "2" to execute the following logic
        InputUtil.onKeyDown(Keys.Two, () => {
            //Stop stance
            stance.stop();
            //Set the stance effect to lower body stance
            stance.blendMode = StanceBlendMode.BlendLower;
            //Play stance
            stance.play();
        });

        // Add a key method: press the keyboard "3" to execute the following logic
        InputUtil.onKeyDown(Keys.Three, () => {
            //Set the animation playback part to the upper body
            animation.slot = AnimSlot.Upper;
            //Play animation
            animation.play();            
        });
    }
}