0
\$\begingroup\$

I stumbled across this really neat algorithm which I don't fully understand. It simulates a camera's bobbing effect commonly witnessed in first person shooters while running/walking. I'm mainly unsure about the timer and translation by totalAxes calculations.

Would someone with some more math skills than myself care to explain it on a comment by comment basis? If so, that's much appreciated.

    Vector3 localPosition = transform.localPosition;

    if (Mathf.Abs(horizontal) == 0 && Mathf.Abs(vertical) == 0)
    {
        timer = 0.0f;
    }
    else
    {
        waveSlice = Mathf.Sin(timer);
        timer = timer + bobSpeed;
        if (timer > Mathf.PI * 2)
        {
            timer = timer - (Mathf.PI * 2);
        }
    }
    if (waveSlice != 0)
    {
        float translateChange = waveSlice * bobDistance;
        float totalAxes = Mathf.Abs(horizontal) + Mathf.Abs(vertical);
        totalAxes = Mathf.Clamp (totalAxes, 0.0f, 1.0f);
        translateChange = totalAxes * translateChange;
        localPosition.y = midPoint + translateChange;
    }
    else
    {
        localPosition.y = midPoint;
    }
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

OK, I'll do my best. Having more information about horizontal and vertical would be helpful though, but I'll try to guess. I'm assuming here that those two variables contain information about the movement of the player (or the position of the controller analog stick, which would have the same effect).

Vector3 localPosition = transform.localPosition;

// If the player does not move, set the timer to zero. This will cause the
// else clause of "if (waveSlice != 0)" to be active in this call.
if (Mathf.Abs(horizontal) == 0 && Mathf.Abs(vertical) == 0)
{
    timer = 0.0f;
}
// If the player moves, calculate the sine of how long the player was
// moving.
else
{
    // This is the most important part. The heart of bobbing here is
    // a sine wave, meaning a transition between -1 and 1 with smooth
    // speed decreases at the upper and lower bounds. See footnote.
    waveSlice = Mathf.Sin(timer);

    // bobSpeed is simply the speed at which the bobbing moves. Since
    // timer is the parameter to the function "sine/Mathf.sin", this
    // determines how fast you advance on the sine.
    timer = timer + bobSpeed;

    // Clamp timer, preventing overflow. 2π is chosen since this is
    // the circumference of a circle with radius 1 (which is what is
    // used in "waveSlice = Mathf.Sin(timer)").
    if (timer > Mathf.PI * 2)
    {
        timer = timer - (Mathf.PI * 2);
    }
}
// If there is any translation of the gun position (from the default 
// position)...
if (waveSlice != 0)
{
    // ...calculate how far the gun moves from its default position
    // (bobDistance is one half of the maximum bobbing travel distance
    // (because it moves up and down relative to origin).
    float translateChange = waveSlice * bobDistance;

    // totalAxes is the sum of both absolute stick movement directions
    // clamped to [0, 1], meaning it is an approximation of the movement 
    // speed. This means that movement speed is calculated by fitting the
    // stick locations in a diamond. A circle (the shape a stick can move
    // in in meatspace) would be more appropriate, but also more expensive
    // to calculate (you need a square root), so I guess this is enough.
    float totalAxes = Mathf.Abs(horizontal) + Mathf.Abs(vertical);
    totalAxes = Mathf.Clamp (totalAxes, 0.0f, 1.0f);

    // (Possibly, depending on how far the stick is pushed) reduce the
    // amount of translation to be done. This means less bobbing when
    // the player moves slower.
    translateChange = totalAxes * translateChange;

    // Set the position of the gun either up or down from where its default
    // position is (remember that translateChange can be positive or
    // negative).
    localPosition.y = midPoint + translateChange;
}
// If not, put the gun at its default position.
else
{
    localPosition.y = midPoint;
}

The long comment aboive totalAxes is a bit hard to explain without more drawing, I the above doesn't help you I'll try to explain it further.

If the trigonometry used here is very unfamiliar to you, I encourage you to read up on it. There is a lot of that stuff in video games, and the more you grasp it, the easier it is for you to understand code written by others and solve your own problems when they come up. And it's really not that difficult once you get the hang of it. The internet is full of different approaches of explaining it, just look around until you find one that suits you.

Footnote: For the way waveSlice gets calculated based on the timer, have a look at this GIF where the blue line corresponds to timer and the red line to waveSlice. Also remember that timer only ever increases or gets reset.

\$\endgroup\$
3
  • \$\begingroup\$ From mathematical point of view you can also mention waveSlice = Mathf.Sin(timer); determines where are you on sine, timer = timer + bobSpeed; how much you advance on sine and "Mathf.PI * 2" is for sine repeats every 2pi radians. \$\endgroup\$
    – wondra
    Commented Sep 8, 2014 at 22:32
  • \$\begingroup\$ @wondra: I added a few sentences in respect to that, thanks for your input. But I'm really trying to get across the basic idea here, I have now idea how to explain sine functions without pen and paper or their digital equivalents. And as I added to the answer, I think an understanding of them and their counterparts (cos, tan, etc.) is fundamental to a lot of graphics code. \$\endgroup\$
    – Kolja
    Commented Sep 8, 2014 at 22:44
  • \$\begingroup\$ Exceptional answer. Couldn't have asked for any better. Definitely going to do more research on Sine waves now. \$\endgroup\$
    – Aequitas
    Commented Sep 9, 2014 at 20:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .