Generate sprites on specific points on a sine/cosine wave

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 :expressionless:

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.

This results in the following result:

What I’d like to achieve is to calculate the Y position on the sine wave based on the distance from Origo which would end up in the following result:

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);
}

Thank you in advance for your answers!

Use the x position to get the y position:

float x=transform.position.x;
float y=Mathf.Sin(x + Time.time * frequency) * amplitude;
transform.position=new Vector2(x,y);
1 Like

Without seeing your code it’s hard to say.

Are you aware that the Mathf trig functions accept values in radians, NOT degrees?

Use Mathf.Deg2Rad / Mathf.Rad2Deg to convert

2 Likes

Edited my question with the movement code

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.

Note: edited the question with the movement code

transform.position=(new Vector3(transform.position.x-5*Time.deltaTime,Mathf.Sin(transform.position.x)*2f,0));

That will move an object horizontally while waving it up and down.

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:

a-sin-bxcd

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);
    }
}

Final product:

Unity discussions