Coordinate overflow in AS3 when scrolling container object? - actionscript-3

Scrolling graph. Currently I show a real-time graph by drawing each piece of data as it is generated. To scroll, I simply move the container object left to compensate and erase all the data left of the bounds.
Simple, fast, surprisingly memory efficient...but is there a concern of the coordinates overflowing as I keep adding contents to the right and scrolling the container left? Is there a limit on the x coordinate of an (empty) object? I'm afraid if I keep scrolling the container left indefinitely and writing to ever increasing x coordinates the program would misbehave eventually. Would this occur? If so, at what max coordinates? Or does AS3 handle this automatically?

I did a test, where I keep drawing an item to a container making it bigger and bigger, and moving it to the left. It stopped getting bigger at 105,000,000 pixels. After that the width reported 0 and the x property reported -107,374,182.4 and wouldn't move beyond that.
If you have bitmap data though, the FP10 limit is 8,191 pixels in width or height, and the total number of pixels cannot exceed 16,777,215 pixels.
Here was my code used to test:
var b:Sprite = new Sprite();
addChild(b);
b.x = stage.stageWidth * .5;
var t:Timer = new Timer(100);
t.addEventListener(TimerEvent.TIMER,tick);
t.start();
var moveAmt:Number = 50;
function tick(e:Event):void {
b.x -= moveAmount;
b.graphics.beginFill(Math.random() * 0xFFFFFF);
b.graphics.drawRect(b.width,0,moveAmount,stage.stageHeight);
b.graphics.endFill();
trace(b.width + " : " + b.x);
}

Related

Increase width without moving image

I'm developing a drawing app, and I ran into a problem, and a possible solution would be to be able to resize horizontally (increase or decrease the width) of an image without altering it's pivot, but ending with a "directional resize", it means, that if I start dragging the right resize anchor, the image starts increasing it's width to the right, instead of always taking into consideration the pivot.
So, what I'm doing now is to increase the width and at the same time I move the image width/2, it works, however when I have to rotate the image.. everything starts to get broken, and even if I set the pivot in the middle of the sprite, since the image (that is contained inside the sprite) x is altered, it doesn't matter.
I've read some about matrices, but I'm not sure if this is possible.
Thanks.
There a few ways you can accomplish this. The first is to use a transform matrix.
Here is a function I use to rotate an object around a point in that fashion:
*You'll need to import fl.motion.MatrixTransformer, if not using flashPro you can get it here: https://code.google.com/p/artitem-as3/source/browse/trunk/ArtItem/src/fl/motion/MatrixTransformer.as?r=3
/**
* Rotates a displayObject around the passed local point.
* #param displayObj
* #param relativeDegrees - the relative amount of degrees to rotate the object
* #param point - a local point to rotate the object around, if null, center of bounding box will be used.
*/
public static function rotateAroundPoint(displayObj:DisplayObject, relativeDegrees:Number, point:Point = null):void {
var m:Matrix = displayObj.transform.matrix.clone();
if (!point) {
//default is center of bounding box
var r:Rectangle = displayObj.getBounds(displayObj);
point = new Point(r.x + (r.width * .5), r.y + (r.height * .5));
}
MatrixTransformer.rotateAroundInternalPoint(m, point.x, point.y, relativeDegrees);
displayObj.transform.matrix = m;
}
Another more lazy way to do this, is to use a dummy parent object and just rotate that:
var dummy:Sprite = new Sprite();
dummy.addChild(yourObjectToRotate);
//this effectively makes the anchor point of dummy in the center, so when you rotate it it rotate from the center.
yourObjectToRotate.x = -(yourObjectToRotate.width * .5);
yourObjectToRotate.y = -(yourObjectToRotate.height * .5);
dummy.rotation = 90;

Loop an image in Flash

