Search Unity

Question Sine wave based movement

Discussion in 'Scripting' started by DanielF-NQX, Feb 9, 2024.

  1. DanielF-NQX

    DanielF-NQX

    Joined:
    Jun 22, 2023
    Posts:
    25
    I'm having a bit of a mess around trying to do some projectile movement research and one common movement type is the sinusoidal wave.

    Effectively I want to make a projectile / weapon that shoots two bullets and they move in a sine wave for given direction, one bullet is the inverted sine making a helix type of visual.

    Code (CSharp):
    1. Vector3 direction = (target.transform.position - startPosition).normalized;
    2. Vector3 waveDir = new Vector3(direction.y, -direction.x, 0);
    3. Vector3 signVal = waveDir * (flipSign ? -Mathf.Sin(time) : Mathf.Sin(time));
    4. transform.position += ((direction * forwardSpeed * Time.deltaTime) + signVal) * Time.deltaTime;
    5. time += Time.deltaTime;
    This is what I have shambled together



    But my result is that they only touch rather than cross over like I would have expected. I'm guessing its to do with addition more than anything - creating an offset. I have logged out the value of signVal and nothing appeared to be out of the ordinary.

    Questions and answers / tutorials / information I have found all relate to single direction waves (either forward or left/right), never seemingly towards a target.
    Idea being you click the mouse, bullets shoot towards mouse, but for now getting the movement right is the main thing.
     
  2. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    It has any gameplay purpose or only estetic? If the second option then you can push this to animation.
     
  3. DanielF-NQX

    DanielF-NQX

    Joined:
    Jun 22, 2023
    Posts:
    25
    It would be gameplay reasons, both bullets would have their own hit box so if one hits a wall but the other curves over it then its just one bullet and the hit effect can be reduced.
     
  4. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    Ok, so scenerio is such. You shoot, red go up, green go down, they meet again at some point and you want to green go up and red go down? Something like spinning around?
     
  5. DanielF-NQX

    DanielF-NQX

    Joined:
    Jun 22, 2023
    Posts:
    25
    Yeah pretty much, though what you have said just sounds like once sin peaks I switch to cos until that peaks and switch back etc.
    Ideally I just have both running on a sin wave like such (one being sin(x) other being -sin(x))
    upload_2024-2-9_17-52-0.png
     
  6. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,996
    I think the problem is transform.position+=(straight movement + sin wave offset). I think all of the code is fine, but it's the wrong way to write the idea. Because as it is now the first bullet is bending upwards as it moves (while the second bends downwards). In your mind, when sin(0) finally drops to 0 the bullet should be back to the base aim line. But with this code it means "OK, stop bending upwards, stay at the new position, and fly straight this frame". I think if you measure the amount they move up and down it will be more than 1 because of all the adding. (I think it approximates the integral).

    To make it work I think you need something like:
    Code (CSharp):
    1. Vector3 myBasePos=transform.position; // set once when spawned
    2.   ...
    3.   myBasePos+=direction... ;
    4.   transform.position=myBasePos+signVal;
     
    DanielF-NQX likes this.
  7. DanielF-NQX

    DanielF-NQX

    Joined:
    Jun 22, 2023
    Posts:
    25
    That seems to be it, I just reused the startPosition I already had. Thanks

    In retrospective why was the straight movement causing the bend stopping?
    In my mind the sine was about shifting over a straight line (the direction). Without doing some dirty parenting, but the imagined outcome should have been the same - only thing thats happening is rotating the graph surely
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
    You shouldn't need to do most of this.

    Keep a transform that is the actual position of the projectile, moving linearly.

    Use sin() calls to offset the projectile's visual and collision box 90 degrees to the travel direction periodically.

    1. Fire the red with phi == 0

    2. Fire the green with phi == pi

    3. ????

    4. Profit!


    EDIT: Trig note: the way you are doing this, because of the differential relationship between these two trig functions, if you are MOVING a projectile by sin() then that is the dx/dt (velocity)... the time integration of that is the position... so if you move by sin() you will get a cos() wave. If you move by cos() you will get a sin() wave. You probably want to choose the one that will result in the bullets being together at launch, which would be "move by sin() to result in a cos() wave."
     
    Bunny83 likes this.
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,983
    No, the issue is because you are simply adding the value of the sine to your current position it means whenever the sine curve is positive you will be moving upwards. When the sine is back to 0 (after the first half wave) the sine becomes negative and it would start moving down again during the second half wave which is of course negative. As a result once you completed a full period you're back where you started.

    As you might know, the sine and cosine has a special property when it comes to the derivative and integral. Unlike the natural exponential function e^x which is it's own derivative and integral, the derivative and integral of the sine function is the cosine and vice versa. So when you keep integrating you would be getting the sine and cosine function alternating.

    Since you're effectively doing a discrete integral of the sine function, you essentially get the cosine behaviour. It starts at 1, goes downwards to -1 and back up to 1. So you can a one sided deviation from your original position. What you want instead is using the cosine. Integrating the cosine will result in a sine wave.

    Though I'm actually with kurt here. Doing relative motion like that can easily drift from the original direction, especially at different framerate. So it's usually easier to move a gameobject in a straight line and perform the wave movement of a child object in local space. Not by integrating the sine or cosine but simply by using the output of the sine function as the offset / local position. In order to have multiple bullets behave differently, you just need to offset their angle. For two bullets, have them separated by 180°. With 3 you may want to separate them by 120° (like a 3-phase power line).

    Though you could even use an AnimationCurve to define the offset and create any crazy pattern you like in a certain interval. Just make sure the curve repeate seamlessly