Завершающий урок по созданию игры Flappy Bird для android. В этом уроке озвучим игру и добавим экран окончания игры, который будет вызываться, когда птица сталкивается с препятствиями.
Скачать звуки и картинку для игры
Исходный код измененных классов под видео:
package info.fandroid.game; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import info.fandroid.game.states.GameStateManager; import info.fandroid.game.states.MenuState; public class FlappyDemo extends ApplicationAdapter { public static final int WIDTH = 480; public static final int HEIGHT = 800; public static final String TITLE = "Flappy Demo"; private GameStateManager gsm; private SpriteBatch batch; private Music music; @Override public void create () { batch = new SpriteBatch(); gsm = new GameStateManager(); music = Gdx.audio.newMusic(Gdx.files.internal("music.mp3")); music.setLooping(true); music.setVolume(0.1f); music.play(); Gdx.gl.glClearColor(1, 0, 0, 1); gsm.push(new MenuState(gsm)); } @Override public void render () { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); gsm.update(Gdx.graphics.getDeltaTime()); gsm.render(batch); } @Override public void dispose() { super.dispose(); music.dispose(); } }
package info.fandroid.game.sprites; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector3; public class Bird { private static final int MOVEMENT = 100; private static final int GRAVITY = -15; private Vector3 position; private Vector3 velosity; private Rectangle bounds; private Animation birdAnimation; private Sound flap; private Texture texture; public Bird(int x, int y){ position = new Vector3(x, y, 0); velosity = new Vector3(0, 0, 0); texture = new Texture("birdanimation.png"); birdAnimation = new Animation(new TextureRegion(texture), 3, 0.5f); bounds = new Rectangle(x, y, texture.getWidth() /3, texture.getHeight()); flap = Gdx.audio.newSound(Gdx.files.internal("sfx_wing.ogg")); } public Vector3 getPosition() { return position; } public TextureRegion getBird() { return birdAnimation.getFrame(); } public void update(float dt){ birdAnimation.update(dt); if (position.y > 0) velosity.add(0, GRAVITY, 0); velosity.scl(dt); position.add(MOVEMENT * dt, velosity.y, 0); if (position.y < 0) position.y = 0; velosity.scl(1 / dt); bounds.setPosition(position.x, position.y); } public void jump(){ velosity.y = 250; flap.play(); } public Rectangle getBounds(){ return bounds; } public void dispose() { texture.dispose(); flap.dispose(); } }
package info.fandroid.game.states; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import info.fandroid.game.FlappyDemo; public class GameOver extends State { private Texture background; private Texture gameover; public GameOver(GameStateManager gsm) { super(gsm); camera.setToOrtho(false, FlappyDemo.WIDTH / 2, FlappyDemo.HEIGHT / 2); background = new Texture("bg.png"); gameover = new Texture("gameover.png"); } @Override public void handleInput() { if(Gdx.input.justTouched()){ gsm.set(new PlayState(gsm)); } } @Override public void update(float dt) { handleInput(); } @Override public void render(SpriteBatch sb) { sb.setProjectionMatrix(camera.combined); sb.begin(); sb.draw(background, 0, 0); sb.draw(gameover, camera.position.x - gameover.getWidth() / 2, camera.position.y); sb.end(); } @Override public void dispose() { background.dispose(); gameover.dispose(); System.out.println("GameOver Disposed"); } }
package info.fandroid.game.states; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import info.fandroid.game.FlappyDemo; public class GameOver extends State { private Texture background; private Texture gameover; public GameOver(GameStateManager gsm) { super(gsm); camera.setToOrtho(false, FlappyDemo.WIDTH / 2, FlappyDemo.HEIGHT / 2); background = new Texture("bg.png"); gameover = new Texture("gameover.png"); } @Override public void handleInput() { if(Gdx.input.justTouched()){ gsm.set(new PlayState(gsm)); } } @Override public void update(float dt) { handleInput(); } @Override public void render(SpriteBatch sb) { sb.setProjectionMatrix(camera.combined); sb.begin(); sb.draw(background, 0, 0); sb.draw(gameover, camera.position.x - gameover.getWidth() / 2, camera.position.y); sb.end(); } @Override public void dispose() { background.dispose(); gameover.dispose(); System.out.println("GameOver Disposed"); } }
package info.fandroid.game.states; import com.badlogic.gdx.Gdx; 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 info.fandroid.game.FlappyDemo; import info.fandroid.game.sprites.Bird; import info.fandroid.game.sprites.Tube; public class PlayState extends State { private static final int TUBE_SPACING = 125; private static final int TUBE_COUNT = 4; private static final int GROUND_Y_OFFSET = -30; private Bird bird; private Texture bg; private Texture ground; private Vector2 groundPos1, groundPos2; private Array<Tube> tubes; public PlayState(GameStateManager gsm) { super(gsm); bird = new Bird(50, 300); camera.setToOrtho(false, FlappyDemo.WIDTH / 2, FlappyDemo.HEIGHT / 2); bg = new Texture("bg.png"); ground = new Texture("ground.png"); groundPos1 = new Vector2(camera.position.x - camera.viewportWidth / 2, GROUND_Y_OFFSET); groundPos2 = new Vector2((camera.position.x - camera.viewportWidth / 2) + ground.getWidth(), GROUND_Y_OFFSET); tubes = new Array<Tube>(); for (int i = 0; i < TUBE_COUNT; i++){ tubes.add(new Tube(i * (TUBE_SPACING + Tube.TUBE_WIDTH))); } } @Override protected void handleInput() { if (Gdx.input.justTouched()) bird.jump(); } @Override public void update(float dt) { handleInput(); updateGround(); bird.update(dt); camera.position.x = bird.getPosition().x + 80; for (int i = 0; i < tubes.size; i++){ Tube tube = tubes.get(i); if (camera.position.x - (camera.viewportWidth / 2) > tube.getPosTopTube().x + tube.getTopTube().getWidth()){ tube.reposition(tube.getPosTopTube().x + ((Tube.TUBE_WIDTH + TUBE_SPACING) * TUBE_COUNT)); } if (tube.collides(bird.getBounds())) gsm.set(new GameOver(gsm)); } camera.update(); } @Override public void render(SpriteBatch sb) { sb.setProjectionMatrix(camera.combined); sb.begin(); sb.draw(bg, camera.position.x - (camera.viewportWidth / 2), 0); sb.draw(bird.getBird(), bird.getPosition().x, bird.getPosition().y); for (Tube tube : tubes) { sb.draw(tube.getTopTube(), tube.getPosBotTube().x, tube.getPosTopTube().y); sb.draw(tube.getBottomTube(), tube.getPosBotTube().x, tube.getPosBotTube().y); } sb.draw(ground, groundPos1.x, groundPos1.y); sb.draw(ground, groundPos2.x, groundPos2.y); sb.end(); } @Override public void dispose() { bg.dispose(); bird.dispose(); ground.dispose(); for (Tube tube : tubes) tube.dispose(); System.out.println("PlayState Disposed"); } private void updateGround(){ if (camera.position.x - (camera.viewportWidth / 2) > groundPos1.x + ground.getWidth()) groundPos1.add(ground.getWidth() * 2, 0); if (camera.position.x - (camera.viewportWidth / 2) > groundPos2.x + ground.getWidth()) groundPos2.add(ground.getWidth() * 2, 0); } }
Больше уроков:
Уроки Android Studio: тут
Инструменты android разработчика: тут
Дизайн android приложений: тут
Уроки создания игр для android: тут
Основы программирования на JAVA: тут
Урок 11. Flappy Bird: добавляем анимацию в игру| Делаем android игры на LibGDX
Здравствуйте.
Пытаюсь расширить функционал, а именно добавить птичке «жизней».
Соответственно нужен счетчик жизней и изменять его нужно при столкновениях со столбами.
Но в месте if (tube.collides(bird.getBounds())) этого сделать не получается — отниманием еденицы дело не заканчивается, отнимаются по 1 до 0. То есть при первом же столкновении сразу считает 321.
В каком месте можно реализовать нормальный счетчик, или общая архитектура данного приложения не подходит для такой цели?