How to start an object halfway through the sine wave

Hi all. After completing the Mathf.Sin() For Oscillation lecture I was setting up a handful of objects and I ran into a problem. I have three objects oscillating up and down and I wanted one to start at the top, one to start in the middle, and one to start at the bottom. I was able to get the first and last easy enough (start the first object at the top with a movement vector of 0,-40,0, and start the last object at the bottom with a movement vector of 0,40,0) but I was having trouble with the middle object.

I got it working by starting the object at the top of the play space, having a movement vector of 0,-40,0, and then adding a StartingMovementFactor variable that is set to 0 for the other two objects, but is set to 0.75 for this one, and which is added to the float cycles = (Time.time / Period); statement to become float cycles = (Time.time / Period) + StartingMovementFactor; (see full code below), but I’m not sure if it’s the best way of doing it and I’m not sure why everything is working as it is.

For the first object I have the object start at the top of my play area with a Movement Vector of 0,-40,0. When I click play this actually moves the object immediately to the middle of the play area and starts moving down.

For the second object I have the object start at the top of my play area with a Movement Vector of 0,-40,0. When I click play this object starts at the top of the play area and starts moving down.

For the third object I have the object start at the bottom of the play area with a Movement Vector of 0,40,0. When I click play this object immediately jumps to the middle of the play area and starts moving up.

So, my actual questions:

  1. Why do the first and second objects immediately jump to different positions when I click Play? I noticed Rick’s does this too, I’m just having trouble wrapping my head around why.
  2. Is a StartingMovementFactor variable a good way to offset the starting location of the object to be part-way through its movement cycle? Is there something easier/cleaner that I could have done?

Imgur album that shows images for what I’ve described above (since the description might be confusing without them): https://imgur.com/a/ex8xNX0

Code from my Oscillator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Oscillator : MonoBehaviour {
	[SerializeField] Vector3 MovementVector;
	[SerializeField] float Period = 5f;
	[SerializeField] [Range(0, 1)] float StartingMovementFactor = 0f;
	
	float MovementFactor;
	Vector3 StartingPosition;

	// Start is called before the first frame update
	void Start() {
		StartingPosition = transform.position;
	}

	// Update is called once per frame
	void Update() {
		float cycles = (Time.time / Period) + StartingMovementFactor; // Continually growing over time

		const float tau = Mathf.PI * 2; // Constant value of 6.282...
		float rawSinWave = Mathf.Sin(cycles * tau); // Going from -1 to 1

		MovementFactor = (rawSinWave + 1f) / 2; // Recalculated to go from 0 to 1

		Vector3 movementOffset = MovementVector * MovementFactor;
		transform.position = StartingPosition + movementOffset;
	}
}

That got a bit long and kinda complex, so if you need to ask any questions before being able to give an answer please do so. Thanks!

Sine is kinda one component of a circle (cosine being the other). What the oscillator is doing is to calculate the y-coordinate of a circle and in your case the circle’s radius is the y value of your MovementVector. You can change the x and z values, but that will give you some strange movement. The cycles you calculate is really an angle that determines where exactly the sine wave should be at a specific point in time. The StartingMovementFactor offsets this angle.

In Unity, 0° is to the right of the origin (y = 0) so to get the first object to start at y = 1 (for now) you need to start at 90°. To keep the middle object in the middle (assume all objects’ y-coordinates are in the middle) you will start it at 0° or 180°. Starting at 0° will start an immediate upward movement, and 180° will start an immediate downward movement. To start the last object at the bottom, you need to start at 270°. These angles should be in radians when you pass them to Mathf.Sine(...) but this oscillator doesn’t concern itself with angles. It just helps to know what value of your StartingMovementFactor corresponds to what angle. After that, multiplying the result with the MovementVector will get you to the position you want.

To get your code to do exactly what you want, leave all the MovementVector at (0, 40, 0). This is setting the radius of the circle to 40 units, which I assume is what you want. Set your StartingMovementFactor to 0.25, 0, and 0.75. This adjusts the angles that the sine uses to 90 (top), 0 (middle), and 270 (bottom) respectively.

1 Like

But I didn’t answer any of your questions

You are calculating a position for these objects at a specific point in time. On the first frame, this time could be 0 and the position that gets calculated is nowhere near where the object currently sits. So it jumps to where it has to be. Once it’s there, the next frame’s calculated result is no longer that far away and it smoothly moves to the next position

Personally I think it is fine. It may be a little strange to use an angle that is normalised to be between 0 and 1 (as you can see in my previous post) but as far as defining the offset, I don’t see a problem

1 Like

Not gonna lie, I had to read that a couple of times for it to click, but it did end up clicking once I drew the circle and noticed where my pen tip was and what direction it was going at each of the example degrees (0, 90, 270). Now that I understand it a bit better I agree that it’s a bit strange to use a value from 0 to 1 as my starting offset so I may change that up some, but at least I understand the logic of it now.

Yup, that sounds about right. I don’t always manage to put my thoughts into words. At least I think you got it.

I made this Desmos Graph that could help visualise the stuff. Bare in mind that I know virtually nothing about using Desmos.

Moving the Time slider (or pressing the play button next to it) will simulate the oscillator with the values specified. You can also adjust the StartingFactor and see where it will start

2 Likes

That’s so cool! I’d never heard of Desmos before a few days ago, but the more I see of it the more I think I need to spend some time really learning how it works because it could come in very handy. At the very least I’m going to add a bookmark to it in my Programming folder.

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

Privacy & Terms