Tutorial #4 Moving a shape and shooting a bullet (simple non box2D example)

Welcome back!  This is tutorial #4.  I’m going to  demonstrate how to draw and move some shapes.  In this tutorial, we will draw a shape that represents a tank and give it the ability to “shoot” a bullet in the direction the shape is facing.   The class we will be creating should be added to the framework we created in Tutorial #3 for managing our tutorial classes.

So let’s begin…..

First we want to create a new class and have it extend from Tutorial.  If you do not have the Tutorial class, head back to my tutorial #3 and create it from there.

In our class we want to create some member variables for holding information about the shapes we are going to draw.

First we need to specify the size of our objects.  Create three constants to store the object size,  bullet size and movement speed (in pixels).  The tank size will be used to set the size of our tank and the bullet size is used to set the size of a bullet.  I created the following:

	private static final int TANK_SIZE = 32;
	private static final int BULLET_SIZE = 5;
	private static final int MOVEMENT_SPEED = 50;

Since all our objects are going to be square in shape, I only need to set one value for the width and height.  Thus our tank will be 32 pixels square, bullet will be 5 pixels square and our objects will move at 50 pixels per second (we’ll get to that in a bit).

Next, we need to create a SpriteBatch object to be used for drawing everything.

	// Used for drawing our items
	private SpriteBatch spriteBatch;

Then we need to to specify 4 Vector2 objects.  Two objects that represent the position of our tank and of our bullet when fired.  Two objects that represent the direction of travel for our tank and bullet.

	// Tank position
	private Vector2 tank_pos;
	// bullet position
	private Vector2 bullet_pos;

	// Tank direction
	private Vector2 objectDirection;
	// Bullet direction
	private Vector2 bulletDirection;

And finally we need a few “helper” variables.  We create a Pixmap object to draw our shapes and we will store the current screen width and height to make our life easier.

	private Pixmap pixmap;
	int screenWidth, screenHeight;

With that set, we are ready to write our methods.  We will need three (3) methods.  We will override the create method and initialize our objects.  We will override the render method to do the drawing of our objects.  We will create a private update method to update the position of our objects.  So let’s start with the create method.

I won’t get into detail as this method only initializes our member variables.  This method should similar to

	@Override
	public void create() {
		spriteBatch = new SpriteBatch();
		screenWidth = Gdx.graphics.getWidth();
		screenHeight = Gdx.graphics.getHeight();
		tank_pos = new Vector2(screenWidth / 2 - TANK_SIZE / 2,
				screenHeight / 2 - TANK_SIZE / 2);
		bullet_pos = null;
		objectDirection = new Vector2(1, 0); // Pointing right
		bulletDirection = new Vector2(1, 0);

		pixmap = new Pixmap(32, 32, Pixmap.Format.RGB565);
	}

The first line creates a new SpriteBatch object.  We do it here obviously cause we only want to create it once.
The next two lines stores the screen width and height.
The next line centers our tank in the window.  The tank itself has to be centered as the bottom left corner is the focus point.
Then we set the bullet position to null because if it’s null, we won’t draw it.
Next, we set the tank to face to the right and guarantee that if the fire button is pressed, the bullet will fly to the right as well.
And finally, we create a new Pixmap to use for drawing our shapes.

Now, we need to override the render method so we can add out code to update and draw our objects.  This method is called every frame.  Unfortunately, there is no update method to override so that’s the reason we created one.  In our render method, we want to call our update method to set the position of our objects.  Then, we want to clear the screen, draw our bullet and then our tank.   Your method should look similar to

	@Override
	public void render() {
		update();
		GL10 gl = Gdx.graphics.getGL10();
		gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		gl.glClearColor(1, 1, 1, 0);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);

		spriteBatch.begin();
		if (bullet_pos != null) {
			// Draw bullet
			pixmap.drawRectangle(0, 0, BULLET_SIZE, BULLET_SIZE);
			spriteBatch.setColor(Color.BLUE);
			spriteBatch.draw(new Texture(pixmap), bullet_pos.x, bullet_pos.y,
					BULLET_SIZE, BULLET_SIZE);
		}
		// Draw object
		pixmap.drawRectangle(0, 0, TANK_SIZE, TANK_SIZE);
		spriteBatch.setColor(Color.GREEN);
		spriteBatch.draw(new Texture(pixmap), tank_pos.x, tank_pos.y,
				TANK_SIZE, TANK_SIZE);
		spriteBatch.end();
	}

