• Sponsored Links

Step 14: Step by Step libGDX Tutorial : Collision and sound effect

Share this :

Collision and sound effect

So till now we have Menu State where once we click on play button Play State is displayed where we see our nice little Brisky Bird . If we click she jumps and if we dont click she falls down and stays at the bottom of our game . Also we see pipes coming in towards the bird with a static gap between them , however at this point bird never dies even if there s a collision between bird and pipe or if she falls on ground of if she touches the sky . Also there is no sound effect and no ground . So in this section we will add up ground to our game and then add logic to detect collision which will kill the bird and transit the game to Menu state and also add sound effect to our game .

Lets first add ground to our play state

Understanding ground addition Logic

We are going to use 2 texture of same ground image and we will re position it when 1 ground texture moves off the screen . Lets follow the below steps . Ground Texture can be taken from the same github repository which we used previously to download other assets from this link BriskyDemo guthub repository.

  • Add the ground texture in play state as we did for background state . As we always do we will first define a variable of Texture class and later initialize it in the constructor
  • Adding an offset variable called GROUND_OFFSET with value -30 for ground so it looks good on the screen
  • Define 2 positions for the ground 1 and 2
  • Draw our ground in the render method
  • Create an update ground method which will update the ground to go to right of the screen if the position of ground moves off of screen on left axis
  • add the newly created update ground method to be executed from the update method
  • Add Below Code in the Class declaration section
  •     private Texture ground;
        private Vector2 groundPos1;
        private Vector2 groundPos2;
        private static final int GROUND_OFFSET = -30;

    Below Code in Constructor section of Play State

  • ground = new Texture("base.png");
    groundPos1 = new Vector2(cam.position.x - cam.viewportWidth / 2, GROUND_OFFSET);
    groundPos2 = new Vector2((cam.position.x - cam.viewportWidth / 2) + ground.getWidth(), GROUND_OFFSET);

    Below in render method

  • sb.draw(ground, groundPos1.x, groundPos1.y);
    sb.draw(ground, groundPos2.x, groundPos2.y);

    Add below updateGround method in PlayState class

  •     private void updateGround() {
            if (cam.position.x - (cam.viewportWidth / 2) > groundPos1.x + ground.getWidth()) {
            	groundPos1.add(ground.getWidth() * 2, 0);
            }
            if (cam.position.x - (cam.viewportWidth / 2) > groundPos2.x + ground.getWidth())
                groundPos2.add(ground.getWidth() * 2, 0);
        }

    Add following line in update method

  • updateGround();

    Once all this is done and you have added the texture file in the assets directory , try executing the code and you will see ground in our play state and also if you click 2 to 3 times bird flying . You may not be able to see bird because maybe bird is behind the ground in stating .

    Brisky Bird with Ground

    Brisky Bird with Ground

Lets now add our collision method . First collision of bird with ground .

First change the line in PlayState constructor which is creating the bird object to show bird to at a particular height on y axis so change code as below

/**
 * bird = new Bird(50,50);
 * to below line in Play State constructor */
 bird = new Bird(50,300);

Add below code in update method . Basically what it does is to push menu state in as soon as bird touches the ground

if (bird.getPosition().y <= ground.getHeight() + GROUND_OFFSET) {
            dispose();
            gsm.set(new MenuState(gsm));
        }

As our dispose function is currently empty . Lets add following in the dispose function so all our assets are disposed as soon as we move ahead from current state

	public void dispose() {
		background.dispose();
		ground.dispose();
	    bird.dispose();
	    for (Tube tube : tubes) {
	        tube.dispose();
	       }
	   System.out.println("Play State Disposed");	
	}

Lets add below code for detection of collision with pipes in the Play State :

if (cam.position.x > 320) {

for (int i = 0; i < TUBE_COUNT; i++) {
if (tubes.get(i).collides(bird.getBounds())) {
dispose();
gsm.set(new MenuState(gsm));
}
}

}

