问题
I have two mc's on the stage, the first one is called missiles and the second one is called boxes.
So accordingly I put boxes in the boxes mc and missiles in the missiles mc. My problem is how to detect whether any of the missiles collide with boxes and make damage to the box.
I am using simple and effective algorithm which works but it is very slow when there are many missiles and boxes on stage. I am using two nested "for" cycles which is a bad practice, but I don't have other idea at the moment.
Here is the code
public function onEnterFrame(e:Event) {
for(var i:int=0;i<ground_mc.boxesLayer_mc.numChildren;i++){
mc1=ground_mc.boxesLayer_mc.getChildAt(i);
for(var j:int=0;j<ground_mc.bmLayer_mc.numChildren;j++){
mc2=ground_mc.bmLayer_mc.getChildAt(j);
if(mc1.hitTestObject(mc2)){
se=new SmallExplosion();
se.x=mc1.x+randNumber(-20,20);
se.y=mc1.y+randNumber(-20,20);
ground_mc.addChild(se);
mc1.decreaseHealth(mc2.getDamage());
}
}
}
}
boxesLayer_mc - boxes movieclip
bmLayer_mc - missiles movieclip
Are there other techniques to solve this problem. I have already tried to add detection code in every missile but it is also slow with "for".
Maybe trees or hash mapped search I don't know? Or maybe graphs :)
Any idea would be helpful
回答1:
For a similar task (2D), what I did was to create a simple quadtree-like construct where each node can either contain other child nodes or a list of display objects for collision detection. Note that the if the objects (boxes in your case) move around a lot this will not be efficient as you will need to move them into the right node everytime the move.
A simple example class would be:
public class WorldPartitionNode {
private var minX : Number;
private var minY : Number;
private var maxX : Number;
private var maxY : Number;
private var width : Number;
private var height : Number;
private var _children : Vector.<WorldPartitionNode>;
private var _objects : Vector.<GameObject>;
public function WorldPartitionNode(x : Number, y : Number,
w : Number, h : Number, childLevels : int)
{
minX = x;
minY = y;
maxX = x + w;
maxY = y + h;
width = h;
height = h;
if (childLevels == 0) {
// This node should have no children, so instead it should
// contain display objects
_objects = new Vector.<GameObject>;
}
else {
_children = new Vector.<WorldPartitionNode>(4,true);
_children[0] = new WorldPartitionTreeNode(minX, minY, width/2, height/2, childLevels-1);
_children[1] = new WorldPartitionTreeNode(minX+width/2, minY, width/2, height/2, childLevels-1);
_children[2] = new WorldPartitionTreeNode(minX, minY+height/2, width/2, height/2, childLevels-1);
_children[3] = new WorldPartitionTreeNode(minX+width/2, minY+height/2, width/2, height/2, childLevels-1);
}
}
public function addObject(obj : GameObject) : void
{
if (_children) {
// This is not a leaf node, so add it to that of the child
// nodes in which it belongs.
var i : uint;
for (i=0; i<4; i++) {
var c : WorldPartitionNode = _children[i];
if (obj.x > c.minX && obj.y > c.minY && obj.x < c.maxX && obj.y < c.maxY) {
c.addObject(obj);
return; // Found node, so bail
}
}
}
else {
// This is a leaf node, so just add to the internal objects vector
_objects.push(obj);
}
}
public function checkCollisions(x : Number, y : Number) : GameObject
{
if (_children) {
// This node has children, so delegate to the right child
var i : uint;
for (i=0; i<4; i++) {
var c : WorldPartitionNode = _children[i];
if (x > c.minX && y > c.minY && x < c.maxX && y < c.maxY) {
return c.checkCollisions(x, y);
}
}
}
else {
// This is a leaf node (with objects directly in it) so loop through
// them all and check collision
var obj : GameObject;
for each (obj in _objects) {
if (obj.collidesWith(x, y))
return obj;
}
return null; //None if reached
}
}
}
Instances of this class will divide itself into four rectangular sections (two rows, two columns) if childLevels > 0
, and create instances of the same class for each of those nodes. Those nodes will in turn divide their space up the same way until there are childLevels
levels in total.
You can thereby create a three-level quad-tree (with 64 partition leaf nodes) where each node is just 1/64 the size of the original space. As you add objects with addObject()
the object will be added to the partition node which corresponds to the square in which it is located.
When you execute checkCollision()
, it will recurse childLevels
times until it finds the right partition, and then perform a regular collision detection loop only for the objects in that partition.
To create a three-level tree, which covers a space that is 1024x1024 pixels:
var treeRoot : WorldPartitionNode = new WorldPartitionNode(0, 0, 1024, 1024, 3);
And to check for collisions between your missiles and the objects in the tree:
var missile : GameObject;
for each (missile in _missiles) {
var obj : GameObject = treeRoot.checkCollisions(missile.x, missile.y);
if (obj != null)
obj.kill(); // Was hit by missile
}
This will likely speed up the collision detection significantly, but relies on the boxes being static (or rarely moving), because if they move they will no longer be in the correct partition node and if they're not the tree will fail to find collisions between them.
There are no doubt smarter partitioning systems out there, but this worked very well for me in a project where I needed to scatter pick-ups (coins et c) in a large space and where the player would fly around to collect them. Each frame I would then let the tree check for collisions between the static pick-ups and the player.
回答2:
Best solution is to use some physics engine(like box2d). And using game engine (like pushButtonEngine) for a game development is even better :) Although, if you want to do it all yourself, R-tree like structures is your choise :)
回答3:
Use a for each it is slightly neater code and less assignments going on
Also change onEnterFrame to a timer event you can slow the timer event down if needed.
public function onEnterFrame(e:Event) {
for each( var box:MovieClip in ground_mc.boxesLayer_mc ){
for each(var missle:MovieClip in <ground_mc.bmLayer_mc ){
if( box.hitTestObject(missle)){
se=new SmallExplosion();
se.x=box.x+randNumber(-20,20);
se.y=box.y+randNumber(-20,20);
ground_mc.addChild(se);
box.decreaseHealth(missle.getDamage());
}
}
}
}
来源:https://stackoverflow.com/questions/6995529/optimizing-collison-detection-code-in-as3