[ libGDX ] Пишем полноценную игру под Android. Часть 1

от автора

Здравствуйте! Я решил попробовать себя на поприще game-dev’а и заодно рассказать и показать как это было.

Игра представляет собой экран, на котором расположены созвездия. Каждая звезда этого созвездия имеет свой цвет (нота). Например, ноту «До» обычно представляют красным цветом, а «Ми» — желтым. Вот что получится в итоге:

image

Итак, каждый уровень — новое созвездие и новая мелодия. Звезды играют первые четыре ноты, а затем вы должны повторить их в той же последовательности. Затем, к первым четырем нотам добавляются еще четыре и так далее, пока уровень не будет пройден.

Писать будем, используя фреймворк libGDX. Мне он больше всех понравился, как новичку в этом деле. Да и информации по нему я нашел больше. Итак, приступим.

Что нам понадобится:

  • Eclipse
  • Gradle
  • Android SDK
  • libGDX последней версии
  • Голова + руки + терпение

Я не буду создавать проект вручную. Мне проще воспользоваться gdx-setup. Итак, запускаем ее:

java -jar gdx-setup.jar 

Далее вводим:

  • Name: «Songs of the Space»
  • Package: «ru.yoursite.songs_of_the_space»
  • Game Class: «MyGame»
  • Destination: path/to/your/workspace/songs_of_the_space
  • Android SDK: path/to/your/sdk
  • libGDX Version: Nightlies
  • Sub Projects: Desktop, Android
  • Extentions: «Freetype»

Нажимаем Generate и, после окончания процесса, идем в Eclipse.

В Eclipse выбираем Import -> Gradle -> Gradle Project. Затем Browse.. ищем наш проект и затем Build. После завершения выбираем все проекты и наживаем Finish. После завершения у вас в списке проектов появятся наши проекты. Сразу идем в основной проект (core) -> MyGame.java. Очищаем все, что создал gdx, а также наследуемся не от ApplicationAdapter, а от Game. В итоге, класс должен получить вид:

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

Далее. Создадим новый пакет, назовем его screens. И в нем три класса:

  • MainMenuScreen — будет отвечать за начальный экран приложения
  • LevelScreen — будет отвечать за экран выбора уровней
  • PlayScreen — будет отвечать за игровой экран

Все они должны наследоваться от интерфейса Screen. Добавляем все методы, которые требует наше наследование, закрываем все, кроме MainMenuScreen. В нем пишем следующее:

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();     } } 

Далее. Делаем имрорт зависимостей (Shift + Ctrl + O). И идем в основной класс MyGame.java. В него добавим следующее:

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(); 	} } 

Делаем импорты и создаем новый package с именем managers (например). В нем класс XMLparse.java. Зачем это нужно? Затем, что уровни и многое другое мы будем брать из xml файлов. Кстати, создайте в папке assets (android проект -> assets) папку xml. В ней папки:

  • images
  • fonts
  • sounds
  • xml

В папку fonts положите шрифт. А в папке xml, создайте папку levels. Обновите Desktop проект ( F5 ), чтобы он подхватил все это. И теперь, давайте наполним наш класс XMLparse.java. В него пишем следующее:

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;     } } 

Ну что? Давайте наконец наполним класс LevelScreen.java. Но перед этим создайте пару xml файлов в папке assets -> xml -> levels с именами 1.xml, 2.xml и так далее. А в класс напишем следующее:

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();     } } 

Ух, много уже написал. В следующей части (если эта пройдет все этапы публикации) мы сделаем следующее:

  • Наполним класс PlayScreen
  • Добавим объекты звезд, нот и еще кое-чего

Файлы урока.

Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/224175/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *