Skip to content

Instantly share code, notes, and snippets.

@samsartor
Last active May 13, 2024 02:08
Show Gist options
  • Save samsartor/a7ec457aca23a7f3f120 to your computer and use it in GitHub Desktop.
Save samsartor/a7ec457aca23a7f3f120 to your computer and use it in GitHub Desktop.
Code for simulating the elytra item in Minecraft
/**
* An accurate simulation of the elytra item as of Minecraft 15w41b
*/
public class ElytraModel
{
public int glideTime;
public int damageTaken;
public double posX;
public double posY;
public double posZ;
public double velX;
public double velY;
public double velZ;
/**
* Simulates a Minecraft tick (20 per second).
* The pitch and yaw are the look direction of the player.
*/
public void tick(boolean isCreative, double yaw, double pitch)
{
if (!isCreative && (this.glideTime + 1) % 20 == 0)
{
this.damageTaken++;
}
//I did some simplifing of the folowing to reduce the number of negatives and trig functions
double yawcos = Math.cos(-yaw - Math.PI);
double yawsin = Math.sin(-yaw - Math.PI);
double pitchcos = Math.cos(pitch);
double pitchsin = Math.sin(pitch);
double lookX = yawsin * -pitchcos;
double lookY = -pitchsin;
double lookZ = yawcos * -pitchcos;
double hvel = Math.sqrt(velX * velX + velZ * velZ);
double hlook = pitchcos; //Math.sqrt(lookX * lookX + lookZ * lookZ)
double sqrpitchcos = pitchcos * pitchcos; //In MC this is multiplied by Math.min(1.0, Math.sqrt(lookX * lookX + lookY * lookY + lookZ * lookZ) / 0.4), don't ask me why, it should always =1
//From here on, the code is identical to the code found in net.minecraft.entity.EntityLivingBase.moveEntityWithHeading(float, float) or rq.g(float, float) in obfuscated 15w41b
this.velY += -0.08 + sqrpitchcos * 0.06;
if (this.velY < 0 && hlook > 0)
{
double yacc = this.velY * -0.1 * sqrpitchcos;
this.velY += yacc;
this.velX += lookX * yacc / hlook;
this.velZ += lookZ * yacc / hlook;
}
if (pitch < 0)
{
double yacc = hvel * -pitchsin * 0.04;
this.velY += yacc * 3.5;
this.velX -= lookX * yacc / hlook;
this.velZ -= lookZ * yacc / hlook;
}
if (hlook > 0)
{
this.velX += (lookX / hlook * hvel - this.velX) * 0.1;
this.velZ += (lookZ / hlook * hvel - this.velZ) * 0.1;
}
this.velX *= 0.99;
this.velY *= 0.98;
this.velZ *= 0.99;
this.posX += this.velX;
this.posY += this.velY;
this.posZ += this.velZ;
this.glideTime++;
}
/**
* Checks if the player is currently in a gliding state.
* As you can see, if the player is in creative, they will remain gliding even if on the ground. They will stop gliding once they move (but that functionality is not shown here).
*/
public boolean isGliding(boolean isCreative, boolean isOnGround, float fallDistance)
{
if (isCreative)
{
return glideTime > 0;
}
else
{
return !isOnGround && fallDistance >= 1.0f;
}
}
}
BuildElytraData[pos_, vel_] := <|"time" -> 0, "pos" -> pos,
"vel" -> vel|>
BuildElytraData[] := BuildElytraData[{0, 0, 0}, {0, 0, 0}]
ElytraTick[dat_, \[Theta]_, \[Phi]_] :=
Module[{out = dat, vel, pitchcos, pitchsin, sqrpitchcos, look, hvel,
hlook, yacc}, vel = dat[["vel"]]/20; pitchcos = Cos[\[Phi]];
pitchsin = Sin[\[Phi]];
look = {Sin[-\[Theta] - \[Pi]]*-pitchcos, -pitchsin,
Cos[-\[Theta] - \[Pi]]*-pitchcos};
hvel = Sqrt[vel[[1]]*vel[[1]] + vel[[3]]*vel[[3]]];
hlook = pitchcos;
sqrpitchcos = pitchcos*pitchcos;
vel[[2]] += -0.08 + sqrpitchcos*0.06;
If[vel[[2]] < 0 && hlook > 0, yacc = vel[[2]]*-0.1*sqrpitchcos;
vel[[2]] += yacc; vel[[1]] += look[[1]]*yacc/hlook;
vel[[3]] += look[[3]]*yacc/hlook];
If[\[Phi] < 0 && hlook > 0, yacc = hvel*-pitchsin*0.04;
vel[[2]] += yacc*3.5; vel[[1]] -= look[[1]]*yacc/hlook;
vel[[3]] -= look[[3]]*yacc/hlook];
If[hlook > 0, vel[[1]] += (look[[1]]/hlook*hvel - vel[[1]])*0.1;
vel[[3]] += (look[[3]]/hlook*hvel - vel[[3]])*0.1];
vel *= {0.99, 0.98, 0.99}; out["pos"] += vel; out["vel"] = vel*20;
out["time"] += 1/20; out];
ElytraSim[initdat_, \[Theta]f_, \[Phi]f_, time_] :=
Module[{out = {initdat}, t, dat = initdat},
While[dat[["time"]] <= time, t = dat[["time"]];
dat = ElytraTick[dat, \[Theta]f[t], \[Phi]f[t]];
AppendTo[out, dat]]; out]
PlotElytraFlight[datlist_, options_] :=
Module[{points, x, y, z, rangeinfo, range, ranges},
points = datlist[[All, "pos"]][[All, {1, 3, 2}]];
rangeinfo = (range = MinMax[#];
Join[range, {Mean[range], range[[2]] - range[[1]]}]) & /@
Transpose[points];
ListPointPlot3D @@
Join[{points, BoxRatios -> (Max[#, 1] & /@ rangeinfo[[All, 4]]),
AxesLabel -> {"x", "z", "y"}}, options]]
FindFlightEq[initdat_, \[Theta]_, \[Phi]_, err_] :=
Module[{lastdat, dat, con = True}, dat = initdat;
While[con, lastdat = dat; dat = ElytraTick[dat, \[Theta], \[Phi]];
con = Max[Abs[dat[["vel"]] - lastdat[["vel"]]]] > err]; dat]
-----------------------------------------------------------------------
Examples
-----------------------------------------------------------------------
PlotElytraFlight[ElytraSim[BuildElytraData[{0, 0, 0}, {0, 0, 0}], 25 # Degree &, 70 Cos[#] Degree &, 20], {Filling -> Bottom, ImageSize -> Large}]
ListLinePlot[Transpose[Table[Join[{{1}, {1}}*180.*\[Phi]/\[Pi], Partition[FindFlightEq[BuildElytraData[], 0, \[Phi], 0.001][["vel"]][[{3, 2}]]*{1, -1}, 1], 2], {\[Phi], -\[Pi]/2, \[Pi]/2, \[Pi]/64}]], AxesLabel -> {"Pitch", None}, PlotLabel -> "Glide Velocity", ImageSize -> Large, PlotLegends -> {"x", "y"}]
@cuitpoisson
Copy link

In the new snapshot 15w42a, the only modification I saw is line 55:
this.velY += yacc * 3.2; //Used to be 3.5

@zbot223
Copy link

zbot223 commented Aug 5, 2020

How would I use this in a mod I'm making? It doesn't show as an item in the game, and I don't know how to model it.

@samsartor
Copy link
Author

@zbot223 I don't think this code is at all usable code in a mod. It was just to help me reverse-engineer the elytra flight mechanics when the item was first released. I'm using "Model" in the statistical sense here.

It has been a while since I've worked on a mod but my understanding is that Minecraft now has a real 3D model format so you don't have to code the geometry any more. Have you looked at https://minecraft.gamepedia.com/Programs_and_editors/3D_modeling?

@Oreoezi
Copy link

Oreoezi commented Nov 2, 2020

Can you update it for 1.16.3? I am pretty sure it changed a lot since then because it seems to be slower than normal

@max-hooper
Copy link

max-hooper commented Dec 12, 2023

For mathematica, how would I code it so that I get a ListLinePlot of the xy position for a certain pitch over a time period. So it would map its changing poition for 5 seconds for a pitch of 5 degrees or so. This is what i had so far but i think im messing it up (Ive never used mathematica before)
ListLinePlot[Table[ElytraSim[BuildElytraData[], 0, 5, 5], {pos, 0, 100, 10}]]
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment