AS3 : How do I clear graphics in a specific pixel/area - actionscript-3

I know that you use graphics.clear to clear all the graphics but that clears the graphics from the stage, I would like to clear graphics in a specific pixel(s) or between x-y value how do I do that?

There's no way to do that with graphics. I just tried, drawing transparent shapes does not create holes, alas.
You should convert the graphics you have into Bitmap instance and work with pixels:
package
{
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
public class Holey extends Sprite
{
public function Holey()
{
super();
// Lets create some example graphics.
graphics.beginFill(0x990000);
graphics.drawCircle(200, 200, 100);
graphics.endFill();
// Convert into raster and make 1 pixel transparent.
var aBit:Bitmap = rasterize(this);
aBit.bitmapData.setPixel32(50, 50, 0x00000000);
graphics.clear();
addChild(aBit);
}
private function rasterize(source:DisplayObject):Bitmap
{
// Obtain bounds of the graphics.
var aBounds:Rectangle = source.getBounds(source);
// Create raster of appropriate size.
var aRaster:BitmapData = new BitmapData(aBounds.width, aBounds.height, true, 0x00000000);
// Make an offset to capture all the graphics.
var aMatrix:Matrix = new Matrix;
aMatrix.translate(-aBounds.left, -aBounds.top);
aRaster.draw(source, aMatrix);
return new Bitmap(aRaster);
}
}
}

The way to do this would be with a mask. Using an alpha mask (both mask and maskee use cacheAsBitmap=true) you can draw transparent pixels onto the mask to erase parts. The basic approach would be:
Put all your graphics that you want to be effected by the mask in a common container (if you mean for everything to be cut, then they are already in a common container: the stage or root timeline.)
Draw a bitmap data object that has a transparent "hole" in the area you want to erase. For example:
// fill the stage with a solid rectangle
var maskBitmapData:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0xff000000);
// erase part of it by drawing transparent pixels
maskBitmapData.fillRect(new Rectangle(20, 20, 200, 100), 0);
// create the mask object
var maskBitmap:Bitmap = new Bitmap(maskBitmapData);
maskBitmap.cacheAsBitmap = true; // this makes the mask an alpha mask
addChild(maskBitmap);
Set the container's .mask property. For example, to mask the entire main timeline:
root.cacheAsBitmap = true; // this makes the mask an alpha mask
root.mask = maskBitmap;

Open stack overflow to answer some questions, think for next hour about how holes are placed in cheese... :)
You could also set blendMode property of your hole object to BlendMode.ERASE in combination with cacheAsBitmap. This works similar to masks except you would be actually drawing the wholes and not the area outside them.
Here is an example:
//make cheese
var cheese:Sprite = new Sprite();
cheese.cacheAsBitmap = true;
stage.addChild(cheese);
cheese.x = cheese.y = 10;
//define holes
var holes:Shape = new Shape();
holes.blendMode = BlendMode.ERASE;
cheese.addChild(holes);
//draw cheese
var g = cheese.graphics;
g.beginFill(0xFFCC00);
g.drawRect(0,0,200,150);
//**Attack chees with mices.
g = holes.graphics;
for (var i:int = 0; i < 10; i++){
g.beginFill(0xFF00FF);
var hx = Math.random()*(cheese.width-7)+7;
var hy = Math.random()*(cheese.height-7)+7;
var s = Math.random()*15;
g.drawCircle(hx, hy, s);
g.endFill();
}
Result would be something like that:
edit:
Turns out that you don't need to use cacheAsBitmap if you set blend mode of parent object to LAYER (doc says it should be set automatically...)
So you can use cheese.blendMode = BlendMode.LAYER; instead of cheese.cacheAsBitmap = true;. And if I remember correctly, masks also don't require cahceAsBitmap, even with NORMAL blending mode.

Related

as3 addChild into a generated triangle