Here we have added if logic because even for rendering of the tubes we started rendering after first 320 pixels , that is because we want first few seconds for user to just keep tapping without any pipe on the screen  . As soon as collision is detected . We dispose current state and go to Menu State . Try executing this code and you will see it runs perfectly . You might see a little image issues in Menu state once we go from PlayState to Menu State but that is due to cam projection . To fix that add cam projections to Menu State .

In Menu State Constructor

cam.setToOrtho(false, BriskyDemo.WIDTH / 2, BriskyDemo.HEIGHT / 2);

In MenuState Render Method

sb.setProjectionMatrix(cam.combined);
sb.draw(playBtn,(cam.viewportWidth/2) - (playBtn.getWidth() / 2), cam.viewportHeight/2);

Try Again and Menu State even after bird dies will show as nice as it shows at first execution .

Lets add sound effect as well in this section . We already have added sound effect as soon as click and bird jumps . Similarly to have a sound effect as soon as a bird dies lets add following asset in the android asset directory named die.ogg from same github repository

Add following line in class variables for Play State

private Sound die;

Add below line in Constructor

die = Gdx.audio.newSound(Gdx.files.internal("die.ogg"));

Add below at both if loops which we just added , so the die sound is played as soon as the bird dies

die.play(0.3f);

With this i will end up today s section . In next section I will explain how to run this game this in your android phones .

 

