📜 ⬆️ ⬇️

[libGDX] Writing a full game for Android. Part 1

Hello! I decided to try myself in the game-dev field and at the same time tell and show how it was. The second part is here .

The game is a screen on which the constellations are located. Each star of this constellation has its own color (note). For example, the note “Do” is usually represented in red, and “Mi” is shown in yellow. Here is the result:

image
')
So, each level is a new constellation and a new melody. The stars play the first four notes, and then you must repeat them in the same sequence. Then, four more notes are added to the first four notes, and so on, until the level is completed.

We will write using the libGDX framework. I liked him the most as a novice in this business. And I found more information on it. So let's get started.

What we need:



I will not create a project manually. It's easier for me to use gdx-setup. So, run it:

java -jar gdx-setup.jar 


Next, enter:



Click Generate and, after the end of the process, go to Eclipse.

In Eclipse, select Import -> Gradle -> Gradle Project . Then Browse .. search for our project and then Build . After completion, select all projects and build Finish . Upon completion, your projects will appear in the list of projects. Immediately go to the main project ( core ) -> MyGame.java . We cleaned everything that created gdx , and also inherited not from the ApplicationAdapter , but from the Game . As a result, the class should get the form:

 public class MyGame extends Game { @Override public void create() { } @Override public void render() { super.render(); } } 


Further. Create a new package, let's call it screens . And there are three classes in it:


