• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Using Affine Animations

19
Posts
7
Years
    • Seen Mar 29, 2024
    To start, I will explain how to scale a sprite (magnify and decrease). First, we need to know what parameters the definition has.

    Code:
    #define AFFINEANIMCMD_FRAME(_xScale, _yScale, _rotation, _duration)

    The X and Y scale, the rotation, and the duration.

    We have to know that as higher "_xScale" and "_yScale", more fast will scale our sprite, This is not the size that will be scaled. If we use a positive value as "16", the scale will be a magnify, if we use a negative one, as "-32", the scale will be of reduction.

    We have to know that the value of scale, and the duration are strictly linked, and I've bothered to take out the formula.

    First, I will shorten the variables to make the formula more comfortable (although it is very simple).

    Code:
    d = _duration
    x = _xScale
    k

    d will be the value of _duration, x will be the value of _xScale (or _yScale, is the same), and finally, k will be the scale which we will give to the sprite. For example, if we want the sprite to be twice as large, k = 2. if we want the sprite to be 2/3 of the original, k = 2/3 (although I do not recommend this value much, the scale will not be too homogeneous)

    Once we know that, let's see the formula (that you can clear to know the value that most interests you, or you can give values directly to know the duration)

    YTTzNVy.png

    LOOK OUT!.- If the result of the formula gives you a value with decimals, I recommend that you choose to use other values that give a similar visual result, but not decimals, since the floats will not be processed. You can play with the scale speed.

    LOOK OUT! (2).- I will use the formula that you will see below, because I will work with a sprite that by default, is loaded with a size of 1x1 pixel, and there is a certain variation in the formula so that it can be scaled to its natural size, specifically the one below. But if you load the sprite directly with its original size, you have to use the previous one. Once you have applied this formula to scale it to its original size, you can apply the previous formula.

    Gd8Brp7.png

    Right, now, before use AFFINEANIM, we have to know that we must use this before every new AffineAnimCmd function (at least to me without this, it does not work):

    Code:
    AFFINEANIMCMD_FRAME(_xScale, _yScale, 0, 0),

    And later, We write the transformation we want to do.

    For example, let's say that we have made the sprite load with dimensions of 1x1, and what we want to do is increase its size to the original, well we want that k = 1, a speed in x and y of about 16 (by default), so, let's calculate the necessary duration:

    d = (240/16)*1 = 15

    Right, so, now that we have all the parameters, let's write the necessary affineanims.

    Code:
    static const union AffineAnimCmd gSpriteAffineAnim_85B1EA0[] =
    {
        AFFINEANIMCMD_FRAME(16, 16, 0, 0),
        AFFINEANIMCMD_FRAME(16, 16, 0, 15),
        AFFINEANIMCMD_END,
    };

    And the result will be:

    WLenoDu.gif

    But Inmortal, you did not modify anything, that was already like that.

    Okay, so, let's try to reduce it by half.

    Now, we want that k = 1/2, and X and Y = 16, so...

    d = (240/16) * 1/2 = 7.5

    Oops... We obtained a result that we do not want, it have decimals, so... What we can do?

    Easy, reduce the speed (_xScale and _yScale) by half, that is the needed to augment d, because they are inversely proportional

    We have that k = 1/2 and x = 8...

    d = (240/8) * 1/2 = 15

    Nice! We have an integer value, Now, let's adapt parameters:

    Code:
    static const union AffineAnimCmd gSpriteAffineAnim_85B1EA0[] =
    {
        AFFINEANIMCMD_FRAME(8, 8, 0, 0),
        AFFINEANIMCMD_FRAME(8, 8, 0, 15),
        AFFINEANIMCMD_END,
    };

    rwJ53Qi.gif

    Analogously to magnify it.

    To rotate it, we have to know that a value of 4, is proportional to a 90º turn, and therefore, 16, is proportional to a complete turn.

    Clarify that you will also use positive or negative values to choose the direction of rotation (positive, counter-clock wise, negative, clockwise)

    Well, I will leave you a little sequence as an "exercise" so you can try to think what will happen:

    Code:
        AFFINEANIMCMD_FRAME(16, 16, 0, 0),
        AFFINEANIMCMD_FRAME(16, 16, 0, 15),
        AFFINEANIMCMD_FRAME(16, 16, 0, 16),
        AFFINEANIMCMD_FRAME(-16, -16, -4, 16),
        AFFINEANIMCMD_END,

    Spoiler:

    And that is all, I think I have covered in a pretty decent way how animations work in pokeemerald.

    Good luck, and comment any question :)
     
    Last edited:
    448
    Posts
    6
    Years
    • Seen May 6, 2024
    Thank you for writing this guide. I recently looked into affine animations for the first time and this helped me to understand how they work.
    However, I think that the FRAME command could be explained in a simpler way and that the other commands deserve a mention too, so here's my take on the subject:


    AFFINEANIMCMD_END
    This command ends the animation and the sprite's affineAnimEnded bit is set to TRUE.


    AFFINEANIMCMD_FRAME(xScale, yScale, rotation, duration)
    This command will add xScale, yScale and rotation to the sprite's current values every frame for duration amount of frames. If duration is 0 the sprite's current values are instead set to xScale, yScale and rotation.

    xScale and yScale describe the scaling factor as .8 fixed point numbers (divide their value by 256 to get the actual scaling factor).
    rotation describes the angle. A full rotation is 256 units so each unit rotates the sprite by 1/256 * 360 degrees (around 1.04 degrees). Positive values rotate counter-clockwise and negative values rotate clockwise.

    With this knowledge we can interpret Inmortal's examples like this:
    Code:
    static const union AffineAnimCmd gSpriteAffineAnim_85B1EA0[] =
    {
        AFFINEANIMCMD_FRAME(16, 16, 0, 0),    //Set xScale and yScale to 16/256 and rotation to 0.
        AFFINEANIMCMD_FRAME(16, 16, 0, 15),    //Every frame for 15 frames add 16/256 to xScale and yScale, and 0 to rotation.
        AFFINEANIMCMD_END,    //End animation: At this point the value of xScale and yScale is (16+15*16)/256 = 256/256 = 1.0
    };
    WLenoDu.gif


    Code:
    static const union AffineAnimCmd gSpriteAffineAnim_85B1EA0[] =
    {
        AFFINEANIMCMD_FRAME(8, 8, 0, 0),    //Set xScale and yScale to 8/256 and rotation to 0.
        AFFINEANIMCMD_FRAME(8, 8, 0, 15),    //Every frame for 15 frames add 8/256 to xScale and yScale and 0 to rotation.
        AFFINEANIMCMD_END,    //End animation: At this point the value of xScale and yScale is (8+15*8)/256 = 128/256 = 0.5
    };
    rwJ53Qi.gif


    Code:
        AFFINEANIMCMD_FRAME(16, 16, 0, 0),    //Set xScale and yScale to 16/256 and rotation to 0.
        AFFINEANIMCMD_FRAME(16, 16, 0, 15),  //Every frame for 15 frames add 16/256 to xScale and yScale and 0 to rotation.
        //At this point xScale and yScale are 256/256 = 1.0
        AFFINEANIMCMD_FRAME(16, 16, 0, 16),  //Every frame for 16 frames add 16/256 to xScale and yScale and 0 to rotation.
        //At this point xScale and yScale are 512/256 = 2.0
        AFFINEANIMCMD_FRAME(-16, -16, -4, 16), //For 16 frames: subtract 16/256 from scale and 4 from rotation (4/256 of a full clockwise rotation)
        AFFINEANIMCMD_END, //End animation: At this point xScale and yScale are 256/256 = 1.0 and rotation is -4*16=-64, which means 64/256 = 1/4 = 90/360 degrees rotation clockwise
    trYLn41.gif



    You can also use the FRAME command to add delay to the animation by setting all values except duration to 0:
    Code:
    AFFINEANIMCMD_FRAME(0, 0, 0, 10),    //pauses animation for 10 frames

    You can combine this with the duration=0 type to assign different values manually each frame like this.



    AFFINEANIMCMD_LOOP(count)
    This command causes the animation to jump to a loop point. After being executed count times, the next command will be executed instead of looping.
    The loop point is initially at the start of the animation, but it can be set at a position with the command AFFINEANIMCMD_LOOP(0).

    Examples:
    Code:
    static const union AffineAnimCmd LoopExample[] =
    {
        AFFINEANIMCMD_FRAME(256, 256, 0, 0),    //Set scaling to 1.0 and rotation to 0
        AFFINEANIMCMD_FRAME(-16, -16, 0, 15),  //Scale down to 0.125 over 15 frames
        AFFINEANIMCMD_FRAME(16, 16, 0, 15),    //Scale back to 1.0 over 15 frames
        AFFINEANIMCMD_LOOP(2),    //Replay the animation from the start 2 more times
        AFFINEANIMCMD_END,    //End animation after the third iteration
    };
    7qDy0G8.gif


    Code:
    static const union AffineAnimCmd LoopPointExample[] =
    {
        AFFINEANIMCMD_FRAME(12, 12, 0, 0),    //Set scaling to 12/256 and rotation to 0
        AFFINEANIMCMD_FRAME(12, 12, 0, 15),    //Scale to 192/256=0.75 over 15 frames
        AFFINEANIMCMD_LOOP(0),    //Set loop point here
        AFFINEANIMCMD_FRAME(0, 0, 1, 32),    //Rotate sprite counter-clockwise by 45 degrees over 32 frames
        AFFINEANIMCMD_FRAME(0, 0, -1, 32),    //Rotate sprite clockwise by 45 degrees over 32 frames
        AFFINEANIMCMD_LOOP(2),    //Replay the rotations 2 more times
        AFFINEANIMCMD_END,    //End animation after looping twice
    };
    l2XEonQ.gif



    AFFINEANIMCMD_JUMP(target)
    This command will jump to the command at the index of target.

    Example:
    Code:
    static const union AffineAnimCmd JumpExample[] =
    {
        AFFINEANIMCMD_FRAME(256, 256, 0, 0),    //[0] : Set scaling to 1.0 and rotation to 0
        AFFINEANIMCMD_FRAME(-4, -4, 0, 32),    //[1] : Scale down sprite to 0.5 over 32 frames
        AFFINEANIMCMD_FRAME(0, 0, 8, 16),    //[2]: Rotate sprite counter-clockwise by 180 degrees over 16 frames
        AFFINEANIMCMD_FRAME(0, 0, -8, 16),    //[3]: Rotate sprite clockwise by 180 degrees over 16 frames
        AFFINEANIMCMD_JUMP(2),     //[4]: Jump to command [2] => commands [2],[3] and [4] will be repeated forever.
    };
    FB1sqgu.gif



    AFFINEANIMCMD_END_ALT(val)
    This one seems to just be a copy of AFFINEANIMCMD_END, but I'm not sure if it does something different.


    I hope this helps!
     
    Back
    Top