I want to have a scene where an image which is 5000 pixels high is moving up 5 pixels each frame-refresh. When the image is all up, I'd like to see the top of the image connected to the bottom of the image. This should be done untill the level is 'done'. How can I 'loop' such an Image?
You can create a copy of that image which you keep hidden/above and the trick is to update the position and loop accordingly so when one image goes bellow the screen it goes back on top and repeats.
Here's a basic snippet to illustrate the idea using the DisplayObject class and the scrollRect property:
//ignore this, you have your content already
var dummyContent:BitmapData = new BitmapData(100,100,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//important stuff starts here
var container:Sprite = addChild(new Sprite()) as Sprite;//make a container
container.scrollRect = new Rectangle(0,0,dummyContent.width,dummyContent.height);//set a scrollRect/'mask'
var part1:DisplayObject = container.addChild(new Bitmap(dummyContent));//add two copies
var part2:DisplayObject = container.addChild(new Bitmap(dummyContent));//of the same content
part2.y -= part2.height;//set the 2nd at the top of the 1st
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
//move both
part1.y += 5;
part2.y += 5;
//check if any reach the bottom so they can be moved back up
if(part1.y >= part1.height) part1.y = -part1.height;
if(part2.y >= part2.height) part2.y = -part2.height;
//the above can also be nicely placed in a loop if you plan on using more seamless looping clips/images
}
Obviously you will have different content, but the principle is the same.
If you're working with images, you can simply use BitmapData's copyPixels method:
var s:int = 5;//scroll speed
//make some content
var w:int = 100;
var h:int = 100;
var dummyContent:BitmapData = new BitmapData(w,h,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//prepare for stiching
var renderPos:Point = new Point();//position to render the current image to
var prenderPos:Point = new Point();//position to render the previous image (the 'hidden' copy above)
var render:BitmapData = new BitmapData(w,h,false);//create a bitmap data instance to render updated pixels int
addChild(new Bitmap(render));//and add it to the stage
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
renderPos.y = (renderPos.y+s)%h;//update the scroll position for the 1st part, % is used to loop back to 0 when the position gets to the content height
prenderPos.y = renderPos.y - h;//update the scroll position for the 2nd part (above)
render.lock();//freeze pixel updates
render.copyPixels(dummyContent,dummyContent.rect,renderPos);//copy pixels from the scroll position to the bottom
render.copyPixels(dummyContent,dummyContent.rect,prenderPos);//copy pixels from the top to the scroll position
render.unlock();//unfreeze/update ALL THE PIXELS
}
You can try to use a Rectangle object which changes height (height-scrollPosition) so you potentially access less pixels each time or you can manually work out single for loops using BitmapData's getVector method, but that's something to look into if performance is actually an issue for such a simple task and it's worth checking what's faster ( copy full bitmap rect vs copy partial bitmap rect vs manually copy values using vector )
Be warned, Flash cannot load an image greater than 16,769,025 pixels (or 4095x4095). The height of 5000 pixels will work as long as the width is not greater than 3353.
That said, I'd loop the image by keeping two copies of the image onstage, move both at the same time with a parent object, and reset to origin once your loop point is met.
Consider the following stage setup:
Stage ¬
0: MainTimeline:MovieClip ¬
0: Container:MovieClip ¬
0: img1:Bitmap
1: img2:Bitmap
Now moving container up, you'd just need to check that the looping second image reaches the origin point of the first image.
function onEnterFrame(e:Event):void {
Container.y = Container.y - 5;
if (Container.y < -5000) {
Container.y = -5;
}
}

Resize as a function of distance between mc's

