mojira.dev
MC-75279

Cape physics while sprint-flying

The bug

When sprinting and flying at the same time, capes will "trail" behind you at a >90 degree angle, almost completely vertical. When turning, the cape will phase very awkwardly.

This also happens when the player has a high speed effect (however this only occurs outside the vanilla range for the speed effect).

Code analysis

Code analysis by @unknown can be found in this comment.

Linked issues

Attachments

Comments 4

I won't be able to test this anymore, if anyone else can confirm they can update the affected versions.

The following is based on a decompiled version of Minecraft 1.12 using MCP 9.40pre-1. (Also, can confirm for Minecraft version 1.12 and 1.12.1)

The method responsible for capes being rendered on players' backs is net.minecraft.client.renderer.entity.layers.LayerCape.doRenderLayer(). In this method, there are a lot of different values which calculate a cape's physics on every game tick. The only two values which are important in this case are float f2 and float f3. f2 is the vertical rotation of the cape which starts from the player's back at 0 (positive Y/positive Pitch). This value has already been clamped by a Mojangster using a simple if-statement, so it can't be lower than zero (negative Y/negative Pitch) and therefore can't glitch through the player character when moving backwards.

if (f2 < 0.0F)
{
	f2 = 0.0F;
}

f3 is the horizontal rotation of the cape, starting from the center of the players' backs. This value decrements when moving to the right (negative X/negative Yaw) and increments when moving to the left (positive X/positive Yaw). To stop the cape from glitching into the player character or detach from its anchor point, those two values need to be clamped in the between two values where the cape doesn't glitch out. (This also fixes MC-5037)

public void doRenderLayer(...)
{
	if (...)
	{
		if (...)
		{
			// ...
			float f2 = (float)(d0 * d3 + d2 * d4) * 100.0F;
			float f3 = (float)(d0 * d4 - d2 * d3) * 100.0F;
			
			// Clamping f2 and f3 ...
			f2 = MathHelper.clamp(f2, 0.0F, 150.0F);
			f3 = MathHelper.clamp(f3, -20.0F, 20.0F);
                
			float f4 = entitylivingbaseIn.prevCameraYaw + (entitylivingbaseIn.cameraYaw - entitylivingbaseIn.prevCameraYaw) * partialTicks;
			f1 = f1 + MathHelper.sin((entitylivingbaseIn.prevDistanceWalkedModified + (entitylivingbaseIn.distanceWalkedModified - entitylivingbaseIn.prevDistanceWalkedModified) * partialTicks) * 6.0F) * 32.0F * f4;
			if (entitylivingbaseIn.isSneaking())
			{
				f1 += 25.0F;
			}
			// ...
		}
	}
}

For f2 I used 0.0F as the minimum value so it doesn't glitch into the players body when the player's moving backwards (I basically just replaced the if-statement here) and 150.0F as the maximum value so it doesn't look like the cape extends from the player's head when the player's sprint-flying. After a few tests, I decided to use 20.0F and -20.0F as the bounds for f3 because it simply looked the best IMO. However, since this is only my opinion and therefore might not apply to everyone, please make sure to alter those values at your convenience.

This is how flying looks like with clamped values.

As always, this might not be the best solution for this issue but it might give one an idea of how it could be fixed. 🙂

A few months back, I tested with version 1.8.9 capes to make it more natural and this is one issue I looked at because clamping itself makes the cape have a hard stop which, in my perspective is unnatural, I found that using a logistic function of sorts to "smooth" out the values, though what I have used is a rough function and static numbers are used, it is not production level, but I will throw it out here as information.

[media][media]

{*}{*}

"y" as in the output and "x" as in the swing/f2 variable per the comment, "M" as the "max height", the static number of 6 is a sort of speed of how sloped the curve be, I.E 1 is a steeper slope and 10 is a gradual slope. 

Here is my used case for my example in version 1.8.9:

public void doRenderLayer(...) {
    if (...) {
        // ...
        float min = ...;
        swing = MathHelper.clamp_float(logiFunc(swing, 170), min, 150.0F);
        if (swing < 0.0f) {
            swing = 0.0f;
        }
             // ...
    }
}


private float logiFunc(float swing, int height) {
    // y = x / (sqrt(6 + (x/M)^2))
    return swing / ((float) Math.sqrt(6 + (Math.pow(swing / height, 2))));
}

There is a flaws in this function, such as  "M" the "max height"  the more gradual the slope is the "lower" the height is (hence why in my case I used a higher number for the max then my preferred 150).

It will never exceed the "M" value so using a clamp in this case was redundant protection for over 150 and also clamp the value from going under 0.

Test: https://youtu.be/4K0fd63xLdc

I am in any way not a mathematician and this is just from researching, just wanted to put this out to see if someone can improve and perhaps even have it implemented in a similar fashion.

 

Christie N

migrated

Confirmed

cape, fly, sprint

Minecraft 1.8.1, Minecraft 1.8.3, Minecraft 1.8.8, Minecraft 15w31a, Minecraft 15w33c, ..., Minecraft 1.12.1 Pre-Release 1, Minecraft 1.12.1, Minecraft 1.12.2 Pre-Release 1, Minecraft 1.12.2 Pre-Release 2, Minecraft 1.12.2

Minecraft 17w48a

Retrieved