Helly everyone! I'm trying to dynamically add (and later remove) some movieclips inside of a triangle. Simple movieclip inside of a movieclip ain't working (it's a square in the end). Drawing a triangle is simple, addChild method is crystal clear too. The tough part comes after. Here's the code I'm trying to develop:
btn_toys_2.confirm.addEventListener(MouseEvent.MOUSE_UP, confirmToys);
import flash.display.Graphics;
var point1:Point = new Point(466, 65);
var point2:Point = new Point(370, 540);
var point3:Point = new Point(570, 540);
var vertices:Vector.<Number> = Vector.<Number>([point1.x, point1.y, point2.x, point2.y, point3.x, point3.y]);
var triangle:Sprite = new Sprite();
triangle.graphics.beginFill(0x00ff00, 1);
triangle.graphics.drawTriangles(vertices);
triangle.graphics.endFill();
addChild(triangle);
function confirmToys(e:MouseEvent){
var toy:MovieClip = new shar_001;
triangle.addChild(toy);
toy.x = Math.random()*30;
toy.y = Math.random()*30;
}
The "toy" movieclip is for some reason placed outside the triangle (0-30 x axis and 0-30 y axis).
The important part is to make "toys" appear within a triangle, doesnt have to be a movieclip. A way around this would be great too!
Thanks in advance!
It is happening this way because you have made your triangles anchor point at zero. You did this when you did
addChild(triangle);
That will always put the added child at (0,0). The only reason you triangle doesn't appear there is because you have added a cushion of empty pixels by making your points be greater than zero. Instead, you will use
addChild(triangle);
triangle.x = 370;
triangle.y = 65;
The point you want the top left corner of your triangle to be at is (370, 65). You should make your triangle points be (96, 0), (0, 475), (200, 475). Now the top left corner of the triangle is at (0,0) on the stage. Now set the triangle to (370, 65) after adding the triangle to the stage. Now the triangles anchor point is still the top left corner of the triangle, not the stage, so when you add the toy, it will be in reference to the point you expect.
// let the minimum x and y be zero, and adjust the others relative to that.
var point1:Point = new Point(96, 0);
var point2:Point = new Point(0, 475);
var point3:Point = new Point(200, 475);
var toyArray:Array = new Array();
var vertices:Vector.<Number> = Vector.<Number>([point1.x, point1.y, point2.x, point2.y, point3.x, point3.y]);
var triangle:Sprite = new Sprite();
triangle.graphics.beginFill(0x00ff00, 1);
triangle.graphics.drawTriangles(vertices);
triangle.graphics.endFill();
addChild(triangle);
// position anchor point on stage
triangle.x = 370;
triangle.y = 65;
function confirmToys(e:MouseEvent){
var p:Point = new Point(Math.random()*triangle.width,Math.random()*triangle.height);
if (isInsideTriangle(Point1,Point2,Point3,p))
{
var toy:MovieClip = new shar_001;
triangle.addChild(toy);
toyArray.push(toy);
toy.x = p.x;
toy.y = p.y;
}
}
private function isInsideTriangle(A:Point,B:Point,C:Point,P:Point):Boolean {
var planeAB:Number = (A.x-P.x)*(B.y-P.y)-(B.x-P.x)*(A.y-P.y);
var planeBC:Number = (B.x-P.x)*(C.y-P.y)-(C.x - P.x)*(B.y-P.y);
var planeCA:Number = (C.x-P.x)*(A.y-P.y)-(A.x - P.x)*(C.y-P.y);
return sign(planeAB)==sign(planeBC) && sign(planeBC)==sign(planeCA);
}
private function sign(n:Number):int {
return Math.abs(n)/n;
}
Removing the toys from the triangle should be pretty straight forward depending on the method you want to use. I added a toyArray that you can iterate through to remove them.
Checking if a position is within the desired boundaries and rejecting it if it's not is certainly a solution. However, this stops the program from being deterministic, because you never know how many tries it takes before a position within the boundaries is found.
Does that mean the program could run forever? Possibly yes, but this is so unlikely that it's not going to happen. Depending on how much of its bounding box a triangle fills, it will still produce quite a few misses though. Misses that have to be checked, rejected and tried again.
I'm not advising against this strategy because it might be a performance problem (and it might actually be one), but rather because it seems to miss the point: if positions in a triangle should be found, let's just do exactly that. All this trial and error and testing and rejecting is counterintuitive.
You only have one pseudo random number generator built in: Math.random().
That produces an evenly distributed random number between 0 and 1. (let's ignore whether the boundaries are possible values or not)
To create a 2D distribution, it's very easy to simply use two of those.
Now the problem with the even distribution is that it's even. To form a non-rectangular shape, transformations have to be applied.
Consider two edges of the triangle to be two vectors. A random point in the triangle is found by combining those two vectors linearly in a random way.
Obviously, with the untransformed random numbers, that would yield a diamond shaped boundary for the random points. To compensate for the fact that the vectors meet at one point and diverge in the other direction the square root is applied to one random number. The math behind that is not too complicated but ain't trivial either. I choose to omit it here. For more information ask a new question, math.se is probably a good place to do this.
Here's a full fledged example code to be used as a document class, which puts 1000 circles into a triangle boundary:
package
{
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Point;
public class Main extends Sprite
{
public function Main()
{
var distribution:EvenDistribution2DTriangleBoundary = new EvenDistribution2DTriangleBoundary(new Point(100, 100), new Point(400, 50), new Point(250, 350));
for (var i:int = 0; i < 1000; ++i)
{
// create an object in each iteration of the loop
var circle:Shape = new Shape();
//add some graphics (this is unnecessary if you use a library symbol)
circle.graphics.beginFill(0xff0000, .6);
circle.graphics.drawCircle(0, 0, 3);
circle.graphics.endFill();
// add it to the display list
addChild(circle);
// reposition it with the help of the distribution object
distribution.positionDisplayObject(circle);
}
}
}
}
import flash.display.DisplayObject;
import flash.geom.Point;
internal class EvenDistribution2DTriangleBoundary
{
private var u:Point;
private var v:Point;
private var position:Point;
public function EvenDistribution2DTriangleBoundary(a:Point, b:Point, c:Point)
{
// consider corner "a" as the position of the triangle, this is arbitrary decision, but has to be consistent with the rest of this constructor
position = a;
// create two vectors from the corner that is the position to the other two corners respectively
u = b.subtract(a);
v = c.subtract(a);
}
public function getRandomPosition():Point
{
// random position formula with two random variables: position + (u + (v-u) * random1) * sqrt(random2)
var r1:Number = Math.random();
// the sqrt transforms the probability density function of the even distribution f(x) = 1 into a triangle g(y) = 2y
var r2:Number = Math.sqrt(Math.random());
// applying the above formula to create an evenly distributed random position within the triangle
return position.add(new Point((u.x + (v.x - u.x) * r1) * r2, (u.y + (v.y - u.y) * r1) * r2));
}
// convenience function to position a display object at a random position in the triangle
public function positionDisplayObject(object:DisplayObject):void
{
var position:Point = getRandomPosition();
object.x = position.x;
object.y = position.y;
}
}
Creating the random distribution is a class of its own. For the sake of simple testing, it's an internal class, thus the entire example is a single class that goes into a single file. Of course, in production code, this should be better organised.
Here are 4 results that I got:
it seems I need to transfer all of my frames/timeline code (and there's a lot!) into the external class
That isn't necessary although it is recommended. You should eventually only use class based code, but of course making that transition within a project isn't very practical.
In my example above, there are two classes: Main and EvenDistribution2DTriangleBoundary. You are only interested in the latter one. Main is just there to use the other class, create and display the circles, etc: it's a demo.
To use EvenDistribution2DTriangleBoundary in your project, create a new text file in the same directory as your .fla file named EvenDistribution2DTriangleBoundary.as with the following content:
package
{
import flash.display.DisplayObject;
import flash.geom.Point;
public class EvenDistribution2DTriangleBoundary
{
private var u:Point;
private var v:Point;
private var position:Point;
public function EvenDistribution2DTriangleBoundary(a:Point, b:Point, c:Point)
{
position = a;
u = b.subtract(a);
v = c.subtract(a);
}
public function getRandomPosition():Point
{
var r1:Number = Math.random();
var r2:Number = Math.sqrt(Math.random());
return position.add(new Point((u.x + (v.x - u.x) * r1) * r2, (u.y + (v.y - u.y) * r1) * r2));
}
public function positionDisplayObject(object:DisplayObject):void
{
var position:Point = getRandomPosition();
object.x = position.x;
object.y = position.y;
}
}
}
Now you can use that class like any other class in your project. For example, you can add the code from Main's constructor to your timeline and it should work:
var distribution:EvenDistribution2DTriangleBoundary = new EvenDistribution2DTriangleBoundary(new Point(100, 100), new Point(400, 50), new Point(250, 350));
for (var i:int = 0; i < 1000; ++i)
{
// create an object in each iteration of the loop
var circle:Shape = new Shape();
//add some graphics (this is unnecessary if you use a library symbol)
circle.graphics.beginFill(0xff0000, .6);
circle.graphics.drawCircle(0, 0, 3);
circle.graphics.endFill();
// add it to the display list
addChild(circle);
// reposition it with the help of the distribution object
distribution.positionDisplayObject(circle);
}
You should see a triangle of red circles similar to those I posted in the image of results above. Does that work?

Actionscript 3.0 - tracing the path of a moving body ;

I'm learning AS3.0 currently. I am trying to design a simple two body planet simulation. I need to show the paths of the planets on the screen. So my question is, once I have the updated x and y coordinates for the planets at each Timer interval, how do I change the color of the pixel (x,y) of the stage so that it shows the path of the planets? Is there some command of the form stage.x = color?
Thanks!
I recommend using BitmapData's draw() method to render your planets as pixels each time you update them. It basically works like a 'screenshot' of the display object you pass it as n argument. If you pass the objects transformation, the position/rotation/scale will be visible (as opposed to drawing from 0,0). This way, you will only be updating pixels instead of continuously creating new display objects.
Here's a basic commented example:
import flash.display.Sprite;
import flash.events.Event;
var trails:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x00000000);//create a transparent bitmap to draw the trails into
var trailsFade:ColorTransform = new ColorTransform(1,1,1,0.025,0,0,0,1);//color transform: keep rgb the same(1,1,1), set alpha to 0.025 out of 1.0
var background:Bitmap = addChild(new Bitmap(trails,PixelSnapping.AUTO,true)) as Bitmap;//add the trails pixels/bitmap data into a Bitmap/display object at the bottom of the display list
var dot:Sprite = addChild(new Sprite()) as Sprite;
dot.graphics.lineStyle(3);
dot.graphics.drawCircle(-4, -4, 8);
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
dot.x = mouseX;
dot.y = mouseY;
//draw trails of the dot
trails.draw(dot,dot.transform.concatenatedMatrix,trailsFade);//draw the dot into the bitmap data using the dot's transformation (x,y, rotation, scale)
}
Notice the trails when you move the mouse and how they are affected by the (update) speed.
Here's a longer example using multiple objects:
import flash.display.*;
import flash.events.Event;
import flash.geom.ColorTransform;
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var trails:BitmapData = new BitmapData(w,h,true,0x00000000);//create a transparent bitmap to draw the trails into
var trailsFade:ColorTransform = new ColorTransform(1,1,1,0.025,0,0,0,0.1);//color transform: keep rgb the same(1,1,1), set alpha to 0.025 out of 1.0
var background:Bitmap = addChild(new Bitmap(trails,PixelSnapping.AUTO,true)) as Bitmap;//add the trails pixels/bitmap data into a Bitmap/display object at the bottom of the display list
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var mercuryPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var venusPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var mercury:Sprite = mercuryPivot.addChild(getCircleSprite(24.40 / 4,0xCECECE)) as Sprite;
var venus:Sprite = venusPivot.addChild(getCircleSprite(60.52 / 4,0xFF2200)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
mercury.x = 5791 / 40;
venus.x = 10820 / 40;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
mercuryPivot.rotation += 0.5;
venusPivot.rotation += 0.25;
earthPivot.rotation += 0.12;
//draw trails
trails.draw(spheres,spheres.transform.concatenatedMatrix,trailsFade);
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
Notice we call trails.draw(spheres,spheres.transform.concatenatedMatrix,trailsFade);
but it could be trails.draw(earth,earth.transform.concatenatedMatrix,trailsFade); if you only want to draw the trails of earth.
In the example above I'm just nesting sprites and using the rotation property to keep things simple. You might want to use a bit of trigonometry to update positions because planets will probably not have perfectly circular orbits and pass through the exact location every single time.
Update
Thinking about this more, using the old school Graphics API might be handy for you if you get started and haven't got used to playing with pixels yet.
It's easy to get started with: objects that can be displayed in flash player can have a graphics property (see the Shape/Sprite/MovieClip classes). (You can have display object that you can't draw into whether you can nest elements into (DisplayObjectContainer) or not(DisplayObject), but that's a whole other thing for you too look into).
This graphics property Sprites and MovieClip has allows you to draw programmatically using simply commands such as: setting a stroke(lineStyle()), a fill (beginFill()/endFill()), moving an imaginary 'pen' without drawing (moveTo), drawing a line (lineTo), a circle, a rectangle, a rounded rectangle, etc. It's all there.
So, a minimal drawing program would look a bit like this:
import flash.events.MouseEvent;
import flash.events.Event;
var mousePressed:Boolean = false;//keep track if the mouse is pressed or not
graphics.lineStyle(1);//set the stroke to have a thickness of 1 (and the other parameters are defaults(color: black, transparency: 100% / 1.0, etc.))
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseEventHandler);//listend for mouse down
stage.addEventListener(MouseEvent.MOUSE_UP,mouseEventHandler);//...and mouse up changes
stage.addEventListener(Event.ENTER_FRAME,update);//update continuously
function mouseEventHandler(e:MouseEvent):void{
mousePressed = (e.type == MouseEvent.MOUSE_DOWN);
graphics.moveTo(mouseX,mouseY);//place the graphics 'pen' at this new location
}
function update(e:Event):void{
if(mousePressed) graphics.lineTo(mouseX,mouseY);//if the mouse is pressed, keep drawing a line to the current mouse location
}
or a more complex version where you use the speed of the mouse movement to influence the stroke thickness and transparency:
import flash.events.MouseEvent;
import flash.events.Event;
import flash.geom.Point;
var prevPos:Point = new Point();//previous mouse position
var currPos:Point = new Point();//current mouse position
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var mousePressed:Boolean = false;//keep track if the mouse is pressed or not
graphics.lineStyle(1);//set the stroke to have a thickness of 1 (and the other parameters are defaults(color: black, transparency: 100% / 1.0, etc.))
stage.doubleClickEnabled = true;
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseEventHandler);//listend for mouse down
stage.addEventListener(MouseEvent.MOUSE_UP,mouseEventHandler);//...and mouse up changes
stage.addEventListener(MouseEvent.DOUBLE_CLICK,function(e:MouseEvent):void{graphics.clear()});//double click to clear
stage.addEventListener(Event.ENTER_FRAME,update);//update continuously
function mouseEventHandler(e:MouseEvent):void{
mousePressed = (e.type == MouseEvent.MOUSE_DOWN);
graphics.moveTo(mouseX,mouseY);
}
function update(e:Event):void{
//currPos.setTo(mouseX,mouseY);//this works for flash player 11 and above instead of setting x,y separately
currPos.x = mouseX;
currPos.y = mouseY;
var mappedValue: Number = Point.distance(currPos,prevPos) / (w+h);//map the distance between points
//prevPos.copyFrom(currPos);//this works for flash player 11 and above instead of setting x,y separately
prevPos.x = mouseX;
prevPos.y = mouseY;
graphics.lineStyle(mappedValue * 100,0,1.0-(0.25+mappedValue));
if(mousePressed) graphics.lineTo(mouseX,mouseY);//if the mouse is pressed, keep drawing a line to the current mouse location
}
So going back to the tracing of a planet path, using the graphics api, my previous example would look like so:
import flash.display.*;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Point;
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var hasMoved:Boolean = false;//has the graphics 'pen' been moved ?
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
earthPivot.rotation += 0.12;
//draw trails
drawTrail(earth,0x0000FF);
}
function drawTrail(s:Sprite,color:int) {
var globalPos:Point = s.localToGlobal(new Point());//convert the local position of the sprite (it might have been nested several times) to the global/stage coordinate system
if(!hasMoved){//if the graphics 'pen' wasn't moved (is still at 0,0), this will happen only once: the 1st time you draw the mouse position
graphics.moveTo(globalPos.x,globalPos.y);//move it to where we're about to draw first
hasMoved = true;//and make sure we've marked that the above was done
}
graphics.lineStyle(1,color);
graphics.lineTo(globalPos.x,globalPos.y);
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
From my experience, using this older drawing API can get slow if you have a lot of lines on stage. I say older because it might actually be 15 years old now. Flash Player 10 introduced a newer drawing API. You can read on it on the Adobe Devnet but I warmly recommend Senocular's Flash Player 10 Drawing API Tutorial and his slides and example code from Flash Camp
Back to pixels: it's not that hard. You use the BitmapData class to manipulate pixels and use a Bitmap instance so you can add those pixels on stage. Here's a minimal drawing program:
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0xFFFFFF);//setup pixels
addChild(new Bitmap(canvas));//add them to the stage
addEventListener(Event.ENTER_FRAME,update);//setup continuous updates
function update(e:Event):void{
canvas.setPixel(int(mouseX),int(mouseY),0x990000);//pretty easy, right ?
}
want to make trippy patterns, sure thing, have a play:
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0xFFFFFF);//setup pixels
addChild(new Bitmap(canvas));//add them to the stage
addEventListener(Event.ENTER_FRAME,update);//setup continuous updates
function update(e:Event):void{
canvas.lock();//when updating multiple pixels or making multiple pixel operations
canvas.perlinNoise(mouseX,mouseY,mouseX/stage.stageWidth * 8,getTimer(),false,true);
canvas.unlock();//when you're done changing pixels, commit the changes
}
So, back to the trails example:
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var canvas:BitmapData = new BitmapData(w,h,false,0xFFFFFF);
addChild(new Bitmap(canvas));
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
earthPivot.rotation += 0.12;
//draw trails
drawTrail(earth,0x0000FF,canvas);
}
function drawTrail(s:Sprite,color:int,image:BitmapData) {
var globalPos:Point = s.localToGlobal(new Point());//convert the local position of the sprite (it might have been nested several times) to the global/stage coordinate system
image.setPixel(int(globalPos.x),int(globalPos.y),color);//colour a pixel at a set position
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
Which looks like this:
Not sure if it's what you want though, but pixels are fun to use and pretty fast too.
With a bit of math you can do some minimal 3D as well.
Also, for your inspiration on drawing in actionscript, you can have a look at some of Keith Peters', Erik Natzke, Joshua Davis, etc.
No, there isn't such a command, but you can always create a very simple Sprite object and add it to the stage at the corresponding position. Something like:
var dot:Sprite = new Sprite();
dot.graphics.beginFill(0xCCCCCC);
dot.graphics.drawRect(-1, -1, 2, 2);
dot.graphics.endFill();
dot.x = x;
dot.y = y;
addChild(dot);

how to draw on bitmap but not draw on transparent pixels?

I need to draw on a bitmap but not draw on the transparent pixels of it?
Example picture here: http://i.stack.imgur.com/QvJTZ.png
I have written this code :
import flash.display.Bitmap;
import flash.events.Event;
import flash.geom.Matrix;
var s:S = new S();
var m_bitmapData = new BitmapData(s.width, s.height, true, 0x00000000);
m_bitmapData.draw(s);
var hole=new Sprite();
var hole_matrix:Matrix
hole.graphics.beginFill(0x000000);
hole.graphics.drawCircle(0,0,30);
var bmp:Bitmap = new Bitmap(m_bitmapData);
bmp.x = 50
bmp.y =50
stage.addChild(bmp);
addEventListener(Event.ENTER_FRAME,asd);
function asd(e:Event):void{
hole_matrix=new Matrix();
hole_matrix.translate(mouseX-bmp.x,mouseY-bmp.y);
m_bitmapData.draw(hole,hole_matrix);
}
But result is such as picture "NO".
Can someone please explain ?
Preserve alpha channel, then copy it back.
var tempBD:BitmapData=new BitmapData(m_bitmapData.width,m_bitmapData.height,true,0);
...
function asd(e:Event):void {
...
tempBD.copyChannel(m_bitmapData,m_bitmapData.rect,new Point(),BitmapDataChannel.ALPHA,BitmapDataChannel.ALPHA);
m_bitmapData.draw(hole,hole_matrix);
m_bitmapData.copyChannel(tempBD,tempBD.rect,new Point(),BitmapDataChannel.ALPHA,BitmapDataChannel.ALPHA);
}
Basically what you do: You have a BitmapData elsewhere that has the same size as your canvas BitmapData, then you copy alpha channel into that BitmapData, draw what you want, then copy alpha channel back. Whatever was transparent, remained transparent. Note, if you never want the alpha channel to change, you can copyChannel() it away only once, and then just restore.

Endless repeating scrolling background

I got a problem with AS3 and AIR. I'm working on a side-scrolling game for smartphones with a plane and I use different backgrounds as layers.
Before all other: I use GPU and only bitmaps, quality is set to low. So Performance settings are all set for smartphone use.
I putted them into a rectangle using the drawing API and move the background with a matrix:
protected var scrollingBitmap:BitmapData;
protected var canvas:Graphics;
protected var matrix:Matrix;
public function move(dx:Number, dy:Number):void {
matrix.translate(dx, dy);
if(dx != 0) matrix.tx %= scrollingBitmap.width;
if(dy != 0) matrix.ty %= scrollingBitmap.height;
drawCanvas();
}
protected function drawCanvas():void {
canvas.clear();
canvas.beginBitmapFill(scrollingBitmap, matrix, true, true);
canvas.drawRect(0, -scrollingBitmap.height, 1404, scrollingBitmap.height);
}
UPDATE2 (
Take a look at this: http://plasticsturgeon.com/2010/06/infinite-scrolling-bitmap-backgrounds-in-as3/
I used this to create my backgrounds.
With this I can simulate that my plane is flying to the right without moving the whole background and I can use a small single graphic which repeats every time (for the foreground layer).
For the background layer I use this method, too, but with a much larger graphic and I move it only with less the speed of my plane to simulate a far background.
My move-method is on an enterframe event. So I can update the background every frame with the "movement" of my plane.
)
The plane can exceed the height of the bitmaps. Everytime the bitmap comes back into the window/screen a real long lag occurs. And when the plane flies very fast, the game start to lag, too.
My first approach was to use .PNG files (but they are very big: 1-3MB size).
My next approach was to use .GIF files (much less size).
With both it's the same. So it can't be that.
I read about draw() and copyPixels() but I don't know, how I can use those to repeat the image.
UPDATE1:
protected var scrollingBitmap:BitmapData;
protected var canvas:Bitmap;
protected function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
canvas = new Bitmap(new BitmapData(1404, scrollingBitmap.height, true), "auto", true);
this.addChild(canvas);
drawCanvas();
}
public function move(dx:Number, dy:Number):void {
if(dx != 0) dx %= scrollingBitmap.width;
if(dy != 0) dy %= scrollingBitmap.height;
drawCanvas(dx, dy);
}
protected function drawCanvas(xPos:Number = 0, yPos:Number = 0):void {
canvas.bitmapData.copyPixels(scrollingBitmap, new Rectangle(0, 0, 1404, scrollingBitmap.height), new Point(xPos, yPos), scrollingBitmap);
}
I think you'd be better off with a Bitmap instead of using the graphics object with fill. copyPixels is very fast. So what you'd do is simply copyPixels over the top of whatever was there before, presuming everything is opaque. If everything is not opaque, you'll need to use your source bitmap as its own alpha data so previously drawn pixels don't show through.
Let's reframe your canvas so it is a Bitmap and not a MC. your new code will look like:
protected function drawCanvas():void {
canvas.bitmapData.copyPixels(scrollingBitmap, new Rectangle(0, 0, scrollingBitmap.width, scrollingBitmap.height), new Point(0,0), scrollingBitmap);
}
Oh, and look at that! Not only is this code faster, it's only one line of code!
EDIT: Added working code
package {
import flash.display.MovieClip;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.geom.Point;
public class EndlessBG extends MovieClip{
//this one stays stationary, we're getting the pixels for the right side of the pic from here
private var _source:BitmapData;
//this is the one moving to the left (the pixels for the right side are not visible except for once a cycle);
private var _movingPixels:BitmapData;
private var _canvas:Bitmap;
private var _xOffset:int = 0;
private var _rect:Rectangle = new Rectangle();;
private var _point:Point = new Point();
public function EndlessBG() {
super();
_source = new BathroomStillLife();
_canvas = new Bitmap(new BitmapData(_source.width, _source.height));
_canvas.bitmapData.draw(_source);
_canvas.x = stage.stageWidth/2 - _canvas.width/2;
_canvas.y = 5;
addChild(_canvas);
addEventListener(Event.ENTER_FRAME, gameLoop);
setGeometryDefaults();
_movingPixels = new BitmapData(_source.width, _source.height);
_movingPixels.copyPixels(_source, _rect, _point);
//turn this on to watch red pixels be drawn where the source pixels are coming in
//_source = new BitmapData(_source.width, _source.height, false, 0xFF0000);
}
private function gameLoop(e:Event):void {
_xOffset--;//where the background is moving to
if (_xOffset < -_source.width) {
_xOffset = 0;
//this doesn't seem to work correctly:
//_movingPixels.scroll(_source.width, 0);
_movingPixels = new BitmapData(_source.width, _source.height);
_movingPixels.copyPixels(_source, _rect, _point);
}
trace(_xOffset);
setGeometryDefaults();
_movingPixels.scroll(-1, 0);
//draw the moved part of the canvas
_canvas.bitmapData.copyPixels(_movingPixels, _rect, _point);
//If we stop here, we get a smear to the right
//so, get the remaining pixels directly from the source
//1) reset our rect and point to be to the right side
_rect.x = 0;
_rect.width = -_xOffset;
_point.x = _source.width + _xOffset;
//2) copy from the source
_canvas.bitmapData.copyPixels(_source, _rect, _point);
}
private function setGeometryDefaults():void {
_rect.x=0;
_rect.y=0;
_rect.width = _source.width;
_rect.height = _source.height;
_point.x = 0;
_point.y = 0;
}
}
}
Not ideal, and not polished enough yet for a blog post, but should get you started.
Edit:
Eventually I did write that blog post.
http://www.greensock.com/blitmask
This might help although not free

