循环问题的碰撞检测,仅测试了一项数组项?

发布于 2024-10-29 00:35:27 字数 10811 浏览 1 评论 0原文

[如果这不是一个真正深入的问题,我深表歉意,但我想一劳永逸地解决这个问题]

我试图研究四叉树,但在没有进行碰撞检测的情况下已经遇到了麻烦任何优化都可以正常工作。搜索了一下,发现了一个非常简洁的例子: 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;
     }
    }

}

Square

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(因此在编辑之前的部分)。

不知道为什么我不能将 checkcollision 放在输入帧函数的循环内? (当我这样做时,不会显示任何碰撞)。将“isHit = false”放入 moveobjects 中以重置对命中的检查似乎也相当混乱。

我猜我似乎无法找出物体何时没有碰撞来重置它们。 在 hittest 上创建 else 语句来检查是否没有碰撞是行不通的,这似乎是合乎逻辑的,因为 hittestcheck 中可能会与不止 2 个项目发生冲突。

有什么想法吗?

[I apologise if this isn't really an in depth question, but I wanted to solve this once and for all]

I was trying to get look into quadtrees, but already ran into trouble getting collision detection without any optimization working properly. Did a search and found a pretty neat example:
http://wonderfl.net/c/kyLx
(onenterframeC part mostly)

Trying to imitate this, it won't work the same.
Only some collisions are detected between particular objects. When the objects are not moving it seems to work alot better for some reason.

I really can't figure out whats the problem, the code is essentially the same as the sample. I did not blindy copy pasted it, I understands whats happening except for this part:

if (j <= i)
                        continue;

J would never become bigger that I right? The line also completely removes any working collisions for me.

Here is what I did:
[view result here: http://martinowullems.com/collision.swf

Main class

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;
     }
    }

}

Square

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;
            }
        }

    }

}

Any help would greatly be appreciated, want to get further on this !


EDIT: Changed some stuff, there is progress but stuff still is unclear to me !

Changed some things in 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);
        }

    }

}

Also added a setter and getter in collisionobject (so section before the edit).

Not sure why I can't put the checkcollision inside the loop on the enter frame function? (when I do no collisions are shown). And placing "isHit = false" inside moveobjects to reset a check for a hit also seems pretty messy.

I can't seem to find out when the objects aren't colliding to reset them I guess.
Making an else statement on the hittest to check if there is no collision doesn't work, seems logical since there could be collisions with more than just 2 items in the hittestcheck.

Any idea's ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吹梦到西洲 2024-11-05 00:35:27

我必须查看原始资料才能理解这一点。如下,供参考。

这一行

if (j <= i) continue;

是一个排列检查;它基本上确保每个组合只检查一次,而不是多次。然而,为此,您需要有两个 For 循环 - 一个内部循环和一个外部循环。

您已经对以下行做了同样的事情:

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

尝试使用 for 循环而不是从零开始的循环。不过,请保留“if j <= i”行注释。我认为这会解决你的问题。 (这两个语句不能在此循环中共存;如果一起使用,它们将导致您所描述的问题。)

祝您好运。

来自网站的原始来源:(

    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;
            }
        }
    }

就其价值而言,我认为这家伙的代码实际上是检查对象自身是否发生碰撞 - 他应该将“if j <= i”行更改为“if j < i”。您解决了这个问题你原来的 for 循环有问题。)

I had to look at the original source to understand this. It is below, for reference.

This line

if (j <= i) continue;

is a permutation check; it basically makes sure that each combination only gets checked once, instead of multiple times. However, for this, you need to have two For loops - an inner one and an outer one.

You've done the same thing with the following line:

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

Try using that for loop instead of the one that starts from zero. Leave the "if j <= i" line commented, though. I think that will solve your problem. (Those two statements can't coexist in this loop; if used together, they'll cause the problems you're describing.)

Good luck.

Original source from website:

    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;
            }
        }
    }

(For what it's worth, I think that this guy's code actually checks objects against themselves for collision - he should change the "if j <= i" line to "if j < i". You solved this problem with your original for loop.)

挽袖吟 2024-11-05 00:35:27

在查看了他的代码并检查了您的代码之后,有几件事。

这部分是必要的,以确保您不会浪费时间进行循环并重新检查已比较的项目。

if (j <= i)
                    continue;

假设数组中有 3 个项目,将项目 3 与项目 0 进行比较。然后将项目 3 与项目 1 进行比较,将项目 3 与项目 2 进行比较。然后将项目 2 与...项目 3 进行比较?你已经这么做了。您希望将第 2 项与小于其自身的项进行比较,以避免重复。

您的代码与他的代码很接近,但是您已经用已经定义的 hitTestObject 替换了他的 hitTest 版本(使用位置 + 维度)。我只在第一个方块上看到一次命中测试。似乎有些不对劲……我会在其中放置一些跟踪语句,看看发生了什么。

最后,当你取消他的代码的注释时,它可以工作吗?

Couple of things, after looking at his code, and reviewing your code.

This part IS necessary to make sure you don't waste time going through the loop and re-checking items that have already been compared.

if (j <= i)
                    continue;

Imagine you have 3 items in the array, and compare item 3 to item 0. Then you compare item 3 to item 1, and item 3, to item 2. THEN you compare item 2 to... item 3? You've already done that. You want to compare item 2 to anything lesser than itself so you avoid duplicates.

Your code is close to his, but you've swapped out his version of the hitTest (which uses position + dimension) with the already-defined hitTestObject. I'm only seeing one hit-test on the first square. Something seems off in that... I'd drop some trace statements in there and see what's up.

Last, when you uncomment his code, does it work?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文