Оговорюсь еще раз. Я шибкий не знаток Java и поэтому следующий далее код, может смутить многих, но игру я написал меньше, чем за неделю и работал скорее на результат, чем на красоту и порядочность кода. Надеюсь, в комментариях найдется тот, кто поможет сделать код и структуру проекта, если не совершенными, то хотя бы привести к хорошему виду и дать возможность мне и остальным стать более хорошими программистами. Ладно, хватит лирики, продолжим наш «хардкор».
Создадим новый package и назовем его objects. В нем создадим класс фона, а в него добавим следующий код:
Файл BackgroundActor.java
package ru.habrahabr.songs_of_the_space.objects; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Sprite; import com.badlogic.gdx.scenes.scene2d.Actor; public class BackgroundActor extends Actor { private Texture backgroundTexture; private Sprite backgroundSprite; public BackgroundActor() { backgroundTexture = new Texture("images/sky.jpg"); backgroundSprite = new Sprite(backgroundTexture); backgroundSprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } @Override public void draw(Batch batch, float alpha) { backgroundSprite.draw(batch); } }
Ничего сложного. Это «актер», который устанавливается по размеру экрана пользователя и делает нашу игру более похожей на звездное небо. Примерно так это должно выглядеть:
Теперь добавим его в MyGame.java и сделаем его доступным извне, для того, чтобы не создавать его на каждом следующем экране. Это избавит нас от мерцания.
Файл MyGame.java
// Перед методом create() public BackgroundActor background; @Override public void create() { ... background = new BackgroundActor(); background.setPosition(0, 0); ... }
Далее, мы должны в каждом новой экране добавлять его на сцену:
stage.addActor(game.background);
Теперь, также в пакете objects создадим класс ноты. Он будет хранить все наши ноты в нужной нам последовательности.
Файл Note.java
package ru.habrahabr.songs_of_the_space.objects; public class Note { private String note; private float delay; private Star star; // Устанавливаем ноты. Ноты будем брать из xml файла уровня. public void setNote(String note) { this.note = note; } public String getNote() { return this.note; } // Устанавливаем задержку для ноты, чтобы можно было создавать мелодии разной сложности public void setDelay(String delay) { this.delay = Float.parseFloat(delay); } public float getDelay() { return this.delay; } // Наша красавица -- звезда public void setStar(Star star) { this.star = star; } public Star getStar() { return this.star; } }
Теперь, когда мы создали ноту, нам нужно создать звезду, которая будет нашим основным актером в нашей космической сцене. Она будет мерцать и петь свою чудную мелодию для будущих пользователей.
Перед тем, как продолжить немного поясню, зачем нам нужен отдельный класс для ноты и для звезды. Мелодия может повторять свои ноты, а каждая звезда должна быть в единственном экземпляре. Когда я только продумывал идею игры, я как раз хранил каждую ноту внутри звезды. В итоге, либо мелодия была слишком простой, либо звезд на небе становилось слишком много и было сложно пройти уровень даже с восемью повторяющимися нотами.
Итак, создаем звезду.
Файл Star.java
package ru.sayakhov.songs_of_the_space.objects; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Sprite; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; public class Star extends Actor { // Звук, если пользователь ошибся private Sound sound, wrong; // Ноты в строковом представлении private String note; // Изображение звезды private Sprite img; private Texture img_texture; // Наш уровень. Он будет говорить, где должна находиться звезда private Level level; public Star(String str_img, String str_sound) { img_texture = new Texture("images/stars/" + str_img + ".png"); img_texture.setFilter(TextureFilter.Linear, TextureFilter.Linear); img = new Sprite(img_texture); // Это я сделал для того, чтобы размер звезды менялся в зависимости от экрана пользователя img.setSize(Gdx.graphics.getHeight() * 15 / 100, Gdx.graphics.getHeight() * 15 / 100); this.note = str_sound; this.sound = Gdx.audio.newSound(Gdx.files.internal("sounds/bells/" + str_sound + ".mp3")); this.wrong = Gdx.audio.newSound(Gdx.files.internal("sounds/bells/wrong.mp3")); // Слушает события касания пользователя и играет соответствующую ноту, а также создает эффект мерцания за счет увеличения звезды в размерах addListener(new ClickListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { img.setScale(1.2f); if (note.equals(level.getCurrentNoteStr())) { level.setCurrentNote(); Gdx.input.vibrate(25); // Дадим пользователю понять, что он нажал немного вибрируя в момент касания getSound().play(); } else { // Если юзер ошибся, то начинаем сначала. Проигрываем первые четыре ноты и играем их. А также сильнее вибрируем, чтобы оповестить его об ошибке. level.setCurrentNote(0); level.setEndNote(true); level.setPlayMusic(); getWrongSound().play(); Gdx.input.vibrate(80); } return true; } @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { img.setScale(1.0f); // Как только пользователь отпустил нашу звезду, делаем ее размер таким же, каким он был } }); setTouchable(Touchable.enabled); // Делаем нашу звезду активной для касания } public void setLevel(Level level) { this.level = level; } // Устанавливаем позицию изображения равной позиции актера, и делаем размеры актера равными размеру звезды @Override public void setBounds(float x, float y, float width, float height) { super.setBounds(x, y, this.img.getWidth(), this.img.getHeight()); this.img.setPosition(x, y); } // В каждый момент исполнения, немного крутим нашу звезду. Пусть потанцует. @Override public void act(float delta) { img.rotate(0.05f); } // Рисуем звезду на сцене @Override public void draw(Batch batch, float alpha) { this.img.draw(batch); } public Sound getSound() { return this.sound; } public Sound getWrongSound() { return this.wrong; } public String getNote() { return this.note; } public Sprite getImg() { return this.img; } }
Теперь создадим наш класс уровня. Он будет отвечать за создания всех актрис и актеров, а также играть мелодию и поздравлять в победой. Я добавил его в пакет objects, но он лучше подходит как менеджер, поэтому можете перенести его туда самостоятельно.
Файл Level.java
package ru.habrahabr.songs_of_the_space.objects; import java.util.HashMap; import java.util.Map; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.utils.Array; public class Level { private XMLparse xml_parse; private Array<Note> notes = new Array<Note>(); private Array<Star> stars = new Array<Star>(); private Map<String, Array<String>> starsPos = new HashMap<String, Array<String>>(); private int currentNote; private int endNote; private float delay; private boolean playMusic; private boolean win; private final Sound winner = Gdx.audio.newSound(Gdx.files.internal("sounds/win.mp3")); // Победный звук аплодисментов public Level(String level) { xml_parse = new XMLparse(); Array<Star> xml_stars = xml_parse.XMLparseStars(); // парсим звезды из всего списка имеющихся notes = xml_parse.XMLparseNotes(level); // парсим ноты для уровня starsPos = xml_parse.getPos(level); // позиции звезд в текущем уровне endNote = 3; delay = 0; this.win = false; setPlayMusic(); for (Note n : this.notes) { for (Star s : xml_stars) { if (n.getNote().equals(s.getNote()) && !this.stars.contains(s, true)) { // Поскольку в одном xml у нас хранятся все возможные варианты звезд, этот код отсеит лишние this.stars.add(s); } if (n.getNote().equals(s.getNote())) n.setStar(s); // А здесь мы устанавливаем для каждой ноты свою звезду } } for (Star s : this.stars) { s.setLevel(this); s.setBounds( // Это нужно для того, чтобы позицию звезды можно было описать в процентом от размера экрана пользователя отношении (так как скопления наших звезд будут стараться походить на настоящие созвездия реального космоса) Gdx.graphics.getWidth() * Float.parseFloat(starsPos.get(s.getNote()).get(0)) / 100, Gdx.graphics.getHeight() * Float.parseFloat(starsPos.get(s.getNote()).get(1)) / 100 - s.getImg().getHeight() / 2, s.getImg().getWidth(), s.getImg().getHeight() ); } } public boolean isWin() { return this.win; } // Устанавливаем последнюю ноту public void setEndNote() { if (this.endNote < this.notes.size - 1) { this.endNote += 4; } } // Переопределяем метод для того, чтобы в случае, когда пользователь ошибся, сделать последней четвертую ноту. // Можно было обойтись и одним методом, но мне так понравилось больше. Переопределяй! Властвуй! public void setEndNote(boolean begin) { if (begin) { this.endNote = 3; } } public void setCurrentNote(int note) { this.currentNote = note; } // Устанавливаем текущую ноту public void setCurrentNote() { if (this.currentNote < this.notes.size - 1) { this.currentNote++; if (currentNote - 1 == endNote) { currentNote = 0; setEndNote(); // Увеличиваем значение на 4 для последней ноты setPlayMusic(); // Играем мелодию с большим количеством нот } } else { // Если пользователь отыграл все ноты, играем победные аплодисменты this.endNote = notes.size - 1; this.currentNote = 0; this.win = true; this.winner.play(); } } public int getCurrentNote() { return this.currentNote; } public String getCurrentNoteStr() { return this.notes.get(this.currentNote).getNote(); } public Array<Note> getNotes() { return this.notes; } public Array<Star> getStars() { return this.stars; } public void setPlayMusic() { if (playMusic) { playMusic = false; } else { playMusic = true; } } // Играем наши ноты для пользователя public void playStars() { if (playMusic) { for (Star s : stars) { s.setTouchable(Touchable.disabled); // Не даем пользователю трогать наши звезды, пока играет мелодия } if (getCurrentNote() < notes.size) { if (getCurrentNote() <= endNote) { Note note = notes.get(getCurrentNote()); delay += note.getDelay(); // delay позволяет создавать задержку по времени между проигрыванием нот if (delay >= 0.9f) note.getStar().getImg().setScale(1.2f); // Увеличиваем активную в данный момент звезду для того, чтобы создать эффект мерцания if (delay >= 1.0f) { delay = 0; setCurrentNote(currentNote + 1); note.getStar().getSound().play(); note.getStar().getImg().setScale(1f); } } else { setPlayMusic(); setCurrentNote(0); } } else { delay = 0; setCurrentNote(0); setPlayMusic(); } } else { for (Star s : stars) { s.setTouchable(Touchable.enabled); // Делаем все наши звезды активными для касания } } } }
Надеюсь, все понятно. Старался максимально комментировать код. Единственное, что может вызвать вопросы — это delay. Поясню немного. Метод playStars() будет вызываться в методе render() класса PlayScreen.java. Поскольку, он выполняется в потоке, каждый раз при совпадении всех условий, delay будет увеличиваться на заданное количество. Таким образом, будет имитироваться задержка в игре нот. Это лучше увидеть в коде. Давайте, наконец наполним наш класс PlayScreen.java. Поскольку, там много кода, я решил его спрятать под спойлер.
package ru.habrahabr.songs_of_the_space.managers; import ru.habrahabr.songs_of_the_space.MyGame; import ru.habrahabr.songs_of_the_space.objects.GamePreferences; import ru.habrahabr.songs_of_the_space.objects.Level; import ru.habrahabr.songs_of_the_space.objects.PlayStage; import ru.habrahabr.songs_of_the_space.objects.PlayStage.OnHardKeyListener; import ru.habrahabr.songs_of_the_space.objects.Star; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.Screen; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.viewport.ScreenViewport; public class PlayScreen implements Screen { final MyGame game; private GamePreferences pref; private Level level; private String sL, nL; private Array<Star> stars; private PlayStage stage; private Table table, table2; public PlayScreen(final MyGame gam, String strLevel, String strNextLevel) { game = gam; this.sL = strLevel; this.nL = strNextLevel; stage = new PlayStage(new ScreenViewport()); stage.addActor(game.background); // Добавляем фон pref = new GamePreferences(); level = new Level(strLevel); stars = level.getStars(); level.setCurrentNote(0); for (final Star s : stars) { stage.addActor(s); // Добавляем всех актрис (звезды) на сцену } LabelStyle labelStyle = new LabelStyle(); labelStyle.font = game.font; // Skin для кнопок, которые показываются в случае победы пользователя 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"); // Для всех кнопок лучше создать таблицу, так как она хорошо справляется с варавниванием table = new Table(); table.padTop(20); table.center().top(); table.setFillParent(true); // label для показа названия созвездия Label label = new Label(game.langStr.get("Constellation"), labelStyle); table.add(label); table.row().padBottom(30); label = new Label(game.langStr.get("level_" + strLevel), labelStyle); table.add(label); table.setVisible(false); stage.addActor(table); table2 = new Table(); table2.center().bottom(); table2.setFillParent(true); table2.row().colspan(2).padBottom(30); label = new Label(game.langStr.get("YouWin"), labelStyle); table2.add(label).bottom(); table2.row().padBottom(20); TextButton button = new TextButton(game.langStr.get("Again"), textButtonStyle); // Нужно не забыть заставить кнопки прослушивания событих клика (касания) // Эта кнопка, после нажатия, запустит уровень сначала button.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, sL, nL)); dispose(); }; }); table2.add(button); // А эта перенесет пользователя обратно на экран выбора уровня button = new TextButton(game.langStr.get("Levels"), textButtonStyle); button.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(); }; }); table2.add(button); table2.setVisible(false); stage.addActor(table2); 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 LevelScreen(game)); } } }); } @Override public void render(float delta) { // Очистка экрана в каждый момент выполнения потока Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Рисуем сцену и вызываем метод act() для дополнительных действий актеров, описанных в одноименном методе каждого (в нашем случае, это вращение звезд) stage.act(delta); stage.draw(); level.playStars(); // Если пользователь выиграл, то показываем ему все наши кнопки, label'ы и прочее if (level.isWin()) { table.setVisible(true); table2.setVisible(true); pref.setLevel(nL); // Это для настроек игры. Объяснения ниже. for (Star s : stars) { s.setTouchable(Touchable.disabled); // Делаем все звезды неактивными для касания после победы пользователя } } } @Override public void resize(int width, int height) {} @Override public void show() {} @Override public void hide() {} @Override public void pause() {} @Override public void resume() {} // На забываем уничтожить сцену и объект класса MyGame @Override public void dispose() { stage.dispose(); game.dispose(); } }
Наверное, код вызвал несколько вопросов, так как в нем можно заметить новый класс GamePreferences.java. Этот класс позволит нам хранить все настройки игры в удобном формате. Для Android приложения будет создан, так называемый «SharedPreferences». Подробнее здесь. В данном случае, в нем мы будем хранить пройденные пользователем уровни.
Ну что? Давайте теперь создадим и наполним его.
Файл GamePreferences.java
package ru.habrahabr.songs_of_the_space.objects; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Preferences; public class GamePreferences { private Preferences pref; private static final String PREFS_NAME = "SONGS_OF_THE_SPACE"; private static final String PREF_LEVEL = "LEVEL_"; public GamePreferences() { pref = Gdx.app.getPreferences(PREFS_NAME); } public boolean getLevel(String level) { pref.putBoolean(PREF_LEVEL + 1, true); pref.flush(); return pref.getBoolean(PREF_LEVEL + level, false); } public void setLevel(String level) { pref.putBoolean(PREF_LEVEL + level, true); pref.flush(); } }
В нем нет ничего сложного. Не буду дублировать документацию, ссылку на нее я дал ниже. Теперь нам нужно немного обновить наш класс XMLparse.java. Так, мы еще не научили нашу парсить звезды и ноты. Сделаем это.
package ru.habrahabr.songs_of_the_space.objects; import java.io.IOException; import java.util.HashMap; import java.util.Map; import com.badlogic.gdx.Application.ApplicationType; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.XmlReader; import com.badlogic.gdx.utils.XmlReader.Element; public class XMLparse { private Array<Star> stars = new Array<Star>(); private Array<Note> notes = new Array<Note>(); private Map<String, Array<String>> starsPos = new HashMap<String, Array<String>>(); // В этом методе мы будем парсить наши переводы. А вы как думали? Мы делаем многоязычную игру! public HashMap<String, String> XMLparseLangs(String lang) { HashMap<String, String> langs = new HashMap<String, String>(); try { Element root = new XmlReader().parse(Gdx.files.internal("xml/langs.xml")); Array<Element> xml_langs = root.getChildrenByName("lang"); for (Element el : xml_langs) { if (el.getAttribute("key").equals(lang)) { Array<Element> xml_strings = el.getChildrenByName("string"); for (Element e : xml_strings) { langs.put(e.getAttribute("key"), e.getText()); } } else if (el.getAttribute("key").equals("en")) { Array<Element> xml_strings = el.getChildrenByName("string"); for (Element e : xml_strings) { langs.put(e.getAttribute("key"), e.getText()); } } } } catch (IOException e) { e.printStackTrace(); } return langs; } // В этом методе парсим звезды public Array<Star> XMLparseStars() { try { Element root = new XmlReader().parse(Gdx.files.internal("xml/stars.xml")); Array<Element> xml_stars = root.getChildrenByName("star"); for (Element el : xml_stars) { Star star = new Star( el.getAttribute("files"), el.getAttribute("files") ); stars.add(star); } } catch (IOException e) { e.printStackTrace(); } return this.stars; } // В этом парсим уровни 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 { // Это хак, так как libGDX почему-то не хотел видеть этот файл при тестировании Desktop приложения dirHandle = Gdx.files.internal(System.getProperty("user.dir") + "/assets/xml/levels"); } for (FileHandle entry : dirHandle.list()) { levels.add(entry.name().split(".xml")[0]); } 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; } // Парсим ноты public Array<Note> XMLparseNotes(String strLevel) { try { Element root = new XmlReader().parse(Gdx.files.internal("xml/levels/" + strLevel + ".xml")).getChildByName("notes"); Array<Element> xml_notes = root.getChildrenByName("note"); for (Element el : xml_notes) { Note note = new Note(); note.setNote(el.getText()); note.setDelay(el.getAttribute("delay")); this.notes.add(note); } } catch (IOException e) { e.printStackTrace(); } return this.notes; } // Парсим позицию для звезд. Знаю знаю, можно было сделать это при парсинге уровня, но мне так легче потом читать этот код, если разбить его по задачам public Map<String, Array<String>> getPos(String strLevel) { try { Element root = new XmlReader().parse(Gdx.files.internal("xml/levels/" + strLevel + ".xml")).getChildByName("positions"); Array<Element> xml_pos = root.getChildrenByName("position"); for (Element el : xml_pos) { Array<String> xy = new Array<String>(); xy.add(el.getAttribute("x")); xy.add(el.getAttribute("y")); this.starsPos.put(el.getAttribute("note"), xy); } } catch (IOException e) { e.printStackTrace(); } return this.starsPos; } }
Осталось немного. Правда. Теперь, раз уж я заикнулся про многоязыковую поддержку, давайте создадим я немного поясню, как это будет. За основу берем локаль пользователя. Для нас она начинается с символом ru, для англичан с en и так далее. Я перевел приложение на два языка, поэтому языковой файл будет таким (и поэтому в коде метода XMLparseLangs немного странное условие):
<?xml version="1.0"?> <langs> <lang key="en"> <string key="Play">Play</string> <string key="Exit">Exit</string> <string key="Again">Again</string> <string key="Levels">Levels</string> <string key="YouWin">You win!</string> <string key="Constellation">Constellation</string> <!-- Levels --> <string key="level_1">Canes Venatici</string> <string key="level_2">Triangulum</string> <string key="level_3">Equuleus</string> <string key="level_4">Apus</string> <string key="level_5">Sagitta</string> <string key="level_6">Musca</string> <string key="level_7">Ursa Minor</string> <string key="level_8">Orion</string> <string key="level_9">Ursa Major</string> <string key="level_10">Eridanus</string> <string key="level_11">Lacerta</string> </lang> <lang key="ru"> <string key="Play">Играть</string> <string key="Exit">Выход</string> <string key="Again">Повторить</string> <string key="Levels">Уровни</string> <string key="YouWin">Вы победили!</string> <string key="Constellation">Созвездие</string> <!-- Levels --> <string key="level_1">Гончие псы</string> <string key="level_2">Треугольник</string> <string key="level_3">Малый Конь</string> <string key="level_4">Райская Птица</string> <string key="level_5">Стрела</string> <string key="level_6">Муха</string> <string key="level_7">Малая медведица</string> <string key="level_8">Орион</string> <string key="level_9">Большая медведица</string> <string key="level_10">Эридан</string> <string key="level_11">Ящерица</string> </lang> </langs>
Как видно, мы берем аттрибут и по нему определяем, что отдавать пользователю. Теперь нужно сделать еще кое-что. Создать XML файлы звезд, нот, уровней. Сделаем это.
<?xml version="1.0"?> <stars> <star files="c5" /> <star files="c#5" /> <star files="d5" /> <star files="d#5" /> <star files="e5" /> <star files="f5" /> <star files="f#5" /> <star files="g5" /> <star files="g#5" /> <star files="a5" /> <star files="a#5" /> <star files="b5" /> <star files="c6" /> <star files="c#6" /> <star files="d6" /> <star files="d#6" /> <star files="e6" /> <star files="f6" /> <star files="f#6" /> <star files="g6" /> <star files="g#6" /> <star files="a6" /> <star files="a#6" /> <star files="b6" /> </stars>
Если бегло глянуть этот файл, то можно заметить, что немного слукавил, когда сказал, что для каждой ноты будет своя звезда. Я сделал разное представление звезд в разной тональности. Зачем? Для улучшения звучания, так как если взять более-менее интересное созвездие, то можно заметить, то оно состоит, как минимум из 8-9 звезд, а писать мелодию для 8-9 разных нот не очень-то хотелось, вот я и решил немного упростить себе жизнь, добавив еще одну октаву.
Теперь приведу файл(для примера) уровня.
<?xml version="1.0"?> <level> <notes> <note delay="0.02f">d5</note> <note delay="0.05f">a6</note> <note delay="0.05f">d6</note> <note delay="0.05f">f#6</note> <note delay="0.02f">e5</note> <note delay="0.05f">a6</note> <note delay="0.05f">c#6</note> <note delay="0.05f">e6</note> <note delay="0.02f">d6</note> <note delay="0.05f">f#6</note> <note delay="0.05f">a6</note> <note delay="0.05f">d5</note> </notes> <positions> <position note="d5" x="5" y="35" /> <position note="a6" x="20" y="43" /> <position note="d6" x="40" y="50" /> <position note="f#6" x="55" y="45" /> <position note="e5" x="67" y="37" /> <position note="c#6" x="77" y="47" /> <position note="e6" x="90" y="50" /> </positions> </level>
Как видно, сначала мы определяем последовательность нот и их задержку, а затем определяем позицию каждой уникальной ноты в процентом отношении. Кажется это все. Если что-то забыл, жду комментариев. Также, жду критики и советов. Если кому-нибудь будет интересно в следующей статье я могу описать процесс подключения AdMob к нашей игре, рассказать как и откуда я брал звуки для игры, и также рассказать о том, как я выкладывал игру в Google Play. Спасибо за внимание!
Файлы проекта и пример готовой игры.
ссылка на оригинал статьи http://habrahabr.ru/post/224223/
Добавить комментарий