How to determine whether a given object is a mask - actionscript-3

Apparently, in Adobe's wisdom, both the object being mask, and the masking object contain a "mask" property. This leads to a cyclical reference that prevents determining which is the actual mask and which is the masked.
For example...
var clip:MovieClip = new MovieClip();
clip.name = "clip";
addChild(clip);
var boundary:Shape = new Shape();
boundary.name = "boundary";
clip.addChild(boundary);
clip.mask = boundary;
trace(clip.mask.name); // outputs "boundary"
trace(clip.mask.mask.name); // outputs "clip"
I've iterated through the properties of both clip and boundary, and there doesn't seem to be anything unique that sets them apart. My first thought was to force a removal of the superfluous "mask" reference in boundary, however, that also sets the mask property in clip to null, thereby removing the mask.
My second thought was to check the parent relationship of a mask. If the parent is the same as the object's mask, then the object in question is itself the mask.
var a:Array = [clip, boundary];
for each (var item in a) {
if (item.mask == item.parent) {
trace(item.name + " is a mask");
}
}
// outputs "boundary is a mask"
Seems to work, and after checking the API reference on masks, it's clear that when caching, a mask will need to be a child of the masked, however... it's also valid to have a mask at the same depth as the masked (I do this from time to time when a mask needs to not travel with the masked content).
For example...
MainTimeline ¬
0: clip ¬
0: boundary
... can also be laid out as ...
MainTimeline ¬
0: clip ¬
1: boundary
So, there's the conundrum. Any ideas on how to resolve this?

The "best" hack I've found so far is to run hitTestPoint on the objects (after making sure they have something to hit under the target). Masks do not appear to ever return true for a full pixel hit test. This seems to work in most basic situations that I've tested:
public function isMask(displayObject:DisplayObject):Boolean {
// Make sure the display object is a Class which has Graphics available,
// and is part of a mask / maskee pair.
if ((displayObject is Shape || displayObject is Sprite) && displayObject.mask) {
// Add a circle at the target object's origin.
displayObject['graphics'].beginFill(0);
displayObject['graphics'].drawCircle(0, 0, 10);
var origin:Point = displayObject.localToGlobal(new Point());
var maskLocal:Point = displayObject.mask.globalToLocal(origin);
// Add a circle at the same relative position on the "mask".
displayObject.mask['graphics'].beginFill(0);
displayObject.mask['graphics'].drawCircle(maskLocal.x, maskLocal.y, 10);
// No matter which is the actual mask, one circle will reveal the other,
// so hit testing the origin point should return true.
// However, it seems to return false if the object is actually a mask.
var hit:Boolean = displayObject.hitTestPoint(origin.x, origin.y, true);
displayObject['graphics'].clear();
displayObject.mask['graphics'].clear();
// Return true if the hit test failed.
return !hit;
} else {
return false;
}
}
Obviously you'd want to cache the graphics in case the objects already have some, and it could do with something more elegant than casting as Sprite so that it can handle Shapes, but it's a start.
Edit: Accessing ['graphics'] lets this accept Shapes, but obviously isn't super efficient. I'm not sure what the best method would be, short of adding an interface.

Great question, haven't run into this before. I wasn't aware of the cyclical reference.
If your masks are exclusively masks, I would suggest just incorporating that into your naming convention. For example calling it clipMask as opposed to boundary.
As noted in the comments, in the situation where the mask is on the same display list, you could use getChildIndex() to compare their position on the display list of the parent.
Typically in that situation I'll have the mask layered over the other display object. This is not enforced obviously, and I don't believe that it has any effect on the result of the mask visually. But it's easier to maintain for a large group than a naming convention.
Still not ideal obviously.

Related

Actionscript 3 - alternatives to .hitTestObject or position constraints

I need to detect when MC2 is over MC1 that it is inside MC1's borders.
to do this I would usually use 4 separate if x y constraints,
and unfortunately .hitTestObject in my creations also seem to need 4 separate if x y + - constraints.
Does anyone know a more simplistic way to achieve this.
or is x y + - constraints still the only way to do this?
Thank you in advance.
The final solution for your problem to detect hit of two shapes, is to use bitmapData.hitTest(). you can detect hit between any shapes and not only Rectangles. for that, you have to draw both of your shapes on bitmapData like line belo:
var shape1Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
var shape2Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
shape1Bitmap.hitTest(new Point(),shape2Bitmap):Boolean;******
to continue usint BitmapData.hitTest(), folow the orders here : https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#hitTest()
http://dougmccune.com/blog/2007/02/03/using-hittestpoint-or-hittest-on-transparent-png-images/
It is a little complicated to add the bitmapData.hitTest() samples here. if any further questions left, please let me know to explain.
Good luck
I don't know of a built in way to do this, but it's easy enough using hitTestPoint with each corner of the square:
function isSquareInsideObject(square:DisplayObject, obj:DisplayObject):Boolean {
if(!obj.hitTestPoint(square.x, square.y, true)) return false;
if(!obj.hitTestPoint(square.x + square.width, square.y, true)) return false;
if(!obj.hitTestPoint(square.x + square.width, square.y + square.height, true)) return false;
if(!obj.hitTestPoint(square.x, square.y + square.height, true)) return false;
return true;
}
For more complex shapes than a square, you'd have to add more points to be accurate and it becomes a less elegant and less performant solution then.
You need that shape argument (third parameter for hitTestPoint) set to true if you want to test against the actual circle shape instead of the rectangular bounding box of the circle. If your circle is a bitmap (and not a shape), then I'd suggest putting a circular mask on the object to achieve the same result.
If your square isn't anchored at 0,0, or you don't mind the extra (small) performance hit, you could also use var bounds:Rectangle = square.getBounds(this) and then use the convenience properties of the rectangle object (bounds.bottomLeft, bottomRight, topLeft, topRight)

Moving 3D charracter - I don't want any physics, expected of collisions, and gravity

I am working on a game. I constructed my player as here: (I am using a gravity on my world)
private ArrayMap<String, GameObject.Constructor> constructors = new ArrayMap<String, GameObject.Constructor>(String.class, GameObject.Constructor.class);
private ArrayList<GameObject> instances = new ArrayList<GameObject>();
assets.load("hand.obj", Model.class);
...
model = assets.get("hand.obj", Model.class);
constructors.put("hand", new GameObject.Constructor(model, new btBoxShape(new Vector3(2.5f, 7.5f, 2.5f)), 1f));
...
hand = constructors.get("hand").construct(); // that construct method returns me model, shape and constructions.. the GameObject extends ModelInstance, so i can use it like a modelinstance
hand.transform.setToTranslation(x, y, z);
hand.body.proceedToTransform(hand.transform);
hand.body.setUserValue(instances.size());
hand.body.setCollisionFlags(hand.body.getCollisionFlags()| btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
world.addRigidBody(hand.body);
hand.body.setContactCallbackFlag(OBJECT_FLAG);
hand.body.setContactCallbackFilter(OBJECT_FLAG);
Then, in render method I am moving it:
if (!hand.body.isActive()) hand.body.activate();
if (Gdx.input.isKeyPressed(Keys.W)){
hand.body.translate(new Vector3(0,0,-1));
}
else if (Gdx.input.isKeyPressed(Keys.S)) {
hand.body.translate(new Vector3(0,0,+1));
}
That's nice! The moving now works good, when I am moving at the flat ground. Whenever there is an object before me, it is not as expected. Because my player shape is biger than
object shape (which is 2.5f, 2.5f, 2.5f), it kind of falls on it. So I would like to set the rotation to be still the same, so the object will not be rotating (so it will not "fall" on the object before). And so I tried to do it, and I failed. Because there are functions like rotate, and I want to something like setRotation
. And so, there is a setToRotation, but you can not pass there a Quaternion.
I need help. I tried to use a btKinematicCharacterController but it was bad. The ghostObject every time falled through object, but the objects got a collision from him.
and so I want to create a player movment, like in games like Wow, minecraft, and so on.
I looked at the btKinematicCharacterController again. The reason why my ghostobject falled through the ground was. Generally, I don't know the reason: D probably I was using another broadphase for ghost, that for world. This line fixes it: characterController.setUseGhostSweepTest(false);
and I am getting another problem, when I am walking on my ground (a lot of objects), the character is getting to lesser Y position. I don't know why.
Here is my construction:
btPairCachingGhostObject ghostObject;
btConvexShape ghostShape;
btKinematicCharacterController characterController;
Vector3 characterDirection = new Vector3();
Vector3 walkDirection = new Vector3();
...
ghostObject = new btPairCachingGhostObject();
ghostObject.setWorldTransform(hand.transform);
ghostShape = new btCapsuleShape(5f, 0.5f);
ghostObject.setCollisionShape(ghostShape);
ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT);
characterController = new btKinematicCharacterController(ghostObject, ghostShape, .00001f);
// And add it to the physics world
characterController.setUseGhostSweepTest(false);
world.addCollisionObject(ghostObject,
(short)btBroadphaseProxy.CollisionFilterGroups.CharacterFilter,
(short)(btBroadphaseProxy.CollisionFilterGroups.StaticFilter | btBroadphaseProxy.CollisionFilterGroups.DefaultFilter));
world.addAction(characterController);
... (in render - moving)
if (!load)
{
if (Gdx.input.isKeyPressed(Keys.LEFT)) {
hand.transform.rotate(0, 1, 0, 5f);
ghostObject.setWorldTransform(hand.transform);
}
if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
hand.transform.rotate(0, 1, 0, -5f);
ghostObject.setWorldTransform(hand.transform);
}
// Fetch which direction the character is facing now
characterDirection.set(-1,0,0).rot(hand.transform).nor();
// Set the walking direction accordingly (either forward or backward)
walkDirection.set(0,0,0);
if (Gdx.input.isKeyPressed(Keys.UP))
walkDirection.add(characterDirection);
if (Gdx.input.isKeyPressed(Keys.DOWN))
walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z);
walkDirection.scl(4f * Gdx.graphics.getDeltaTime());
// And update the character controller
characterController.setWalkDirection(walkDirection);
// And fetch the new transformation of the character (this will make the model be rendered correctly)
}
world.stepSimulation(delta, 5, 1f/60f);
if (!load)
ghostObject.getWorldTransform(hand.transform);
How to fix this?
I set up the debugDrawer, so i was able to see the shapes of the bullet objects.. and my problem was that: the ghostObject(charController) was pushing my objects down.. Although my objects were static. So i set the mass of the objects to 0 and problem is fixed. But I still dont know, how it could push static objects. But i dont care. :)
EDIT: i will accept this answer in 2 hours, because now i cant.

need AS3 movieclip parent child related functions

