Why scrollRect only effects in the next frame? - actionscript-3

running this code
var sp:Sprite = new Sprite();
sp.graphics.beginFill(0xff);
sp.graphics.drawRect(0,0,400,400);
sp.graphics.endFill();
sp.scrollRect = new Rectangle(0,0,350,350);
addChild(sp);
var count:int = 0;
var f:Function = function(...args):void{
trace(count++,sp.width,sp.height);
if(count>5){
removeEventListener(Event.ENTER_FRAME,f);
}
}
addEventListener(Event.ENTER_FRAME,f);
get
0 400 400
1 350 350
2 350 350
3 350 350
4 350 350
5 350 350
is there anyway i can made the scrollRect work at the first frame?

I just checked, and its probably because of some internal rendering stuff in Flash Player (it actually takes two frames to get the real dimensions, the current one, and the one after). Visually the rectangle doesn't change sizes, so it's only the values of the object's bounds that are updated two frames later (you can easily prove this by setting a very low framerate, like 1 FPS).
One workaround is to force a "hard" rendering pass by drawing the stage to a 1x1 BitmapData... this will effectively update the entire display list, and those values should be correct immediately after the draw() call. So your code would look like this:
var sp:Sprite = new Sprite();
sp.graphics.beginFill(0xff);
sp.graphics.drawRect(0,0,400,400);
sp.graphics.endFill();
sp.scrollRect = new Rectangle(0,0,350,350);
addChild(sp);
var count:int = 0;
function f(event:Event = null):void{
new BitmapData(1,1).draw(stage);
trace(count++,sp.width,sp.height);
if(count>5){
removeEventListener(Event.ENTER_FRAME,f);
}
}
addEventListener(Event.ENTER_FRAME,f);
f();
(Note that I changed the way you declared the function, as this is the preferred way among most (if not all) Flash developers.)
You could do the draw call only once after adding the object to the stage, but I don't know how safe it would be if the object changes.

Related

AS3 display object height not reported correctly

This should be simple. What am I missing?
Create a Sprite (container), put it on the displaylist, add a new Sprite (rect) to container.
Change height of the child (rect).
Height values of both parent (container) and child (rect) are not reported correctly although rendered correctly.
Thanks for your assistance.
var container:Sprite = new Sprite();
addChild(container);
[Embed(source = "../lib/rectangle.swf")] // height is 100
var Rect:Class;
var rect:Sprite = new Rect();
trace(rect.height); // 100 is correct before placement in container
container.addChild(rect);
trace(rect.height); // 100 is correct after placement in container
trace(container.height); // 0 is not correct; should be 100
rect.height = rect.height + 100; // renders correctly at new height
trace(rect.height); // 100 is not correct; should be 200
trace(container.height); // 0 is not correct; should be 200
Apparently your container is not on the stage display list. If a measured DisplayObject is not on the display list (anywhere), its dimensional properties are not properly recalculated, when its own display list is altered. This is apparently to save time for Flash engine to process building of a complex MovieClip frame or any other complex container faster, and recalculation is only done if the container is placed to stage. The resolution is to place the object to be measured to stage (anywhere, anyhow), gather its properties then remove it from stage. An example:
trace(this.stage); // [object Stage] - to make sure we can access stage in here
var sp:Sprite=new Sprite();
var b:Bitmap=new Bitmap(new BitmapData(100,100));
trace(sp.width); // should return 0
trace(sp.height); // 0 also
sp.addChild(b);
trace(sp.height); // 0 again
trace(b.height); // should return 100, as the bitmap data is specified
// as well as in your case, the class's width and height are precalculated
addChild(sp);
trace(sp.height); // returns 100, as expected
removeChild(sp);
trace(sp.height); // 100, stored
sp.removeChild(b);
trace(sp.height); // should also return 100, while there's no content in the sprite
There is also another possibility, width of an object off stage can be in the negative, the solution is again the same - put object on stage, get width, remove object from stage.

AS3 Having trouble understanding Rectangle Intersection

I'm trying to get Rectangle.intersection to provide me with the rectangle of the intersection area of 2 overlapping shapes but not having much success.
The code below is simply 2 shapes the same size. The top most shape is draggable.
When the drag is stopped I perform a bottomRect.intersection(topRect) call but this always returns the full size of the rect, not the intersection size.
(the code can be copied and pasted into a new ActionScript file on the first frame and run.)
Does anyone have an idea where I'm going wrong?
Thanks
import flash.geom.Rectangle;
import flash.display.Sprite;
var bottomSprite:Sprite = new Sprite();
addChild(bottomSprite);
var bottomRect:Shape = new Shape;
bottomRect.graphics.beginFill(0xFF0000);
bottomRect.graphics.drawRect(0, 0, 320,480);
bottomRect.graphics.endFill();
bottomSprite.addChild(bottomRect);
var topSprite:Sprite = new Sprite();
addChild(topSprite);
var topRect:Shape = new Shape;
topRect.graphics.beginFill(0x000033);
topRect.graphics.drawRect(0, 0, 320,480);
topRect.graphics.endFill();
topSprite.addChild(topRect);
var bottomBoundsRect:Rectangle = stage.getBounds(bottomSprite);
trace("START: bottomBoundsRect ", bottomBoundsRect);
var topBoundsRect:Rectangle = stage.getBounds(topSprite);
trace("START: topBoundsRect ", topBoundsRect);
topSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
topSprite.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
function mouseDownHandler(evt:MouseEvent):void{
topSprite.startDrag();
}
function mouseUpHandler(evt:MouseEvent):void{
topSprite.stopDrag();
topBoundsRect = stage.getBounds(topSprite);
trace("INTERSECTION RECT", bottomBoundsRect.intersection(topBoundsRect));
}
The problem is because of wrong toIntersect property that you pass:
topBoundsRect = stage.getBounds(topSprite);
trace("INTERSECTION RECT", bottomBoundsRect.intersection(topBoundsRect));
What you do is that you get the bounds of the topSprite agains the Stage. If you trace it, it will give you something like this:
(x=-62, y=-41, w=382, h=521)
So you have a bounds that start at 0,0 and have bigger width/height, because you move the topSprite - here I've moved it 62 pixels to the right (382 - 320 [width]), and 41 pixels down (521 - 480 [height]).
The actual intersection of this rectangle against the bottom one, is exactly the size of the bottom one.
What you should do is something similar to this:
// somehow get the rectangle of the bottom sprite
var br:Rectangle = new Rectangle(bottomSprite.x, bottomSprite.y, bottomSprite.width, bottomSprite.height);
// somehow get the rectangle of the top sprite
var tr:Rectangle = new Rectangle(topSprite.x, topSprite.y, topSprite.width, topSprite.height);
trace (br.intersection(tr)); // intersect them
There are few ways to get the bounds, but this is also working and shows the idea.
Hope that helps! :)

Which one executes faster: hitTestObject or Point.distance?

I am trying to optimize my collision detection alghorithm written in AS3.
I am wondering if there is any improvements in performance if I use
Point.distance(pointObject1, pointObject2);
between two objects instead of using
object1.hitTestObject(object2);
My objects are all more or less convex so the border doesn't really matter.
Point.distance is much more faster (4 and more times!) if your test objects are complicated containers with several children inside. If you use simple Sprite's it'll be only near 25% difference in functions execution time.
It's so, because Point.distance is just counting a hypotenuse from Pythagoras' theorem. So, we've got only 2 subtractions, 1 addition and 3 involutions to count. Many modern processors have involution instruction, so it's fast. If we use hitTest, there are much more actions to be performed. And the number of these actions will grow with the complexity of hitTest'ing Sprite (because it's harder to count it's bounds).
I've just made some tests. The result confirmed that I was right.
var ar:Vector.<Sprite> = Vector.<Sprite>([]); //Sprites for hitTest
for(var i:int = 0; i < 100000; i++) {
var sp:Sprite = new Sprite(); //!The results will be other, is case of use a huge container with come objects here!
sp.graphics.drawCircle(0, 0, randomIntBetween(1, 200)); //add some shapes
sp.graphics.drawRect(0, 0, randomIntBetween(1, 200), randomIntBetween(1, 200));
sp.x = randomIntBetween(-800, 800);
sp.y = randomIntBetween(-800, 600);
sp.rotation = randomIntBetween(-360, 360); //rotate and scale in random way
sp.scaleX = sp.scaleY = Math.random();
ar.push(sp);
}
var tim:Number = new Date().time;
for each(var spr:Sprite in ar) {
ar[0].hitTestObject(spr);
}
tim = new Date().time - tim;
trace(tim);
var pn:Vector.<Point> = Vector.<Point>([]); //Points for Point.distance
for(i = 0; i < 100000; i++) {
var point:Point = new Point(randomIntBetween(-800, 800), randomIntBetween(-800, 800));
pn.push(point);
}
tim = new Date().time;
for each(var pnt:Point in pn) {
Point.distance(pn[0], pnt);
}
tim = new Date().time - tim;
trace(tim);
Actually, you can't compare those two with each other. If all your objects are single-pixel bitmaps the distance test would work fine. But I imagine that is not the case.
hitTestObject essentially checks the bounding rectangles of the objects, so it's pretty fast.
You could always check the distance to see if they're close enough before doing a pixel level check, because it's more expensive and you don't want to do it too much.
The distance check is great for when you set up circular bounds around an object and determining that two such circles do not intersect. If they do intersect then you have to do another check with hitTestObject to ensure the two object shapes actually overlap. So essentially you would use both, the distance check as a first pass check and hitTestObject as a follow up for accuracy.

AS 3 simple ease

How can I move an object and be able to physically see it when it is moving? Not just disappear and appear on a different location like it would be using the following code.
buttonL2_btn.addEventListener(MouseEvent.CLICK, left);
function left(event:Event):void{
box_mc.x =241.5;
}
This is going to move myObject to any location specified, but again I want to be able to see it when moving.
In your example you are just setting it's X position when some button is pressed, when you need to change X into an EnterFrame event, like this:
this.addEventListener(Event.ENTER_FRAME, move);
function move(event:Event):void{
box_mc.x -= 5
}
Your box_mc should move left 5 pixels accordingly with your framerate.
You can use a easing library to that easily. I strongly recommend TweenMax.
Okay I am getting a bit sick of people constantly suggesting some tweening engine. Sure they rock, but it won't help the OP to understand what he is doing.
Kircho to move an object with a really easy tween I suggest the following code in an onEnterFrame event for your object to move:
addEventListener(Event.ENTER_FRAME, onEnterFrame);
var xGoal:Number = 100; //:: The target X destination for your object
var yGoal:Number = 100; //:: The target Y destination for your object
var smothness:Number = 10; //:: Smoothness factor for movement. The lower the value the faster the movement.
function onEnterFrame(e:Event):void
{
box_mc.x += (xGoal - box_mc.x) / smothness;
box_mc.y += (yGoal - box_mc.y) / smothness;
}
Will move/ease your box object to the desired location with a set smoothness.
You can install any of the 437 available tweening engines
or you can add a few lines of code
set up a variable that holds the destination value
var dest:Number = 241.5; // this is what gets updated on mouse click
on enterframe event for box:
function onBoxEnterFrame(e:MouseEvent):void{
if (dest != box_mc.x){
var easeNum:Number = 0.4 // between 0 and 1, the higher the number, the slower the transition
box_mc.x = box_mc.x * easeNum + dest * (1-easeNum);
}
}
you can add a few more lines to snap the position when it is close (less than 0.1 difference) or use a more linear change where you adjust incrementally like box_mc.x += 5; until it matches the dest number

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.