2
\$\begingroup\$

I have been making an asteroids clone, in C#. However, the ship's movement is... not quite right.

My plan is this: there is a speed, and two directions, namely the direction the ship is facing (visual), and the one is is going (motion). The ship would travel its speed in its motion direction continually (no friction). If you were to press the up key, however, it would generate a vector of a predetermined value (thrust per step), and it would add this vector with the current, and thus, go more in that direction. However, it is... not working. Here is my code:

KeyboardState keyboard = Keyboard.GetState();

        if (keyboard.IsKeyDown(Keys.Left))
            direction -= 3;
        else if (keyboard.IsKeyDown(Keys.Right))
            direction += 3;

        if (speed == 0)
            speed = 0.1;

        Vector2 currentVector;

        currentVector.X = (float)(Math.Cos(DegreeToRadian.degreeToRadian(motionDirection)) * speed);
        currentVector.Y = (float)(Math.Sin(DegreeToRadian.degreeToRadian(motionDirection)) * speed);

        Vector2 keyVector;

        keyVector.X = ((float)(Math.Cos(DegreeToRadian.degreeToRadian(direction)) * 10)) * (Keyboard.GetState().IsKeyDown(Keys.Up) ? 1 : 0);
        keyVector.Y = ((float)(Math.Sin(DegreeToRadian.degreeToRadian(direction)) * 10)) * (Keyboard.GetState().IsKeyDown(Keys.Up) ? 1 : 0);

        Vector2 newVector = Vector2.Add(currentVector, keyVector);

        if (!(newVector.X == 0 || newVector.Y == 0))
        {
            speed = Math.Sqrt(Math.Pow(newVector.X, 2) + Math.Pow(newVector.Y, 2));
            motionDirection = Math.Atan(newVector.Y / newVector.X);
        }


        if (speed > 10)
            speed = 10;

        position.X += (float) (Math.Cos(DegreeToRadian.degreeToRadian(motionDirection)) * speed);
        position.Y += (float) (Math.Sin(DegreeToRadian.degreeToRadian(motionDirection)) * speed);

Note that this is C# code, but I think the issue is more of a non-language logic issue. Note that C#'s trig functions take radians, and the degreeToRadian works as expected. The keyboard works as you would expect, and is not the issue. The vectors are an object with and x and y, both floats.

The problem? Well, as I said, it.. doesn't really work. It visually turns correctly, but only moves to the right. Holding the up arrow increases its speed, of which the values are yet to be fine-tuned, but does not apply thrust in any direction but the way it was going, seemingly. Debugging through seems to indicate that the y values generated are exceedingly small - say, 3.1x10^-6 or similar. I cannot think of why this would be the case, however. In addition, thrusting left, goes right, so there is more than one issue at play here, it would seem.

Any ideas?

EDIT: I have revised my code to use radians, and it now looks like so:

