I’m working on a system that can generate sprites on a sine/cosine wave. The sprite movement over a sine wave is already done and working well, the only thing I can not wrap my head around is how to spawn the sprites on specific points over the sine/cosine wave

I know the distance from the Origo which is based on an iterative function and I might be able to calculate the time that the sprite would need to reach the Origo based on its speed. So let’s say that the position is:

var pos = new Vector2(
DistanceFromOrigo + DistanceBetweenSprites * index,
0, // This is the value that would need be calculated
);

Where:

DistanceFromOrigo – 300f

DistanceBetweenSprites – 50f

index is the index of the sprite generated starting from 0.

Is this done via deriving? Is this much more simpler and I need to calculate the length of adjacent/opposite sides of a right triangle? If yes, what trigonometric function would I use?

Edit: Updated the post with the movement code

// distance is set at initialisation
_distance = DistanceFromActionArea + DistanceBetweenCombos * Index;

private Vector2 Move(ComboButton cb, Vector2 to) {
var rt = cb.GetComponent<RectTransform>();
var from = rt.anchoredPosition;
var cbSpeed = cb.Speed;
var speed = ComboController.SpeedMultiplier * (cbSpeed * 0.5f) * Time.deltaTime;
// Calculate angle once, otherwise it would stop at the target position
if (!_angleSet) {
var adjacent = to.x - from.x;
var opposite = to.y - from.y;
_angle = Mathf.Atan2(adjacent, opposite);
_angleSet = true;
}
var period = _distance * Time.deltaTime;
var posX = from.x + Mathf.Sin(_angle) * Time.deltaTime;
// var posY = from.y + Mathf.Cos(period + _elapsedTime) + speed;
_elapsedTime += Time.deltaTime;
cb.anchoredPosition = new Vector2(posX, posY);
}

That actually solves the placement – sort of, I still have to figure out the proper amplitude – but now the problem is the movement. When it’s moving towards the Origin, it is moving along the Sine wave but the movement is a bit weird.

Thanks @zulo3d, your solution led me down the right path to come up with the proper format. The key was to use the oscillation of the Sine wave based on the distance from the Origo and to set the vertical offset of the Sine wave to the Origo’s Y position. The other key was to use (obviously) the same equation for both the initialisation of the sprites and for the movement as well with the addition of adding the elapsed time as the phase shift.

A very useful graph to help me was the following:

The initialisation happens in GetStartPos(), while the movement happens in the Move() part of the script.

Note: These are just segments of the full script and are just for demonstrative purpose. Actual script is abstract and is being called by a Controller, etc…

public class SineMovement {
private const int DistanceFromActionArea = 300;
private const int DistanceBetweenCombos = 180;
private float _angle;
private float _amplitude;
private float _distance;
private bool _angleSet;
private float _elapsedTime;
public void Vector2 GetStartPos(ComboButton cb, Vector2 anchoredPos) {
_distance = DistanceFromActionArea + DistanceBetweenCombos * Index;
_amplitude = 100f;
var xPos = anchoredPos.x + _distance;
var yPos = anchoredPos.y + Mathf.Sin(_distance * cb.Speed) * _amplitude;
return new Vector2(xPos, yPos);
}
public void Vector2 Move(ComboButton cb, Vector2 to) {
var rt = cb.GetComponent<RectTransform>();
var from = rt.anchoredPosition;
var hSpeed = cb.Speed * ComboController.SpeedMultiplier;
float posX = 0;
float posY = 0;
// Calculate angle once, otherwise it would stop at the target position
if (!_angleSet) {
var adjacent = to.x - from.x;
var opposite = to.y - from.y;
_angle = Mathf.Atan2(adjacent, opposite);
_angleSet = true;
}
posX = from.x + Mathf.Sin(_angle) * hSpeed * Time.deltaTime;
posY = to.y + Mathf.Sin((_distance + _elapsedTime) * cb.Speed) * _amplitude;
_elapsedTime += Time.deltaTime;
return new Vector2(posX, posY);
}
}