Trying to "translate" C++ to C#, fix interpolation movement issue

EDIT: perhaps helpful gif strange problem, I’m not sure if the character is actually moving or not, maybe ever so slightly. also, now, after a key has been used, the “horizontal” movement stops automatically updating but the “vertical” movement keeps going.

(Note that this is just learning/testing, I’m not in “desperate” need of help since this isn’t for a specific game.)

I’ve been trying to follow some “math for game devs” tutorials made by Jorge Rodriguez, and they’re pretty good but they’re in C++ so I’m trying to “translate” them to C# and Unity. (Note: I’m open to the idea of discontinuing them if it’s not ultimately worth trying to “translate” all the time.) Current video

I’m having issues though; for right now I just want to get the smooth interpolation movement working correctly.

Jorge’s source code:

(“approach” function) MathForGameDevelopers/math/maths.h at lerping · BSVino/MathForGameDevelopers · GitHub

Trying to implement Jorge’s version, I have a script that checks horizontal (applied to x) and vertical movement (applied to z) and sets a velocity goal to reach, and a “smooth movement interpolation” script that sets the position and velocity based on the current position, the goal, and deltaTime * a scalar. (I don’t understand 100% how it works but am trying.)

I’m having an issue with position and velocity being applied even when a key isn’t being pressed. “rbVel.x + gravity * Time.deltaTime” is always applying a slight velocity to the rb.velocity because gravity is never 0. For some reason this doesn’t seem to be an issue with the C++ version, “box.vecVelocity = box.vecVelocity + box.vecGravity * dt;”. (Maybe it has something to do with the C++ version returning a “true” bool seemingly whenever a key is pressed? I’m not sure how that bool is used in the C++ Update method though.) If possible I’d like to know why this difference is (why the C++ version character isn’t constantly moving despite gravity always being added [u]vs C# character)[/u], but failing at that just some ideas for solutions. Should I just just use some bools to only let transform and velocity be affected when a key is pressed? (simple solution but I’m curious what the C++ version does different.)

Thanks for any info and help.

C++ portion from MathForGameDevelopers/game/main.cpp at lerping · BSVino/MathForGameDevelopers · GitHub :

bool CGame::KeyPress(int c)
{
    if (c == 'W')
    {
        box.vecVelocityGoal.z = 15;
        return true;
    }
    else if (c == 'A')
    {
        box.vecVelocityGoal.x = 15;
        return true;
    }
    else if (c == 'S')
    {
        box.vecVelocityGoal.z = -15;
        return true;
    }
    else if (c == 'D')
    {
        box.vecVelocityGoal.x = -15;
        return true;
    }
    else if (c == ' ')
    {
        box.vecVelocity.y = 2;
        return true;
    }
    else
        return CApplication::KeyPress(c);
}

// This method gets called when the player releases a key.
void CGame::KeyRelease(int c)
{
    if (c == 'W')
    {
        box.vecVelocityGoal.z = 0;
    }
    else if (c == 'A')
    {
        box.vecVelocityGoal.x = 0;
    }
    else if (c == 'S')
    {
        box.vecVelocityGoal.z = 0;
    }
    else if (c == 'D')
    {
        box.vecVelocityGoal.x = 0;
    }
    else
        CApplication::KeyPress(c);
}

// In this Update() function we need to update all of our characters. Move them around or whatever we want to do.
void Update(float dt)
{
//Approach is called in a separate script; function is shown below
    box.vecVelocity.x = Approach(box.vecVelocityGoal.x, box.vecVelocity.x, dt * 65);
    box.vecVelocity.z = Approach(box.vecVelocityGoal.z, box.vecVelocity.z, dt * 65);

    // Update position and vecVelocity.
    box.vecPosition = box.vecPosition + box.vecVelocity * dt;
    box.vecVelocity = box.vecVelocity + box.vecGravity * dt;

    // Make sure the player doesn't fall through the floor. The y dimension is up/down, and the floor is at 0.
    if (box.vecPosition.y < 0)
        box.vecPosition.y = 0;
}

C++ “Approach” method in a different script:

float Approach(float flGoal, float flCurrent, float dt)
#pragma once
float Approach(float flGoal, float flCurrent, float dt)
{
float flDifference = flGoal - flCurrent;
if (flDifference > dt)
return flCurrent + dt;
if (flDifference < -dt)
return flCurrent - dt;
return flGoal;
}

My C# keyboard script (vecVelocityGoal is set here and used in interpolate script below):

    //the velocity goal
    [HideInInspector]
    public Vector3 vecVelocityGoal;
    public static bool keyDown = false;
    //the fastest the player can move

    public float topSpeedX = 10;
    public float topSpeedZ = 10;

    // Update is called once per frame
    void Update()
    {
        //if moving left
        if (Input.GetAxisRaw("Horizontal") < 0)
        {
            //set the fastest possible speed as negative
            vecVelocityGoal.x = topSpeedX * -1;
        }
        //else if moving right
        else if (Input.GetAxisRaw("Horizontal") > 0)
        {
            //set the fastest possible speed as positive
            vecVelocityGoal.x = topSpeedX;
        }
      
        //if moving down
        if (Input.GetAxisRaw("Vertical") < 0)
        {
            //set the fastest possible speed as negative (down)
            vecVelocityGoal.z = topSpeedZ * -1;
        }
        //if moving up
        else if (Input.GetAxisRaw("Vertical") > 0)
        {
            //set the fastest possible speed as positive (up)
            vecVelocityGoal.z = topSpeedZ;
        }

        //if not moving left or right
        if (Input.GetAxisRaw("Horizontal") == 0)
        {
            //set the fastest possible speed as unmoving
            vecVelocityGoal.x = 0;
        }

        //if not moving up or down
        if (Input.GetAxisRaw("Vertical") == 0)
        {
            //set the fastest possible speed as unmoving
            vecVelocityGoal.z = 0;
        }
    }

My C# “SmoothMovementInterpolation” script:

public class EulerSmoothMovementInterpolation : MonoBehaviour {

    EulerKeyboardControls kc;
    float gravity = 4;
    Rigidbody rb;

    //scale the deltaTime by this (to speed up or slow interpolation)
    float dtScaler = 35;

    // Use this for initialization
    void Start () {
        rb = GetComponent<Rigidbody>();
        kc = GetComponent<EulerKeyboardControls>();
    }

    //interpolate up or down to the goal speed
    float Approach(float goal, float current, float dt)
    {
        //how much left do we have to go before we reach our goal
        float diff = goal - current;

        //if dt is not enough to take us to our goal
        if (diff > dt)
        {
            //return one step farther to our goal
            return current + dt;
        }

        //if interpolating in the downward (on the graph) direction (ie, player is slowing down) and the difference is not enough for us to get to our goal
        if (diff < -dt)
        {
            //return one step farther to our goal, in the downward/slowing direction
            return current - dt;
        }
      
        //return goal if in range (previous returns will stop this return if need be)
        return goal;
    }

    // Update is called once per frame
    void Update()
    {

        //store velocity in a variable to access
        Vector3 rbVel = rb.velocity;

        float dt = Time.deltaTime;
        if (dt > 0.15f)
            dt = 0.15f;

        //apply interpolation method and return x and z velocities and store in vars
        rbVel.x = Approach(kc.vecVelocityGoal.x, rb.velocity.x, dt * dtScaler);
        rbVel.z = Approach(kc.vecVelocityGoal.z, rb.velocity.z, dt * dtScaler);
      
        //update position based on previous position + amount supposed to change * dt (to slow down)
        transform.position = new Vector3(transform.position.x + rbVel.x * dt, transform.position.y, transform.position.z + rbVel.z * dt);
  
        //update velocity based on the previous velocity + gravity * dt (to slow down)
        rb.velocity = new Vector3(rbVel.x + gravity * dt, rb.velocity.y, rbVel.z + gravity * dt);
    }
}

Without looking too much into it: You’re moving the position of your transform. You also have a rigidbody, which moves automatically based on it’s velocity (and gravity). In the c++ version, it seems like the box is only moved by the script. So that’s the main difference.

You generally want to move objects in one of three ways:
1: You do all of the movement yourself by moving the transform
This gives absolute control, at the cost of having to do all collision and such manually
2: You move a rigidbody by setting it’s velocity
This gives very good control of the movement, while still getting collisions and triggers “for free” from the built-in physics system
3: You move a rigidbody by applying forces.
This gives little direct control, but gives the most “realistic” (ie. simulated) physics.

Your c++ script seems to be doing 1. Your C# script is doing xz- movement with both 1 and 2, and y-movement with 2, which is going to give bonkers results.