Пробуем AndEngine и physicsbox2d

    В этой заметке я хочу рассказать о существовании такого расширения для AndEngine как physics2box.
    AndEngine — это бесплатный движок для игр под Andtoid, один из наиболее популярных. Для него существует ряд расширений, одно из них — physicsbox2d. Эта библиотека позволяет создавать физику в игровом мире.

    Про AndEngine на Хабре уже отлично написал stepango . Я с удовольствием воспользовался его трудами, и, заодно, прикрутил physicsbox2d. Это оказалось довольно просто.

    Я не берусь рассказать обо всех, или даже о какой-то части возможностей этих двух инструментов, а только подскажу интересующимся, с чего начать. А начать вот с чего:

    Создаем стандартный проект под Android, и добавляем в него библиотеки andengine.jar, andenginephysicsbox2dextension.jar. Также, понадобится папка armeabi с файлами libandenginephysicsbox2dextension.so и armeabilibxmp.so. В Nebeans достаточно положить все это в папку libs в проекте. Как добыть все это — описывать не буду, что помешает праздно любопытствующим, но не остановит действительно заинтересовавшихся. Теперь можно приступать.

    1. Унаследуем нашу Activity от AndEngine-вской BaseGameActivity.
    2. Реализуем нужные нам методы onLoadEngine и onLoadScene

    Вот код:
    public class MainActivity extends BaseGameActivity
    {
        // Ширина экрана камеры типа пикселях
        private static final int CAMERA_WIDTH = 800;
        // Высота экрана камерыв пикселях
        private static final int CAMERA_HEIGHT = 480;
        // Параметры физической среды в нашем мире
        private static final FixtureDef FIXTURE_DEF = PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f);
        // Камера, через которую мы смотрим на мир
        private Camera mCamera;
        // Сцена, на которую мы смотрим через камеру
        private Scene mScene;
        // Физический Мир, в котором все происходит.
        private PhysicsWorld mPhysicsWorld;
        
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
        }
    
        // загрузка движка
        public Engine onLoadEngine() {
            DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
            // Тут происходят загадочные манипуляции с разрешением, чтоб
            // не думать о нем в дальнейшем. RatioResolutionPolicy об этом вроде
            // заботится. Однако детали я еще не выяснял, не судите строго
            return new Engine(new EngineOptions(true, ScreenOrientation.PORTRAIT,
                    new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
                    this.mCamera));
        }
    
        public void onLoadResources() {
        }
    
        public Scene onLoadScene() {
            // Создаем новую сцену. Можно создать многослойную сцену, но это не для нас, новичков.
            mScene = new Scene(1);
            // Создаем физический мир с земной гравитацией, действущей вниз.
            this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
            // Физический мир будет рулить сценой
            mScene.registerUpdateHandler(mPhysicsWorld);
            return mScene;
        }
    
        public void onLoadComplete() {
        }
      
    }
    
    


    Запускаем — и видим дружелюбный вакуум. В нашем мире пока ничего нет.

    2. Загрузим текстуры. Немного совершенствуя пример stepango , используем статические переменные. Ходят слухи, что так лучше. Для этого создадим отдельный класс:

    public class Textures {
    
        private Texture mTexture;
        private static TextureRegion mBallTextureRegion;
        private static TextureRegion mBackgroundTextureRegion;
    
        public Textures(final BaseGameActivity activity, final Engine engine) {
            // Здесь как бы атлас текстур - на этой "площадке" будут располагаться
            // все текстуры.
            mTexture = new Texture(1024, 1024,
                    TextureOptions.BILINEAR_PREMULTIPLYALPHA);
    
            // Размер изображения - 512х1024.
            mBackgroundTextureRegion = TextureRegionFactory.createFromAsset(
                    mTexture, activity, "gfx/bkg.png", 0, 0);
            // обратите внимание - кладем некстуру на атлас с координат
            // 512,0 - иначе мячик наложится на фон.
            mBallTextureRegion = TextureRegionFactory.createFromAsset(
                    mTexture, activity, "gfx/ball.png", 512, 0);
    
            engine.getTextureManager().loadTexture(mTexture);
        }
    
        public static TextureRegion getBackground() {
            return mBackgroundTextureRegion;
        }
    
        public static TextureRegion getBallTextureRegion() {
            return mBallTextureRegion;
        }
    }
    
    


    Нужно инициализировать сей объект в activity в соответствующем методе:

    public void onLoadResources() {
            // Загружаем текстуры
            mTextures = new Textures(this, getEngine());
        }
    


    То, что не очень правильно работаем со статическими объектами — не в этом суть примера. По хорошему, тут бы какой-нибудь singleton подошел.

    3. Теперь у нас есть текстуры. Самое время в методе onLoadScene создать объекты — боковые стенки, пол, и мячик.
    Метод преображается следующим образом:

    public Scene onLoadScene() {
            // Создаем новую сцену. Можно создать многослойную сцену, но это не для нас, новичков.
            mScene = new Scene(1);
            // Создаем физический мир с земной гравитацией, действущей вниз.
            this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
            
            // Физический мир будет рулить сценой
            mScene.setBackground(new SpriteBackground(new Sprite(0, 0, Textures.getBackground())));
            
            // Создаем линии - будут границами
            Line line_top = new Line(0, 0, CAMERA_WIDTH, 0, 5.0f * metrics.density);
            Line line_left = new Line(0, 0, 0, CAMERA_HEIGHT, 5.0f * metrics.density);
            Line line_right = new Line(CAMERA_WIDTH, 0, CAMERA_WIDTH, CAMERA_HEIGHT, 5.0f * metrics.density);
            Line line_bottom = new Line(0, CAMERA_HEIGHT, CAMERA_WIDTH, CAMERA_HEIGHT, 5.0f * metrics.density);
            // Создаем тела на основе линий
            Body wall_top = PhysicsFactory.createLineBody(mPhysicsWorld, line_top, FIXTURE_DEF);
            Body wall_left = PhysicsFactory.createLineBody(mPhysicsWorld, line_left, FIXTURE_DEF);
            Body wall_right = PhysicsFactory.createLineBody(mPhysicsWorld, line_right, FIXTURE_DEF);
            Body wall_bottom = PhysicsFactory.createLineBody(mPhysicsWorld, line_bottom, FIXTURE_DEF);
    
            // Создаем спрайт мячика
            Sprite mSprite = new Sprite(0.0f, 0.0f, 100, 100, Textures.getBallTextureRegion());
            // Устанавливаем его в нужное место
            mSprite.setPosition(100, 100);
            // Создаем физическое тело мячика
            Body mBody = PhysicsFactory.createCircleBody(mPhysicsWorld, 100, 100, 50, 0, BodyType.DynamicBody, FIXTURE_DEF);
                   
            // Связываем спрайты с их физическими телами
            this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_top, wall_top, true, true));
            this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_left, wall_left, true, true));
            this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_right, wall_right, true, true));
            this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(line_bottom, wall_bottom, true, true));
            mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(mSprite, mBody, true, true));
            
            // Загружаем в сцену спрайты
            this.mScene.attachChild(mSprite);
            this.mScene.attachChild(line_top);
            this.mScene.attachChild(line_left);
            this.mScene.attachChild(line_right);
            this.mScene.attachChild(line_bottom);
     
            mScene.registerUpdateHandler(mPhysicsWorld);
            return mScene;
        }
    
    


    Вот и все. При запуске у нас появится фон, и мячик вверху слева. Он тут же начнет падать, долетит до конца экрана, отскочит… и дальше все как в жизни, без вского вмешательства прогаммиста.

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

    Вот ссылка на статью stepango , которая мне помогла в моих изысканиях:
    habrahabr.ru/blogs/android_development/120716
    Поделиться публикацией

    Комментарии 6

      +1
      В AndEngine действительно проблема с документацией, но вот с примерами вы ошибаетесь, посмотрите вот здесь и вы убедитесь, что эти примеры раскрывают практически все аспекты движка. В частности, описанное вами исследование — это практически точная копия PhysicsExample.java.
        0
        Ну, так то да! Мне следовало написать «туториалы» вместо «примеры»
        +2
        Прочитал вступление — информации НОЛЬ.
        Что такое AndEngine и physics2box? Ни пол-слова, ни одной ссылки.
        Как будто хабр только этому посвящен и все в курсе.
          0
          Замечение ценное, подправил
          +1
          По названию и началу статьи всё думал, так это про box2d или нет.
          Правильно physicsbox2d, а не physics2box.
          А то вводит в заблуждение. Я подумал, что какая-то новая либа.
            0
            точно.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое