I've created a series of classes that can be used to generate and render images. I want to store a copy of the last frame displayed so I can mix it with the current frame to create a video sustain effect. A brief overview of the classes involved in this example:
MasterContainer: a subclass of Sprite used as the main display object. Generative classes are placed in the MasterContainer, and redrawn when the container is told to render
CustomWave: a subclass of Shape used to contain, draw, and manipulate a GraphicsPath object. One of the aforementioned 'generative classes'
My current attempt involves the use of two MasterContainer objects - one for the current frame, and one for the last frame. If I'm not mistaken, the current appearance of one MasterContainer (and its children) can be copied to the other with a command like lastMaster.graphics.copyFrom(master.graphics);. Consider the following code:
var time:Number;
var master:MasterContainer = new MasterContainer(); //current frame
var lastMaster:MasterContainer = new MasterContainer(); // last frame
var wave:CustomWave = new CustomWave(new <Number>[0,0,0,0],0xffffff,5); //generator for current frame
master.RegisterComponent(wave); //adds CustomWave and registers with the rendering loop
addChild(lastMaster); //add last frame to stage
addChild(master); //add current frame to stage
addEventListener(Event.ENTER_FRAME, perFrame);
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //copy previous frame's graphics
UpdatePoints(); //update the path of the CustomWave
UpdateColor(); //update the color of the CustomWave
master.fireRenderCannon(); //redraw objects registered to master
}
This seems to work in theory, but as far as I can tell lastMaster ends up with no visible graphics content even though master renders as expected. I've tried several times to test whether this is the case, and am pretty convinced that that it is, but am newish to AS3 and am concerned I am overlooking something - the code looks like it should work. Does anyone have suggestions on how to test this properly? Are there obvious defects within this code that would cause lastMaster to be visually blank? Is there an better way of accomplishing my goal?
I think I'm in over my head on this... I would love any input. Thanks!
After you copied graphics, what do you try to do with it?
Method copyFrom works as clocks, without any problems. Isn't here logic bug in your code?
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //Here
//master.graphics.copyFrom(lastMaster.graphics);
UpdatePoints();
UpdateColor();
master.fireRenderCannon();
}
Example of copyFrom, it works fine with any complexity of graphics:
var complex: Shape = new Shape();
adobeExample(complex.graphics);
var test2: Shape = new Shape();
test2.graphics.copyFrom(complex.graphics);
addChild(test2);
private function adobeExample(graphics: Graphics):void{
// define the line style
graphics.lineStyle(2,0x000000);
// define the fill
graphics.beginFill(0x666699);//set the color
// establish a new Vector object for the commands parameter
var star_commands:Vector.<int> = new Vector.<int>();
// use the Vector array push() method to add moveTo() and lineTo() values
// 1 moveTo command followed by 3 lineTo commands
star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
// establish a new Vector object for the data parameter
var star_coord:Vector.<Number> = new Vector.<Number>();
// use the Vector array push() method to add a set of coordinate pairs
star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
graphics.drawPath(star_commands, star_coord);
}
After the comments made by Bennet and Nicolas, it became obvious that my requirements were (nearly) impossible without a fair amount of redesign. The changes made are as follows:
Generators are no longer DisplayObjects. They are only used to calculate vectors containing the IGraphicsData objects necessary to draw the generated graphic with the drawGraphicsData method.
MasterContainer is now a shape subclass that retrieves the Vector.<IGraphicsData> from each registered generator in order to draw the output.
A bitmap subclass is used to render the contents of the MasterContainer, combining it with a color-dampened version of the previous frame.
An abridged version of the bitmap subclass:
private var constantSustain:Number;
private var linearSustain:Number;
private var sustain:ColorTransform;
private var lastFrame:BitmapData;
public function BitmapManipulator(constantSustain:Number = 0.998, linearSustain:Number = 0.98) {
this.constantSustain = Math.min(Math.max(constantSustain, 0), 1);
this.linearSustain = Math.min(Math.max(linearSustain, 0), 1);
this.UpdateSustain();
this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage)
}
private function UpdateSustain():void {
var constantRelease:Number = 255 * (this.constantSustain - 1);
this.sustain = new ColorTransform(this.linearSustain, this.linearSustain, this.linearSustain, 1,
constantRelease, constantRelease, constantRelease, 0);
}
private function OnAddedToStage(event:Event) {
this.lastFrame = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
}
public function DrawFrame(container:MasterContainer):void {
this.lastFrame.draw(container);
this.bitmapData = lastFrame;
this.lastFrame = this.bitmapData
this.lastFrame.colorTransform(getBounds(this), this.sustain);
}
...and finally the results #60fps when using an indigo sine wave of shifting phase as the input for the CustomWave:
In AS3, how can I check to see if mouse is inside a rectangle after the stage has been clicked in that area?
for example:
var rec:Rectangle = new Rectangle(50, 200, 50, 200);
And the onclick function would do:
if (mouseX <=rec.left && etc...
I've tried many methods, but have still come up empty. Any ideas?
(What I really wanted to do was make an event listener for the rectangle, but it kept throwing errors so I'm trying to figure out a way to do it with an if statement instead.)
This isn't pertinent that I know this, because I can just do this with flat numbers. I'm trying to learn more about rectangles and points. I figured this would work with a rectangle too, but nope.
You can use Rectangles function contains which accepts 2 arguments x and y.
Like this:
var r:Rectangle = new Rectangle(50,200,50,200);
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:MouseEvent):void
{
if(r.contains(e.stageX,e.stageY))
{
trace("inside");
}
}
I found this answer (Circular Slider in ActionScript 3) which creates a drag-able point over a circle..In the answer there is the code in the CircleSlider class .
private function mouseUpEventHandler(event:MouseEvent):void
{
mThumb.removeEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
stage.removeEventListener(Event.MOUSE_LEAVE, mouseUpEventHandler);
trace(mAngle);
}
when i put the trace code it shows the angle when i slide the point on the circle..i want to get that angle to a text box in main script.(frame script).can someone help me?
use text property of your TextField object. Something like this: TEXT_FIELD_NAME.text = String(mAngle);
If you use 1 frame and the textfield obj is into this frame you can use this keyword otherwise you must address right frame/place that contains the textfield object.
I've been having a problem with my Actionscript code. I am fairly new to Flash and AS3, so I apologize if my code seems crude or rudimentary, but I'm doing this as best as I can.
Well, in this project I'm trying to get a bullet to ricochet off a wall once. If it hits a wall again after ricocheting, the bullet will disappear.
I've created a for loop which moves the bullets, in an array. At the same time, I try to keep track of each bullet's individual number of ricochets. This works fine when I shoot a first bullet - it will ricochet and then disappear after hitting another wall. However, every bullet I fire after that disappears on the first wall it hits, before it has ricocheted. I've tried to get this to work but I just can't seem to do it.
I would be grateful if somebody could show me the problem, or suggest a change to my code.
Here is a link to my code as it is now.
Thanks, to anybody who helps.
Here are some suggestions I have:
1: Create a Bullet class that tracks its own collisions against walls. I'd also move the clearBullet() method into the bullet class itself.
public class Bullet extends Sprite
{
public var collisions:int = 0;
public var xv:Number = 0;
public var yv:Number = 0;
public function clear():void
{
if(parent)
parent.removeChild(this);
}
}
2: Update your loop to deal with this new info.
for each(var i:Bullet in bulletholder)
{
// Move bullet.
// Check for collision.
// When there is a collision, do this:
i.collisions ++;
if(i.collisions >= 2)
{
var n:int = bulletholder.indexOf(i);
bulletholder.splice(n, 1);
i.clear();
}
else
{
// Deal with changing bullet position.
}
}
I see at least a couple of problems with your code:
Your ricochetcount is clearly out of sync. i.e. you need to delete an element from that array as well.
When you delete an element from the bulletholder array (via clearBullet), you're still incrementing i, which means you end up inadvertently skipping an element.
Also I'm not sure why you need clearBullet(). You already have the index i as well as a reference to the bullet object right there in the main loop.
I created two empty Sprites to serve as layers, bottom_spr and top_spr
When clicking a button, a MovieClip appears and follows your mouse, untill you click, then its position is fixed.
As soon as the button is clicked, I addChild the MovieClip to the correct Sprite.
Unfortunately, the layer system doesn't see to work, because they are layered in the order I place them, the Sprites don't seem to influence it.
How is this possible?
private var ground_spr:Sprite;
private var units_spr:Sprite;
public function Game() {
addEventListeners();
ground_spr = new Sprite();
units_spr= new Sprite();
addChild(ground_spr);
addChild(units_spr);
}
private function addEventListeners(){
groundBtn.addEventListener(MouseEvent.CLICK, clickGroundBtn);
unitBtn.addEventListener(MouseEvent.CLICK, clickUnitBtn);
}
private function clickGroundBtn(event:MouseEvent){
var ground = new Ground_mc();
follow();
ground_spr.addChild(ground);
}
private function clickUnitBtn(event:MouseEvent){
var unit = new Unit_mc();
follow();
units_spr.addChild(unit);
}
Your question is very vague but I will attempt to answer it. If you clarify how you want your objects ordered exactly, I can better answer your question. It is fine to add layer of abstraction over the depth handling, if you need it. In this case I dont see much of a need, but I will show you a few things you could do.
To add an object to the back of the screen:
this.setChildIndex(objName, 0);
To add an object to the front of the screen:
this.setChildIndex(objName, this.numChildren - 1);
To swap the depths of two objects:
this.swapChildren(objA, objB);
//Note this is expecting two DisplayObjects so you may have to do:
this.swapChildren(objA as DisplayObject, objB as DisplayObject);