I need to do some simple collision detection in Away3D. I found the away3d.bounds.AxisAlignedBoundingBox class, but it seems I can only check collisions between the bounding box and a Vector.
Is there any way to check the collision between two bounding boxes?
if you are using/can upgrade to 4.4.x,
look into Mesh.worldBounds, particularly wordBounds.overlap(someOtherWorldBounds).
Example (away3d setup elided):
// setup objects and materials
cubeMaterial:ColorMaterial;
cube:Mesh;
sphereMaterial:ColorMaterial;
sphere:Mesh;
collideMaterial:ColorMaterial;
cubeMaterial = new ColorMaterial(0x3333FF);
cube = new Mesh(new CubeGeometry(), cubeMaterial);
cube.x = -100;
cube.showBounds = true;
sphereMaterial = new ColorMaterial(0xFF3333);
sphere = new Mesh(new SphereGeometry(), sphereMaterial);
sphere.x = 100;
sphere.showBounds = true;
collideMaterial = new ColorMaterial(0x33FF33);
in your enterFrame handler:
// process your object movement here
if (cube.worldBounds.overlaps(sphere.worldBounds) cube.material = collideMaterial;
else cube.material = cubeMaterial;
view.render();
Related
It seems that if a display object has a perspective projection applied to it, calling localToGlobal gives you the wrong coordinates. In the following code, I draw a rectangle, rotate it slightly about its X axis, then draw an oval inside it using coordinates derived from localToGlobal. It works fine, until I try to apply a perspective projection, then the coordinates are all wrong. Anyone know how to get around this problem?
var w:uint = 300, h:uint = 150;
var s:Sprite = new Sprite();
s.graphics.beginFill(0x000000);
s.graphics.drawRect(-w/2,0,w,h);
s.graphics.endFill();
s.x = 275; s.y = 200; s.z = 600;
s.rotationX = -45;
addChild(s);
var point00:Point = new Point(0,0);
var point0h:Point = new Point(0,h);
var midL:Point = new Point(-w/2,h/2);
var midR:Point = new Point(w/2,h/2);
/*var VP:PerspectiveProjection = new PerspectiveProjection();
VP.fieldOfView = 55;
var p:Point = new Point(275,100);
VP.projectionCenter = p;
s.transform.perspectiveProjection = VP;*/
var o:Shape = new Shape();
o.graphics.beginFill(0x00ff00);
o.graphics.drawEllipse(
s.localToGlobal(midL).x,
s.localToGlobal(point00).y,
s.localToGlobal(midR).x - s.localToGlobal(midL).x,
s.localToGlobal(point0h).y - s.localToGlobal(point00).y
);
addChild(o);
It seems that setting perspectiveProjection does not force the player to redraw the object, and localToGlobal depends on redraw for correct results. You can wait 1 tick like you found (setTimeout for 0ms or enterFrame for 1 frame will do it) or you can force the player to redraw using BitmapData/draw():
// ...
s.transform.perspectiveProjection = VP;
new BitmapData(1, 1).draw(s); // forces player to redraw the sprite
var o:Shape = new Shape();
o.graphics.beginFill(0x00ff00);
o.graphics.drawEllipse(
s.localToGlobal(midL).x,
s.localToGlobal(point00).y,
s.localToGlobal(midR).x - s.localToGlobal(midL).x,
s.localToGlobal(point0h).y - s.localToGlobal(point00).y
);
addChild(o);
I found a workaround that isn't terrible. All you have to do is put a slight delay before the localToGlobal calls and then they will return the right coordinates. That also fixes local3DToGlobal.
To make things quick, I have an arrangement of tiles that a player and an enemy are on.
public static var floor1:Array = new Array(7);
floor1[0] = [0,1,1,1,1,1,0];
floor1[1] = [1,1,1,1,1,1,1];
floor1[2] = [1,1,1,0,1,1,1];
floor1[3] = [1,1,0,0,0,1,1];
floor1[4] = [1,1,1,0,1,1,1];
floor1[5] = [1,1,1,1,1,1,1];
floor1[6] = [0,1,1,1,1,1,0];
public function Main()
{
var tilew:int = 60;
var tileh:int = 60;
for (var i:int=0; i<floor1.length; i++)
{
for (var u:int=0; u<floor1[i].length; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(floor1[i][u]);
cell.x = ((u-i)*tileh);
cell.y = ((u+i)*tilew/2);
addChild(cell);
cell.addEventListener(MouseEvent.ROLL_OVER, mouseover);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseout);
cell.addEventListener(MouseEvent.CLICK, mouseclick);
cell.addEventListener(Event.ENTER_FRAME, beginfloor1);
}
}
var player:Player = new Player();
addChild(player);
player.mouseEnabled = false;
player.x = 5 * (tileh);
player.y = 5 * (tilew/2);
var enemy:Enemy = new Enemy();
addChild(enemy);
enemy.mouseEnabled = false;
enemy.x = 9 * (tileh);
enemy.y = 9 * (tileh/2);
My goal is to have the enemy move randomly on tiles in his range. What I did was create a square graphic called enemyVisionArea that checks which tile is hitting the enemy, which is basically surrounding tiles.
I have a timer function that tells the enemy to move every 5 seconds if the player isn't near him and if he's next to an available tile.
function timerenemy (event:TimerEvent){
if (enemy.enemyVisionArea.hitTestObject(enemyMover) && !player.visionPoint.hitTestObject(enemyMover.tileMiddle))
{
enemy.x = (enemyMover.x)+55;
enemy.y = (enemyMover.y)+20;
trace("moved");
}
}
enemyMover is a variable that I made equal to the tile objects.
function beginfloor1(event:Event)
{
enemyMover = event.currentTarget as Tile;
}
It just stays where it is. I'm just want to have the enemy move on its own on any tile that its enemyVisionArea is hitTesting a nearby tile. The beginfloor1 function doesn't seem to be working. Is there any way I can declare enemyMover = event.currentTarget as Tile and have the enemy move on a random tile that its enemyVisionArea is hitTesting?
If this is confusing, I can post the full code.
You are assigning 49 enterframe listeners which are called in sequence, and they ALL change one single variable to the cell they are attached to. Of course, the last tile is what's always assigned.
I expect that you want an enemy to check if there's a tile available for it to move to. You are essentially checking for one tile which is enemyMover - how do you determine what's that tile? You have to check all available tiles that are around the enemy, make a list of them and select one out of that list that's not the current tile, then move the enemy there.
So, first you need a complete tileset to be addressable from somewhere. The best way will be to declare a class-wide var tileset:Array and fill it where you make new tiles. Drop the Event.ENTER_FRAME listener from the code there, as it's useless. Then, in your timerevent that's for the enemy you do check all of the tileset if they are within your enemy's vision area (you use hitTestObject, I'd use clear distance grid-wise or coordinate-wise - it's a whole lot faster), if so, you add them to the TEMPORARY array you create within that function. Of course, if your enemy is at the currently processed cell, you ignore it - you have to move your enemy, not make him stand in place. Then, select (somehow, it's up to you) what cell your enemy should move to, and execute a move. Yes, if you want your enemy to move randomly, select a cell at random by its index via Math.floor(Math.random()*selectedcells.length).
I does some tests with Box2D and stuck with it.
Here is my body-construct code:
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.fixedRotation = true;
var center:Number = Consts.stageToB2(Consts.worldSize / 2);
bodyDef.position.Set(center, center);
var body:b2Body = physicWorld.CreateBody(bodyDef);
var shape:b2CircleShape = new b2CircleShape(Consts.stageToB2(w) * 0.5); // our monster is in circle shape.
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
body.CreateFixture(fixtureDef);
I created such two bodies, but they doesn't collide! the debugDraw also doesn't light up the bodies. but when I add an angular velocitiy for one of them:
body.SetAngularVelocity(Math.PI / 89);
They'll start collide. Could you explain What happens here?
I have a feeling that Box2D won't check for collisions between objects that haven't had any forces applied to them. It seems logical because why would the engine waste resources to check those objects if they're not doing anything.
If you're placing the objects atop each other and then expecting something to happen without a gravity set or applying any forces to the boxes, that could be why.
What about gravity? If they are not moving at all , even not falling , it seems like you don't have a gravity in your world..
private var gravity:b2Vec2 = new b2Vec2(0, 9.8);
...
private var your_world:b2World = new b2World(gravity, true)
I'm drawing bitmaps of movieclips which I then feed into my hittest function to test for collisions. However, I'm not quite sure how i would add to the code below to take into account and draw bitmaps for movieclips which have been scaled and/or rotated. The below code obviously only works for non-transformed movieclips. I've included in comments code which i've already tried but to no success.
When adding the drawn bitmap to the stage, no matter whether the movieclip in question is transformed or not, the drawn bitmap is "cut off" and incorrectly drawn - it appears to only draw a section of it. However, this does not particularly affect the collision testing for the non-transformed movieclips, but has an adverse effect on transformed movieclips.
All of the movieclips I want to be drawn have been created through the graphics property.
//for example:
var mymc:MovieClip = new MovieClip();
var g:Graphics = mymc.graphics;
g.moveTo(0,0);
g.lineTo(17.5,0);
g.lineTo(8.75,17.5);
g.lineTo(-8.75,17.5);
g.lineTo(0,0);
main code:
for each(var mc:MovieClip in impassable) {
//var bMatrix:Matrix = new Matrix();
//bMatrix.scale(mc.scaleX, mc.scaleY);
//bMatrix.rotate(mc.rotation * (Math.PI/180));
var bData:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
//bData.draw(mc, bMatrix);
bData.draw(mc);
var bitmap:Bitmap = new Bitmap(bData);
bitmap.x = mc.x;
bitmap.y = mc.y;
var HitTest:Number = newCollision(bitmap, centerX, centerY, 13.7);
Any thoughts? thanks
This function will create a BitmapData clone of a DisplayObject, taking into account its transform matrix, though it doesn't take into account bitmap filters. (Based on this answer.)
function createBitmapClone(target:DisplayObject):BitmapData {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
return cloneData;
}
When you call successive transforms on a Matrix, the ordering is very important and can really mess things up.
Luckily there is a helper method that allows you to specify translation, rotation and scaling in one go and avoid those issues - createBox
For your case, something like this:
var matrix:Matrix = new Matrix();
matrix.createBox(mc.scaleX, mc.scaleY, mc.rotation*Math.PI/180, 0, 0);
(the two zeros are for x and y translation)
var theTextField:TextField = new TextField();
var theText:TextField = new TextField();
theTextField.type = TextFieldType.INPUT;
theTextField.border = true;
theTextField.x = 50;
theTextField.y = 10;
theTextField.height = 20;
theTextField.multiline = true;
theTextField.wordWrap = true;
theText.border = false;
theText.x = 10;
theText.y = 10;
theText.text = "Angle";
addChild(theText);
addChild(theTextField);
submit.addEventListener(MouseEvent.CLICK, click_handler);
function click_handler(event:MouseEvent):void
{
var txt:String = theTextField.text;
ang = Number(txt);
if (ang<0)
{
angle = - ang;
}
else
{
angle = 360 - ang;
}
var circleSlider:CircleSlider=new CircleSlider(120,angle); //draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
circleSlider.x = stage.stageWidth / 2;
circleSlider.y = stage.stageHeight / 2;
circleSlider.addEventListener(CircleSliderEvent.CHANGE, circleSliderEventHandler);
addChild(circleSlider);
}
Can someone help me.
var circleSlider:CircleSlider=new CircleSlider(120,angle);//draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
this code is the problem. CircleSlider is a separate class.I tried like this
circleSlider.CircleSlider(120,angle);
but it gives an error "" Call to a possibly undefined method CircleSlider through a reference with static type CircleSlider.""
when i run the program and input value as 90.
then i enter another value as 180 then it becomes
how can i overcome this error
Every time your click handler is executed you're creating a new instance of your circle class and adding it to the stage without removing the old instance. I think the best way to resolve it would be to move the logic you have in the constructor of your CircleSlider class into a separate public method, say draw and call that in the click handler.
Your code would look something like this:
// Set up the circle once
var circleSlider = new CircleSlider();
circleSlider.x = stage.stageWidth / 2;
circleSlider.y = stage.stageHeight / 2;
circleSlider.addEventListener(CircleSliderEvent.CHANGE, circleSliderEventHandler);
// and add it to the stage once
addChild(circleSlider);
function click_handler(event:MouseEvent):void
{
var txt:String = theTextField.text;
ang = Number(txt);
if (ang<0)
{
angle = - ang;
}
else
{
angle = 360 - ang;
}
// Now simply redraw in the same circle instance
circleSlider.draw(120,angle); //draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
}
Assuming you're using the drawing API to draw the graphic, you could draw the circle (which seems to be constant) in the constructor (once) and the line illustrating the angle in the draw method (repeatedly). You'll need to clear the old line each time like this:
// Assumes you're drawing in the graphics property of the class
this.graphics.clear();