- Home /
Convert recorded points to jump movement
The goal
.
To re-create Super Mario Maker 2's player jump in Unity3D using recorded position points in game that were collected from multiple recordings I made.
.
.
The peak of the player's jump is 5.4 units high, and is reached on the 33rd frame when the game is running at 60fps.
.
Current Progress
.
I am able to re-create part of this by following Sebastian Lague's YouTube tutorial series on how to create a 2D Platformer Controller, which shows the following formulas needed for solving for the values of gravity and jump velocity to be able to reach the 5.4 units peak at frame 33 /60fps:
.
gravity = (2 * jumpHeight) / (timeToJumpPeak^2)
jumpVelocity = gravity * timeToJumpPeak
.
I uploaded a copy of my current project folder here if needed. The project is setup to run at 60fps and has the player as a white square jump one frame after it detects the floor. This is for letting us skip frame by frame in the editor to make sure the position matches the SMM2 position recorded. Raycasts are used for collision detection for custom physics.
My Findings
.
I do not believe the jump arc follows a common parabola. It better matches a polynomial when given a 4th order, but I do not know how to convert a polynomial to code. I simply used Excel to draw the jump arc using the recorded position coordinates of the jump arc and found the polynomial option with 4th order lined up with the positions really well:
.
.
I found a GDC video titled "Math for Game Programmers: Building a Better Jump" where they mention using Runge-Kutta (RK4) which looked promising, but I could not find how to relate that technique to coding a jump. I do think the jump is made up of two parts though. When rising the player follows one path (parabola), and when falling the math for gravity pulling the player changes (follows a different parabola).
.
The Y Positions
.
Pasted y positions here if needed: https://pastebin.com/NVRpeGw1
Question
.
Any guidance in the right direction would be helpful, as I have spent a few months trying to find a way to replicate the jump on my own with no luck so far. Thank you
.
Answer by Eno-Khaon · Oct 19, 2020 at 05:15 AM
While I can't speak for Mario Maker's implementation of Mario Bros. physics, I came across a handy, very-tall image which provides some excellent detail on the forces behind Mario's physics (jumping included) in the original Super Mario Bros. game.
Namely, the values 0x4000, 0x0200, and 0x0700 (standing still). Converted to decimal, they're 16384, 512, and 1792 respectively.
First, a bit of analysis: 16384 / 512 = 32
(the same ratio applies to a running jump, despite different values) -- On the first frame, the velocity is 16384. After reducing that value for 32 frames, you're down to 0 on the dot. Therefore, frame 33 will have a vertical speed of 0, putting Mario at the peak of his jump.
With that information in mind, here's a simple conceptual implementation of them:
// Just an example; implement something smarter than this, please
void Update()
{
if(grounded)
{
if(Input.GetButtonDown("Jump"))
{
velocity.y = 16384;
}
else
{
velocity.y = 0;
}
}
else if(Input.GetButton("Jump") && velocity.y >= 0)
{
velocity -= 512;
}
else // Not grounded, not holding jump + moving up
{
velocity -= 1792;
}
transform.position += velocity;
}
At any rate, one of the key points to be made is that an analysis of this movement would find that, despite being simple to implement as an algorithm, it can't as easily be broken down from a mathematics standpoint because the "gravity" of the jump varies so greatly based on the current state of input.
The image is a little hard to understand for me. In the section labeled "Jumping/vertical physics" is has values for "Initial Velocity", Holding A Gravity, and falling Gravity, but I don't know how those are being used.
But I think I understand what you are saying to do, which I believe I am currently doing at the moment.
Right now I used the following formulas to solve for the values of gravity and velocity needed to reach the 5.4 unit height of the jump on the 33rd frame:
gravity = (2 * jumpHeight) / (timeToJumpPeak^2)
jumpVelocity = gravity * timeToJumpPeak
So when the player presses jump, their velocity upwards is 19.63636 and I subtract the value of gravity*time.deltaTime (- 0.5950413) each frame.
If you start with 19.63636 velocity upwards, and subtract -0.5950413 every frame on the 33rd frame your velocity should be about zero.
-0.5950413 * 33 = 19.6363629
The issue I am having is the rising speed is different from the recorded points I collected and my way of making the player jump creates a parabola which does not match the jump arc made in game.
To clarify what the Jumping/Vertical Physics section is talking about, let's look back at a part of my answer:
Initial Velocity (vertical speed, in this case) when not moving, is 0x4000, or 16384 (while running, this value is 0x5000, or 20480). That's $$anonymous$$ario's upward speed on the first frame of a jump. On every frame after that, gravity pushes against $$anonymous$$ario. If the player continues to hold A/jump, that speed is reduced by 0x0200, or 512, every frame (0x0280, or 640, when running). Because 20480/640=32
we know that the 32nd speed reduction will result in 0 upward speed and, furthermore, since that reduction didn't apply on the first frame of the jump, $$anonymous$$ario's upward speed will reach 0 after the 33rd frame.
Following this, when $$anonymous$$ario starts moving downward (or if the player pre-emptively releases the A button), gravity increases to 0x0700, or 1792 acceleration per frame (0x0900, or 2304 when running). By association, we could also say that when jumping upward, $$anonymous$$ario applies a counter-force of 1280 against gravity (1664 on a running jump).
Now, let's put all of this together into what you have:
Based on $$anonymous$$ario making a running jump, an initial upward speed of 19.63636 (20480) with an effective (while jumping) gravity of 0.5950413 (640) means that your baseline gravity when jumping (assu$$anonymous$$g a 60 fps reference point) would be approximately 35.70. This would put falling gravity at 2.8x that, or 99.96.
Now, those numbers are a bit odd, so let's tackle this from a different perspective:
$$anonymous$$ario is 16 (when small) or 32 pixels tall (when large), which means 1 or 2 tiles tall respectively. He jumps up to a maximum height of 0x42000 or, more importantly, 4 blocks + 2 pixels (66 pixels) height when standing still. When running, he jumps to a maximum of 5 blocks, 2.5 pixels (82.5) height. That about matches up with your findings of 5.4 blocks' height (82.5 pixels = 5.15625 blocks).
Continued in next reply
Continued from previous reply
Now, let's change $$anonymous$$ario's effective size to be in blocks. If he's 1 block tall and jumps 5.15625 blocks upward, that means his jumping strength would be approximately 0.3125 and gravity would be 2.109375 (0.03515625 per frame at 60 fps) or 0.5859375 (running) when jump is held, for a difference of 1.5234375
If those are the scale with $$anonymous$$ario set as 1 unit tall, then they can be multiplied from there based on the height you define for $$anonymous$$ario:
// Simple example of height
float marioHeight = mario.Getcomponent<Collider>().bounds.size.y;
// $$anonymous$$ax jump height = 5.15625f "mario" units
float gravity = 2.109375f * marioHeight;
float runJumpForce = 0.3125f * marioHeight;
float runJumpFloat = 1.5234375f * marioHeight;
To convert values from their hexadecimal origins to "block" units, you could use something like this:
float HexToBlocks(string hex)
{
return System.Int32.Parse(hex, System.Globalization.NumberStyles.AllowHexSpecifier) / 65536f;
}
// Example:
float runJumpInitial = HexToBlocks("5000");
float runJumpFloat = HexToBlocks("280") * 60f;
float runJumpGravity = HexToBlocks("900") * 60f;
float runJumpCounterGravity = runJumpGravity - runJumpFloat;
I just now noticed your reply, and am still trying to figure out how to recreate the jump arc.
After looking at the recorded positions a little closer, I saw that $ario has a constant velocity when jumping for 4 frames, and then his velocity changes as if the velocity value is following an array of values over time.
If this is the case, I was trying to find a formula to create a parabola, but there isn't one. The arc could have been manually made using points. I almost remember reading somewhere that an array was used to change the player's position when jumping (on NES).
I now have the following code made to try and recreate the jump using the known velocity to be used at time specific moments, but my code is not frame independent.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Temp$$anonymous$$ove : $$anonymous$$onoBehaviour
{
public float timer = 0.0f;
public Vector3 velocity;
void Start()
{
velocity.y = 14.25f;
}
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
// 5/60 <= timer < 7/60
if (timer >= 5/60f && timer < 7/60f)
{
velocity.y = 13.5f;
}
// 7/60 <= timer < 11/60
if (timer >= 7/60f && timer < 11/60f) // 11/60
{
velocity.y = 12.75f;
}
// 11/60 <= timer < 16/60
if (timer >= 11/60f && timer < 16/60f)
{
velocity.y = 12f;
}
// Update the players position using velocity
transform.Translate(velocity * Time.deltaTime);
}
}
Thanks for trying to explain before. If I find the answer I will update and share for others to learn from too.
Your answer
Follow this Question
Related Questions
Adding a jump feature help? 2 Answers
Others can't jump in my game 0 Answers
Anti-Gravity isn't working 1 Answer
Unity 2D: Variable Jump Height with a Double Jump 0 Answers
Sphere can jump, but doesn't stop. 1 Answer