[Из песочницы] [ 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 XMLparseLevels () { Array levels = new Array(); Array int_levels = new Array();

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 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 Добавим объекты звезд, нот и еще кое-чего Файлы урока.

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

© Habrahabr.ru