Bullet shooting in mouse direction

I found other posts on this, but things were to complicated for me to understand. Knowing the pythag. theory, how would I make a little more sophisticated shooting?

This is a perfect example of what I’m trying to do.

http://www.kizi2games.co.uk/img/yo-ho-ho-cannon.gif

(Wonder how those dots work ???)

Please, people, learn vector math…
http://www.wildbunny.co.uk/blog/vector-maths-a-primer-for-games-programmers/

Some more info
o you need to understand 2d vectors
-> they are variables with an x and an y component and can be used to do stuff like described in the link above
o you need to understand the difference between vectors and points
-> technically they look the same (having x and y), but logically they are different (a point is a position, a vector represents direction and length)
o you need to understand game loops
-> the loop that periodically calls your update and render methods
o you need to understand what a timestep is
-> the time period between your update calls
o you need to understand fixed and variable timesteps
-> fixed timesteps are locked. For example to 1/60s.
o fixed timestep is easier for animating your game
-> but the gameloop needs to make sure of it by variable sleep times to wait before the next gameloop iteration
o you need to understand when to use floats vs. ints
-> only use ints for final rendering. use floats (or doubles) for variables needed in computation or to store (varying) positions

regarding your question:
o you need a direction vector pointing from the turret to the mouse
-> it’s easily calculated by substracting the turret position from the mouse position (and normalize the result)
o you need a speed variable containing the pixels per timestep
-> the amount of pixels the bullet should fly per frame
o you need a bullet position variable that adds up the directionspeedtimestep per update call
-> this needs to have float components

pseudo code:


// assume this values
float timeStep=1f/60f;
float turretX=10f;
float turrety=10f;
float bulletSpeed=3f; // speed is 3 pixels per second

// if a bullet is fired it is initialized like this
float bulletX=turretX;
float bulletY=turretY;

// and you need to calculate the direction vector for it's movement
float dirX= getMouseXFromSomeWhere()-turretX;
float dirY= getMouseYFromSomeWhere()-turretY;
// you need to "normalize" the direction vector to be able to use the speed variable
float dirLength= Math.sqrt(dirX*dirX + dirY*dirY);
dirX=dirX/dirLength;
dirY=dirY/dirLength;

// now on every update, you can add up the direction * speed * timestep to the bullet
bulletX=bulletX+(dirX*bulletSpeed*timeStep);
bulletY=bulletY+(dirY*bulletSpeed*timeStep);

// on every render, you can render the bullet sprite at that position
g.drawImage(bulletImage, (int)bulletX, (int)bulletY, null); // rendering often uses ints for coordinates

as you can see, standalone x and y variables are just the components of vectors grouped by a naming convention, so you could also use a vector class for that:


// some minimal vector class
class Vec
{
    public float x, y;
    public Vec(float x, float y)
    {
        this.x=x;
        this.y=y;
    }
    public normalize()
    {
        float l=Math.sqrt(x*x + y*y);
        x/=l;
        y/=l;
    }
}

// since vectors and points look technically similar, you can "abuse" a vector to store positions:
Vec turret= new Vec(10f,10f);

// creating the direction vector
Vec dir=new Vec(getMouseXFromSomewhere(),getMouseYFromSomewhere());
dir.x-=turret.x;
dir.y-=turret.y;
dir.normalize();

// initializing a new bullet
Vec bullet=new Vec(turret.x, turret.y);

// animate the bullet on update...
bullet.x+=speed*dir.x*timeStep;
bullet.y+=speed*dir.y*timeStep;

// render the bullet sprite
g.drawImage(bulletImage, (int)bullet.x, (int)bullet.y, null);

Of course you can add more methods to the Vec class to make the code using vectors look more concise…

Get the position of the mouse (mouseX, mouseY), and the position of the gun (gunX, gunY). The vector from the gun to the mouse is (mouseX-gunX, mouseY-gunY).

Create a bullet at (gunX, gunY), and depending on how you handle movement: increment the position of the bullet by (mouseX-gunX, mouseY-gunY), multiplied by a constant depending on the speed of the bullet. The constant would be 1 if you want it to hit straight away, 0.5 if you want it to hit in two frames etc.

I’m trying and so far I got some weird, slow jiggly movement. While I mess with it, here’s my current screen class.

public class GameScreen implements Screen{
	
	MyGame mg;
	
	float delta = 1/60f;
	
	
	SpriteBatch batch;
	Sprite s;
	
	float dx = 5, dy = 5;
	
	float x = 10, y = 10;
	
	Vec turret= new Vec(10f,10f);
	Vec dir;
	
	public GameScreen(MyGame mg) {
		this.mg = mg;
		
	}

	@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(1F, 1F, 1F, 1F);
 		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		update(delta);
		