hi i've taken on a new coding technique and its leaving me a little stranded, alot of concepts ive previously applied now take new syntax and sometimes create unforseen problems.
OK, so i make multiplayer flash games. In order to cut down on clutter i no longer use multiple class.as files, instead, i have my stage, and one library object called triggers, which i place just out of sight in the upper left of the stage. i then make a class.as file for this one movieclip object, and from there i instantiate everything else in my program - so far a login splash-screen movieclip, a game-window movieclip, a lobby movieclip, and finally the game-instance movieclip. these come in and out of .visible appropriately, and when not in use they are stored at off screen x and y values, they progress sequentially based on userinput. additionally i have public arrays which store importantMessages[], myplayerarray[], myArrowsarray[], myenemyarray[]
now my biggest issue at the moment is i'll recieve in from the server the variables i need to build a new arrow and monster unit -- so ill do like movieclip orc, with orc.speed, orc.xstartlocation orc.hp and so on, and ill have a similar arrow movieclip with arrow.speed, arrow.gravity, and so on. both of these movieclips, with added properties, are then pushed onto the appropriate public arrays, and not added to the stage, but instead, are added to the stage.add(gamewindow:Movieclip) (the reasoning behind this is so that later if i want to move everything on the stage at once, they are already oriented on a single cohesive movieclip, then i can just move this movieclip)
ok now onto the problem stuff, when i have two gamewindow.movieclips collide, like an arrow versus an orc (lets say arrow13 hittest orc42 == true) i remove the arrow movieclip object from the gamewindow:movieclip and splice it from its myarrowarray, however, even though the graphic dissapears, it continues to move its current trajectory and hit everything else on its way. I believe the reasoning behind this is because during the creation of the movieclip with its variables, i include an eventlistener on enterframe, i think its removing the clip but not the event listener (see very bottom for instantiated arrow Movieclip class)
so this brings me to my concise question:
QUESTION ONE:
is it possible to not only gamewindow.removeChild(arrow13) but also gamewindow.removeChild(arrow13[and all variables and eventlisteners at once])
QUESTIONTWO:
my second question is a bit easier: since switching to movieclip() instead of object() ive been using brute force, what would be a 1 line piece of code to do all of the following:
var newarrow:MovieClip = new playerarrow();
newarrow.theowner = username
newarrow.thespeed = speed
newarrow.thegravity = gravity
newarrow.thepower = power
newarrow.arrownumber = arrowid
and my third question goes back to my splashscreen movieclips idea, im having trouble playing around with thier z-values
basically when i call the importantmessage() its creates a new movieclip in the lower left, which alpha fades to 0 and it removes itself, however i have a problem where my new movieclip windows will overwrite these messages since they were added a split second after, the example in my program is i will have 2 messages spit out stage.add "attempting to connect to server" "connected" then the next major function is invoked and it instantiates the loginsplash:movieclip = new loginwindow -- i've tried taking this new stage.addchild(loginsplash) and do setChildIndex(loginsplash, 0) as well as -1 and 1. both 1's are out of bounds and 0 produces : The supplied DisplayObject must be a child of the caller.
QUESTION THREE:
so if i have gamemsg z = 0 gamemsg2 z = 1 and loginsplash z = (0?), how can i get the game messages to always lay on top ( i think its more of a referenceing problem then anything else
========================================
connection.addMessageHandler("newarrow",
function(m:Message, username, speed, gravity, power, arrowid)
{
var newarrow:MovieClip = new playerarrow();
newarrow.theowner = username
newarrow.thespeed = speed
newarrow.thegravity = gravity
newarrow.thepower = power
newarrow.arrownumber = arrowid
for each(var p in myplayerarray)
if (p.mpname == username){
newarrow.x = p.theanimation.x + 100
newarrow.y = p.theanimation.y + 100
}
myarrowarray.push(newarrow)
gw.addChild(newarrow)
newarrow.addEventListener(Event.ENTER_FRAME, arrowenterframe)
function arrowenterframe(e:Event){
newarrow.thegravity = 0 //+=.6
speed = 5
newarrow.x = newarrow.x+speed
newarrow.y = newarrow.y + newarrow.thegravity
//ROTATE FUNCTION
newarrow.rotation = Math.atan(newarrow.thegravity / speed ) /(Math.PI/180)
if (speed < 0) {newarrow.rotation +=180}
for each(var d in myenemyarray){
if (newarrow.hitTestObject(d.orcicon)){
connection.send("arrowhitmonster", newarrow.arrownumber, d.monsternumber)
trace("hitting monster")
}
}
if(newarrow.hitTestObject(gw.theground)){
}
}
})
Q1 ... is possible, but not with a single command. I would recommend you use the casalib (which I tend to recommend often) If you use CasaMovieClip instead of MovieClip, it extends it by adding some additional functions that deal with these issues like removeEventListeners() and removeAllChildrenAndDestroy()(which removes listeners). With the event listeners, just be aware that it destroys only events that this object is listening to, and not the listeners that other objects have to this mc. Instead of trying to convert assets to use CasaMovieClip, you could also just look at the code and implement it over top of your classes/MCs
Another alternative to dealing with event listeners is to switch to using signals by Robert Penner. It's a much more elegant way of working with event notifications, and by the sounds of your setup (relying on few classes with big reach), it might work better when all communication between objects is happening through a single channel rather than being handled by every object individually.
Q2 - you could create a factory function.
public function createMC($mc:MovieClip,$owner:String,$speed:int,...etc):MovieClip{
$mc.theowner = $owner;
// etc.
return $mc;
}
or
public function createMC($mc:MovieClip,$properties:Object):MovieClip{
$mc.theowner = $owner;
for (var $property:String in $properties)
if ($mc.hasOwnProperty($property))
$mc[$property] = $properties[$property];
return $mc;
}
where you call the function like this var newarrow:MovieClip = createMC(new playerarrow(), { theowner:username});
but I'm not sure why you would want to really
Q3 - the way I deal with these is set up movie clip holders. The critical messages will always be on top, the game menu bellow, the game background always on bottom. In the main view I would have a gameholder MC and above the menu and above that the criticalMessage holder, any objects that are added and removed are only within the appropriate holder.

How to detect if the area was 100% painted in as3

I´m making a game that simulates an industry of pan, and one of the process is Painting.
What I want to do is to let the player paint the pan, but i don´t want it to be easy using FILL, i want that the player paint the pan with an brush and then the game detects if all the area was painted and let the player advance.
For the painting i intend to use that library: http://www.nocircleno.com/graffiti/
But i have no idea how to detect if all the area was painted. Can someone show me some way of doing that?
One of the ways would be - you make a shielding BitmapData that has transparency and is opaque in place which you need your player to paint. (Color it as needed, but make sure the color is fully opaque). Then gather histogram() then query alpha vector for 255th value, this will be the initial value for zero percent filled. These range from 0-255, so you can't use 100 or any other fixed value. Then, while the player is painting, you draw the brush over that BitmapData with blendMode parameter set to BlendMode.ERASE, this will net your BitmapData to gain transparency where the brush was drawn. After your player finishes drawing by any means (say, the paint is used up), you run another histogram() over the BitmapData, and query the 255th value of alpha channel vector. 0 means the bitmap is fully transparent (or at least, only a small amount of pixels is left opaque), thus you can count a zero as 100% fill, for anything greater use the proportion.
var bd:BitmapData=new BitmapData(w,h,true,0x0); // fully transparent initial bitmap
bd.draw(yourPaintBase); // a shape that designates area to be painted. Must be fully opaque
var bm:Bitmap=new Bitmap(bd);
// position it as needed, so the area which should be painted is aligned to wherever you need
addChild(bm);
addEventListener(Event.ENTER_FRAME,doPaint);
var baseValue:int=bd.histogram()[3][255]; // Vector #3 contains alpha, #255 contains
// percentage of those pixels that have alpha of 255 = fully opaque
function doPaint(e:Event):void {
if (!areWePainting) return;
var sh:Shape=getBrush(); // shuold return an existing Shape object reference for performance
sh.x=e.localX;
sh.y=e.localY; // we are drawing where the mouse is
bd.draw(sh,null,null,BlendMode.ERASE);
decreasePaint(); // we have used some paint
if (noMorePaint()) {
e.target.removeEventListener(Event.ENTER_FRAME,doPaint);
var endValue:int=Math.floor(100*(1-bd.histogram()[3][255]/baseValue));
// aligning to percentage. This is the value you seek
reportFilledPercentage(endValue);
}
}
You can iterate over the pixels on your BitmapData and use getPixel() to check if the color of all those pixels is not white. If a white one is found, the image is not fully painted.
Something like this:
function containsWhite(bitmapData:BitmapData):Boolean
{
for(var c:int = 0; c < bitmapData.width; c++)
{
for(var r:int = 0; r < bitmapData.height; r++)
{
// Check if pixel is white.
if(bitmapData.getPixel(c, r) >= 0xFFFFFF)
{
return true;
}
}
}
return false;
}
Your essentially dealing with a collision detection problem. From looking at their API you could try something like a for loop with getColorAtPoint and try to determine they have drawn at each pixel.
If all else fails look into collision between the objects the library generates using the .hitTestObject method of an object.
See this: http://sierakowski.eu/list-of-tips/39-collision-detection-methods-hittest-and-hittestobject-alternatives.html
And this to see how someone handles collision with pixels: http://www.emanueleferonato.com/2010/08/05/worms-like-destructible-terrain-in-flash-part-2/

Is there a way to get the actual bounding box of a glyph in ActionScript?

I'm learning ActionScript/Flash. I love to play with text, and have done a lot of that kind of thing with the superb Java2D API.
One of the things I like to know is "where, exactly, are you drawing that glyph?" The TextField class provides the methods getBounds and getCharBoundaries, but these methods return rectangles that extend far beyond the actual bounds of the whole text object or the individual character, respectively.
var b:Sprite = new Sprite();
b.graphics.lineStyle(1,0xFF0000);
var r:Rectangle = text.getCharBoundaries(4);
r.offset(text.x, text.y);
b.graphics.drawRect(r.x,r.y,r.width,r.height);
addChild(b);
b = new Sprite();
b.graphics.lineStyle(1,0x00FF00);
r = text.getBounds(this);
b.graphics.drawRect(r.x,r.y,r.width,r.height);
addChild(b);
Is there any way to get more precise information about the actual visual bounds of text glyphs in ActionScript?
Richard is on the right track, but BitmapData.getColorBounds() is much faster and accurate... I've used it a couple of times, and optimized for your specific needs its not as slow as one might think.
Cory's suggestion of using flash.text.engine is probably the "correct" way to go, but I warn you that flash.text.engine is VERY (very!) hard to use compared to TextField.
Not reasonably possible in Flash 9 -- Richard's answer is a clever work-around, though probably completely unsuitable for production code (as he mentions) :)
If you have access to Flash 10, check out the new text engine classes, particularly TextLine.
I'm afraid all the methods that are available on TextField are supposed to do what you have already found them to do. Unless performance is key in your application (i.e. unless you intend to do this very often) maybe one option would be to draw the text field to a BitmapData, and find the topmost, leftmost, et c colored pixels within the bounding box retrieved by getCharBoundaries()?
var i : int;
var rect : Rectangle;
var top_left : Point;
var btm_right : Point;
var bmp : BitmapData = new BitmapData(tf.width, tf.height, false, 0xffffff);
bmp.draw(tf);
rect = tf.getCharBoundaries(4);
top_left = new Point(Infinity, Infinity);
btm_right = new Point(-Infinity, -Infinity);
for (i=rect.x; i<rect.right; i++) {
var j : int;
for (j=rect.y; j<rect.bottom; j++) {
var px : uint = bmp.getPixel(i, j);
// Check if pixel is black, i.e. belongs to glyph, and if so, whether it
// extends the previous bounds
if (px == 0) {
top_left.x = Math.min(top_left.x, i);
top_left.y = Math.min(top_left.y, j);
btm_right.x = Math.max(btm_right.x, i);
btm_right.y = Math.max(btm_right.y, j);
}
}
}
var actualRect : Rectangle = new Rectangle(top_left.x, top_left.y);
actualRect.width = btm_right.x - top_left.x;
actualRect.height = btm_right.y - top_left.y;
This code should loop through all the pixels that were deemed part of the glyph rectangle by getCharBoundaries(). If a pixel is not black, it gets discarded. If black, the code checks whether the pixels extends further up, down, right or left than any pixel that has previuosly been checked in the loop.
Obviously, this is not optimal code, with nested loops and unnecessary point objects. Hopefully though, the code is readable enough, and you are able to make out the parts that can most easily be optimized.
You might also want to introduce some threshold value instead of ignoring any pixel that is not pitch black.