First, we call our update function.  We’ll write that in a bit.   After that, we clear our screen and then tell the spriteBatch object to begin “recording” our draw calls.
We then check to see if a bullet has been created (not null).  If so, we use the pixmap object to create a rectangle of bullet size in memory.  Then we set our draw color to be blue and we use the spriteBatch object place in it’s buffer that we want to draw this rectangle at the current bullet position with a size of bullet size.  We need to set the size because this pixmap is also used for drawing our tank and if we didn’t specify a size, the bullet would be the size of the pixmap which is 32×32.  A pretty big bullet.
After that, we draw our tank using the same method as drawing the bullet except we use a different color.
Finally, we basically tell the spriteBatch object to stop recording and perform all the draw calls all at once.

Finally, we need to create our update method.  In this method, we need to get what keystrokes were pressed, creaing our bullets and moving our tank.

First, we create a new Vector2 object to store temporary store a direction.
Next, we create a variable (you can name it delta) for storing the “delta time.”  multiplied by the overall movement speed.

Delta time is the time that has passed since the beginning of the last frame in milliseconds.  

This is used to control how often our objects get updated.  We multiple this value by the movement speed because all our objects will move at the same rate.  If they had their own individual speeds, then we wouldn’t do the multiplication here but when we’re setting our object position.  By the way, if we didn’t use this “delta” variable,  then our objects would move too fast.  For example, if we moved our tank at a rate of 50 pixels.  It would fly off the screen cause this function gets called hundreds of times a second and holding down a button can easily cause your tank to move 500+ pixels in any give direction instantly.  But using this variable, we can control things a bit.  We can then say our tank moves at a rate of 50 pixels per second or basically, if we held down a key, our tank would take 1 second to move 50 pixels.  Big difference huh.

Back to our update function.  We then check to see if one or all of the directional pads are pressed.  if the right or left is pressed, then the temporary direction variable’s x value is set to 1 or -1 (respectively) multiplied by our delta variable.
We then check to see if our temporary direction variable has had either of it’s x or y values set and if so, we add this value to the tank’s position.  This will move the tank by that direction amount.   If the tanks’ position was 1,1 and the direction was 1,1 then the tank’s position will be 2,2.
There is then a check to make sure our tank doesn’t go off the screen.  We make sure the x value is greater than 0 and less than the screen width minus the tank size.  Similar for the y value.  It has to be  greater than 0 and less than the screen height minus the tank size.  Again, remember the tank’s focal point is in the bottom left corner so  we need to subtract the tank’s width so that the right side or top side does not go off the screen.

After we position the tank if necessary, we check to see if the “F” key (for fire) has been pressed.  If so, we create a new bullet with an origin in the center of the tank with it’s direction equal to that of the tank.

Finally, we check to see if a bullet has been created and if so we move it in it’s intended direction.  If the bullet goes off screen, we remove it by setting it to null.  That’s it.  The code looks like

	private void update() {
		Vector2 direction = new Vector2(0, 0);
		float delta = Gdx.graphics.getDeltaTime() * MOVEMENT_SPEED;
		if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) {
			direction.x = 1 * delta;
		}
		if (Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) {
			direction.x = -1 * delta;
		}
		if (Gdx.input.isKeyPressed(Keys.DPAD_UP)) {
			direction.y = 1 * delta;
		}
		if (Gdx.input.isKeyPressed(Keys.DPAD_DOWN)) {
			direction.y = -1 * delta;
		}
		if (direction.x != 0 || direction.y != 0) {
			tank_pos.add(direction);
			if (tank_pos.x < 0)
				tank_pos.x = 0;
			if (tank_pos.x > this.screenWidth - TANK_SIZE)
				tank_pos.x = this.screenWidth - TANK_SIZE;
			if (tank_pos.y < 0)
				tank_pos.y = 0;
			if (tank_pos.y > this.screenHeight - TANK_SIZE)
				tank_pos.y = this.screenHeight - TANK_SIZE;
			objectDirection.set(direction);
		}

		if (Gdx.input.isKeyPressed(Keys.F)) {
			bullet_pos = new Vector2(tank_pos.cpy().add(
					TANK_SIZE / 2 - BULLET_SIZE / 2,
					TANK_SIZE / 2 - BULLET_SIZE / 2));
			bulletDirection.set(objectDirection);
		}

		if (bullet_pos != null) {
			bullet_pos.add(bulletDirection);
			if (bullet_pos.x < 0 || bullet_pos.x > this.screenWidth
					|| bullet_pos.y < 0 || bullet_pos.y > this.screenHeight) {
				bullet_pos = null;

			}
		}
	}

If you run your Tutorial application and run the ShapeMovementTutorial, you should see the tank in green.  Use the arrow keys on your keyboard or directional keys on your phone to move the tank and click “F” to fire a blue bullet.  Cool.

Figure 4-1

As always, the source for this tutorial is here.  No libgdx jars files are included.

This entry was posted in LibGDX Tutorials. Bookmark the permalink.