		batch.begin();
			batch.draw(s, x, y);
		batch.end();
		
	}
	
	public void update(float delta) {
		
		//x += dx;
		//y += dy;
		
		
		
		
		if(Gdx.input.isKeyPressed(Keys.SPACE)) {
			dir.x-=x;
			dir.y-=y;
			dir.normalize();
			
			
			
		}
		
		x+=dx*dir.x*delta;
		y+=dy*dir.y*delta;
		
		
	}

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void show() {
		batch = new SpriteBatch();
    	s = new Sprite(new Texture(Gdx.files.internal("HUDS/button_Back.png")));
    	dir=new Vec(Gdx.input.getX(), Gdx.input.getY());
		dir.x-=x;
		dir.y-=y;
		dir.normalize();
	}

	@Override
	public void hide() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		
	}

}

The Vec class is the same as what cylab showed me.

EDIT: Forgot to add the second position facepalm

(After some League XD) I got everything working, almost fine. The only problem is the image doesn’t move in the oposite direction, and it isn’t exactly where it should be shot.

public class GameScreen implements Screen{
	   
	   MyGame mg;
	   
	   
	   SpriteBatch batch;
	   Sprite s;
	   
	   
	   
	   float timeStep=1f/60f;
	   float turretX=10f;
	   float turretY=10f;
	   float bulletSpeed=0.1f; 
	   
	   float bulletX=turretX;
	   float bulletY=turretY;
	   
	   float dirX= 100 - turretX;
	   float dirY= 100 - turretY;
	   
	   public GameScreen(MyGame mg) {
	      this.mg = mg;
	      
	      
	      float dirLength= (float) Math.sqrt(dirX*dirX + dirY*dirY);
	      dirX=dirX/dirLength;
	      dirY=dirY/dirLength;
	      
	      
	      
	      
	   }

	   @Override
	   public void render(float delta) {
	      Gdx.gl.glClearColor(1F, 1F, 1F, 1F);
	       Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
	      update(delta);
	      
	      batch.begin();
	         batch.draw(s, bulletX, bulletY);
	      batch.end();
	      
	   }
	   
	   public void update(float delta) {
		   if(Gdx.input.isKeyPressed(Keys.SPACE)) {
		   dirX= Gdx.input.getX() - turretX;
		   dirY= Gdx.input.getY() - turretY;
		   }
		   
		   bulletX=bulletX+(dirX*bulletSpeed*timeStep);
		   bulletY=bulletY+(dirY*bulletSpeed*timeStep);
	      
		   
		   
	   }

	   @Override
	   public void resize(int width, int height) {
	      // TODO Auto-generated method stub
	      
	   }

	   @Override
	   public void show() {
	      batch = new SpriteBatch();
	       s = new Sprite(new Texture(Gdx.files.internal("HUDS/button_Back.png")));
	       
	   }

	   @Override
	   public void hide() {
	      // TODO Auto-generated method stub
	      
	   }

	   @Override
	   public void pause() {
	      // TODO Auto-generated method stub
	      
	   }

	   @Override
	   public void resume() {
	      // TODO Auto-generated method stub
	      
	   }

	   @Override
	   public void dispose() {
	      // TODO Auto-generated method stub
	      
	   }

	}

http://i.minus.com/i2otwyaEZi111.gif

What do you mean by “image doesn’t move in the opposite direction”? If you mean that the Y value is flipped that’s because the way your camera is set up. In LibGDX the mouse position (0; 0) means the top-left corner, not the bottom left. You can fix that by using this instead of your current bullet launching code:


if(Gdx.input.isKeyPressed(Keys.SPACE)){
	dirX= Gdx.input.getX() - turretX;
	dirY = Gdx.graphics.getHeight() - Gdx.input.getY() - turretY;
}

Looking through your code everything seems to be alright, expect for a few general coding problems.
Ditch your timestep variable, use delta instead. Start using LibGDX’s Vector2 classes instead of storing X and Y parameters in separate floats. The vector2 class has lots of useful methods like nor, to normalize the vector, this way you can avoid the whole divide the components by the vector’s length procedure in the constructor.

time step was delta, but I just changed the name for sake of the code. And I meant that I wanted to press space bar and the image fly past the mouse’s exact location.

That happens because you only compute the direction once and then translate your object in that direction every frame.

You can’t expect your projectile to just simply stop where you want it to. What you want to do is rather than computing the direction on the press of the spacebar you want to save the mouse’s location and compute the direction against that every frame. However, this way your projectile will likely end up going back and forth around your point and that’s because it “steps too big”, so it goes over your point, and then tries to come back again, repeating the process from here. The way to fix this is to compute the distance from your projectile’s location to the target location and if the distance is smaller or equal to the distance of your projectile’s step vector’s length then set the projectile’s location to be at the target location and stop the movement.

I know this probably doesn’t make too much sense the way I just explained it, but I’m too sleepy to explain it in more detail so I’ll come back to the topic in the morning and try to elaborate. :stuck_out_tongue:

Alright, If you’re not too sleepy, I’ve been trying to make it slingshot based on where a mouse saves a vector point but I’m not so successful. Hopefuly I get it soon :confused:

if(Gdx.input.isButtonPressed(0)) {
		    dir=new Vec(Gdx.input.getX(), Gdx.input.getY());
		   
		   }
		   
		  // if(!Gdx.input.isButtonPressed(0)) {
			//    dir=new Vec(0, 0);
			   
			 //  }
		   
		   if(Gdx.input.isKeyPressed(Keys.SPACE)) {
			   dirX= dir.x * turretX;
			   dirY= dir.y * turretY;
		   }
		   
		   if(Gdx.input.isKeyPressed(Keys.SPACE)) {
			   dirX= dir.x ;
			   dirY= dir.y ;
		   }
		   
		   
		   
		   if(Gdx.input.getX() < bulletX){
		   bulletX=bulletX-(dirX*bulletSpeed*timeStep);
		   }
		   if(Gdx.input.getX() > bulletX) {		   
		   bulletX=bulletX+(dirX*NegbulletSpeed*timeStep);
		   }
		   
		   if(Gdx.input.getY() < bulletY){
			   bulletY=bulletY+(dirY*bulletSpeed*timeStep);
			   }
		  
		   
		   if(Gdx.input.getY() > bulletY){			   
		   bulletY=bulletY-(dirY*NegbulletSpeed*timeStep);
		   }

Why the conditionals? Also if you’re using libGDX it has a nice Vector2 class.


// remember to flip y axis
Vector2 mouse = new Vector2(Gdx.input.getX(), Gdx.graphics.getHeight() - 1 - Gdx.input.getY());

Vector2 dir = mouse.sub(turret).nor().mul(bulletSpeed * timeStep);

bullet.add(dir); // move in direction 'dir' at 'bulletSpeed' for 'timeStep'

I used the conditions to atempt to get it done. In this line of your code:

		   Vector2 dir = mouse.sub(turret).nor().mul(bulletSpeed * timeStep);

It throws a syntax error:

The method mul(Matrix3) in the type Vector2 is not applicable for the arguments (float)

And when I run it… the exception… is…:

Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.lang.Error: Unresolved compilation problem: 
	The method mul(Matrix3) in the type Vector2 is not applicable for the arguments (float)

	at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)
Caused by: java.lang.Error: Unresolved compilation problem: 
	The method mul(Matrix3) in the type Vector2 is not applicable for the arguments (float)

Nearly the same.

My bad, it’s [icode].scl(bulletSpeed * timeStep);[/icode]

I’m getting a nullpointer from the bullet Vector. I’m not sure why, but is it too much to ask for a small code example? Mainly the logic to making an image in the bottom left corner of your screen slide to your mouse wherever it is? In my case I need it to fly past the mouse.


class Bullet {
	
	public static final float BULLET_SPEED = 10; // modify to your liking

	Vector2 velocity = new Vector2();
	boolean velocityHasBeenSet = false;
	Vector2 position = new Vector2(); // alternatively set this in constructor

	public void update(float delta) {
		if(Gdx.input.isButtonPressed(0) && !velocityHasBeenSet) {
			Vector2 dir = new Vector2(Gdx.input.getX(), Gdx.graphics.getHeight() - 1 - Gdx.input.getY());
			dir.sub(position).nor().scl(BULLET_SPEED);
			velocity.set(dir);
			velocityHasBeenSet = true;
		}

		position.add(velocity.scl(delta));
	}

	public void render(SpriteBatch batch) {
		// render your image or whatever at position
	}
}

So you’d probably init the bullet at the position of your turret, and call update() and render() on all bullet entities each frame.
Do what you like in regards to velocityHaBeenSet, you said you wanted it to fly past the mouse, so the simplest solution is to only set the velocity the first time the mouse is pressed. The bullet will fly off in that direction forever after.

Just to add on to his excellent answer, there is such a thing as a position vector. It really is the exact same thing as a point, however it does exist. (You may have said this later in the post, however I saw you saying that there’re only directional/length vectors.) Incredible explanation other than that!

for doing this on one of my older game I used this to get the increments of the bullet and angle , just put it in a method then use a for loop to get the bullet to move.

delta_x = (mh.x / 2) - (play.getx() - xoffset) - 16;
		delta_y = (mh.y / 2) - (play.gety() - yoffset) - 16;
		//anglea = Math.toDegrees(Math.atan2(delta_y, delta_x));
		angle = Math.atan2(delta_x, delta_y);

If I was supposed to render like so

batch.draw(s, position.x, position.y);

I think something is wrong with that neat code. The image is static at 0,0.

Derp:


-position.add(velocity.scl(delta));
+position.add(velocity.cpy().scl(delta));

Other than that just make sure you call update() on your Bullets.

Is this a way of saying add and remove that?

Still no use, I’ll link a pastebin.
http://pastebin.com/mRCUBufY