[SOLVED] Move one game object to another (moving) game object with libGDX

I need to do what I thought would be a fairly simple and common task - move one game object (some sort of throwing/shooting weapon) to another object (the target of the shot). The target will be moving slowly and I want the attack to always hit.

I googled extensively for this, but only found a few things that might help and none of them were Java or libGDX specific. The resources I did find were this one and this one. The last one references libGDX, but the answer doesn’t seem to me to be libGDX specific.

As a result of trying to translate those solutions into libGDX/Java I now have the following in the class representing the object to be moved:


Vector2 direction;

    public void init(TigerActor target) {
        this.target = target;
        direction = new Vector2();
        direction.x = target.getX() - position.x;
        direction.y = target.getY() - position.y;
        direction = direction.nor();
    }

@Override
    public void act() {
        position.x += direction.x;
        position.y += direction.y;
    }

The class TigerActor is a purely custom class inspired by a Scene2D actor but not a Scene2D Actor because I’m not using a Stage here.

The code above doesn’t work - it simply causes the moving object to fly along very quickly on a horizontal line.

Any suggestions on how to improve on this code or how to do it properly from “scratch” would be very much appreciated.

Thanks in advance.

The code itself is fine, I have tested it in libgdx with this code:

package com.mygdx.gtest;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.math.Vector2;

public class Test extends ApplicationAdapter{
	Bullet b;
	TigerActor t;
	
	@Override
	public void create() {
		b = new Bullet();
		t = new TigerActor();
		b.init(t);
	}
	

	@Override
	public void render() {
		b.act();
	}


	@Override
	public void dispose() {
		super.dispose();
	}
	
	private class Bullet {
		TigerActor target;
		Vector2 direction;
		Vector2 position = new Vector2(40,40);
		
		public void init(TigerActor target) {
			this.target = target;
			direction = new Vector2();
			direction.x = target.getX() - position.x;
			direction.y = target.getY() - position.y;
			direction = direction.nor();
			System.out.println(direction);
		}
		
		public void act(){
			position.x += direction.x;
			position.y += direction.y;
			System.out.println(position);
		}
	}
	
	private class TigerActor{
		int x = 10;
		int y = 10;
		
		public int getX(){
			return this.x;
		}
		public int getY(){
			return this.y;
		}
	}
}

Try printing out or debugging the code and inspect the target position and direction.

Thanks for that - at least if it’s working for you, I know I must be close…

I tried adding some debugging:


if (Gdx.app.getLogLevel() == Application.LOG_DEBUG) {
            Gdx.app.debug(TAG, "bullet: " + position.x + " : " + position.y);
            Gdx.app.debug(TAG, "target: " + target.getX() + ": "  + target.getY());
        }

and, sure enough, the bullet moves (infinitely) along x, but not at all along y. I can’t see what I’m doing differently to you, but at least I have something to work with now!

OK, I think the way I’m calculating the direction (and possibly everything else as well…) is wrong. Having logged the result of calculating the direction I can see it’s 1.0 : 0.0 - which explains the horizontal movement…

Just dug out some very old code from a Bullet class, this took the start position of the bullet and the mouse cursor position to create a new vector:

double rad = Math.atan2(destX - startX, startY - destY);
dx = (float) Math.sin(rad) * speed;
dy = -(float) Math.cos(rad) * speed;

I think I looked at Catacomb Snatch source at the time: https://github.com/Maescool/Catacomb-Snatch/blob/3fe9b83d3fe2213ed4fbb252b2cebaa8403ec5c9/src/main/java/com/mojang/mojam/entity/Bullet.java

Was it long time ago!

[quote]Having logged the result of calculating the direction I can see it’s 1.0 : 0.0
[/quote]
This looks like your float values are being converted to Integers. Are any of the values you use such as Target.getX() or Position Integers? I assumed they would all be Vector2.

They are all either floats or Vector2

I can’t see any reason in the code you provided that would make the bullet object go horizontally other than the target being in a position horizontal to the bullet. I suggest monitoring the values of all items in the init method for each step to see what the values are at each step.

