Why do we need a sinwave and all these codes?

Hi,

I have a couple questions that I am still unclear about this particular code

// to do protect against period is zero
        if (period <= Mathf.Epsilon) { return; } // protect against period is zero
        float cycles = Time.time / period; //grows continually from 0
        
        const float tau = Mathf.PI * 2f; // about 6.28 radian
        
        float rawSineWave = Mathf.Sin(cycles * tau); // goes from -1 to +1

        movementFactor = rawSineWave / 2f + 0.5f;
        //movementFactor = rawSineWave; 
        Vector3 offset = movementFactor * movementVector;
        transform.position = StartingPos + offset;
  1. Why do we need this code at the first place ? do we we want to make the movement of an object (in this case Obstacle) smoother ?

  2. Why do we need a variable float period = 2f , particularly in relation to this line
    float cycles = Time.time / period; //grows continually from 0

  3. What does the comment mean “//grows continually from 0`” ?

  4. Does the variable cycles have a purpose to find a number of total sinewaves in certain period ?

  5. What is the purpose of this line if (period <= Mathf.Epsilon) { return; } . It says that to protect against period is zero. Could you explain to me that comment please ?

Thanks for the clarifications in advanced
Cheers

Hi Ihshan,

What is the purpose of this line if (period <= Mathf.Epsilon) { return; } . It says that to protect against period is zero. Could you explain to me that comment please ?

Log Mathf.Epsilon into your console. You’ll notice that it is a value which is almost 0. Actually, you could have written period == 0 because Mathf.Epsilon is usually used when calculating with floats. All number types in programming are unprecise meaning you do not have a definite number of digits behind the dot. It might be that you are calculating something which is actually 0 but due to the unprecise type, the result is not 0 but 0.000000001 or something like that. It’s an example to visualise the problem.

To solve this, programmers invented Mathf.Epsilon which generates a very low value which is almost 0 to compare a result with it and treat it as though it was 0.

Why do we need this code at the first place ? do we we want to make the movement of an object (in this case Obstacle) smoother ?

Not smoother but it is supposed to appear more interesting. It’s difficult to explain. Go to this website and test the different curves to see how the function affects the movement. Ben is doing something similar with the sine function. You can imitate a part of the sine curve on that website and compare it to the linear movement.

What does the comment mean “//grows continually from 0`” ?

The time starts at 0. 0 seconds, 0.12 seconds, 0.245 seconds, and so on. 1001.02 seconds. You have two axes: time and offset in your imaginary diagram. The offset ranges from -1 to 1, and the time starts at 0 and never ends. With your function, you could pass on 1001.02 seconds to get the offset at this particular moment.


Maybe it helps if we take a brief look at a sine wave. I really like this website:
https://www.desmos.com/calculator/w9jrdpvsmk

I labeled the horizontal axis “time”, and the vertical axis “offset”. When you execute float offset = Mathf.Sin(Mathf.PI / 2);, you get offset == 1. Can you tell me what the result of Mathf.Sin(3f * Mathf.PI / 2) is just by looking at the curve and the axes? If you can do that, you know how Ben utilises this curve.

Pi is approximately 3.14. If we define the horizontal axis in time in seconds, this would mean that the offset is 0 at 3.14 seconds. Time.time represents the game time in seconds.

Does that make sense so far?

If so, we can get this curve by passing on Time.time to the sine method:

float rawSineWave = Mathf.Sin(Time.time);

However, 3.14 is a fairly inconvenient value to intepret. How fast does our object move back and forth completely? According to our wave (see above), it’s 2*PI seconds, 6.28 seconds. Wouldn’t it be nicer if we could say it takes 1 seconds?

float tau = Mathf.PI * 2f;
float rawSineWave = Mathf.Sin(Time.time * tau);

I changed the step to 1 on the x-axis, and I get this result:

image


Now, what happens if we manipulate Time.time by dividing it?

