6

I want to move the star (coin in my code) to the exact top right corner of the screen when the car hit the star. Both star and road are moving downward at a constant speed during each update. The car does not move but appears to be moving upward because of the road moving downward. Although it can move to the left and right lane on user's command.

So I calculated the angle between the star and the top right corner of the screen using the following method

public double AngleBetween(Vector2 a, Vector2 b)
{
    return Math.Atan2(b.Y - a.Y, b.X - a.X);
}

In my Update method, the following calculate the velocity to move and send it to the top right corner of the screen

double angleBetween = coin.AngleBetween(coin.Position, new
   Vector2(currentGame.GraphicsDevice.Viewport.Bounds.Right, 0));              
collidedCoinVelocity = new Vector2((float)Math.Sin(angleBetween), 
   -(float)Math.Cos(angleBetween));

In my Draw method, I updated the coin.Position using

coin.Position += collidedCoinVelocity * 10 ;

The problem is the star (coin) is not sent to the top right corner as I wanted but it's somewhere in the middle of the right screen bound.

When the star is hit when it's on the right lane, the angle between it and the top right corner is always

1.2196048576751 radians = 69.878211 degree

When the star is on the left lane the angle is

0.952588487628243 radians = 54.5793 degree

Am I calculating the angle correctly, what am I missing? Perhaps I am forgetting to consider the downward movement of the star?

enter image description here

enter image description here

EDIT

I have updated the image to show the angle I am trying to calculate and edited my question to make it clearer.

EDIT 2

Added a second image to show where the star goes after being hit.

PutraKg
  • 2,226
  • 3
  • 31
  • 60

4 Answers4

4

It seems you swapped sin and cos by accident, and there seems to be a random negative in there. Thus this line

collidedCoinVelocity = new Vector2((float)Math.Sin(angleBetween), 
   -(float)Math.Cos(angleBetween));

should probably be

collidedCoinVelocity = new Vector2((float)Math.Cos(angleBetween), 
   (float)Math.Sin(angleBetween));

Although you don't even need to calculate so many angles. To get a unit vector without angles just use

double dx = b.X - a.X;
double dy = b.Y - a.Y;
double mag = Math.Sqrt(dx * dx + dy * dy);
collidedCoinVelocity = new Vector2(dx, dy) / mag;
Zong
  • 6,160
  • 5
  • 32
  • 46
  • I tried swapping sin and cos as you suggested, but the star is still not being sent to the top right corner of the screen. – PutraKg Sep 14 '13 at 04:50
  • @PutraKg I think it may have to do with how you negated the y-axis value. As long as you're consistent you shouldn't have to do this. – Zong Sep 14 '13 at 04:56
  • OK remove the negated y-axis value. Now the star is sent closer to the top right corner of the screen but not quite there yet. It appears that the angle calculated is smaller than it should? – PutraKg Sep 14 '13 at 05:03
  • @PutraKg Are you still using (cos, sin) as it should be? And if you haven't you can try the simple normalization way of doing it. – Zong Sep 14 '13 at 05:06
  • You are right I do not have to calculate that angle but I think I have to go with the other answer because I think it's a little bit clearer for me. – PutraKg Sep 17 '13 at 08:56
2

Erhm... Just FYI; converting from cartesian coordinates to using angle, and then back to cartesian makes no sense here.

Just derive your final velocity like this:

Vector2 direction = new Vector2(currentGame.GraphicsDevice.Viewport.Bounds.Right, 0) - coin.Position;
direction.Normalize();

Velocity = direction * 10;
Position += Velocity;

Also; NEVER update position in Draw. Draw is for Drawing, not updating. Updating stays in Update!

Also 2; You should generalize your code. all moving objects should inherit the same base that includes things like Velocity, Position and Acceleration, and the code to handle these. That way, you only have to change your logic to manipulate Velocity and/or Acceleration to make stuff move.

MovingObject.Update:

Velocity += Acceleration * deltaTime;
Positioin += Velocity * deltaTime;

(deltaTime = time in seconds since last frame, or (float)gameTime.ElapsedGameTime.TotalSeconds)

Then you just use base.Update() at the end of your subClasses update, and position and speed will work :) as long as you set the correct values.

1

I think you should do:

public double AngleBetween(Vector2 a, Vector2 b)
{
    return Math.Atan2(-(b.Y - a.Y), b.X - a.X);
}

Considering the reversed Y axis you need to negate Y components.

Then, as Zong Zheng Li wrote, you've swaped Sin and Cos:

collidedCoinVelocity = new Vector2((float)Math.Cos(angleBetween), 
   -(float)Math.Sin(angleBetween));

but you still need to negate the Vector2.Y component, due to the reversed Y axis.

pinckerman
  • 4,115
  • 6
  • 33
  • 42
  • Yes, the y axis for the graphics surface is "reversed", but it shouldn't matter as long as that's the axis you use for the game as well. – Zong Sep 14 '13 at 21:49
  • I always do it in this way, if I don't negate the Y axis I always find something that don't work correctly. – pinckerman Sep 14 '13 at 21:53
  • Well, you have to negate it (and do so back and forth) only if you choose to think of the game as having a "normal" y axis and the graphics having a "reversed" y axis. If you are always consistent you avoid this completely. – Zong Sep 14 '13 at 21:56
  • Yeah, I prefer to think as having a normal Y axis, I find it easier when handling trigonometry. – pinckerman Sep 14 '13 at 22:00
0

Vector2 provides a static function for interpolation between points.

star_pos = Vector2.Lerp(position_when_hit, destinatin, percent);
                percent += 0.005f;

Using this method, when star is hit, set Point A and Point B.
Pass it to the Vector2.Lerp function.
Include a Percentage_of_distance_moved float value.

This is the modified star class:

class star
    {
        Texture2D starT;
        public Vector2 star_pos;
        Vector2 position_when_hit;
        Vector2 destinatin;
        float percent;
        bool star_moving_up;

        public star(Random rand,Texture2D star)
        {
            this.starT = star;
            star_moving_up = false;
            destinatin = new Vector2(800, 0);
            star_pos = new Vector2(rand.Next(500), rand.Next(500));
        }
//Try to call this only once
        public void on_hit(){
            if (!star_moving_up)
            {
                position_when_hit = star_pos;
                star_moving_up = true;
            }
        }

        public void update(GameTime gameTime)
        {
            if (star_moving_up)
            {
                star_pos = Vector2.Lerp(position_when_hit, destinatin, percent);
                //a larger percent value will move the star faster.
                percent += 0.001f;
            }
            else
            {
                //Your Logic for moving it down
            }
        }

        public void draw(SpriteBatch sp)
        {
            sp.Draw(starT, star_pos, Color.White);
        }
}

And this is how I used it:

public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D star;
        Random rand;
        star STAR;



public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }


    protected override void Initialize()
    {
        graphics.PreferredBackBufferWidth = 800;
        graphics.PreferredBackBufferHeight = 600;

        base.Initialize();
    }


    protected override void LoadContent()
    {
        rand = new Random();
        spriteBatch = new SpriteBatch(GraphicsDevice);            
        star = Content.Load<Texture2D>("Bitmap1");
        STAR = new star(rand, star);

    }


    protected override void UnloadContent()
    {

    }


    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
//calls on_hit every update call after 4 seconds. just for demo purpose. 
        if (gameTime.TotalGameTime > TimeSpan.FromSeconds(4))
            STAR.on_hit();
        STAR.update(gameTime);
        base.Update(gameTime);
    }


    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();
        STAR.draw(spriteBatch);
        spriteBatch.End();
        base.Draw(gameTime);
    }
}
pinckerman
  • 4,115
  • 6
  • 33
  • 42
rahulroy9202
  • 2,730
  • 3
  • 32
  • 45