-->

collision testing with multiple objects on stage

2019-08-22 11:14发布

问题:

I'm trying to create a sort of tree diagram such that, if you click on one of the circles, its child circles spread outward with some degree of randomness from the original circle, connected by lines. I have this working, but now what I want to do is make sure that none of the circles collide with each other and none of the lines criss cross. You can see the screenshot for what is currently happening. My code is below. How do I change this code so that it checks for collisions and avoids them? I've read up on flash's hitTestObject command, but that only works in the context one object to another. I want to test for one object contacting any display object.

import com.greensock.TweenMax;

var sw = stage.stageWidth;
var sh = stage.stageHeight;
var cr = 3; //circle radius
var moveRange = 25;
var circleColor = 0xcccccc;
var numCircles = 4;
var lineCanvas:Sprite = new Sprite();
addChild(lineCanvas);
var lineColor = 0xe9e9e9;
var lineWeight = 1;

function init(){
    firstCircle();
}

function firstCircle(){
    var xPos = randomRange(cr, sw-cr);
    var yPos = randomRange(cr, sh-cr);
    var newCircle:Shape = new Shape();
    newCircle.graphics.beginFill(circleColor);
    newCircle.graphics.drawCircle(0,0,cr);
    newCircle.graphics.endFill();

    var circleClip:MovieClip = new MovieClip();
    circleClip.childCircles = 2;
    circleClip.x = xPos;
    circleClip.y = yPos;
    circleClip.addChild(newCircle);

    addChild(circleClip);
    circleClip.addEventListener(MouseEvent.CLICK,clickCircle);
}

function clickCircle(e:MouseEvent):void {
    var thisCircle = e.target;
    for (var i=0; i<thisCircle.childCircles;i++){
        drawCircle(thisCircle);
    }
}

function drawCircle(parentCircle){
    var xPos = parentCircle.x;
    var yPos = parentCircle.y
    //var xPos = randomRange(cr, sw-cr);
    //var yPos = randomRange(cr, sh-cr);
    var newCircle:Shape = new Shape();
    newCircle.graphics.beginFill(circleColor);
    newCircle.graphics.drawCircle(0,0,cr);
    newCircle.graphics.endFill();

    var circleClip:MovieClip = new MovieClip();
    circleClip.childCircles = 2;
    circleClip.x = xPos;
    circleClip.y = yPos;
    circleClip.addChild(newCircle);
    addChild(circleClip);
    circleClip.addEventListener(MouseEvent.CLICK,clickCircle);
    moveCircle(circleClip,xPos,yPos);
}

function drawLine(childCircle,parentX,parentY){
        lineCanvas.graphics.lineStyle(lineWeight,lineColor);
        lineCanvas.graphics.moveTo(parentX,parentY);
        lineCanvas.graphics.lineTo(childCircle.x,childCircle.y);

//Want to check if either the line or the circle is contacting anything here. If it is, I want to kill the tween to the circle (thus also stopping the drawing of the line).

}

function moveCircle(childCircle,parentX,parentY){
    var curX = childCircle.x;
    var curY = childCircle.y;
    var moveX = randomRange(curX-moveRange,curX+moveRange);
    var moveY = randomRange(curY-moveRange-cr,curY+moveRange+cr);
    TweenMax.to(childCircle,.5, { x: moveX, y: moveY, onUpdate:drawLine, onUpdateParams:[childCircle,parentX,parentY]});
}

function randomRange(minNum:Number, maxNum:Number):Number {  
    return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);  
}

init();

回答1:

There's 2 ways you could do this:

  1. Re-factor your code to use an Actionscript Physics Library (i.e. Box2D or whichever you prefer). Then, consider your "circles" and "lines" as physical objects that will collide with each other, probably having the same effect as what you're trying to do here. The PRO to this is that the library comes with an assortment of extended classes to handle physical interaction. The CON is the overhead in implementation.
  2. The manual/custom way to do this is to add an eventlistener for ENTER_FRAME, on the stage OR the initial circle, that will loop through it's children, and call the hitTestObject method on each "child" circle, compared with every other "child" circle. The lines are within the children circles, so should be fine.

I notice you're not actually adding the childCircles to the initial circle, and instead adding them to the stage. You might want to push these circles into an Array to reference later for this "collisionDetection" method.



回答2:

Fundamentally you'll need to maintain a list of all the circles on the screen, and then every time something moves (or every frame) you'll need to check the moving circle against all the other circles to see if they are colliding.

An Array should work nicely for keeping track of all the circles.

Then, every time something moves, as long as it's moving, you should iterate through the array and check if it has hit any of the other circles. Use something like:

function drawLine (...) 
{

    // Your code here

    for (var i:int = 0; i < Array.length; i++)
    {  
        childCircle.hitTestObject(Array[i]);  
    }
}

Alternatively, you can specify an onEnterFrame function that checks every circle against every other circle every frame, although this uses more resources. I'm guessing resources aren't an issue for you at this point, so go with the simpler route and optimize if you need it later.