Jump to content

Confirmation / clarification of arm swing parameters used for model animation


jabelar

Recommended Posts

Sorry for the long post but this simple concept has a convoluted implementation in vanilla Minecraft...

 

As most people know, to create the walking animations where your arms and legs swing, the entity's model class uses some parameters and trigonometry to create cyclical rotations to implement the animation.

 

Looking at the code for a while but I'm still fairly confused as to what each field represents.

 

In EntityLivingBase there are these four fields and related methods:

- swingProgressInt

- swingProgress

- getSwingProgress() method seems to return a value between 0 and 1 that takes into account partialTicks

- getArmSwingAnimationEnd() method (comment says "Returns an integer indicating the end point of the swing animation, used by {@link #swingProgress} to provide a progress indicator.) The default value seems to be 6 (and changes with dig potion modifiers).

- updateArmSwingProgress() method seems to increment the swingProgressInt each tick and compare it with the the animation end, returning a ratio between the two.

- limbSwing (comment says "Only relevant when limbYaw is not 0(the entity is moving). Influences where in its swing legs and arms currently

are.

- limbSwingAmount (in moveEntityWithHeading() method, this seems to be the amount that is directly added to limbSwing in each update)

 

Most of these are not overridden by specific entities, but in the models for some entities the math is different using these values.

 

Anyway, putting it all together I think it works like this:

1) every tick the swingProgressInt is updated by 1 and swingProgress is set to ratio of swingProgressInt to the animation end. By default the animation progresses over 6 ticks. But it seems wrong to me that a full animation would only be 6 ticks (i.e. only 1/3 of a second), so I must be understanding something wrong here - is it really 6 ticks for a full cycle?

2) the Render class copies the swingProgress to the associated Model class

3) the Model class does a bunch of math to use the swingProgress (accessed directly) as well as limbSwing and limbSwingAmount (passed as parameters) to rotate the arms/legs.

 

For example, Model Biped has the following code for the right arm rotation x (I deleted the other body parts as well as the parts related to holding bows, sneaking, isChild, to make it easier to follow):

    /**
     * Sets the model's various rotation angles. For bipeds, par1 and par2 are used for animating the movement of arms
     * and legs, where par1 represents the time(so that arms and legs swing back and forth) and par2 represents how
     * "far" arms and legs can swing at most.
     */
    public void setRotationAngles(float p_78087_1_, float p_78087_2_, float p_78087_3_, float p_78087_4_, float p_78087_5_, float p_78087_6_, Entity p_78087_7_)
    {
        this.bipedRightArm.rotateAngleX = MathHelper.cos(p_78087_1_ * 0.6662F + (float)Math.PI) * 2.0F * p_78087_2_ * 0.5F;

        float f6;
        float f7;

        if (this.swingProgress > -9990.0F)
        {
            f6 = 1.0F - this.swingProgress;
            f6 *= f6;
            f6 *= f6;
            f6 = 1.0F - f6;
            f7 = MathHelper.sin(f6 * (float)Math.PI);
            float f8 = MathHelper.sin(this.swingProgress * (float)Math.PI) * -(this.bipedHead.rotateAngleX - 0.7F) * 0.75F;
            this.bipedRightArm.rotateAngleX = (float)((double)this.bipedRightArm.rotateAngleX - ((double)f7 * 1.2D + (double)f8));
        }

        this.bipedRightArm.rotateAngleX += MathHelper.sin(p_78087_3_ * 0.067F) * 0.05F;

    }

 

The math seems a little unusual to me. The comment for the method says "par1 represents the time(so that arms and legs swing back and forth) and par2 represents how "far" arms and legs can swing at most". But then the math for the bipedRightArm.rotateAngleX takes the following steps:

 

        this.bipedRightArm.rotateAngleX = MathHelper.cos(p_78087_1_ * 0.6662F + (float)Math.PI) * 2.0F * p_78087_2_ * 0.5F;

 

This is pretty straight-forward but doesn't exactly match the comments since the second parameter doesn't directly limit "the swing at most". This formula also implies that the first parameter is in radian units, but it is also multiplied by 0.6662F (presumably to slow down the swing rate).

 

