Вторник, 12.12.2017, 09:21
Поиск
Никнэйм
Сертификат на никнейм Olelucoye, зарегистрирован на Тимофеев Константин Михайлович
Зарегистрируй свой никнейм
Обратная связь
olelucoye.tk@yandex.ru
Реклама AdSense
Реклама
Друзья сайта
  • Лига медицинского права
  • Гостиница "Зай"
  • FAQ по системе
  • Инструкции для uCoz
  • Главная » Статьи » libGDX

    libGDX. Обработка столкновений. Библиотека Bullet.

    libGDX. Обработка столкновений. Библиотека Bullet.

    Большинство 3D игр требует обработки столкновений между 3D объектами. Если не включить обработку столкновений в код, движущиеся объекты будут проходить сквозь друг друга, не смогут взаимодействовать между собой (например, персонаж будет проходить сквозь стены, не будет возможности обработать попадание пули в объект и т.п.). В предыдущих статьях мы рассматривали вспомогательные объекты ограничивающие 3D объекты. Мы их использовали для усечения видимости и взаимодействия с пользователем. С помощью этих же вспомогательных объектов можно отслеживать и взаимодействия между 3D объектами, но при большом количестве разнообразных моделей и способов их взаимодействия написание кода становится неудобным и запутанным.

    Для удобства обработки обнаружения и физики столкновений в libGDX включена поддержка библиотеки Bullet. Эта библиотека написана на C++, поэтому для её использования в libGDX созданы специальные классы-оболочки, большинство из которых имеют префикс bt.

    Что бы включить библиотеку Bullet в проект, нужно при его создании отметить пункт "Bullet" в разделе Extensions.

    В большинстве англоязычных источников обработка столкновений с помощью Bullet рассматривается на примере падающих на поверхность предметов. Его мы и будем использовать, потому что это наиболее простой вариант демонстрации применения Bullet, как мне показалось.

    Создадим проект (не забываем включить галочку Bullet) и напишем заготовку кода.

    public class CollisionsTest extends ApplicationAdapter {
        public PerspectiveCamera cam;
        public Model model;
        public Array<ModelInstance> instances;
        public ModelInstance groundInstance;
        public ModelInstance thingInstance;
        public ModelBatch modelBatch;
        public Environment environment;
        public CameraInputController camController;
        boolean collision_flag;
           
        @Override
        public void create () {
            modelBatch = new ModelBatch();
            environment = new Environment();
            environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
            environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
            cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
            cam.position.set(10f, 10f, 10f);
            cam.lookAt(0f, 0f, 0f);
            cam.near = 1f;
            cam.far = 300f;
            cam.update();
            camController = new CameraInputController(cam);
            Gdx.input.setInputProcessor(camController);
                    
            ModelBuilder modelBuilder = new ModelBuilder();
            modelBuilder.begin();
            modelBuilder.node().id = "ground";
            modelBuilder.part("box", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                     new Material(ColorAttribute.createDiffuse(Color.GRAY))).box(10f, 0.1f, 10f);
            modelBuilder.node().id = "ball";
            modelBuilder.part("ball", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                     new Material(ColorAttribute.createDiffuse(Color.RED))).sphere(1f, 1f, 1f, 20, 20);
            model = modelBuilder.end();
            
            thingInstance = new ModelInstance(model, "ball");
            thingInstance.transform.setToTranslation(0f, 5f, 0f);
            groundInstance = new ModelInstance(model, "ground");
            instances = new Array<ModelInstance>();
            instances.add(thingInstance);
            instances.add(groundInstance);
        }
        @Override
        public void render () {
            Gdx.gl.glViewport ( 0 , 0 , Gdx.graphics.getWidth (), Gdx.graphics.getHeight ());
            Gdx.gl.glClearColor(0.3f, 0.5f, 1f, 1f);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT|GL20.GL_DEPTH_BUFFER_BIT);
            
            final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime());
            
            if(!collision_flag){
                thingInstance.transform.translate(0f, -delta, 0f);
                collision_flag = CheckCollision();
            }
            modelBatch.begin(cam);
            modelBatch.render(instances, environment);
            modelBatch.end();
        }
        
        private boolean CheckCollision() {
            return false;
        }
        @Override
        public void dispose(){
            modelBatch.dispose();
            model.dispose();
       }
    }

    Создаем и настраиваем камеру и свет. Далее строим модель с двумя узлами - "ground" и "ball". Из модели получаем экземпляры ModelInstance используя id узлов. Такой способ удобен тем, что можно создавать только одну модель и уничтожать нужно тоже только одну. Оба созданных экземпляра добавляем в массив instances.

    В render() сделаем падение шарика в сторону плоскости ground. Для этого вычислим изменение положение шарика по оси y - delta. Для этого используем Gdx.graphics.getDeltaTime() чтобы определить время прошедшее с последнего вызова render(). Чтобы избежать дерганого движения, в случае задержки вызова render, введем ограничение с помощью функции Math.min. Для хранения состояния столкновения объявим булеву переменную collision_flag. Проверяем её состояние. Если столкновения не было изменяем положение шарика. Проверяем не произошло ли столкновения методом CheckCollision() (который на данный момент у нас является заглушкой, всегда возвращающей false) и записываем результат в collision_flag.

    Если запустить программу сейчас, шарик пролетит сквозь землю.

    Пора подключать Bullet и обработать столкновение.

    Первое что нужно сделать - проинициализировать библиотеку Bullet:

    public void create () {
            Bullet.init();
            ......
        }

    Это нужно сделать до использования любых классов Bullet, иначе при компиляции вывалится ошибка. Теперь нам понадобятся задать формы столкновений - это аналоги ограничивающих объектов, которые мы использовали в предыдущих статьях для определения невидимых и выбираемых объектов.

        btCollisionShape groundShape, thingShape;
        .....
        
        @Override
        public void create () {
            Bullet.init();
            ....
            thingShape = new btSphereShape(0.5f);
            groundShape = new btBoxShape(new Vector3(5f, 0.05f, 5f));
        }

    Для форм столкновений мы можем использовать различные фигуры: как примитивные -шар, цилиндр, конус, капсула, коробка, так и более сложные сочетания примитивных. Для наших целей сгодится шар и коробка. Заметьте, что при указании размеров формы указывается половина размеров объекта.

    Но форм столкновений недостаточно. Нужно создать объекты столкновений.

        btCollisionShape groundShape, thingShape;
        btCollisionObject groundObject, thingObject;
        .....
        
        @Override
        public void create () {
            Bullet.init();
            ......
            thingShape = new btSphereShape(0.5f);
            groundShape = new btBoxShape(new Vector3(5f, 0.05f, 5f));
            groundObject = new btCollisionObject();
            groundObject.setCollisionShape(groundShape);
            groundObject.setWorldTransform(groundInstance.transform);
            thingObject = new btCollisionObject();
            thingObject.setCollisionShape(thingShape);
            thingObject.setWorldTransform(thingInstance.transform);
        }
        public void render () {
           ......
            if(!collision_flag){
                thingInstance.transform.translate(0f, -delta, 0f);
                thingObject.setWorldTransform(thingInstance.transform);
                collision_flag = CheckCollision();
            }
            ......
        }

    Как видим объекты столкновений содержат в себе формы столкновений и матрицы преобразований экземпляров моделей. Следует учесть, что Bullet берет в расчет только преобразования перемещения и поворота. Остальные преобразования, как, например, масштабирование, не учитываются, поэтому рекомендуется воздержаться от них. Матрицу нужно передавать объекту столкновений после каждого изменения положения экземпляра модели.

    Прежде чем мы начнем писать код обнаружения столкновений, нам понадобятся еще пара дополнительных объектов:

        btCollisionConfiguration collisionConf;
        btDispatcher collisionDispatcher;
        
        @Override
        public void create () {
            Bullet.init();
            .....
            collisionConf = new btDefaultCollisionConfiguration();
            collisionDispatcher = new btCollisionDispatcher(collisionConf);
        }

    Для чего нужны эти объекты увидим позже. А пока не забывая, что все эти объекты не являются родными для Java и сборщик мусора их не уничтожит, позаботимся об освобождении ресурсов.

        public void dispose(){
            thingShape.dispose();
            groundShape.dispose();
            thingObject.dispose();
            groundObject.dispose();
            collisionConf.dispose();
            collisionDispatcher.dispose();
            ....
        }

    Теперь можем приступить к написанию метода обнаружения столкновений CheckCollision.

        private boolean CheckCollision() {
            CollisionObjectWrapper COWmodel = new CollisionObjectWrapper(thingObject);
            CollisionObjectWrapper COWground = new CollisionObjectWrapper(groundObject);
            btCollisionAlgorithmConstructionInfo constInfo = new btCollisionAlgorithmConstructionInfo();
            constInfo.setDispatcher1(collisionDispatcher);
            btCollisionAlgorithm alg = new btSphereBoxCollisionAlgorithm(null, constInfo, COWmodel.wrapper, COWground.wrapper, false);
            btDispatcherInfo info = new btDispatcherInfo();
            btManifoldResult result = new btManifoldResult(COWmodel.wrapper, COWground.wrapper);
            alg.processCollision(COWmodel.wrapper, COWground.wrapper, info, result);
            boolean res = result.getPersistentManifold().getNumContacts()>0;
            result.dispose();
            info.dispose();
            alg.dispose();
            constInfo.dispose();
            COWmodel.dispose();
            COWground.dispose();
            return res;
        }

    Создаем для каждого объекта столкновений объект-оболочку CollisionObjectWrapper. Заметьте, что они не имеют префикса bt, потому что являются объектами классов libGDX (они, в свою очередь, обертывают класс btCollisionObjectWrapper). Далее мы создаем объект btCollisionAlgorithmConstructioninfo. Он используется для хранения сведений об алгоритме столкновения. Мы будем использовать настройки по умолчанию, поэтому просто свяжем его с диспетчером collisionDispatcher, который мы создали ранее.

    Теперь создадим алгоритм столкновения. Мы будем использовать btSphereBoxCollisionAlgorithm, то есть алгоритм столкновения шара и коробки. В качестве аргументов он принимает объекты-оболочки и сведения об алгоритме. Чтобы выполнить этот алгоритм необходимы дополнительные объекты - btDispatcherInfo, который хранит дополнительную информацию о алгоритме, и btManifoldResult, в который запишется результат проверки. Обратите внимание, что алгоритм является экземпляром btCollisionAlgorithm, который является суперклассом для всех алгоритмов столкновений.

    Запускаем алгоритм на исполнение методом processCollision. полученный результат считываем в булеву переменную, которую возвращаем в вызывающий метод. Не забываем освободить память от не нужных более объектов. Можно запускать код и увидим как шарик остановится при столкновении с поверхностью "земли".

    В статье использованы материалы туториала xoppa.

    Полный листинг главного класса.

    import com.badlogic.gdx.ApplicationAdapter;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.Color;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.graphics.PerspectiveCamera;
    import com.badlogic.gdx.graphics.VertexAttributes.Usage;
    import com.badlogic.gdx.graphics.g3d.Environment;
    import com.badlogic.gdx.graphics.g3d.Material;
    import com.badlogic.gdx.graphics.g3d.Model;
    import com.badlogic.gdx.graphics.g3d.ModelBatch;
    import com.badlogic.gdx.graphics.g3d.ModelInstance;
    import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
    import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
    import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
    import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
    import com.badlogic.gdx.math.Vector3;
    import com.badlogic.gdx.physics.bullet.Bullet;
    import com.badlogic.gdx.physics.bullet.collision.CollisionObjectWrapper;
    import com.badlogic.gdx.physics.bullet.collision.btBoxShape;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionAlgorithm;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionAlgorithmConstructionInfo;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionConfiguration;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionDispatcher;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionObject;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionShape;
    import com.badlogic.gdx.physics.bullet.collision.btDefaultCollisionConfiguration;
    import com.badlogic.gdx.physics.bullet.collision.btDispatcher;
    import com.badlogic.gdx.physics.bullet.collision.btDispatcherInfo;
    import com.badlogic.gdx.physics.bullet.collision.btManifoldResult;
    import com.badlogic.gdx.physics.bullet.collision.btSphereBoxCollisionAlgorithm;
    import com.badlogic.gdx.physics.bullet.collision.btSphereShape;
    import com.badlogic.gdx.utils.Array;
    public class CollisionsTest extends ApplicationAdapter {
        public PerspectiveCamera cam;
        public Model model;
        public Array<ModelInstance> instances;
        public ModelInstance groundInstance;
        public ModelInstance thingInstance;
        public ModelBatch modelBatch;
        public Environment environment;
        public CameraInputController camController;
        
        boolean collision_flag;
        
        btCollisionShape groundShape, thingShape;
        btCollisionObject groundObject, thingObject;
        btCollisionConfiguration collisionConf;
        btDispatcher collisionDispatcher;
        
        @Override
        public void create () {
            Bullet.init();
                    
            modelBatch = new ModelBatch();
            environment = new Environment();
            environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
            environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
                            
            cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
            cam.position.set(10f, 10f, 10f);
            cam.lookAt(0f, 0f, 0f);
            cam.near = 1f;
            cam.far = 300f;
            cam.update();
            
            camController = new CameraInputController(cam);
            Gdx.input.setInputProcessor(camController);
                    
            ModelBuilder modelBuilder = new ModelBuilder();
            modelBuilder.begin();
            modelBuilder.node().id = "ground";
            modelBuilder.part("box", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GRAY))).box(10f, 0.1f, 10f);
            modelBuilder.node().id = "ball";
            modelBuilder.part("ball", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.RED))).sphere(1f, 1f, 1f, 20, 20);
            model = modelBuilder.end();
            
            thingInstance = new ModelInstance(model, "ball");
            thingInstance.transform.setToTranslation(0f, 5f, 0f);
            groundInstance = new ModelInstance(model, "ground");
            instances = new Array<ModelInstance>();
            instances.add(thingInstance);
            instances.add(groundInstance);
            
            thingShape = new btSphereShape(0.5f);
            groundShape = new btBoxShape(new Vector3(5f, 0.05f, 5f));
            groundObject = new btCollisionObject();
            groundObject.setCollisionShape(groundShape);
            groundObject.setWorldTransform(groundInstance.transform);
            thingObject = new btCollisionObject();
            thingObject.setCollisionShape(thingShape);
            thingObject.setWorldTransform(thingInstance.transform);
            collisionConf = new btDefaultCollisionConfiguration();
            collisionDispatcher = new btCollisionDispatcher(collisionConf);
        }
        @Override
        public void render () {
            Gdx.gl.glViewport ( 0 , 0 , Gdx.graphics.getWidth (), Gdx.graphics.getHeight ());
            Gdx.gl.glClearColor(0.3f, 0.5f, 1f, 1f);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT|GL20.GL_DEPTH_BUFFER_BIT);
            
            final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime());
            if(!collision_flag){
                thingInstance.transform.translate(0f, -delta, 0f);
                thingObject.setWorldTransform(thingInstance.transform);
                collision_flag = CheckCollision();
            }
            modelBatch.begin(cam);
            modelBatch.render(instances, environment);
            modelBatch.end();
        }
        
        private boolean CheckCollision() {
            CollisionObjectWrapper COWmodel = new CollisionObjectWrapper(thingObject);
            CollisionObjectWrapper COWground = new CollisionObjectWrapper(groundObject);
            btCollisionAlgorithmConstructionInfo constInfo = new btCollisionAlgorithmConstructionInfo();
            constInfo.setDispatcher1(collisionDispatcher);
            btCollisionAlgorithm alg = new btSphereBoxCollisionAlgorithm(null, constInfo, COWmodel.wrapper, COWground.wrapper, false);
            btDispatcherInfo info = new btDispatcherInfo();
            btManifoldResult result = new btManifoldResult(COWmodel.wrapper, COWground.wrapper);
            alg.processCollision(COWmodel.wrapper, COWground.wrapper, info, result);
            boolean res = result.getPersistentManifold().getNumContacts()>0;
            result.dispose();
            info.dispose();
            alg.dispose();
            constInfo.dispose();
            COWmodel.dispose();
            COWground.dispose();
            return res;
        }
        @Override
        public void dispose(){
            thingShape.dispose();
            groundShape.dispose();
            thingObject.dispose();
            groundObject.dispose();
            collisionConf.dispose();
            collisionDispatcher.dispose();
            modelBatch.dispose();
            model.dispose();
            
        }
    }

    libGDX. Интерактивное взаимодействие с 3D объектами.
    libGDX. Отбраковка объектов не попадающих в обзор камеры.
    3D модель  для libGDX. Пишем код.
    3D модель для движка libGDX
    Строим модель с помощью ModelBuilder
    libGDX. Основы 3D программирования.
    Игра Flower. Ловим капли.
    TexturePacker.Создаем атлас текстур.
    Создаем проект на движке libGDX
    Кастомизация EditText
    Кастомизация SeekBar'а
    9-patch изображения для Андроид
    Кастомный ползунок в виде дуги (аналог SeekBar)
    Анимация в Андроид
    Кастомизация элементов управления в Android
    Создание кастомного View-элемента интерфейса.
    Создание виджета - электронные часы с кастомным шрифтом
    Программируем калькулятор на андроид. Урок 1

    Категория: libGDX | Добавил: Olelucoye (25.04.2015)
    Просмотров: 2239
    | Теги: bullet, Collision, формы столкновений, обработка столкновений, 3D, libGDX | Рейтинг: 0.0/0
    Всего комментариев: 0
    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]
    Меню сайта
    Категории раздела
    Андроид разработка [23]
    libGDX [24]
    Мои андроид проекты [6]
    Excel [7]
    Железяки [5]
    Скрипты в блокноте [4]
    Разное [1]
    Форма входа
    Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
    Яндекс Метрика
    Яндекс.Метрика