Use bitmapData.hitTest on two bitmapData with centered registration point - actionscript-3

I've spent all the day on this, it's time to ask for your help :)
I'm trying to do collision detection of two display objects, both have centered registration point.
On my stage I have fixed elements that when added to stage are pushed in an Array called "zoneUsed". All the displayObject in my project have the registration point in the center.
My goal is to click on the stage, and check if in the clicking coords I could create a circle. My plan was to create a Sprite for the new object, cycle on the zoneUsed array, and check if the new sprite have enough space to live.
Here my code so far:
private function checkSpaceForNewMarker (markerToCheck:Sprite):Boolean {
var isPossible:Boolean = true;
var bmdataToCheck:BitmapData = new BitmapData (markerToCheck.width, markerToCheck.height, true, 0);
var m:Matrix = new Matrix ();
m.tx = markerToCheck.width/2;
m.ty = markerToCheck.height/2;
bmdataToCheck.draw (markerToCheck, m);
for (var i:int = 0; i<zoneUsed.length; i++) {
trace ("*** CHECKING ****");
var bmddataOnTheTable:BitmapData = new BitmapData (zoneUsed[i].width, zoneUsed[i].height, true, 0);
var tableMatrix:Matrix = new Matrix ();
tableMatrix.tx = zoneUsed[i].width/2;
tableMatrix.ty = zoneUsed[i].height/2;
bmddataOnTheTable.draw(zoneUsed[i], tableMatrix);
if (bmdataToCheck.hitTest(new Point(markerToCheck.x, markerToCheck.y), 255, bmddataOnTheTable, new Point (zoneUsed[i].x, zoneUsed[i].y), 255)) {
trace ("COLLISION");
isPossible = false;
} else {
trace ("NO COLLISION");
isPossible = true;
}
}
return isPossible;
}
....But right now the results are weird. Depending on the zones, my traces work or not. What am I doing wrong?

The problem is , you are drawing 1/4 (quarter) part of every object.
BitmapData is not like a Shape, Sprite, MovieClip, and it crops all the pixels, when the drawing bounds is out of the bounds of (0,0,bitmapdata.width, bitmapdata.height) rectangle.
Just remove this lines:
m.tx = markerToCheck.width/2;
m.ty = markerToCheck.height/2;
and also
tableMatrix.tx = zoneUsed[i].width/2;
tableMatrix.ty = zoneUsed[i].height/2;
You don't need this translations.
Also your code may be cause for memory leak. You are creating bitmapdata, but do not dispose it. The garbage collector will not release the memory you have allocated.You must release memory explicitly. Call bitmapdata.dispose() every time you have no need of that bitmapdata.

I'm not sure that the origin of the bitmap has anything to do with the test itself. The very nature of the test would seem to imply that the hittest is based on the RGBA value of the two supplied bitmaps. Anyway rather than picking apart your own implementation I'll just refer you to a tutorial by Mike Chambers (adobe platform evangelist). http://www.mikechambers.com/blog/2009/06/24/using-bitmapdata-hittest-for-collision-detection/
Also for more flash tutorials check out www.gotoandlearn.com.

Related

How Can I Make AS3 Var Loop move speed on the Y Axis on repeat my university project

I'm tring to get the car image to move down the screen on the y Axis and make it a repeat and colide with another object
//creates the new Car
for (var c:int=0; c<8; c++){
var newcar = new car();
newcar.x = 55*c;
newcar.y = 100;
EntityArray.push(newcar);
stage.addChild(newcar);
trace("Car Created"+c)
}
How to make it colide with the following and remove it from screen
//creates the new Frog
for (var f:int=0; f<1; f++){
var newfrog = new frog();
newfrog.x = 210;
newfrog.y = 498;
EntityArray.push(newfrog);
stage.addChild(newfrog);
trace("Frog Created"+f)
}
[image][1][1]: https://i.stack.imgur.com/Ihsfx.png
Though I'm quite pleased to hear that today they still tell you about ActionScript at college, it's a bit hard
to give you advice here since I don't know what they've covered yet.
Generally speaking, you could realize this with a simple game loop, that runs periodically and in it's most simple
form:
checks user input (in your case most likely pressing left/right to move the frog)
update game state (move the cars & the frog ; check for collision)
draw everything to screen
For creating the periodical loop, Flash/ActionScript offers a powerful event called ENTER_FRAME. Once started, it
will fire with the framerate of the movie. So if you set your movie to 60fps, it will execute it's callback function
roughly every 17ms.
I assume your instances of Frog and Car extend Flash's Sprite or MovieClip class - so collision detection is also pretty
easy since you can use the inherited hitTestObject() method.
To make things a bit easier though I'd recommend that you don't put the reference to the frog instance inside the EntityArray.
Better use a global reference. (Also, you don't need a for-loop because there's just one frog)
As another sidenote, it's quite common that classnames start with a capital letter.
private var newfrog:frog; // defines a class variable we can access anywhere inside our class
//Later on instantiate new cars and the frog:
for (var c:int=0; c<8; c++){
var newcar = new car();
newcar.x = 55*c;
newcar.y = 100;
EntityArray.push(newcar);
stage.addChild(newcar);
}
newfrog = new frog();
newfrog.x = 210;
newfrog.y = 498;
stage.addChild(newfrog);
addEventListener(Event.ENTER_FRAME, loop); // register an ENTER_FRAME listener for the main game loop
private function loop(e:Event):void
{
var tempCar:car;
for(var a:int=0;a<EntityArray.length;a++)
{
tempCar=EntityArray[a]; // get a car from the EntityArray
tempCar.y++; // move it down on screen
if(tempCar.y>600) // if it's vertical position is greater than 600...
{
tempCar.y=0; // ...move it back to the top
}
if(newfrog.hitTestObject(tempCar)) // evaluates to true, if a car and the frog's bounding boxes overlap
{
trace("there's a collision!"); // do something
}
}
}

AS3 drawing Sprite on BitmapData memory leak

I have an AIR app that renders images from a Flash editor. You can customize several surfaces - they all have the same width and height. Each surface is then rendered by the AIR app.
I'm struggling with a memory leak I fail to address.
For each surface I need to render, I have a Sprite containing many components (other sprites - some having event listeners, BitmapDatas and other children components, sprites, etc).
I'm already aware of BitmapData issues to be garbage collected, I tested both :
creating a new BitmapData for each rendering then dispose() and point to null
reuse a single BitmapData for each rendering
Memory leak is still happening in equal proportion.
Here is my loop :
var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
var mtx:Matrix = new Matrix();
trace('before drawing :'+(System.privateMemory/1024));
bm.draw(myBigSprite, mtx, null, null, null, true);
trace('after drawing :'+(System.privateMemory/1024));
var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);
//return result and encode Bitmap to png
result.bitmapData.dispose();
result.bitmapData = null;
result = null;
Result :
before drawing :208364
after drawing :302816
Wrote bitmap to file: surface0.png
before drawing :303296
after drawing :446160
Wrote bitmap to file: surface1.png
before drawing :446160
after drawing :565212
Wrote bitmap to file: surface2.png
before drawing :565924
after drawing :703100
Wrote bitmap to file: surface3.png
before drawing :703572
after drawing :834420
Wrote bitmap to file: surface4.png
I feel like I'm missing something in draw function behaviour. It seems like I have newly created instances of the components of myBigSprite that persists after draw operation.
I tried to completely destroy myBigSprite at the end of each loop, it does not change anything....
Any hint would be appreciated !
Ok guys, I eventually understood and fixed this issue.
First of all, I installed and ran Adobe Scout. Excellent tool.
As you may not see (plus it's in French language), I generated 3 surfaces corresponding to the edges. The "big" green bar on the right which is mass memory consuming represent "Bitmap display objects". Interesting ! Never heard of those before.
A Google search later, I found this article : https://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e26.html
It explains
For example, in the code excerpt shown earlier, once the load
operation for the pict Loader object is complete, the pict object will
have one child display object, which is the bitmap, loaded. To access
this bitmap display object, you can write pict.getChildAt(0).
So I began to undestand that, somehow, maybe Bitmap object are attached as children on some objects of myBigSprite.
Finally, I created a recursive function to search and destroy all Bitmap, BitmapData and ByteArray objects contained in myBigSprite AFTER the draw operation
//inside render function
bm.draw(myBigSprite, mtx, null, null, null, true);
destroyDisplayObjects(myBigSprite);
...
private function destroyDisplayObjects(obj):void{
if ("numChildren" in obj){
for (var i:int = 0; i<obj.numChildren; i++)
{
destroyDisplayObjects(obj.getChildAt(i));
}
}
else {
if (flash.utils.getQualifiedClassName(obj) == "flash.display::Bitmap"){
//trace ('FREE BITMAP');
obj.bitmapData.dispose();
obj.bitmapData = null;
obj = null;
return;
}
else if (flash.utils.getQualifiedClassName(obj) == "flash.display::BitmapData"){
//trace ('FREE BITMAPDATA');
obj.dispose();
obj = null;
return;
}
else if (flash.utils.getQualifiedClassName(obj) == "flash.display::ByteArray"){
//trace ('FREE BYTEARRAY');
obj.clear();
obj = null;
return;
}
return;
}
}
Et voilĂ , memory is 100% cleaned after the draw operation, no more leak :)
You should declare you Bitmap and BitmapData outside of any functions and then simply recycle them for usage inside of your loop (instead of creating a new anything to be added in memory).
Use .dispose() only on the last image when you're sure you don't need bitmap data from bm variable anymore. Otherwse, if disposed, you'll have to create a new alternative var someThing :BitmapData = new BitmapData again for further usage.
////# declare globally (not inside some specific function..)
//var destDim :Point = new Point(your_X_num , your_Y_num);
//var bgColor :uint = 0x505050;
var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);
//result.bitmapData = bm; //can be set here but done within function for clarity...
var mtx:Matrix = new Matrix();
////# update bitmap by replacing its bitmapdata with new colour values of existing pixels
function Here_is_my_loop (): void
{
trace('before drawing :'+(System.privateMemory/1024));
//overwrite pixel colours in bitmap (result)
bm.draw(myBigSprite, mtx, null, null, null, true);
result.bitmapData = bm; //update bitmap
trace('after drawing :'+(System.privateMemory/1024));
//return result and encode Bitmap to png
//result.bitmapData.dispose();
//result.bitmapData = null;
//result = null;
}

AS3 tween object not working with .hitTestObject()

I am having a major problem in my new browser app.
Okay so I made game where different cubes (squares) spawn at the top of the screen and I use the Tween class to make them go down the screen and then disappear.
However I want to detect a collision when a cube hits the player (that is also a flying cube).
I tried everything, truly everything but it does not seem to work. The problematic thing is that when I remove the "Tween" function it does detect collision with the hitTestObject method but when I add the "Tween" line collision won't be detected anymore.
It looks like this:
function enemiesTimer (e:TimerEvent):void
{
newEnemy = new Enemy1();
layer2.addChild(newEnemy);
newEnemy.x = Math.random() * 700;
newEnemy.y = 10;
if (enemiesThere == 0)
{
enemiesThere = true;
player.addEventListener(Event.ENTER_FRAME, collisionDetection)
}
var Tween1:Tween = new Tween(newEnemy, "y", null, newEnemy.y, newEnemy.y+distance, movingTime, true);
}
And the collision detection part:
private function collisionDetection (e:Event):void
{
if (player.hitTestObject(newEnemy))
{
trace("aaa");
}
}
I am desperate for some information/help on the topic, it's been bugging me for days.
Thanks for your time, I would be very happy if someone could help me out^^
First, make sure the "newEnemy" instance and the "player" instance are within the same container. If they are not, their coordinate systems might not match up and could be the source of your problem.
Otherwise, you need to keep a reference to each enemy instance you create. It looks like you are only checking against a single "newEnemy" variable which is being overwritten every time you create a new enemy. This might be why you can successfully detect collision between the player and the most recent "enemy" instance.
So... you need a list of the enemies, you can use an Array for that.
private var enemyList:Array = [];
Every time you create an enemy, push it to the Array.
enemyList.push(newEnemy);
In your "collisionDetection" function, you need to loop through all of the enemies and check if the player is touching any of them.
for(var i:int = 0; i < enemyList.length; i++)
{
var enemy = enemies[i];
if (player.hitTestObject(enemy))
{
trace("Collision Detected!");
enemy.parent.removeChild(enemy); // remove the enemy from the stage
enemies.splice(i, 1); // remove the enemy from the list
}
}
I'd suggest that you move to TweenMax, it just might solve your problem, and in my experience it's much better in every possible way.
Scroll down the following page to see a few variations of this library, I myself use TweenNano, they're completely free of charge:
https://greensock.com/gsap-as
I think some plugins cost money, but I doubt you'll ever need them.

Compositing the stage's last frame

I've created a series of classes that can be used to generate and render images. I want to store a copy of the last frame displayed so I can mix it with the current frame to create a video sustain effect. A brief overview of the classes involved in this example:
MasterContainer: a subclass of Sprite used as the main display object. Generative classes are placed in the MasterContainer, and redrawn when the container is told to render
CustomWave: a subclass of Shape used to contain, draw, and manipulate a GraphicsPath object. One of the aforementioned 'generative classes'
My current attempt involves the use of two MasterContainer objects - one for the current frame, and one for the last frame. If I'm not mistaken, the current appearance of one MasterContainer (and its children) can be copied to the other with a command like lastMaster.graphics.copyFrom(master.graphics);. Consider the following code:
var time:Number;
var master:MasterContainer = new MasterContainer(); //current frame
var lastMaster:MasterContainer = new MasterContainer(); // last frame
var wave:CustomWave = new CustomWave(new <Number>[0,0,0,0],0xffffff,5); //generator for current frame
master.RegisterComponent(wave); //adds CustomWave and registers with the rendering loop
addChild(lastMaster); //add last frame to stage
addChild(master); //add current frame to stage
addEventListener(Event.ENTER_FRAME, perFrame);
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //copy previous frame's graphics
UpdatePoints(); //update the path of the CustomWave
UpdateColor(); //update the color of the CustomWave
master.fireRenderCannon(); //redraw objects registered to master
}
This seems to work in theory, but as far as I can tell lastMaster ends up with no visible graphics content even though master renders as expected. I've tried several times to test whether this is the case, and am pretty convinced that that it is, but am newish to AS3 and am concerned I am overlooking something - the code looks like it should work. Does anyone have suggestions on how to test this properly? Are there obvious defects within this code that would cause lastMaster to be visually blank? Is there an better way of accomplishing my goal?
I think I'm in over my head on this... I would love any input. Thanks!
After you copied graphics, what do you try to do with it?
Method copyFrom works as clocks, without any problems. Isn't here logic bug in your code?
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //Here
//master.graphics.copyFrom(lastMaster.graphics);
UpdatePoints();
UpdateColor();
master.fireRenderCannon();
}
Example of copyFrom, it works fine with any complexity of graphics:
var complex: Shape = new Shape();
adobeExample(complex.graphics);
var test2: Shape = new Shape();
test2.graphics.copyFrom(complex.graphics);
addChild(test2);
private function adobeExample(graphics: Graphics):void{
// define the line style
graphics.lineStyle(2,0x000000);
// define the fill
graphics.beginFill(0x666699);//set the color
// establish a new Vector object for the commands parameter
var star_commands:Vector.<int> = new Vector.<int>();
// use the Vector array push() method to add moveTo() and lineTo() values
// 1 moveTo command followed by 3 lineTo commands
star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
// establish a new Vector object for the data parameter
var star_coord:Vector.<Number> = new Vector.<Number>();
// use the Vector array push() method to add a set of coordinate pairs
star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
graphics.drawPath(star_commands, star_coord);
}
After the comments made by Bennet and Nicolas, it became obvious that my requirements were (nearly) impossible without a fair amount of redesign. The changes made are as follows:
Generators are no longer DisplayObjects. They are only used to calculate vectors containing the IGraphicsData objects necessary to draw the generated graphic with the drawGraphicsData method.
MasterContainer is now a shape subclass that retrieves the Vector.<IGraphicsData> from each registered generator in order to draw the output.
A bitmap subclass is used to render the contents of the MasterContainer, combining it with a color-dampened version of the previous frame.
An abridged version of the bitmap subclass:
private var constantSustain:Number;
private var linearSustain:Number;
private var sustain:ColorTransform;
private var lastFrame:BitmapData;
public function BitmapManipulator(constantSustain:Number = 0.998, linearSustain:Number = 0.98) {
this.constantSustain = Math.min(Math.max(constantSustain, 0), 1);
this.linearSustain = Math.min(Math.max(linearSustain, 0), 1);
this.UpdateSustain();
this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage)
}
private function UpdateSustain():void {
var constantRelease:Number = 255 * (this.constantSustain - 1);
this.sustain = new ColorTransform(this.linearSustain, this.linearSustain, this.linearSustain, 1,
constantRelease, constantRelease, constantRelease, 0);
}
private function OnAddedToStage(event:Event) {
this.lastFrame = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
}
public function DrawFrame(container:MasterContainer):void {
this.lastFrame.draw(container);
this.bitmapData = lastFrame;
this.lastFrame = this.bitmapData
this.lastFrame.colorTransform(getBounds(this), this.sustain);
}
...and finally the results #60fps when using an indigo sine wave of shifting phase as the input for the CustomWave:

How can I load a Papervision/Flex application (SWF) as a material on a Papervision plane?

I am trying to build a portfolio application similar to the used by Whitevoid. I am using Flex 4 and Papervision3D 2. I have everything working except for one issue. When I try to load an external SWF as a material on one of the planes, I can see any native Flex or Flash components in their correct positions, but the papervision objects are not being rendered properly. It looks like the viewport is not being set in the nested swf. I have posted my code for loading the swf below.
private function loadMovie(path:String=""):void
{
loader = new Loader();
request = new URLRequest(path);
loader.contentLoaderInfo.addEventListener(Event.INIT, addMaterial);
loader.load(request);
}
private function addMaterial(e:Event):void
{
movie = new MovieClip();
movie.addChild(e.target.content);
var width:Number = 0;
var height:Number = 0;
width = loader.contentLoaderInfo.width;
height = loader.contentLoaderInfo.height;
//calculate the aspect ratio of the swf
var matAR:Number = width/height;
if (matAR > aspectRatio)
{
plane.scaleY = aspectRatio / matAR;
}
else if (matAR < aspectRatio)
{
plane.scaleX = matAR / aspectRatio;
}
var mat:MovieMaterial = new MovieMaterial(movie, false, true, false, new Rectangle(0, 0, width, height));
mat.interactive = true;
mat.smooth = true;
plane.material = mat;
}
Below I have posted two pictures. The first is a shot of the application running by itself. The second is the application as a MovieMaterial on a Plane. You can see how the button created as a spark object in the mxml stays in the correct position, but papervision sphere (which is rotating) is in the wrong location. Is there something I am missing here?
Man. I haven't seen that site in a while. Still one of the cooler PV projects...
What do you mean by:
I cannot properly see the scene rendered in Papervision
You say you can see the components in their appropriate positions, as in: you have a plane with what looks like the intended file loading up? But I'm guessing that you can't interact with it.
As far as I know, and I've spent a reasonable amount of time trying to make something similar work, the MovieMaterial (which I assume you're using) draws a Bitmap of whatever contents exist in your MovieClip, and if you set it to animated=true, then it will render out a series of bitmaps - equating animation. What it's not doing, is displaying an actual MovieClip (or SWF) on the plane. So you may see your components, but this is how:
MovieMaterial.as line 137
// ______________________________________________________________________ CREATE BITMAP
/**
*
* #param asset
* #return
*/
protected function createBitmapFromSprite( asset:DisplayObject ):BitmapData
{
// Set the new movie reference
movie = asset;
// initialize the bitmap since it's new
initBitmap( movie );
// Draw
drawBitmap();
// Call super.createBitmap to centralize the bitmap specific code.
// Here only MovieClip specific code, all bitmap code (maxUVs, AUTO_MIP_MAP, correctBitmap) in BitmapMaterial.
bitmap = super.createBitmap( bitmap );
return bitmap;
}
Note in the WhiteVoid you never actually interact with a movie until it "lands" = he's very likely swapping in a Movie on top of the bitmap textured plane.
The part that you are interacting with is probably another plane that holds the "button" that simply becomes visible on mouseover.
I think PV1.0 had access to real swfs as a material but this changed in 2.0. Sadly. Hopefully Molehill will.
cheers