That isn't the weird part, but then it does this:

            f6 = 1.0F - this.swingProgress;

            f6 *= f6;

            f6 *= f6;

            f6 = 1.0F - f6;

            f7 = MathHelper.sin(f6 * (float)Math.PI);

            float f8 = MathHelper.sin(this.swingProgress * (float)Math.PI) * -(this.bipedHead.rotateAngleX - 0.7F) * 0.75F;

            this.bipedRightArm.rotateAngleX = (float)((double)this.bipedRightArm.rotateAngleX - ((double)f7 * 1.2D + (double)f8));

 

(The swingProgress is copied from the entity#swingProgress.).

 

So this means that the formula is f7 = sin((1.0F - (1.0F-this.swingProgress)^3)*PI)

 

That math again seems a little unusual to me -- graph it on a graphing calculator and you'll see it is pretty strange function. I guess it works fine in practice, but I'm not sure why they didn't use a simpler trig function -- should be able to just use an arc-sine function or something which would be standard way to make a cycle.

 

The next strange thing is that the f7 gets added to the angle. I think that is weird because I doubt that f7 is really in angle units -- the result of a sine function is unitless (you put angles in, not get angles out). So basically, they are taking a non angle, putting it into a trigonometric function and using the result as an angle. Kinda hacky I think.

 

On top of that, the swingProgress is also used to set a value for f8 which is then also added to the rotation.

 

Finally, after all the above, the rotateAngleX gets the third parameter added to it:

        this.bipedRightArm.rotateAngleX += MathHelper.sin(p_78087_3_ * 0.067F) * 0.05F;

 

Anyway, my point is that the overall math hurts my head. Personally, if I was making a cycle I would simply step it (based on movement) through an arcsine function where the input is limited to whatever swing range is interesting.

 

In conclusion what I am interested to know is: which out of these various parameters and fields is best base value to base my own animation cycles on? swingProgress? swingAmount? etc.

 

I am capable of creating my own progress field (i.e. by monitoring movement and incrementing counter) but was just hoping to understand the built-in ones and make use of those...

 

Anyone have a clear explanation of the fields and math for arm swing in ModelBiped?

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Personally, I would just ignore all of that and make your own, but here's how I understand it:

 

- swingProgressInt, swingProgress, and #getSwingProgress are for when the player performs an action that triggers the 'attack' swing animation, having nothing to do with the cyclical swinging motion of the arms, and that attack animation does indeed take about 6 ticks from start to finish; the actual duration is determined by #getArmSwingAnimationEnd

 

- limbSwing and limbSwingAmount, however, are used for the cyclical arm swinging motion, which increases in frequency (i.e. speed) based on the entity's current velocity. They are also used to calculate the values for the first two float parameters passed to #setLivingAnimations and #setRotationAngles, as can be seen in RenderLivingEntity#doRender.

 

I believe limbSwingAmount represents the entity's actual current angle in radians, or a rough approximation thereof, but honestly I have no idea why

limbSwing - entity.limbSwingAmount * (1.0F - partialTicks)

capped at 1.0F is representative of a time unit, unless they mean 'time' as in progress toward the end of the animation. But even then, it's not the usual way of doing it.

 

Anyway, I wouldn't worry too much about it - the 1st float is basically animation progress (or 'time') and the 2nd float is the target rotation angle (i.e. the 'max'), and you can do whatever you want with those 2 values in combination with all the other values at hand to animate however you like ;)

Link to comment
Share on other sites

Personally, I would just ignore all of that and make your own, but here's how I understand it:

...

Anyway, I wouldn't worry too much about it - the 1st float is basically animation progress (or 'time') and the 2nd float is the target rotation angle (i.e. the 'max'), and you can do whatever you want with those 2 values in combination with all the other values at hand to animate however you like ;)

 

Thanks. The point about some of those being for item swinging makes a lot of sense.

 

Anyway, yeah I figured I'd do it like that (maybe I'll just make my own additional fields in the entity). I was just hoping to understand whether there was a straight-forward way to think about it.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.