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

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

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

    Продолжаем рассматривать обнаружение столкновений. В прошлой статье мы рассмотрели как обнаружить столкновение шара и коробки. А как быть, если у нас много различных форм столкновений? Предусматривать все варианты вручную - не самая лучшая идея. Есть вариант удобнее - мы можем попросить диспетчер столкновений подобрать нам соответствующий алгоритм.

        private boolean CheckCollision(btCollisionObject obj1, btCollisionObject obj2) {
            CollisionObjectWrapper COWmodel = new CollisionObjectWrapper(obj1);
            CollisionObjectWrapper COWground = new CollisionObjectWrapper(obj2);
            btCollisionAlgorithm alg = collisionDispatcher.findAlgorithm(COWmodel.wrapper,
                               COWground.wrapper);

            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;
            collisionDispatcher.freeCollisionAlgorithm(alg.getCPointer());
            result.dispose();
            info.dispose();
            COWmodel.dispose();
            COWground.dispose();
            return res;
        }

    Теперь диспетчер сам определит нужный алгоритм. Заметьте, что мы также избавились от вспомогательного объекта btCollisionAlgorithmConstructionInfo. Поскольку алгоритм был создан не нами, а диспетчером, мы не можем напрямую вызвать для него метод dispose(). Поэтому нам нужно сообщить диспетчеру, что мы закончили работу с алгоритмом. Для этого служит метод freeCollisionAlgorithm. В параметре мы передаем ему указатель на алгоритм (Java не работает с указателями, но не забываем. что мы работаем с библиотекой написанной на С++). Причем в этом случае алгоритм не уничтожается в памяти, а "складируется" и ждет подходящего случая, чтобы использоваться повторно.

    Обратите внимание, что теперь метод CheckCollision принимает в параметрах два объекта столкновений.

    Мы получили метод проверки столкновений для любой пары объектов. Теперь расширим класс Model Instance - будем хранить объект столкновения вместе с экземпляром модели, а также добавим вложенный класс - "фабрику" для удобного построения объектов и флаг moving, для определения движется ли объект.

        static class ModelInstanceAdv extends ModelInstance implements Disposable{
            public final btCollisionObject collObj;
            public boolean moving;
            public ModelInstanceAdv(Model model, String node, btCollisionShape collShape) {
                super(model, node);
                collObj = new btCollisionObject();
                collObj.setCollisionShape(collShape);
            }
            @Override
            public void dispose() {
                collObj.dispose();
            }
            
            static class Constructor implements Disposable{
                public final Model model;
                public final String node;
                public final btCollisionShape collShape;
                public Constructor(Model model, String node, btCollisionShape collShape){
                    this.model = model;
                    this.node = node;
                    this.collShape = collShape;
                }
                
                public ModelInstanceAdv create(){
                    return new ModelInstanceAdv(model, node, collShape);
                }
                @Override
                public void dispose() {
                    collShape.dispose();                
                }
            }
        }

    Теперь изменяем код построения форм и объектов столкновений.

    public Array<ModelInstanceAdv> instances;
    public ArrayMap<String, ModelInstanceAdv.Constructor> constructors;
    ......
    public void create () {
            ........
            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);
            modelBuilder.node().id = "box";
            modelBuilder.part("box", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                    new Material(ColorAttribute.createDiffuse(Color.GREEN))).box(1f, 1f, 1f);
            modelBuilder.node().id = "cone";
            modelBuilder.part("cone", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                    new Material(ColorAttribute.createDiffuse(Color.BLUE))).cone(1f, 2f, 1f, 20);
            modelBuilder.node().id = "cylinder";
            modelBuilder.part("cylinder", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                    new Material(ColorAttribute.createDiffuse(Color.YELLOW))).cylinder(1f, 2f, 1f, 20);
            modelBuilder.node().id = "capsule";
            modelBuilder.part("capsule", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal,
                    new Material(ColorAttribute.createDiffuse(Color.MAGENTA))).capsule(0.5f, 2f, 20);
            model = modelBuilder.end();
            
            constructors = new ArrayMap<String, ModelInstanceAdv.Constructor>(String.class, ModelInstanceAdv.Constructor.class);
            constructors.put("ground", new ModelInstanceAdv.Constructor(model, "ground", new btBoxShape(new Vector3(5f, 0.05f, 5f))));
            constructors.put("ball", new ModelInstanceAdv.Constructor(model, "ball", new btSphereShape(0.5f)));
            constructors.put("box", new ModelInstanceAdv.Constructor(model, "box", new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f))));
            constructors.put("cone", new ModelInstanceAdv.Constructor(model, "cone", new btConeShape(0.5f, 2f)));
            constructors.put("cylinder", new ModelInstanceAdv.Constructor(model, "cylinder", new btCylinderShape(new Vector3(0.5f, 1f, 0.5f))));
            constructors.put("capsule", new ModelInstanceAdv.Constructor(model, "capsule", new btCapsuleShape( 0.5f, 1f)));
            instances = new Array<ModelInstanceAdv>();
            instances.add(constructors.get("ground").create());
            .....
        }

    Изменим тип массива instances - теперь будем использовать ModelInstanceAdv. А также объявим именованный массив объектов класса ModelInstanceAdv.Constructor. В модель добавляем еще несколько узлов разной формы. Затем инициализируем массив конструкторов и заполняем его используя узлы модели и соответствующие формы столкновений. Теперь мы можем добавлять объекты в массив instances, извлекая из массива конструкторов нужную заготовку экземпляра модели и создавая его методом create.

    Не забываем освободить память в методе dispose:

        public void dispose(){
            for(ModelInstanceAdv inst:instances)inst.dispose();
            instances.clear();
            for(ModelInstanceAdv.Constructor cons:constructors.values())cons.dispose();
            constructors.clear();
            .......
        }

    Осталось изменить метод render. Будем скидывать на поверхность случайный объект через определенный промежуток времени. Для создания случайного объекта напишем дополнительный метод birthObject.

        public void birthObject(){
            ModelInstanceAdv obj = constructors.values[1 + MathUtils.random(constructors.size - 2)].create();
            obj.moving = true;
            obj.transform.setFromEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));
            obj.transform.trn(MathUtils.random(5f, -5f), 9f, MathUtils.random(5f, -5f));
            obj.collObj.setWorldTransform(obj.transform);
            instances.add(obj);
        }

    Здесь мы случайным образом выбираем из массива один из конструкторов (исключая первый - это у нас земля) и создаем экземпляр модели. Ставим флаг движения в true. Случайным образом поворачиваем объект, используя углы Эйлера (тем кто не знает о чем речь - добро пожаловать в Википедию). И также случайным образом перемещаем объект в некоторую точку над поверхностью земли (оставляем постоянной только высоту). Передаем матрицу перемещений объекту столкновений и добавляем созданный объект в массив экземпляров моделей.

    Теперь, собственно, сам цикл отрисовки:

        float birthTime;
        .......
        public void render () {
            .......
            final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime());
            for(ModelInstanceAdv inst:instances){
                if(inst.moving){
                    inst.transform.trn(0f, -delta, 0f);
                    inst.collObj.setWorldTransform(inst.transform);
                    if(CheckCollision(inst.collObj, instances.get(0).collObj))
                        inst.moving = false;
                }
            }
            if((birthTime -= delta) < 0){
                birthObject();
                birthTime = 1.5f;
            }
            ......
        }

    Для каждого объекта из массива экземпляров моделей проверяем флаг движения. Если объект движется изменяем его высоту. Передаем измененную матрицу объекту столкновений. Затем проверяем объект на столкновение с землей ("ground" - самый первый элемент массива). В случае, если столкновение было - ставим флаг движения в false (останавливаем объект).

    Далее проверяем состояние переменной birhTime, которая будет отвечать за промежуток времени, через который появляется новый объект. Если переменная стала меньше нуля, вызываем метод создания нового объекта и восстанавливаем значение birthTime (у нас она равна полутора секундам). Дальше идет отрисовка объектов как обычно. Запускаем код на исполнение.

    В статье использованы материалы туториала 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.MathUtils;
    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.btCapsuleShape;
    import com.badlogic.gdx.physics.bullet.collision.btCollisionAlgorithm;
    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.btConeShape;
    import com.badlogic.gdx.physics.bullet.collision.btCylinderShape;
    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.btSphereShape;
    import com.badlogic.gdx.utils.Array;
    import com.badlogic.gdx.utils.ArrayMap;
    import com.badlogic.gdx.utils.Disposable;
    public class CollisionsTest extends ApplicationAdapter {
        public PerspectiveCamera cam;
        public Model model;
        public Array<ModelInstanceAdv> instances;
        public ArrayMap<String, ModelInstanceAdv.Constructor> constructors;
        public ModelBatch modelBatch;
        public Environment environment;
        public CameraInputController camController;
        boolean collision_flag;
        btCollisionShape groundShape, thingShape;
        btCollisionObject groundObject, thingObject;
        btCollisionConfiguration collisionConf;
        btDispatcher collisionDispatcher;
        float birthTime;
        
        static class ModelInstanceAdv extends ModelInstance implements Disposable{
            public final btCollisionObject collObj;
            public boolean moving;
            public ModelInstanceAdv(Model model, String node, btCollisionShape collShape) {
                super(model, node);
                collObj = new btCollisionObject();
                collObj.setCollisionShape(collShape);
            }
            @Override
            public void dispose() {
                collObj.dispose();
            }
            
            static class Constructor implements Disposable{
                public final Model model;
                public final String node;
                public final btCollisionShape collShape;
                public Constructor(Model model, String node, btCollisionShape collShape){
                    this.model = model;
                    this.node = node;
                    this.collShape = collShape;
                }
                
                public ModelInstanceAdv create(){
                    return new ModelInstanceAdv(model, node, collShape);
                }
                @Override
                public void dispose() {
                    collShape.dispose();                
                }
            }
        }
        
        @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);
            modelBuilder.node().id = "box";
            modelBuilder.part("box", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GREEN))).box(1f, 1f, 1f);
            modelBuilder.node().id = "cone";
            modelBuilder.part("cone", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.BLUE))).cone(1f, 2f, 1f, 20);
            modelBuilder.node().id = "cylinder";
            modelBuilder.part("cylinder", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.YELLOW))).cylinder(1f, 2f, 1f, 20);
            modelBuilder.node().id = "capsule";
            modelBuilder.part("capsule", GL20.GL_TRIANGLES, Usage.Position|Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.MAGENTA))).capsule(0.5f, 2f, 20);
            model = modelBuilder.end();
            
            constructors = new ArrayMap<String, ModelInstanceAdv.Constructor>(String.class, ModelInstanceAdv.Constructor.class);
            constructors.put("ground", new ModelInstanceAdv.Constructor(model, "ground", new btBoxShape(new Vector3(5f, 0.05f, 5f))));
            constructors.put("ball", new ModelInstanceAdv.Constructor(model, "ball", new btSphereShape(0.5f)));
            constructors.put("box", new ModelInstanceAdv.Constructor(model, "box", new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f))));
            constructors.put("cone", new ModelInstanceAdv.Constructor(model, "cone", new btConeShape(0.5f, 2f)));
            constructors.put("cylinder", new ModelInstanceAdv.Constructor(model, "cylinder", new btCylinderShape(new Vector3(0.5f, 1f, 0.5f))));
            constructors.put("capsule", new ModelInstanceAdv.Constructor(model, "capsule", new btCapsuleShape( 0.5f, 1f)));
            instances = new Array<ModelInstanceAdv>();
            instances.add(constructors.get("ground").create());
            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());
            for(ModelInstanceAdv inst:instances){
                if(inst.moving){
                    inst.transform.trn(0f, -delta, 0f);
                    inst.collObj.setWorldTransform(inst.transform);
                    if(CheckCollision(inst.collObj, instances.get(0).collObj))
                        inst.moving = false;
                }
            }
            if((birthTime -= delta) < 0){
                birthObject();
                birthTime = 1.5f;
            }
            modelBatch.begin(cam);
            modelBatch.render(instances, environment);
            modelBatch.end();
        }
        
        private boolean CheckCollision(btCollisionObject obj1, btCollisionObject obj2) {
            CollisionObjectWrapper COWmodel = new CollisionObjectWrapper(obj1);
            CollisionObjectWrapper COWground = new CollisionObjectWrapper(obj2);
            btCollisionAlgorithm alg = collisionDispatcher.findAlgorithm(COWmodel.wrapper, COWground.wrapper);
            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;
            collisionDispatcher.freeCollisionAlgorithm(alg.getCPointer());
            result.dispose();
            info.dispose();
            COWmodel.dispose();
            COWground.dispose();
            return res;
        }
        
        public void birthObject(){
            ModelInstanceAdv obj = constructors.values[1 + MathUtils.random(constructors.size - 2)].create();
            obj.moving = true;
            obj.transform.setFromEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));
            obj.transform.trn(MathUtils.random(5f, -5f), 9f, MathUtils.random(5f, -5f));
            obj.collObj.setWorldTransform(obj.transform);
            instances.add(obj);
        }
        @Override
        public void dispose(){
            for(ModelInstanceAdv inst:instances)inst.dispose();
            instances.clear();
            for(ModelInstanceAdv.Constructor cons:constructors.values())cons.dispose();
            constructors.clear();
            collisionConf.dispose();
            collisionDispatcher.dispose();
            modelBatch.dispose();
            model.dispose();
        }
    }

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

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

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