double speed = 0;
    double motionDirection;
    double direction;

    Polygon polygon;

    Vector2 position;


    public Player()
    {
        polygon = new Polygon();

        polygon.addPoint(new Vector2(50, 0));
        polygon.addPoint(new Vector2(-30, -10));
        polygon.addPoint(new Vector2(-30, 10));
    }

    public void update()
    {
        KeyboardState keyboard = Keyboard.GetState();

        if (keyboard.IsKeyDown(Keys.Left))
            direction -= 0.05;

        if (keyboard.IsKeyDown(Keys.Right))
            direction += 0.05;

        if (speed == 0)
            speed = 0.1;



        Vector2 currentVector = new Vector2((float) (Math.Cos(motionDirection) * speed), (float) (Math.Sin(motionDirection) * speed));
        Vector2 keyVector = keyboard.IsKeyDown(Keys.Up) ? new Vector2((float) (Math.Cos(direction)*2), (float) (Math.Sin(direction)*2))  : Vector2.Zero;
        Vector2 newVector = Vector2.Add(currentVector, keyVector);


        if (Vector2.Distance(Vector2.Zero, newVector) != 0)
        {
            speed = (float)Math.Sqrt(Math.Pow(newVector.X, 2) + Math.Pow(newVector.Y, 2));
            motionDirection = (float)Math.Atan(newVector.Y / newVector.X);
        }

        if (speed > 2)
            speed = 2;

        position.X += newVector.X;
        position.Y += newVector.Y;

It sort of works, and sort of does not. It seems to - but I cannot guarantee this - that it works going, if you picture a circle, in a direction that fallsin the right half, but shudders and has odd shifts of momentum while in the left half.

\$\endgroup\$
2
  • \$\begingroup\$ I don't really understand your final update to the position vector. It seems you compute the ships current motion vector (you called it newVector) and ignore it. You should probably do something like: position += Time.deltaTime * newVector; \$\endgroup\$ Apr 16, 2013 at 18:26
  • \$\begingroup\$ Is that the reason it is messed up, though? I get the desired components (speed, direction) from the new vector, and move using it, just not in the same block, as far as I can tell. \$\endgroup\$ Apr 16, 2013 at 18:49

2 Answers 2

4
\$\begingroup\$

The main reason behind the behavior you notice is that in the line motionDirection <- Math.Atan(newVector.Y / newVector.X); You set motionDirection to a radian measure, but then assume it is a degree measure on the following line. Since radian values are much smaller than degree values, this tends to just push the angle towards 0.

The main reason your code is hard to read is because you aren't using vectors properly. I generally advise people to use radian angles for calculation just so you don't have to keep converting back and forth and forget a conversion here and there. However, oftentimes you can avoid using angles entirely. For example, if you track motionDirection as a vector (we typically call it velocity in that case, and it would serve the same function as your currentVector variable), and direction as a radian, your code would turn out to be something like the following:

var keyVector = keyboard.IsKeyDown(Keys.Up) ? new Vector2(Math.Sin(direction), Math.Cos(direction)) : Vector2.Zero;
var newVector = velocity + keyVector;
var newspeed = newVector.Length();
velocity = newspeed < 10 ? newVector : newVector * 10 / newspeed;
speed = velocity.Length();
position += velocity;
\$\endgroup\$
5
  • \$\begingroup\$ I hadn't considered that (in C++, I didn't use radians for sin and cos), but that is pretty valid, as I convert it everywhere for no real good reason. I will try to make those changes. I am not quite certain what to change, however. I had: Math.Atan(DegreeToRadian.degreeToRadian(newVector.Y / newVector.X)); This seems to exhibit the same behavior as before, however. MSDN seems to indicate that it takes its argument in radians. I seemed to have tried things similar to above in other configurations though (to the Sin functon, or each argument), and it is always the same. I will try radians tho \$\endgroup\$ Apr 16, 2013 at 19:01
  • \$\begingroup\$ Math.Atan(DegreeToRadian.degreeToRadian(newVector.Y / newVector.X)); is definitely wrong. DegreeToRadian.degreeToRadian(Math.Atan(newVector.Y / newVector.X)) would be better. \$\endgroup\$
    – Jimmy
    Apr 16, 2013 at 19:06
  • \$\begingroup\$ This was the first thing I tried. Although, wouldn't that assign a radian to an angle? Hm. I will try to switch everything to radians. I will then reformat the movement as you suggested above. If it is still in error at that point, it should be easier to find. \$\endgroup\$ Apr 16, 2013 at 19:14
  • \$\begingroup\$ oh wait, sorry, my mistake. You'd have to use a radianToDegree. \$\endgroup\$
    – Jimmy
    Apr 16, 2013 at 19:15
  • \$\begingroup\$ sorry this took so long. I edited my answers with the new code that uses radians. Do you see what is wrong with it now? \$\endgroup\$ Apr 17, 2013 at 2:24
1
\$\begingroup\$

I found the problem. First I want to say, Jimmy's answer was useful advice, and I would recommend following it (having everything in radians).

That being said, the problem was incorrect use of atan. Atan has quadrant-case issues, which I did not account for. This can be fixed by using atan2, which was made with that purpose in mind, or manually doing it. You can do it manually by determining the quadrant, and taking specific actions based on which it is to calculate the correct answer. I recommend using atan2 however.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .