Обнаружение столкновения для проблемы петли, проверен только один элемент массива? - PullRequest
0 голосов
/ 02 апреля 2011

[Я прошу прощения, если это не очень глубокий вопрос, но я хотел решить это раз и навсегда]

Я пытался заглянуть в дерево, ноуже столкнулся с проблемами при обнаружении столкновений без какой-либо оптимизации, работающей должным образом.Сделал поиск и нашел довольно аккуратный пример: http://wonderfl.net/c/kyLx (в основном часть onenterframeC)

Попытка подражать этому не сработает.Только некоторые столкновения обнаруживаются между конкретными объектами.Когда объекты не движутся, кажется, по какой-то причине они работают намного лучше.

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

if (j <= i)
                        continue;

J никогда не станет больше, чем я прав?Линия также полностью удаляет все рабочие столкновения для меня.

Вот что я сделал: [посмотреть результат здесь: http://martinowullems.com/collision.swf

Основной класс

package  
{
    import com.martino.objects.Square;
    import com.martino.world.TestWorld;
    import flash.display.MovieClip;
    import flash.events.Event;
    import net.hires.debug.Stats;
    /**
     * ...
     * @author Martino Wullems
     */
    public class CollisionTest extends MovieClip
    {
        var world:TestWorld;

        public function CollisionTest() 
        {
            addEventListener(Event.ADDED_TO_STAGE, onStage);
        }

        private function onStage(e:Event):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, onStage);

            SetupWorld();
            addChild(new Stats());
        }

        private function SetupWorld():void 
        {
            world = new TestWorld();
            addChild(world);

            addObjects(50);
        }

        private function addObjects(amount:int):void 
        {
            for (var i:int = 0; i < amount; ++i) {

                var square:Square = new Square(14);
                world.addObject(square);
                square.x = Math.random() * stage.stageWidth;
                square.y = Math.random() * stage.stageHeight;
                square.speedX = (Math.random() * 1) + 1;
                square.speedY = (Math.random() * 1) + 1;
            }
        }

    }

}

TestWorld

package com.martino.world 
{
    import com.martino.objects.Ball;
    import com.martino.objects.CollisionObject;
    import flash.display.MovieClip;
    import flash.events.Event;
    /**
     * ...
     * @author Martino Wullems
     */
    public class TestWorld extends MovieClip
    {
        public var objects:Array;

        public function TestWorld()
        {
            initWorld();
        }

        private function initWorld():void 
        {
            objects = new Array();
            addEventListener(Event.ENTER_FRAME, loopWorld);
        }

        private function loopWorld(e:Event):void 
        {
            for (var i:int = 0; i < objects.length; i++) {

                MoveObject(objects[i]);
                CheckCollision(i, objects[i]);
            }
        }

        private function CheckCollision(i:int, object:CollisionObject):void 
        {
            //for (var j:int = i + 1; i < objects.length; j++) {

            for (var j:int = 0; j < objects.length; j++) {
                //if (j <= i)
            //continue;

                var objectB:CollisionObject = objects[j];

                //hittest
                if (object.hitTestObject(objectB)) {

                    object.isHit = true;
                    objectB.isHit = true;
                }else {

                    object.isHit = false;
                    objectB.isHit = false;
                }

                /////////////////
                //  CHECK X Y //
                ////////////////

                /*if (object.x + object.width < objectB.x) {
                    } else if (object.x > objectB.x + objectB.width) {
                               object.isHit = objectB.isHit = false;

                    } else if (object.y + object.height < objectB.y) {
                               object.isHit = objectB.isHit = false;

                    } else if (object.y > objectB.y + objectB.height) {

                               object.isHit = objectB.isHit = false;
                    } else {
                        object.isHit = objectB.isHit = true;
                    }*/

                object.debugDraw();
                objectB.debugDraw();
            }
        }

        private function MoveObject(object:CollisionObject):void 
        {
            object.x += object.speedX;
            object.y += object.speedY;

            ////////////////////
            //check boundaries//
            ////////////////////

            if (object.x > stage.stageWidth)
            {
                object.speedX *= -1;

            }else if (object.x < 0)
            {
                object.speedX *= -1;

            }else if (object.y > stage.stageHeight)
            {
                object.speedY *= -1;

            }else if (object.y < 0)
            {
                object.speedY *= -1;
            }


        }

        public function addObject(object:CollisionObject):void
        {
            objects.push(object);
            addChild(object);
        }

    }

}

CollisionObject

package com.martino.objects 
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    /**
     * ...
     * @author Martino Wullems
     */
    public class CollisionObject extends Sprite
    {
        public var size:int;
        public var speedX:int = 0;
        public var speedY:int = 0;
        public var graphic:Sprite;
        var sleeping:Boolean = false;

        public var isHit:Boolean = false;


        public function CollisionObject() 
        {
            addEventListener(MouseEvent.MOUSE_DOWN, grab);
            addEventListener(MouseEvent.MOUSE_UP, letGo);
        }

        private function grab(e:MouseEvent):void 
        {
            startDrag();
            speedX = 0;
            speedY = 0;
        }

        private function letGo(e:MouseEvent):void 
        {
            stopDrag();
        }

        public function Collision():void{


        }

        //////////////////////
    // setter and getter//
    //////////////////////


     public function set isHit(value:Boolean):void {
          _isHit = value;
          graphic.visible = _isHit;
          hitGraphic.visible = !_isHit;
     }

    public function get isHit():Boolean {
          return _isHit;
     }
    }

}

Квадрат

package com.martino.objects 
{
    import flash.display.Sprite;
    /**
     * ...
     * @author Martino Wullems
     */
    public class Square extends CollisionObject
    {
        public var hitGraphic:Sprite;

        public function Square(Size:int) 
        {
            size = Size;

            drawSquare();
        }

        private function drawSquare():void 
        {
            graphic = new Sprite();
            graphic.graphics.beginFill(0xFF0000);
            graphic.graphics.drawRect(0, 0, size, size);
            graphic.graphics.endFill();
            addChild(graphic);

            hitGraphic = new Sprite();
            hitGraphic.graphics.beginFill(0x0066FF);
            hitGraphic.graphics.drawRect(0, 0, size, size);
            hitGraphic.graphics.endFill();
            addChild(hitGraphic);
            hitGraphic.visible = false;
        }

        override public function Collision():void {

            trace("I collided with a friend (inside joke)");
        }

        public override function debugDraw():void {

            if (isHit) {

                graphic.visible = false;
                hitGraphic.visible = true;

            }else {

                graphic.visible = true;
                hitGraphic.visible = false;
            }
        }

    }

}

Любая помощь будет принята с благодарностью, хотите узнать об этом подробнее!


РЕДАКТИРОВАТЬ: Изменено некоторые вещи, есть прогресс, но мне все еще неясно!

Изменено некоторые вещи в TestWorld.as:

package com.martino.world 
{
    import com.martino.objects.Ball;
    import com.martino.objects.CollisionObject;
    import flash.display.MovieClip;
    import flash.events.Event;
    /**
     * ...
     * @author Martino Wullems
     */
    public class TestWorld extends MovieClip
    {
        public var objects:Array;

        public function TestWorld()
        {
            initWorld();
        }

        private function initWorld():void 
        {
            objects = new Array();
            addEventListener(Event.ENTER_FRAME, loopWorld);
        }

        private function loopWorld(e:Event):void 
        {
            var object:*;

            for (var i:int = 0; i < objects.length; i++) {

                MoveObject(objects[i]);
                //CheckCollision(i);// doesn't work here for some reason [case 1]

            }

            CheckCollision(0); //[case 2]

        }

        private function CheckCollision(i:int):void 
        {               
                //test collision
                for (var i:int = 0; i < objects.length; i++){ //only use in case 2

                    var elementA:CollisionObject;
                    var elementB:CollisionObject;
                    elementA = objects[i];

                for (var j:int = i + 1; j < objects.length; j++) {

                    if (j <= i){
                        continue; //j resets each I loop and therefor sets collision to false while it could be true
                          }

                    elementB = objects[ j ]// as ObjSprite;

                    if (elementA.hitTestObject(elementB)) {
                       elementA.isHit = elementB.isHit = true;

                    }
                }
            } //[case 2]



        }

        private function MoveObject(object:CollisionObject):void 
        {
            object.x += object.vx;
            object.y += object.vy;

            ////////////////////
            //check boundaries//
            ////////////////////

            if (object.x > stage.stageWidth)
            {
                object.vx *= -1;

            }else if (object.x < 0)
            {
                object.vx *= -1;

            }else if (object.y > stage.stageHeight)
            {
                object.vy *= -1;

            }else if (object.y < 0)
            {
                object.vy *= -1;
            }

            object.isHit = false;// where do we check when it isn't colliding? this seems messy!
        }

        public function addObject(object:CollisionObject):void
        {
            objects.push(object);
            addChild(object);
        }

    }

}

Также добавленоsetter и getter в объекте collision (поэтому раздел перед редактированием).

Не уверен, почему я не могу поместить checkcollision внутри цикла в функцию enter frame?(когда я не вижу столкновения).И размещение «isHit = false» внутри объектов перемещения для сброса проверки на попадание также кажется довольно грязным.

Кажется, я не могу определить, когда объекты не сталкиваются для сбросаих я думаю.Создание оператора else для проверки попадания, чтобы проверить, не происходит ли столкновения, не работает, кажется логичным, поскольку в проверке попадания могут быть столкновения с более чем двумя элементами.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 03 апреля 2011

Пара вещей, после просмотра его кода и просмотра вашего кода.

Эта часть необходима для того, чтобы не тратить время на прохождение цикла и повторную проверку уже сопоставленных элементов.

if (j <= i)
                    continue;

Представьте, что у вас есть 3 элемента в массиве, и сравните элемент 3 с элементом 0. Затем вы сравниваете элемент 3 с элементом 1 и элемент 3 с элементом 2. Затем сравниваете элемент 2 с ... элементом 3? Вы уже сделали это. Вы хотите сравнить пункт 2 с чем-то меньшим, чем он сам, чтобы избежать дублирования.

Ваш код близок к его, но вы обменяли его версию hitTest (которая использует позицию + размерность) на уже определенный hitTestObject. Я вижу только один хит-тест на первом квадрате. В этом что-то не так ... Я бы добавил туда несколько трассировочных операторов и посмотрел, что случилось.

Последнее, когда вы раскомментируете его код, он работает?

0 голосов
/ 03 апреля 2011

Мне пришлось взглянуть на первоисточник, чтобы понять это. Это ниже, для справки.

Эта строка

if (j <= i) continue;

- проверка перестановки; это в основном гарантирует, что каждая комбинация проверяется только один раз, а не несколько раз. Однако для этого вам нужно иметь два цикла For - внутренний и внешний.

Вы сделали то же самое со следующей строкой:

for (var j:int = i + 1; i < objects.length; j++) {

Попробуйте использовать цикл for вместо того, который начинается с нуля. Оставьте строку "if j <= i" с комментариями. Я думаю, что это решит вашу проблему. (Эти два оператора не могут сосуществовать в этом цикле; если их использовать вместе, они вызовут проблемы, которые вы описываете.) </p>

Удачи.

Оригинальный источник с сайта:

    for (i = 0; i < myrects.length; i++) {
        elementA = myrects[ i ] as ObjMyRect;
        for (j = 0; j < myrects.length; j++) {
            if (j <= i)
                continue;
            elementB = myrects[ j ] as ObjMyRect;
            if (elementA.rect.x + elementA.rect.width < elementB.rect.x) {
            } else if (elementA.rect.x > elementB.rect.x + elementB.rect.width) {
            } else if (elementA.rect.y + elementA.rect.height < elementB.rect.y) {
            } else if (elementA.rect.y > elementB.rect.y + elementB.rect.height) {
            } else {
                elementA.isHit = elementB.isHit = true;
            }
        }
    }

(Для чего бы это ни было важно, я думаю, что код этого парня на самом деле проверяет объекты на предмет их столкновения - он должен изменить строку «если j <= i» на «если j <i». для цикла.) </p>

...