All of them must be inherited from the Screen interface. We add all the methods that our inheritance requires, we close everything except MainMenuScreen. We write the following in it:

 public class MainMenuScreen implements Screen { //    final MyGame game; //     private Stage stage; private TextButton play, exit; private Table table; private LabelStyle labelStyle; //       ( ) public MainMenuScreen(final MyGame gam) { game = gam; //  --           stage = new Stage(new ScreenViewport()); //   .        Skin skin = new Skin(); TextureAtlas buttonAtlas = new TextureAtlas(Gdx.files.internal("images/game/images.pack")); skin.addRegions(buttonAtlas); TextButtonStyle textButtonStyle = new TextButtonStyle(); textButtonStyle.font = game.font; textButtonStyle.up = skin.getDrawable("button-up"); textButtonStyle.down = skin.getDrawable("button-down"); textButtonStyle.checked = skin.getDrawable("button-up"); labelStyle = new LabelStyle(); labelStyle.font = game.font; table = new Table(); table.setFillParent(true); //  .   listener,    .  ,       ,     play = new TextButton("", textButtonStyle); play.addListener(new ClickListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { Gdx.input.vibrate(20); return true; }; @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { game.setScreen(new LevelScreen(game)); dispose(); }; }); //  .    .   ,       . exit = new TextButton("", textButtonStyle); exit.addListener(new ClickListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { Gdx.input.vibrate(20); return true; }; @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { Gdx.app.exit(); dispose(); }; }); table.add(play); table.row(); table.add(exit); stage.addActor(table); Gdx.input.setInputProcessor(stage); //        (, ,  etc.) Gdx.input.setCatchBackKey(true); //    ,    ,          } @Override public void render(float delta) { //        Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //   stage.act(delta); stage.draw(); } @Override public void resize(int width, int height) {} @Override public void show() {} @Override public void hide() {} @Override public void pause() {} @Override public void resume() {} @Override public void dispose() { //     game. stage.dispose(); game.dispose(); } } 


Further. We do import of dependences (Shift + Ctrl + O). And go to the main class MyGame.java . Add the following to it:

 public class MyGame extends Game { //        (    ) public BitmapFont font, levels; private static final String FONT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789][_!$%#@|\\/?-+=()*&.;,{}\"´`'<>"; @Override public void create() { //    RussoOne  Google Fonts.    TTF. (  ,  ttf  ) FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/russoone.ttf")); FreeTypeFontParameter param = new FreeTypeFontParameter(); param.size = Gdx.graphics.getHeight() / 18; //  .       .  ,     ,   . param.characters = FONT_CHARACTERS; //   font = generator.generateFont(param); //   param.size = Gdx.graphics.getHeight() / 20; levels = generator.generateFont(param); font.setColor(Color.WHITE); //   levels.setColor(Color.WHITE); generator.dispose(); //     . @Override public void render() { super.render(); } } ? public class MyGame extends Game { //        (    ) public BitmapFont font, levels; private static final String FONT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789][_!$%#@|\\/?-+=()*&.;,{}\"´`'<>"; @Override public void create() { //    RussoOne  Google Fonts.    TTF. (  ,  ttf  ) FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/russoone.ttf")); FreeTypeFontParameter param = new FreeTypeFontParameter(); param.size = Gdx.graphics.getHeight() / 18; //  .       .  ,     ,   . param.characters = FONT_CHARACTERS; //   font = generator.generateFont(param); //   param.size = Gdx.graphics.getHeight() / 20; levels = generator.generateFont(param); font.setColor(Color.WHITE); //   levels.setColor(Color.WHITE); generator.dispose(); //     . @Override public void render() { super.render(); } } \\ / - + = () * &;, {} \."'` '<> "; public class MyGame extends Game { //        (    ) public BitmapFont font, levels; private static final String FONT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789][_!$%#@|\\/?-+=()*&.;,{}\"´`'<>"; @Override public void create() { //    RussoOne  Google Fonts.    TTF. (  ,  ttf  ) FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/russoone.ttf")); FreeTypeFontParameter param = new FreeTypeFontParameter(); param.size = Gdx.graphics.getHeight() / 18; //  .       .  ,     ,   . param.characters = FONT_CHARACTERS; //   font = generator.generateFont(param); //   param.size = Gdx.graphics.getHeight() / 20; levels = generator.generateFont(param); font.setColor(Color.WHITE); //   levels.setColor(Color.WHITE); generator.dispose(); //     . @Override public void render() { super.render(); } } 


Make imports and create a new package called managers (for example). It contains the XMLparse.java class. Why do you need it? In order that we will take levels and many other things from xml files. By the way, create an xml folder in the assets folder ( android project -> assets ). In her folder:



In the fonts folder, put the font. And in the xml folder, create a folders folder. Update your Desktop project (F5) so that it picks it all up. And now, let's fill our class XMLparse.java . We write the following to it:

 public class XMLparse { public Array<String> XMLparseLevels() { Array<String> levels = new Array<String>(); Array<Integer> int_levels = new Array<Integer>(); FileHandle dirHandle; if (Gdx.app.getType() == ApplicationType.Android) { dirHandle = Gdx.files.internal("xml/levels"); } else { dirHandle = Gdx.files.internal(System.getProperty("user.dir") + "/assets/xml/levels"); //   desktop ,    -    .     assets    desktop-   assets android- } for (FileHandle entry : dirHandle.list()) { levels.add(entry.name().split(".xml")[0]); } //             .       .    Java .    :) for (int i = 0; i < levels.size; i++) { int_levels.add(Integer.parseInt(levels.get(i))); } int_levels.sort(); levels.clear(); for (int i = 0; i < int_levels.size; i++) { levels.add(String.valueOf(int_levels.get(i))); } return levels; } } 


Well? Let's finally fill in the LevelScreen.java class. But before that, create a pair of xml files in the assets -> xml -> levels folder with the names 1.xml, 2.xml, and so on. And in the class we will write the following:

 public class LevelScreen implements Screen { final MyGame game; private Stage stage; private Table table; private LabelStyle labelStyle; private TextButton level; private Array<String> levels; public LevelScreen(MyGame gam) { game = gam; stage = new Stage(new ScreenViewport()); Skin skin = new Skin(); TextureAtlas buttonAtlas = new TextureAtlas(Gdx.files.internal("images/game/images.pack")); skin.addRegions(buttonAtlas); TextButtonStyle textButtonStyle = new TextButtonStyle(); textButtonStyle.font = game.levels; textButtonStyle.up = skin.getDrawable("level-up"); textButtonStyle.down = skin.getDrawable("level-down"); textButtonStyle.checked = skin.getDrawable("level-up"); //   XMLparse parseLevels = new XMLparse(); levels = parseLevels.XMLparseLevels(); labelStyle = new LabelStyle(); labelStyle.font = game.levels; //      MyGame table = new Table(); table.row().pad(20); //   +  table.center(); table.setFillParent(true); for (int i = 0; i < levels.size; i++) { final String cur_level = levels.get(i); level = new TextButton(cur_level, textButtonStyle); level.addListener(new ClickListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { Gdx.input.vibrate(20); return true; }; @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { game.setScreen(new PlayScreen(game, cur_level)); //     PlayScreen dispose(); }; }); table.add(level); //     ,               float indexLevel = Float.parseFloat(String.valueOf(i)) + 1; if (indexLevel % 5.0f == 0) table.row().padLeft(20).padRight(20).padBottom(20); } stage.addActor(table); //        Gdx.input.setInputProcessor(stage); Gdx.input.setCatchBackKey(true); //  ,         .      . stage.setHardKeyListener(new OnHardKeyListener() { @Override public void onHardKey(int keyCode, int state) { if (keyCode == Keys.BACK && state == 1){ game.setScreen(new MainMenuScreen(game)); } } }); } @Override public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); stage.act(delta); stage.draw(); } @Override public void resize(int width, int height) {} @Override public void show() {} @Override public void hide() {} @Override public void pause() {} @Override public void resume() {} @Override public void dispose() { stage.dispose(); game.dispose(); } } 


Wow, a lot has already written. In the next part (if this one goes through all the stages of publication) we will do the following:


Lesson files

Thanks for attention!

UPDATE

I was in such a hurry with the article that I forgot to describe one important point, because of which the project would not start. I apologize. So, we also need to create our own Stage class, which will be the successor of the libGDX Stage . We go to our package managers and create a class in it called PlayStage.java . Its code is:

 public class PlayStage extends Stage { public PlayStage(ScreenViewport screenViewport) { super(screenViewport); } //      @Override public boolean keyDown(int keyCode) { if (keyCode == Keys.BACK) { if (getHardKeyListener() != null) getHardKeyListener().onHardKey(keyCode, 1); } return super.keyDown(keyCode); } @Override public boolean keyUp(int keyCode) { if (keyCode == Keys.BACK){ if (getHardKeyListener() != null) getHardKeyListener().onHardKey(keyCode, 0); } return super.keyUp(keyCode); } public interface OnHardKeyListener { // ,     ,       (     ) public abstract void onHardKey(int keyCode, int state); } private OnHardKeyListener _HardKeyListener = null; public void setHardKeyListener(OnHardKeyListener HardKeyListener) { _HardKeyListener = HardKeyListener; } public OnHardKeyListener getHardKeyListener() { return _HardKeyListener; } } 


Now, in LevelScreen we create an object of the PlayStage class, and not a Stage and there should be no more errors. And one more addition. Let's prepare the PlayScreen class, since it needs to specify a constructor with parameters. We write it and generate the remaining methods so that the compiler does not swear. Code:

 public class PlayScreen implements Screen { final MyGame game; //        ,     .     . public PlayScreen(final MyGame gam, String strLevel) { game = gam; } @Override public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); } @Override public void resize(int width, int height) {} @Override public void show() {} @Override public void hide() {} @Override public void pause() {} @Override public void resume() {} @Override public void dispose() { game.dispose(); } } 

Source: https://habr.com/ru/post/224175/


All Articles