public void init(TigerActor target) {
        this.target = target;
        System.out.println("TargetValStart"+target.getX()+":"target.getY()); // maybe init is being called before target x and y has been set
        System.out.println("PositionValStart"+position.x+":"position.y);
        direction = new Vector2();
        direction.x = target.getX() - position.x;
        direction.y = target.getY() - position.y;
        System.out.println("b4Nor:"+direction);
        direction = direction.nor();
        System.out.println("AfterNor:"+direction);
    }

For reference I looked back into a project I had weapons in and found this snippet:

public void baseShoot(Entity ent,float targetX,float targetY, Array<Bullet> activeBullets,boolean playSound){
		float shooterX = ent.getBody().getPosition().x; // get shooters x pos
		float shooterY = ent.getBody().getPosition().y;  // get shooters y pos
		float velx = targetX - shooterX; // get distance from shooter to target on x axis
		float vely = targetY - shooterY; // get distance from shooter to target on y axis
		float length = (float) Math.sqrt(velx * velx + vely * vely); // get distance to target direct
		// normalise 
		if (length != 0) {
		      velx = velx / length;
		      vely = vely / length;
		}
		// get bullet from pool
		Bullet bullet = (Bullet) bp.obtain();
		// add to array for rendering
		activeBullets.add(bullet);
		// activate bullet (set start pos, velocity, and owener)
		bullet.activate(new Vector2(shooterX,shooterY), new Vector2(velx*speed,vely*speed), ent);

		if(playSound){
			this.shootSound();
		}
		// update weapon shot timer
		this.hasShot();
	}

OK - I’m getting very close!

I can currently get the projectile (a shuriken) moving towards the target (a ninja) and it looks smooth even though the ninja is moving(slowly.)

The code that’s working is as follows and is based on examples by @dfour in the previous post and also from the accepted answer here.


...
private float remainingFlightTime = 10f;
...
float deltaTime = Gdx.graphics.getDeltaTime();
        remainingFlightTime -= deltaTime;

        //get distance to target x & y
        float velocityX = (target.getX() - getX()) / remainingFlightTime,
                velocityY = (target.getY() - getY()) / remainingFlightTime;
        float distance = (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY);

        //normalise
        if (distance != 0) {
            velocityX /= distance;
            velocityY /= distance;
        }

        if (!arrived) {
            position.x += velocityX * deltaTime;
            position.y += velocityY * deltaTime;
            Rectangle ninjaRectangle = target.getBoundingRectangle();
            Rectangle shurikenRectangle = getBoundingRectangle();
            if (ninjaRectangle.overlaps(shurikenRectangle)) {
                arrived = true;
            }
        }

The code after the ellipsis is run every frame to account for the fact that the target is moving.

The only problem now is that my attempts to control the speed aren’t working. If I remove all reference to remainingFlightTime then the shuriken flies at a ridiculous speed, but, otherwise, it moves far too slowly - regardless of what I set remainingFlightTime to. In fact, if I set it to 5 then it turns around and goes backwards part way through the flight and disappears off the screen.

So the main challenge is met - I just need to figure out how to have more control over the speed of the projectile…

Since velocityX and velocityY are normalised you can multiply them by the amount of units you want the object to travel per second.

So if you want the unit to travel at 5 units per second you can use


        float speedPerSecond = 5;
        //normalise
        if (distance != 0) {
            velocityX /= distance;
            velocityY /= distance;
        }

        if (!arrived) {
            position.x += (velocityX * speedPerSecond)  * deltaTime;
            position.y += (velocityY * speedPerSecond)  * deltaTime;
            Rectangle ninjaRectangle = target.getBoundingRectangle();
            Rectangle shurikenRectangle = getBoundingRectangle();
            if (ninjaRectangle.overlaps(shurikenRectangle)) {
                arrived = true;
            }
        }

This is dependant on your banana units, if you are using pixel perfect units then you will travel at 5 pixels per second, if you’re using 16pixels per world unit then it 16 * 5 per second.

Excellent - thanks, that’s nailed it!