I hope this hasn't been asked too much before. When I search I only get questions pertaining to rescaling to window size.
Now my question. I got one space ship firing a beam against another ship. I want the beam to show for some time and I want it to "bridge" the two ships. In other words, I want the beam to extend its width between the two ships.
I try to do this with a dot movie clip that is 1 pixel wide and high (and aligned left edge). I try to resize it with the following code: (target is the ship to be fire at and owner is the ship firing)
dist.vx = target.x - owner.x;
dist.vy = target.y - owner.y;
dist.dist = Math.sqrt(dist.vx*dist.vx + dist.vy*dist.vy);
width = dist.dist;
x = owner.x;
y = owner.y;
rotation = Math.atan2(target.y-y, target.x-x)*180/Math.PI;
This doesn't work as intended because 1) dot also gets alot bigger in the other dimension - how can I "turn off" this behavior? and 2) sometimes it seems to get way to wide - but only in certain angles...
Any suggestions on either solving the heigh/width scaling or on another way to achieve the same effect?
(I'm new to coding and flash.) Thanks!
By resizing a dot, you will have a rectangle...
You can dynamically create a sprite covering both ships and moveTo the hit point of one ship then lineTo the other ship... You do not need distance calculation at all. What you have to do is being careful on the placement of the sprite. So that you can calculate relative hitting points by simple math.
Suppose you have mc space contining mc ship1 and mc ship2, and hit point coords on ships are named hx, hy and you will use sprite s, calculation will be as follows.
// calculate hit points relative to mc space
var s1HX:int = ship1.x + ship1.hx,
s1HY:int = ship1.y + ship1.hy,
s2HX:int = ship2.x + ship2.hx,
s2HY:int = ship2.y + ship2.hy,
// sprite relative moveTo lineTo coords will be these.
mX: int, mY: int,
lX: int, lY: int;
// top left of sprite will be minimum of the hit coords.
s.x = (s1HX <= s2HX)? s1HX : s2HX;
s.y = (s1HY <= s2HY)? s1HY : s2HY;
// now we can get sprite relative moveTo lineTo coordinates:
mX = s1HX - s.x;
mY = s1HY - s.y;
lX = s2HX - s.x;
lY = s2HY - s.y;
The rest is implementation with using these with fancy line styles etc...
To create a new sprite:
var s:Sprite = new Sprite();
Adding / removing it to/from mc space:
space.addChild(s);
space.removeChild(s);
For graphics use the graphics object of sprite.
s.graphics
For setting line styles you can use:
s.graphics.lineStyle(...) ,
s.graphics.lineBitmapStyle(...),
s.graphics.lineGradientStyle(...)
Functions, please read the manual for usage.
After setting the line style to draw the line use:
s.graphics.moveTo(mX,mY);
s.graphics.lineTo(lX,lY);
For pulsating effects you have to do a little more complicated things such as using tween class which you can read about here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/transitions/Tween.html
Note that:
Sprites are no complicated magic, they are like mc's but they do not have timelines etc.
Sprites try to scale when width or height change programmatically. So do not touch them, moveTo lineTo automatically sets the size of a sprite...

Scaling tile grid

I'm working on my own tile bliting engine, this one is using hexagonal tiles - but I think it doesn't differ much from regular tiles.
I have huge x,y array of tiles and they have their x,y coordinates for rendering on canvas, I iterate only the ones that should be visible on canvas in current camera position.
So I'm stuck with scaling and cant resolve this on my own.
Here is my code for drawing tiles on canvas:
public function draw():Void{
clearCanvas(); //Clear canvas (bitmapData)
var _m:Matrix;
iterateTiles(function(_tile:HexTile):Void{ // loop every tile that is visible on screen
_m = new Matrix();
_m.translate(_tile.x + cameraPoint.x,_tile.y + cameraPoint.y);//Get pre calculated tile x,y and add camera x,y
_m.scale(matrixScale, matrixScale);
drawToCanvas(_tile,_m);//Send to draw tile on canvas using Matrix
},true);
}
This works nice and fast but only problem is it scales tiles from left top corner (like regular scale would work)
Before scale
After scale
My question is how to transform tiles to always scale from center. So if tile 10:10 is in center of screen before scaling, then it should
stay there after scaling.
Sorry, I misunderstood the question, but I think I've got it now:
// Scale the distance from the original point to the center of the canvas
var xDistance:Number = ((_tile.x + cameraPoint.x) - xCenter) * matrixScale;
var yDistance:Number = ((_tile.y + cameraPoint.y) - yCenter) * matrixScale;
// Add the distances to the center of the canvas. This is where you want the tile
// to appear.
var x:Number = xCenter + xDistance;
var y:Number = yCenter + yDistance;
// Because the coordinate is going to be scaled, you need to increase it first.
x = (1 / matrixScale) * x;
y = (1 / matrixScale) * y;
_m.translate(x, y);
I have not tested this, I've just drawn it out on graph paper. Let me know if it works.

Flex, creationComplete and Rotation

Firstly I'm no expert with Flex so the following might be 101 gotchas.
My first problem is that I'm attempting to rotate a group of objects by 90 degrees that are contained within a canvas, each object is uniform in size containing text and an image. My first problem is that rotating the objects causes them to resize to the bounds of itself. For example if the object is 200 x 250 with an image stretching from 0 to 250 pixels, after rotation the contents would have resized to 200 pixels meaning the image is now 200 pixels. I can subsequently resize the object back up to the appropriate dimensions but feels awkward and clumsy. Is there a better way of performing this rotation where it rotates the object without the being restricted to the bounds of the object?
Secondly, I ideally need this code to run within the creationComplete event. When it does, the positioning / centering of the object is ignore and is different when rendered on the screen (for example, if I run the following code in my click event it rotates perfectly on its center point; through the creationComplete it offsets the object like the center point doesn't exist)?
Any pointers?
My rotation code...
<mx:Script>
<![CDATA[
// Capture the init for the application
private function InitialRotate( e:Event ):void{
var offsetWidth:Number = (this.width / 2);
var offsetHeight:Number = this.y + (this.height / 2);
// Rotates on the center (but resizes)
var tempMatrix:Matrix = this.transform.matrix;
tempMatrix.translate(-offsetWidth,-offsetHeight);
tempMatrix.rotate(90 * (Math.PI / 180));
tempMatrix.translate(+offsetWidth,+offsetHeight);
this.transform.matrix = tempMatrix;
// Adjusts the objects position and width to size appropriately
this.y -= 5.5;
this.width = 256;
}
]]>
</mx:Script>
I uses the rotate property of a spark rect class (recently.spark.primitives.supportClasses.GraphicElement.rotation) and it was as easy as
line.rotation=90;
Maybe this blog is helpful: http://blog.flexexamples.com/2007/09/14/rotating-images-using-the-matrix-class/