Search code examples
javalibgdxtrigonometryangle

Libgxd: Rotate an object to angle necessary to move to another point


Ok, so I have tried many different approaches and I have landed with one that works some of the time. The idea is to get a bee sprite to move toward a flower sprite, here are the things I have accomplished with this so far:

  1. Bee can rotate to the left or right based on a movement speed
  2. Bee can move in the direction that it is rotated (by using the sin and cos of angle)
  3. Bee is sometimes able to make it to the flower and sometimes not (usually the flower is very close when this happens)

This is the working example:

enter image description here

The bee turns to the right and eventually hits the angle it is looking for in the condition and makes a beeline (no pun intended), for the flower.

On the other hand, when the flower is closer to the bees starting point (the middle of the screen):

enter image description here

It goes around the right side of the flower and down into the bottom of the window.

Here is the code for the bee class:

public class Bee {
    public Vector2 position;
    public Sprite sprite;
    public float topSpeed = 300f;
    public float xSpeed = 0f;
    public float ySpeed = 500f;
    public float rotationSpeed = 200f;
    public float rotationAngle = 360f;
    public float moveAngle;
    public Vector2 flowerPosition;
    public boolean collided = false;

    public Bee(Texture img, Vector2 flowerVector) {
        sprite = new Sprite(img);
        sprite.setScale(2);
        position = new Vector2(Gdx.graphics.getWidth()/2f - sprite.getWidth(),
                Gdx.graphics.getHeight()/2f - sprite.getHeight());
        flowerPosition = flowerVector;
    }

    public Rectangle getBoundingBox() {
        return sprite.getBoundingRectangle();
    }

    public void updateCollided(boolean collidedBool) {
        collided = collidedBool;
    }

    public boolean beeHittingRightWall() {
        return position.x + sprite.getWidth() > Gdx.graphics.getWidth();
    }

    public boolean beeHittingLeftWall() {
        return position.x < 0;
    }

    public boolean beeHittingTopWall() {
        return position.y + sprite.getHeight() > Gdx.graphics.getHeight();
    }

    public boolean beeHittingBottomWall() {
        return position.y < 0;
    }

    public void updateBeeCoordinates(float deltaTime) {
        position.x += deltaTime * xSpeed;
        position.y += deltaTime * ySpeed;
    }

    public void checkBeeHittingBorders() {
        if(beeHittingRightWall()) {
            position.x -= sprite.getWidth();
        }
        if(beeHittingLeftWall()) {
            position.x += sprite.getWidth();
        }
        // Check if bee is hitting top and bottom walls
        if(beeHittingTopWall()) {
            position.y -= sprite.getHeight();
        }
        if(beeHittingBottomWall()) {
            position.y += sprite.getHeight();
        }
    }

    // gets quadrant of coordinate
    public float getCoordinateQuadrantMinuend(float x, float y) {
        float yAxis = Gdx.graphics.getWidth() / 2f;
        float xAxis = Gdx.graphics.getHeight() / 2f;

        if(x > yAxis && y > xAxis) {
            return 90f;
        }
        else if(x > yAxis && y < xAxis) {
            return 180f;
        }
        else if(x < yAxis && y < xAxis) {
            return 270f;
        }
        else {
            return 360f;
        }
    }

    // gets the angle bee needs to be rotated to, to move to a certain coordinate
    public float getPathfindingRotationAngle(float x1, float y1, float x2, float y2) {
        float c = (y2 - y1) / (x2 - x1);
        float minuend = getCoordinateQuadrantMinuend(x1, y1);

        return minuend - MathUtils.atanDeg(c);
    }

    public float targetAngle() {
        return 360 - getPathfindingRotationAngle(flowerPosition.x, flowerPosition.y, position.x, position.y);
    }

    public void moveForward(float deltaTime) {
        if(!collided) {
            moveAngle = targetAngle();
            updateBeeCoordinates(deltaTime);
            checkBeeHittingBorders();
        }
    }

    public void rotateLeft(float deltaTime) {
        rotationAngle += deltaTime*rotationSpeed;
        if(rotationAngle > 360) {
            rotationAngle -= 360;
        }
    }

    public void rotateRight(float deltaTime) {
        rotationAngle += 360 - deltaTime*rotationSpeed;
        if(rotationAngle > 360) {
            rotationAngle -= 360;
        }
    }

    public void updateRotationAngle(float deltaTime) {
        float myAngle = targetAngle();
        if(rotationAngle > myAngle) {
            rotateRight(deltaTime);
        }
        if(rotationAngle < myAngle) {
            rotateLeft(deltaTime);
        }
    }

    // This is called update velocity
    // because velocity implies a speed and direction
    // since it is a vector (this function does both of these)
    public void updateVelocity() {
        // Adjust X and Y speeds based on angle

        /*
            looks like this:
                   0
                  360
                   |
             90____|____270
                   |
                   |
                  180
         */

        float xSpeedMultiplier;
        float ySpeedMultiplier;
        if(rotationAngle < 360 && rotationAngle > 180) {
            xSpeedMultiplier = Math.abs(MathUtils.sinDeg(rotationAngle));
        }
        else{
            xSpeedMultiplier = -1 * (MathUtils.sinDeg(rotationAngle));
        }
        ySpeedMultiplier = MathUtils.cosDeg(rotationAngle);


        xSpeed = topSpeed * xSpeedMultiplier;
        ySpeed = topSpeed * ySpeedMultiplier;
    }

    public void Update(float deltaTime) {
        moveForward(deltaTime);
        updateRotationAngle(deltaTime);
        updateVelocity();
    }

    public void Draw(SpriteBatch batch) {
        Update(Gdx.graphics.getDeltaTime());
        sprite.setPosition(position.x, position.y);
        sprite.setRotation(rotationAngle);
        sprite.draw(batch);
    }
}

The main function I have been trying to debug is updateRotationAngle(float deltaTime).

Thank you in advance for any help.


Solution

  • Problematic scenario I can see in yours code is the angle comparison. In the scenario when current angle is 1° and target is 359°, then the bee would have to perform almost whole circle rotation. You should be calcuating the direction by this formula or similar way: ((myAngle-rotationAngle)%360+360+179)%360-179 and then based on the fact if number is positive or negative change your direction.

    The other thing I see the use of atan function and I am not sure if you perform the quadrant correction right. The simplest thing you can do is to use atan2Deg function instead which doesn't require quadrant correction. The arguments will be y2 - y1, x2 - x1. Also I can see you are trying to perform some angle correction in updateVelocity, which I am also not sure if is correct.