Below is the code till today :

  • BriskyDemo.java -- No Changes
  • Bird.java   -- No changes
  • Tube.java     -- No Changes
  • GameStateManager.java -- No Changes
  • MenuState.java
  • < ?prettify linenums=true?>
    package States;
    
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.Texture;
    import com.badlogic.gdx.graphics.g2d.SpriteBatch;
    import com.versionpb.briskybird.BriskyDemo;
    
    public class MenuState extends State {
    
    private Texture background; //New Texture explained here
    private Texture playBtn;
    
    public MenuState(GameStateManager gsm) {
    super(gsm);
    cam.setToOrtho(false, BriskyDemo.WIDTH / 2, BriskyDemo.HEIGHT / 2);
    background = new Texture("playBg_menu.png");
    playBtn = new Texture("playButton.png");
    
    }
    
    @Override
    public void handleInput() {
    if(Gdx.input.justTouched()){
    gsm.push(new PlayState(gsm));
    dispose();
    }
    
    }
    
    @Override
    public void update(float dt) {
    handleInput();
    
    }
    
    @Override
    public void render(SpriteBatch sb) {
    sb.setProjectionMatrix(cam.combined);
    sb.begin();
    sb.draw(background, 0, 0, BriskyDemo.WIDTH, BriskyDemo.HEIGHT);
    sb.draw(playBtn,(cam.viewportWidth/2) - (playBtn.getWidth() / 2), cam.viewportHeight/2);
    sb.end();
    
    }
    
    @Override
    public void dispose() {
    background.dispose();
    playBtn.dispose();
    
    }
    
    }
  • PlayState.java
  • < ?prettify linenums=true?>
    package States;
    
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.audio.Sound;
    import com.badlogic.gdx.graphics.Texture;
    import com.badlogic.gdx.graphics.g2d.SpriteBatch;
    import com.badlogic.gdx.math.Vector2;
    import com.badlogic.gdx.utils.Array;
    import com.versionpb.briskybird.BriskyDemo;
    
    import Sprites.Bird;
    import Sprites.Tube;
    
    public class PlayState extends State {
    private Bird bird; //add this line
    private Texture background;
    private static final int TUBE_COUNT = 4;
    private static final int TUBE_SPACING = 125;
    private Array<Tube> tubes;
    
    private Texture ground;
    private Vector2 groundPos1;
    private Vector2 groundPos2;
    private static final int GROUND_OFFSET = -30;
    
    private Sound die;
    
    public PlayState(GameStateManager gsm) {
    super(gsm);
    cam.setToOrtho(false, BriskyDemo.WIDTH / 2, BriskyDemo.HEIGHT / 2);
    bird = new Bird(50,300);
    
    background = new Texture("playBg_menu.png");
    
    tubes = new Array<Tube>();
    for (int i = 1; i <= TUBE_COUNT; i++) { // for loop for adding tubes to the array
    tubes.add(new Tube(i * (TUBE_SPACING + Tube.TUBE_WIDTH)));
    
    }
    
    ground = new Texture("base.png");
    groundPos1 = new Vector2(cam.position.x - cam.viewportWidth / 2, GROUND_OFFSET);
    groundPos2 = new Vector2((cam.position.x - cam.viewportWidth / 2) + ground.getWidth(), GROUND_OFFSET);
    
    die = Gdx.audio.newSound(Gdx.files.internal("die.ogg"));
    }
    
    @Override
    public void handleInput() {
    if(Gdx.input.justTouched())
    bird.jump();
    
     
    
    }
    
    @Override
    public void update(float dt) {
    
    handleInput();
    updateGround();
    bird.update(dt);
    cam.position.x = bird.getPosition().x + 80;
    
     
    
    for (Tube tube : tubes){
    if (cam.position.x - (cam.viewportWidth / 2 ) > tube.getPosTopTube().x + tube.getTopTube().getWidth()){
    tube.reposition(tube.getPosTopTube().x + (Tube.TUBE_WIDTH + TUBE_SPACING) * TUBE_COUNT);
    }
    }
    
    if (cam.position.x > 320) {
    
    for (int i = 0; i < TUBE_COUNT; i++) {
    if (tubes.get(i).collides(bird.getBounds())) {
    die.play(0.3f);
    
    gsm.set(new MenuState(gsm));
    dispose();
    }
    }
    
    }
    
     
    
    if (bird.getPosition().y <= ground.getHeight() + GROUND_OFFSET) {
    die.play(0.3f);
    
    
    
    gsm.set(new MenuState(gsm));
    dispose();
    }
    
     
    
     
    
    cam.update();
    
    }
    
    @Override
    public void render(SpriteBatch sb) {
    sb.setProjectionMatrix(cam.combined);
    sb.begin();
    //sb.draw(background, 0, 0, BriskyDemo.WIDTH, BriskyDemo.HEIGHT);
    sb.draw(background, cam.position.x - (cam.viewportWidth / 2), 0, BriskyDemo.WIDTH, BriskyDemo.HEIGHT);
    //sb.draw(bird,50,50);
    sb.draw(bird.getTexture(), bird.getPosition().x,bird.getPosition().y);
    
    for (Tube tube : tubes) {
    if (tube.getPosTopTube().x > 320) {
    sb.draw(tube.getTopTube(), tube.getPosTopTube().x, tube.getPosTopTube().y);
    sb.draw(tube.getBottomTube(), tube.getPosBottomTube().x, tube.getPosBottomTube().y);
    }
    
    }
    
    sb.draw(ground, groundPos1.x, groundPos1.y);
    sb.draw(ground, groundPos2.x, groundPos2.y);
    
    sb.end();
    
    }
    
    @Override
    public void dispose() {
    background.dispose();
    ground.dispose();
    bird.dispose();
    //die.dispose();
    for (Tube tube : tubes) {
    tube.dispose();
    }
    System.out.println("Play State Disposed");
    }
    
    private void updateGround() {
    if (cam.position.x - (cam.viewportWidth / 2) > groundPos1.x + ground.getWidth()) {
    groundPos1.add(ground.getWidth() * 2, 0);
    }
    if (cam.position.x - (cam.viewportWidth / 2) > groundPos2.x + ground.getWidth())
    groundPos2.add(ground.getWidth() * 2, 0);
    
     
    
    }
    
    }
  • State.java -- No Changes

Leave a Reply

Your email address will not be published. Required fields are marked *

+ 35 = 44