Friday, 24 July 2015

Entity Rotations and Animation

This post is just some notes about Entity Animation and Rotations which I had to learn the hard way and might be of interest.

If you are using an EntityLivingBase, it has a few key variables which determine where it is rendered and which way it is facing.  An EntityLivingBase usually has

  • a body which can rotate left<-->right ("yaw"); and
  • a head which can rotate left<-->right ("yaw") and tilt up<-->down ("pitch")




Minecraft variables used for head orientation.

  • The position of the Entity is given by posX, posY, posZ, which corresponds to the centre of the body, at the entity's feet.  In order to get the entity's eyes you need to add Entity.getEyeHeight() to the Y position.
  • EntityLivingBase uses four variables related to the rotation of the Entity:
  1. rotationYawHead, which indicates which way the head is pointing left<-->right.  0 degrees is south and increases clockwise.
  2. rotationPitch, which indicates where the head is looking tilting up/down.  0 degrees is horizontal, -90 is up, 90 is down.
  3. renderYawOffset, which indicates which way the body is pointing, similar to rotationYawHead.  It is used for rendering.
  4. rotationYaw, which indicates which way the body was pointing when it last moved.  This is similar to renderYawOffset, however it is used in different ways not directly related to the way the entity is rendered.
In most cases, these variables are updated in a couple of key methods & classes
  1. EntityBodyHelper (deobfuscated version here) is used to update the body and head rotations:
    a) if the entity is moving, the body is instantly rotated to the direction of movement (rotationYaw).  The head yaw is rotated if necessary to match the body yaw within +/- 90 degrees (i.e. the head must not be twisted past the shoulders)
    b) if the entity is not moving, after a short delay the body is slowly rotated to the direction that the head is facing (rotationYawHead)
    This code is executed on both client and server.
  2. EntityLookHelper is used by AI on the server side to update the head yaw and pitch:
    a) If the entity is looking at a target (another entity, or an [x,y,z] location) then the method sets the head yaw and pitch appropriately
    b) If the entity is not looking at anything, the method slowly returns the head yaw to match the body yaw.
    If the entity is moving, it will also constrain the head yaw to be within +/- 75 degrees of the body yaw.
  3. EntityMoveHelper is used by AI on the server side to set the rotationYaw.
The variables are synchronised occasionally from the server to the client, using for example
S16PacketEntityLook.

When rendering, the variables are used as follows:

RenderManager::
doRenderEntity(Entity entity, 
               double x, double y, double z, 
               float rotationYaw, float partialTicks, 
               boolean showOutline);


this then calls the Entity Renderer:
RendererLivingEntity::

doRender(Entity entity, 
         double x, double y, double z, 
         float rotationYaw, float partialTicks);

This method 
  1. rotates the rendering frame by renderYawOffset
  2. calculates the 'netHeadYaw' (the angle between the head yaw and the body yaw) and then calls setRotationAngles() for the main model (ModelBase)
  3. calls ModelBase.render().
LimbSwing and LimbSwingAmount are animation parameters/timers used for animating the leg positions properly when the entity is moving.

ModelBase::

setRotationAngles(limbSwing, limbSwingAmount, ageInTicks,
                  netHeadYaw, headPitch, 0.0625F, entity);
// 0.0625F is a scaling factor







ModelBase::
render(limbSwing, limbSwingAmount, ageInTicks,
                  netHeadYaw, headPitch, 0.0625F, entity);
// 0.0625F is a scaling factor


Although rotationYaw is passed to the RenderLivingEntity, it doesn't use it for rendering the model.  It is used when rendering some other Entity types, or (for example) when rendering name labels.


3 comments:

  1. YOu are talking about the pitch at the beginning ... but I see that it is never passed to the doRender and doRenderEntity methods. I created a fish mobs, which rotate perfectly, but I was never able to adjust the pitch. I am hunting that thing for months.

    ReplyDelete
    Replies
    1. Hi
      I'm not sure I understand exactly what you mean. Perhaps you are getting head pitch and body pitch mixed up?
      Only heads have pitch; body doesn't have pitch. If you want your fish body to have pitch, you will need to add custom code inside the renderer you use for the body.
      -TGG

      Delete