float cycles = Time.time / 2f;
float tau = Mathf.PI * 2f;
float rawSineWave = Mathf.Sin(cycles * tau);

Type the values yourself on the linked website and toy around with them.

image

image


As you can see, the remaining problem is that the values on the y-axis range from -1 to +1. We want 0 to 1.

4 Likes

I don’t know what this is for, but I’ll take a shot at answering some of these because it helps my own learning process.

#1. The only reason I can think of to put Mathf.Sin into a movement script is if you want a steady back and forth movement. That’s what Sin waves are really good for. They can not only move stuff back and forth, but you can predict where something should be in between the two points based at a specific point in time. Because it’s based on a Sin formula. You punch in a time, you get a position. You punch in a position, you get a time. Think of Sin just as a specific relationship between time and position.

#2. Not sure on this one. (edit: Kind of figured this out while thinking of #3, below.)

#3. Well, Time.time starts at 0 and just keeps counting up as the game progresses. Assuming that period is constant and doesn’t change, then yeah, cycles will start at 0 but over time will gradually grow larger. Generally speaking, the cycles will be the number of periods that have passed. So I guess, period would be the total amount of time over which you want your stuff to move from one point to the other. Cycles is the number of periods that have passed. This way you can edit the period as much as you want without interfering with the rest of the formula.

#4. Period is the total time it takes to do one cycle. Cycles are number of total periods that have passed (with a decimal point indicating part of a period has passed).

#5. This one’s a two parter, and is just part of Ben’s style of coding, I think. You’ll notice that we divide by “period” in the next line. Well, we want to make sure that “period” is never 0. Dividing by 0 is a mathmatical impossibility and sometimes does bad things to computer processors in the form of errors and crashes. Your game won’t run properly if you try to divide by 0.

Mathf.Epsilon is a const variable built into Unity. It is equal to the smallest floating value that Unity can keep track of. So if period is equal to or smaller than this, then the function’s just going to return out and not do anything. It’s called an “early escape”. Most of the time, however, this If statement is going to be FALSE because the “period” will be higher than Mathf.Episolon. (I believe you mentioned it’s hardcoded at 2f.) So because the IF statement is false, the {return;} code won’t happen and the method will continue on normally. It’s just a little bit of defensive coding to protect against possible errors.


Basically what this code seems to do is,
Check how much time has passed, and determine how many periods have passed in that time.
Take the typical length of a sin wave and multiply it by the number of periods that have passed.
Get the Sin of this value.
Because sin waves are cyclical, the actual number of periods (cycles) does not matter when calculating the sin. Mathf.Sin(1 * tau) = Math.Sin(2 * tau) = Mathf.Sin(3 * tau) = Mathf.Sin(4 * tau), etc.
So you can ignore the actual number of cycles that have passed. The thing is when you calculate cycles (Time / period), you’re likely going to get a decimal number and this is what’s important. It’s that remainder.
The remainder indicates only part of a period has passed. So let’s say 25 seconds have passed and your period is 2. When you calculate cycles (time / period), you’re going to get 12.5. That means you’ve gone back and forth 12 times. Who cares? If you start at position X, and you go back and forth from position Y 12 times, you’ll still be at position X afterward. It doesn’t matter. That extra 0.5 is important though. It means you’re not at that starting place, you’re half way through a trip back and forth. In this case, it means you’re actually at a different position right now. We can predict that in another 0.5 second, you will be back at position X.

All this means is that if we set a period, at any given point in time, we’re going to get a value from -1 to 1. But the last couple of lines multiply this value by a vector. So now we get a vector that gradually gets larger (up to 1, or 100%) then starts to get smaller (until it gets to 0), then reverses direction and starts again, and keeps doing that.

6 Likes

Hello Nina,

Thank you for this math explanation. I have a better understanding now for how the object moves.

Cheers

Hello @Anthony_Juarez ,

Your simple explanation is awesome. It helps me to have deeper understanding in a simple way after reading Nina’s explanation.

Thanks a lot

Best

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms