Moving an object at a consistent speed from point A to B
Asked Answered
C

1

7

I'm attempting to create my own primitive 2D graphics based game engine. The core component of the game is firing different projectiles at enemies. I need to get this component working before I can continue working.

What I have now moves my projectile along a line going through the Starting point (x, y) and the Target point (x1, x2). I make use of the linear function y = mx + b. The problem is that how I update the position of the projectile causes an inconsistent velocity depending on the slope of the line. (greater slopes cause it to move away more quickly).

Here's the core structure of the game loop I'm running:

    private void executeGameLoop() 
    {

        long nextFrameStart = System.nanoTime();
        while(panel.getRunning()) 
        {
            do 
            {
                panel.update();
                nextFrameStart += FRAME_PERIOD;
            } while(nextFrameStart < System.nanoTime());

            long remaining = nextFrameStart - System.nanoTime();
            panel.repaint();

            if (remaining > 0) 
            {
                try 
                {
                    Thread.sleep(remaining / 1000000);
                } 
                catch(Throwable e) 
                {
                System.out.println(e.getMessage()); 
                }
            }
        }
    }

This is just the mechanism for updating the structure and graphics. Every time this calls panel.update the projectile updates its position, given certain circumstances. Here are the methods that update the projectile:

This tells the projectile it has a target and sets up information about the line.

public void setHasTarget(boolean hasTargetIn)
    {
        if(hasTargetIn)
        {
            deltaX = getTargetX() - getX();
            deltaY = getTargetY() - getY();
            slope = deltaY / deltaX;
            intercept = getY();
            System.out.println("y = " + slope + "x + " + intercept); //line the projectile will follow
        }
        hasTarget = hasTargetIn;
    }

This next method sets the position to the next step on the line. (X is updated by velocity, y is updated dependent on x)

public void setNext()
        {
            float temp = (slope) * (getX() + velocity) + intercept;
            System.out.println("Slope: " + (slope) * (getX() + velocity));
            System.out.println("Current: (" + getX() + ", " + getY() + ")");
            System.out.println("Next: (" + (getX() + velocity)  + ", " + (getY() + temp) + ")");
            setX(getX() + velocity);
            setY(temp);
        }

This last method calls setNext() and is called by the main loop.

public void update()
        {
            if(hasTarget)
                setNext();
        }

As I've said, given my current code, the result when I run is a projectile that moves on the screen at inconsistent speeds dependent on the slope of the line. I would like to be able to change my code so that the projectile moves on the screen at a consistent rate over any trajectory. Thank you in advance for any help.

Capriole answered 11/9, 2013 at 4:16 Comment(0)
S
11

In general the best way to handle directional movement is using trigonometry. Your projectile needs two things for this: direction (in radians) and speed.

The three trig functions you need are sin, cos, and arctan

For updating your X: setX(getX() + (speed * Math.cos(direction)));

For updating your Y: setY(getY() + (speed * Math.sin(direction)));

For calculating the direction: Math.atan(slope)

You should add the fields direction and speed to your class and declare them as doubles.

public void setHasTarget(boolean hasTargetIn)
{
    if (hasTargetIn)
    {
        deltaX = getTargetX() - getX();
        deltaY = getTargetY() - getY();
        direction = Math.atan(deltaY / deltaX); // Math.atan2(deltaY, deltaX) does the same thing but checks for deltaX being zero to prevent divide-by-zero exceptions
        speed = 5.0;
    }
    hasTarget = hasTargetIn;
}

public void setNext()
{
    setX(getX() + (speed * Math.cos(direction)));
    setY(getY() + (speed * Math.sin(direction)));
}
Saretta answered 11/9, 2013 at 4:36 Comment(4)
Thank you, that worked more or less perfectly. I was reluctant to use trig due to my inexperience with it, but I can see why this method is much better.Capriole
No problem. Just a note to be wary of: it will crash if deltaX is 0. Replacing Math.arctan(deltaY/deltaX) with Math.arctan2(deltaY, deltaX) will protect against that.Saretta
That's actually the first thing I noticed. Another note, and perhaps it's an older format, but Math.arctan should be Math.atan. My code is: (float)Math.atan2(deltaY, deltaX)Capriole
Good catch. Apparently I've been doing too much physics lately. =) I'll change that.Saretta

© 2022 - 2024 — McMap. All rights reserved.