actionscript3: reflect-class applied on rotationY

I'm using a class which applies a visual reflection-effect to defined movieclips.
I use a reflection-class from here:
link to source.
It works like a charm except when I apply a rotation to the movieclip.
In my case the reflection is still visible but only a part of it.
What am I doing wrong? How could I pass/include the rotation to the Reflection-Class ?
Thanks in advance!
This is how you apply the Reflection Class to your movieclip:
var ref_mc:MovieClip = new MoviClip();
addChild(ref_mc);
var r1:Reflect = new Reflect({mc:ref_mc, alpha:50, ratio:50,distance:0, updateTime:0,reflectionDropoff:1});
Now I apply a rotation to my movieclip:
ref_mc.rotationY = 30;
And Here the Reflect-Class:
package com.pixelfumes.reflect{
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.utils.setInterval;
import flash.utils.clearInterval;
public class Reflect extends MovieClip{
//Created By Ben Pritchard of Pixelfumes 2007
//Thanks to Mim, Jasper, Jason Merrill and all the others who
//have contributed to the improvement of this class
//static var for the version of this class
private static var VERSION:String = "4.0";
//reference to the movie clip we are reflecting
private var mc:MovieClip;
//the BitmapData object that will hold a visual copy of the mc
private var mcBMP:BitmapData;
//the BitmapData object that will hold the reflected image
private var reflectionBMP:Bitmap;
//the clip that will act as out gradient mask
private var gradientMask_mc:MovieClip;
//how often the reflection should update (if it is video or animated)
private var updateInt:Number;
//the size the reflection is allowed to reflect within
private var bounds:Object;
//the distance the reflection is vertically from the mc
private var distance:Number = 0;
function Reflect(args:Object){
/*the args object passes in the following variables
/we set the values of our internal vars to math the args*/
//the clip being reflected
mc = args.mc;
//the alpha level of the reflection clip
var alpha:Number = args.alpha/100;
//the ratio opaque color used in the gradient mask
var ratio:Number = args.ratio;
//update time interval
var updateTime:Number = args.updateTime;
//the distance at which the reflection visually drops off at
var reflectionDropoff:Number = args.reflectionDropoff;
//the distance the reflection starts from the bottom of the mc
var distance:Number = args.distance;
//store width and height of the clip
var mcHeight = mc.height;
var mcWidth = mc.width;
//store the bounds of the reflection
bounds = new Object();
bounds.width = mcWidth;
bounds.height = mcHeight;
//create the BitmapData that will hold a snapshot of the movie clip
mcBMP = new BitmapData(bounds.width, bounds.height, true, 0xFFFFFF);
mcBMP.draw(mc);
//create the BitmapData the will hold the reflection
reflectionBMP = new Bitmap(mcBMP);
//flip the reflection upside down
reflectionBMP.scaleY = -1;
//move the reflection to the bottom of the movie clip
reflectionBMP.y = (bounds.height*2) + distance;
//add the reflection to the movie clip's Display Stack
var reflectionBMPRef:DisplayObject = mc.addChild(reflectionBMP);
reflectionBMPRef.name = "reflectionBMP";
//add a blank movie clip to hold our gradient mask
var gradientMaskRef:DisplayObject = mc.addChild(new MovieClip());
gradientMaskRef.name = "gradientMask_mc";
//get a reference to the movie clip - cast the DisplayObject that is returned as a MovieClip
gradientMask_mc = mc.getChildByName("gradientMask_mc") as MovieClip;
//set the values for the gradient fill
var fillType:String = GradientType.LINEAR;
var colors:Array = [0xFFFFFF, 0xFFFFFF];
var alphas:Array = [alpha, 0];
var ratios:Array = [0, ratio];
var spreadMethod:String = SpreadMethod.PAD;
//create the Matrix and create the gradient box
var matr:Matrix = new Matrix();
//set the height of the Matrix used for the gradient mask
var matrixHeight:Number;
if (reflectionDropoff<=0) {
matrixHeight = bounds.height;
} else {
matrixHeight = bounds.height/reflectionDropoff;
}
matr.createGradientBox(bounds.width, matrixHeight, (90/180)*Math.PI, 0, 0);
//create the gradient fill
gradientMask_mc.graphics.beginGradientFill(fillType, colors, alphas, ratios, matr, spreadMethod);
gradientMask_mc.graphics.drawRect(0,0,bounds.width,bounds.height);
//position the mask over the reflection clip
gradientMask_mc.y = mc.getChildByName("reflectionBMP").y - mc.getChildByName("reflectionBMP").height;
//cache clip as a bitmap so that the gradient mask will function
gradientMask_mc.cacheAsBitmap = true;
mc.getChildByName("reflectionBMP").cacheAsBitmap = true;
//set the mask for the reflection as the gradient mask
mc.getChildByName("reflectionBMP").mask = gradientMask_mc;
//if we are updating the reflection for a video or animation do so here
if(updateTime > -1){
updateInt = setInterval(update, updateTime, mc);
}
}
public function setBounds(w:Number,h:Number):void{
//allows the user to set the area that the reflection is allowed
//this is useful for clips that move within themselves
bounds.width = w;
bounds.height = h;
gradientMask_mc.width = bounds.width;
redrawBMP(mc);
}
public function redrawBMP(mc:MovieClip):void {
// redraws the bitmap reflection - Mim Gamiet [2006]
mcBMP.dispose();
mcBMP = new BitmapData(bounds.width, bounds.height, true, 0xFFFFFF);
mcBMP.draw(mc);
}
private function update(mc):void {
//updates the reflection to visually match the movie clip
mcBMP = new BitmapData(bounds.width, bounds.height, true, 0xFFFFFF);
mcBMP.draw(mc);
reflectionBMP.bitmapData = mcBMP;
}
public function destroy():void{
//provides a method to remove the reflection
mc.removeChild(mc.getChildByName("reflectionBMP"));
reflectionBMP = null;
mcBMP.dispose();
clearInterval(updateInt);
mc.removeChild(mc.getChildByName("gradientMask_mc"));
}
}
}
If you have set the height and the width of your bitmapData to the same width and height of the movieclip your reflecting, you will get 'cut off'
When you rotate your movieclip, it does effectively become wider... and taller - of course at this point you've already made the bitmapData canvas area at a set width -
What you need to do it collect the real width and height of the rotating movieclip and when you redraw the bitmapData, use the new values.
I cheated when I made the same thing and just made my bitmapData 1.5 (bounds.width * 1.5) times the size of the movieclip. But thats a hack and I' a bad person for doing it.
I got it, I have to reset the bounds of the reflect Class:
myReflectClass.setBounds(newWidth,newHeight);