1
\$\begingroup\$

I am trying to make an enemy for a space game which has Sine style movement. I pretty much have it working how I want, but right now I have to manually fiddle with the 't' value to make the wave equal on both sides from the starting position x.

I tried first using a real time variable i made from incrementing my 'aliveTime' by Time.deltaTime every update frame.

Then I realised I should probably use its Y position as the timeline because that is what i am trying to base the sideways movement from. But no matter how hard I think (and try random permutations of the formula/code below) I cannot get it to always be 'centred' in the wave (ie. when i change the figures for amplitute and speed_x , it skews the wave off in seemingly unpredictable ways).

Anyone have ideas for what to pass as the 't' parameter in the Mathf Sin line below? (thanks!)

public class Enemy_SineWaveAttack : EnemyBehaviour
{
public float speed_x;
public float amplitute;

protected override void FixedUpdate()
{
    base.FixedUpdate();
    Vector2 vel = rb.velocity;
    vel.x = amplitute * Mathf.Sin(transform.position.y * speed_x); // WHAT CAN I USE HERE TO CENTRE THE WAVE START POSITION?

    vel.y = -Game_Manager.instance.Global_Enemy_Speed * Time.deltaTime;
    rb.velocity = vel;

}
}

Screenshots (if they help to explain what i need) Note, middle enemy is the only one Im talking about here): enter image description here

See how it moves in wave, but it started the wave from original position and moves to the right from there, and NOT to the left of the original position ever)

enter image description here

enter image description here


Ok, before seeing the answer from DM Gregory I tried my own solution which was to subract half from the overall value of 't' .

I ended up with this and it seems to work with various values for speed and amplitute

 protected override void FixedUpdate()
{
    base.FixedUpdate();
    Vector2 vel = rb.velocity;
    vel.x = amplitute * Mathf.Sin((transform.position.y * speed_x) * 0.5f);
    //vel.x = amplitute * Mathf.Sin(transform.position.y * speed_x - ((transform.position.y * speed_x) / 2f));
    vel.y = -Game_Manager.instance.Global_Enemy_Speed * Time.deltaTime;
    rb.velocity = vel;

}
\$\endgroup\$
  • \$\begingroup\$ It is not really clear to me from your explanation what behavior exactly you want and what behavior you get instead. Maybe a few screenshots with the expected and actual enemy movement drawn on them would help. \$\endgroup\$ – Philipp Jul 8 at 16:11
  • \$\begingroup\$ well i will make some screens now, but basically its topdown shooter. the enemy starts top of screen and moves down at constant speed. the x movement is controlled by the Sin function to create a wave effect \$\endgroup\$ – Big T Larrity Jul 8 at 16:13
  • \$\begingroup\$ I want it to start in the centre (ie. where the 'time' line is in image) I tried making the 't' parameter as: transform.position.y * speed_x - (speed / 2f) and lots of other things like that. I got it close, but when i change the values of amplitute and/or speed_x , it skewed the wave (ie it was no longer centred) \$\endgroup\$ – Big T Larrity Jul 8 at 16:20
  • \$\begingroup\$ I still don't understand how the enemy actually moves from your images. But it could have something to do with you changing its velocity and not its position. \$\endgroup\$ – Philipp Jul 8 at 16:23
  • \$\begingroup\$ OMG, once again simply typing my problem up has helped me solve it!! I used this line: vel.x = amplitute * Mathf.Sin(transform.position.y * speed_x - ((transform.position.y * speed_x) / 2f)); and now it is working. :D thanks for trying to understand my babblings , and sorry to have wasted your time. I will perhaps post this as an answer once i have fully tested it out \$\endgroup\$ – Big T Larrity Jul 8 at 16:25
1
\$\begingroup\$

If you want your position to vary as the sine of lifetime, then your velocity, as the derivative of position, should vary as the derivative of sine: ie. the cosine of lifetime.

public float maxHorizontalSpeed = 5f;
public float oscillationsPerSecond;
float spawnTime = 0f;
Rigidbody2D body;

void Start() {
    spawnTime = Time.time;
    body = GetComponent<Rigidbody2D>();
}

void FixedUpdate() {
     float lifetime = Time.time - spawnTime;
     float phase = lifetime * oscillationsPerSecond " 2f * Mathf.PI;

     Vector2 velocity = body.velocity;
     velocity.x = maxHorizontalSpeed * Mathf.Cos(phase );

     body.velocity = velocity;
}

Note that since this is a discrete simulation, we can still accumulate integration errors (we're not sampling the velocity at every point along the curve, only once per physics step, so we can undershoot/overshoot in places). For most applications the errors will be tolerable, but if you find the objects are wandering too far from your desired path, you can instead compute a desired position using sine, then compute a velocity to chase that position. This keeps the object more rigidly "on its rail."

\$\endgroup\$
  • \$\begingroup\$ wowzer thank you for this. Its some new mathematics to try and get my head around yay! :D. Did you see my latter comment above, I seem to have it working now is there any reason why the new code would fail (that you can think of)? I will add it as an edit to the question now... \$\endgroup\$ – Big T Larrity Jul 8 at 16:32
  • \$\begingroup\$ defintely going to try to understand this and add it into the game when i do understand it \$\endgroup\$ – Big T Larrity Jul 8 at 16:38
  • \$\begingroup\$ ah i think i see the problem with my version. The speed value seems to have an effect on the amplitute somehow as if I put high speed values the distance it moves goes way down \$\endgroup\$ – Big T Larrity Jul 8 at 16:41
  • \$\begingroup\$ Sorry to be a pain, but do you know if this code (Your exact code) would oscillate the same speed on most machines, or should i add '* time.FixedDeltaTime' or just usual time.deltaTime? thanks! \$\endgroup\$ – Big T Larrity Jul 8 at 16:55
  • \$\begingroup\$ Your amplitude variable is measuring the amplitude of the velocity sinusoid, not the amplitude of the position sinusoid, which is its integral. The lower the frequency (oscillations per second), the longer the object keeps moving with positive velocity, so it travels further. You can compute your velocity amplitude from your position amplitude if you prefer the Inspector parameter to measure position. \$\endgroup\$ – DMGregory Jul 8 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.