Предыдущие две статьи были посвящены необходимым вещам, без который приложение написать невозможно. Существует еще ряд полезностей, которые могут быть включены в проект. Эти вещи существенно облегчат нам разработку в будущем, сделают приятным для чтения код. И плюс, сделают приложение более оптимальным с точки зрения расхода памяти, ресурсов и т.п.
Это тот перечень, который использовал когда-либо я. Возможно, есть более универсальныеоптимальныедурацкиеплохие вещи, которые можно было бы включить сюда.
1. Texture Packer GUI
Скачать: http://code.google.com/p/libgdx/wiki/TexturePacker
В процессе разработки приложения (игры) приходит все время иметь дело с кадрами, картинками (текстурами) которые нужно позиционировать на экране. В зависимости от масштабов приложения таких кадров может быть от нескольких до нескольких сотен. Причем, существует такое ограничение – размерность каждой картинки должна быть кратна степени двойки. То есть, каждая картинка должна быть размеров 2 х 2, 4 х 2, 2 х 1024. Произвольной длинны либо ширины у картинки, загружаемой в память быть не может. Последние модели смартфонов не скажут вам ничего, но на более ранних устройствах приложение будет просто валиться.
Как с этим бороться? Во-первых, можно конечно же каждую картинку подгонять под нужные размеры, иметь при этом кучу лишнего места на картинках (=> использовать лишнюю память), потом обрезать вручную каждую картинку. Вариант второй – создавать спрайты – изображения, которые включают в себя набор кадров. К примеру, мы можем создать спрайт размером 2048 х 2048, замостить его всеми объектами какой-нибудь конкретной сцены и в последующем загрузить в память только один этот спрайт.
Как это решает Texture Packer. Создаем там так называемый .pack, выбираем папку, в которую сбрасываем все свои текстуры, задаем максимальный размер текстур для спрайта и нажимаем кнопку “Упаковать”. На выходе мы получим один (или несколько – если все картинки не поместились на текстуру) страйт и .pack файл, который хранит расположение (пиксельные координаты) каждой картинки в спрайте.
Далее в коде приложения мы можем для начала загрузить атлас (в терминологии LibGdx):
TextureAtlas atlas = new TextureAtlas( Gdx.files.internal( gameAssetsPath(this.atlasFile) ) );
И потом найти нужную нам картинку:
TextureRegion region = atlas.findRegion("region_name");
2. Hiero
Скачать: http://code.google.com/p/libgdx/downloads/detail?name=hiero.jar&can=2&q=
LibGdx такова, что не умеет работать с теми шрифтами, которые установлены в ОС. Поэтому все надписи, которые придется рисовать, мы должны и будем делать с помощью растровых шрифтов. Растровый шрифт представляет собой спрайт (см. рисунок справа), на который нанесены все символы, которые нам нужны будут теоретически + текстовый файл (.fnt для LibGdx), который описывает пиксельную позицию каждого символа на спрайте.
Hiero – программа, которая поможет нам генерировать паки (спрайт + .fnt) для LibGdx. Запускаем его. Выбираем себе нужную стилистику – размер шрифта, тени, эффекты, сам шрифт. Нажимаем File -> Save BMFont files и сохраняем файлы.
Отмечу сразу, что в пак нельзя включить одновременно нормальный, полужирный, курсивный тексты.
Программно шрифт подгрузить можно будет так:
BitmapFont font = new BitmapFont(".fnt file", "sprite file", false);
3. Universal Tween Engine (UTE)
Скачать: http://code.google.com/p/java-universal-tween-engine/
В процессе работы с объектами некоторые из них нам придется так или иначе модифицировать – увеличиватьуменьшать, сдвигать, убиратьдобавлять. Чтобы это выглядело плавно, нам очень поможет UTE. По сути он делает аппроксимацию между исходными данными и конечными.
Пример: нам нужно сдвинуть плавно на 10 влево пикселей объект на экране. Нам нужно сообщить UTE следующее: текущую позицию объекта, конечную координату (x + 10), конечную кординату y, время, за которое нужно пройти эти 10 пикселей (скажем 1 секунду). В итоге UTE будет менять на протяжении секунды координату x до x+10. А мы будем просто рисовать объект.
Как это выглядит программно. Для каждого типа объектов нам необходимо сделать .java класс Accessor, унаследованный от шаблонного класса TweenAccessor<?> и определить там два метода getVakues и setValues. Так это будет выглядеть для спрайта:
public class SpriteAccessor implements TweenAccessor<Sprite> { public static final int POS_XY = 1; public static final int CPOS_XY = 2; public static final int SCALE_XY = 3; public static final int ROTATION = 4; public static final int OPACITY = 5; public static final int TINT = 6; @Override public int getValues(Sprite target, int tweenType, float[] returnValues) { switch (tweenType) { case POS_XY: returnValues[0] = target.getX(); returnValues[1] = target.getY(); return 2; case CPOS_XY: returnValues[0] = target.getX() + target.getWidth()/2; returnValues[1] = target.getY() + target.getHeight()/2; return 2; case SCALE_XY: returnValues[0] = target.getScaleX(); returnValues[1] = target.getScaleY(); return 2; case ROTATION: returnValues[0] = target.getRotation(); return 1; case OPACITY: returnValues[0] = target.getColor().a; return 1; case TINT: returnValues[0] = target.getColor().r; returnValues[1] = target.getColor().g; returnValues[2] = target.getColor().b; return 3; default: assert false; return -1; } } @Override public void setValues(Sprite target, int tweenType, float[] newValues) { switch (tweenType) { case POS_XY: target.setPosition(newValues[0], newValues[1], false); break; case CPOS_XY: target.setPosition(newValues[0] - target.getWidth()/2, newValues[1] - target.getHeight()/2); break; case SCALE_XY: target.setScale(newValues[0], newValues[1]); break; case ROTATION: target.setRotation(newValues[0]); break; case OPACITY: Color c = target.getColor(); c.set(c.r, c.g, c.b, newValues[0]); target.setColor(c); break; case TINT: c = target.getColor(); c.set(newValues[0], newValues[1], newValues[2], c.a); target.setColor(c); break; default: assert false; } } }
Далее нам нужно зарегистрировать факт того, что SpriteAccessor используется для типа Sprite и нужен менеджер – объект класса TweenManager:
Tween.registerAccessor(Sprite.class, new SpriteAccessor()); tweenManager = new TweenManager();
Так же нужно при каждой отрисовке обновлять tweenManager, например так:
tweenManager.update(Gdx.graphics.getDeltaTime());
Собственно все, теперь мы можем модифицировать объекты. Мы можем делать отдельные Tween’ы
Tween.to(objectToRun, SpriteAccessor.POS_XY, 8f).target(objectToRun.getX() + 10, objectToRun.getY()).start(tweenManager);
и так же можем группировать Tween’ы в Timeline’ы
Timeline.createSequence() .push(Tween.to(objectToRun, ActorAccessor.SCALE_XY, 1f).target(2f, 2f).ease(TweenEquations.easeNone)) .push(Tween.to(objectToRun, SpriteAccessor.POS_XY, 8f).target(objectToRun.getX() + 10, objectToRun.getY())) .start(tweenManager);
При этом вначале объект увеличится в два раза за 1 секунду, а потом сдвинется на 10 пикселей за 8 секунд.
4. Box2D – editor
Скачать: http://code.google.com/p/box2d-editor/
LibGdx содержит в себе реализацию движка Box2D. Этот движок позволяет работать с физическими телами так, как это происходило бы в реальном времени. Каждую модельку для этого движка поэтому необходимо снабдить необходимыми атрибутами физического тела – масса, плотность и т.п. В мир, в котором происходит взаимодействие моделей необходимо привнести гравитацию и пр. параметры реального мира.
Box2D – editor позволяет визуально настраивать модели – задавать им области, массу и пр. атрибуты.
5. Particle Editor
Скачать: http://www.badlogicgames.com/wordpress/?p=1255
Эта программа помогает в случаях, когда необходимо сделать эффект разлетающихся частиц, сделать анимацию взрыва или огня.
Пример того, как можно использовать все эти тулзовины можно найти вот тут – http://habrahabr.ru/post/137861/. Мы же, попробуем шаг за шагом сделать сцену, на примере которой рассмотрим работу 2D-графического приложения и LibGdx в частности.