понедельник, 4 июля 2011 г.

Neon Physics:разработка

Всем людям свойственно лениться, и я не являюсь исключением. До этого момента я с мобильными технологиями никогда не работал. Собрав всю волю в кулак, я решил освоить java и android sdk, написать более менее серьёзное приложение и разместить его на android market. Я решил сделать игрушку с физической составляющей.

За пару часов было составлено техническое задание и правила игры. Правила оказались очень простыми. В игре есть 16 уровней. Пройдя уровень, открывается следующий. На каждом уровне есть различные объекты, некоторые можно передвигать, некоторые зафиксированы. Так же на игровом уровне присутствует генератор, который создает шарики, и объект-цель, куда эти шарики должны попасть. На шары действует гравитация. Чтобы выиграть шарик должен попасть в цель. Игру решил назвать Neon Physics.

Прочитав некоторое количество литературы на различных сайтах, в том числе о hello world, я понял, что писать с нуля нет смысла. За основу я взял готовый движок andengine для рендера opengl + box2d для расчета физики. Что бы их подключить нужно заглянуть на этот сайт. Там есть детальное видео, как сделать так, что бы все заработало в три клика. Так же целый список примеров можно посмотреть тут.

Дальше я расскажу как, использовав пример ниже, я написал простенькую игрушку. Пример находиться тут. Если вы сделали все так как было показано в видео, то вы сможете скомпилировать данный пример.

<code>

public class PhysicsExample extends BaseExample implements IAccelerometerListener, IOnSceneTouchListener {

//Размеры изображения для рендеринга
private static final int CAMERA_WIDTH = 720;
private static final int CAMERA_HEIGHT = 480;

//Переменная отвечающая за физические характеристики объекта
private static final FixtureDef FIXTURE_DEF = PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f);

//Текстура
private Texture mTexture;

//4 изображения анимированных изображения
private TiledTextureRegion mBoxFaceTextureRegion;
...

//Переменная отвечающая за физический движок
private PhysicsWorld mPhysicsWorld;

//Количество объектов
private int mFaceCount = 0;

//Событие, которое срабатывает, когда сцена загрузилась
@Override
public Engine onLoadEngine() {
//Вывод сообщения "Touch the screen to add objects."
Toast.makeText(this, "Touch the screen to add objects.", Toast.LENGTH_LONG).show();

//Создание камеры для сцены
final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);

//Устанавливаем флаг true для работы в отдельном потоке обработки прикосновений
engineOptions.getTouchOptions().setRunOnUpdateThread(true);

//Создаем Engine с заданными параметрами
return new Engine(engineOptions);
}

//Событие, которое срабатывает, когда грузятся ресурсы
@Override
public void onLoadResources() {
//Создание текстуры с альфа каналом
//Размеры текстуры в opengl должны быть степенью двойки
this.mTexture = new Texture(64, 128, TextureOptions.BILINEAR_PREMULTIPLYALPHA);

//Устанавливаем папку, откуда грузить ресурсы
TextureRegionFactory.setAssetBasePath("gfx/");

//Загрузка 4 спрайтов
//Последние четыре числа:положение на текстуре по x и y, количество кадров по x и y
this.mBoxFaceTextureRegion = TextureRegionFactory.createTiledFromAsset(this.mTexture, this, "face_box_tiled.png", 0, 0, 2, 1); // 64x32
...

this.mEngine.getTextureManager().loadTexture(this.mTexture);

//Включаем работу G-sensor
this.enableAccelerometerSensor(this);
}

//Событие, которое срабатывает, когда грузятся сцена
@Override
public Scene onLoadScene() {
//Подключаем FPSLogger
this.mEngine.registerUpdateHandler(new FPSLogger());

//Создаем сцену с двумя слоями, слои нужны, что бы не перекрывать объекты
final Scene scene = new Scene(2);

//Устанавливаем задний фон
scene.setBackground(new ColorBackground(0, 0, 0));

//Включаем обработку прикосновений
scene.setOnSceneTouchListener(this);

//Создаем mPhysicsWorld, box2d
//Устанавливаем гравитацию в зависимости от G-sensor
this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);

//Создаем четыре объекты-стенки, которые будут ограничивать наш физический движок по краям, что бы объекты не улетали за границу экрана
final Shape ground = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH, 2);
...

//Переменная отвечающая за физические характеристики объекта
final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.5f);

//Создаем физическое представление четырех объектов, которые были описаны выше
PhysicsFactory.createBoxBody(this.mPhysicsWorld, ground, BodyType.StaticBody, wallFixtureDef);
...

//Добавляем наши объекты в сцену
scene.getFirstChild().attachChild(ground);
....

//включаем mPhysicsWorld
scene.registerUpdateHandler(this.mPhysicsWorld);

return scene;
}

...

//Обработка нажатия на экран
//При нажатии добавляем новый объект
@Override
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
if(this.mPhysicsWorld != null) {
if(pSceneTouchEvent.isActionDown()) {
this.addFace(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
return true;
}
}
return false;
}

...

//Процедура добавления объекта
//В зависимости от количества объектов добавляет один из четырех объектов
private void addFace(final float pX, final float pY) {
//получение текущей Scene
final Scene scene = this.mEngine.getScene();

//увеличиваем счетчик количества объектов
this.mFaceCount++;

//Переменная визуального представления
final AnimatedSprite face;
//Переменная физического представления
final Body body;

//В зависимости от mFaceCount % 4 создаем объект
//Визуальное представление + физическое представление
if(this.mFaceCount % 4 == 0) {
face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion);
body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
} else
...

//Включаем смену кадров с задержкой в 200 мс
face.animate(200);

//Добавляем в рендер список объект
scene.getLastChild().attachChild(face);

//Добавляем в список на обработку физики
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, body, true, true));
}

//В стандартной поставке box2d есть только геометрия для прямоугольного и круглого объекта. Нам надо задать произвольную геометрию для треугольного и гексагонального объекта

...
}
</code>

На основе этого примера я сделал свое первое приложение для android и поместил его на market. Справедливости ради надо сказать, что на разработку я потратил около недели в свободное время. Много времени было потрачено на дизайн и оформление. Художник из меня очень плохой.

В течение первых дней его скачало около ста человек. Приложение является бесплатным. Для монетизации приложения добавил admob, но <s>больших</s> денег он мне не принес. Для себя считаю это небольшим успехом, так как получил новый опыт в разработке. Ссылка на android marke.

Комментариев нет:

Отправить комментарий