AS3 - Using Tween on integer? - actionscript-3

Not been using the Tween functions before, so I'd like a little help.
I want to tween between two integers.
Example:
I buffer 360 frames (images).
I want to skip to frame 100 (from frame 1), but I want to use ease to make it look better.
All I need is to tween an integer that I will use for the current image displayed.
So far so good, but I'm not getting how to update my image in the tween:
public function timerEvent(event:TimerEvent):void{
TweenLite.to(this, 2, {_currentFrame: 50, ease:Strong.easeOut});
if (_currentFrame>=358) _currentFrame -= 359;
if (_currentFrame<0) _currentFrame += 359;
var myBitmap:Bitmap = new Bitmap(buffer[_currentFrame+1]);
myBitmap.smoothing = true;
imageBuffer.data = myBitmap;
}

I would seriously recommend using Greensock's TweenLite and TweenMax libraries over the built in Tweening functions.
http://www.greensock.com/tweenmax/
The beauty of these is that you can tween any numeric property of an object, and apply easing, and you can even tween frames of a MovieClip directly by using the Frames plug that is built into TweenMax:
import com.greensock.TweenMax;
import com.greensock.easing.Strong;
TweenMax.to(this,2,{frame:100,ease:Strong.easeOut});
To Tween a counter value is just as easy, and because it doesn't require the Frames plugin you can use the lighter TweenLite:
import com.greensock.TweenLite;
import com.greensock.easing.Strong;
var counter:int = 0;
TweenLite.to(this,2,{counter:100,ease:Strong.easeOut});
Edit to include new code
As the tween runs you can capture an update event which lets you perform actions on the current values of your parameters. You could then do something like this:
TweenLite.to(this, 2, {_currentFrame: 50, ease:Strong.easeOut, onUpdate:updateCallback});
function updateCallback():void
{
var myBitmap:Bitmap = new Bitmap(buffer[_currentFrame+1]);
myBitmap.smoothing = true;
imageBuffer.data = myBitmap;
}

Related

AS3 best way to mass crop tiles from a tilesheet?

Okay so I'm trying to create a program that reads a map from a csv file and then draw each tile using a tilesheet. Reading in the map works fine and I could draw certain tiles depending on the value read in but only if I embedded the images. Obviously this is impractical when it comes to having >20 different tiles; embedding them all just wouldn't be smart.
This is my code for drawing the tiles from the tilesheet.
package
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;
public class Tile extends Sprite
{
[Embed(source="../bin/lib/TileSheet.png")]
private var BitmapClass:Class;
private var tileBitmap:Bitmap = new BitmapClass();
var tileSize = 25;
var tileSheetLength = 20;
var sheetColumns:int = tileBitmap.bitmapData.width / tileSize;
var pt:Point = new Point(0, 0);
var bmp:Bitmap = new Bitmap(new BitmapData(tileSize, tileSize, true, 0));
public function Tile(collide:Boolean, id:int)
{
Draw(id);
}
private function Draw(id:int):void
{
var col:int = id % sheetColumns;
var row:int = Math.floor(id / sheetColumns);
var rect:Rectangle = new Rectangle(col * tileSize, row * tileSize, tileSize, tileSize);
bmp.bitmapData.copyPixels (tileBitmap.bitmapData, rect, pt, null, null, true);
this.addChild(bmp);
}
public function Update():void
{
}
}
}
'
So what I need help with is optimising this code so that I can run it around 1,900 times rather than the 910-911 times it can handle right now before just closing without errors. If there is a better way of doing this please let me know and any constructive criticism is always appreciated!
You have a Tile class which has a BitmapClass instance. Perhaps that should be a static property (belonging to the class, not every instance) to begin with. My guess is you're using the memory for the whole tile sheet every since time you instantiate a single tile which you probably don't want to do.
Another thing I'm noticing is you're creating a new BitmapData for each tile, when in fact you probably just need the tile data (it's id/coordinates) so you can copy pixels into the final BitmapData which gets displayed on stage. Perhaps you need to a class to manage resources(embedded bitmaps) and another to manage the different Tile instance(which should hold render data and references to pixels, but shouldn't store the actual data) and copying to the main buffer.
Also, it's a good idea to use BitmapData's lock() and unlock() functions for performance when doing multiple pixel operations on an image.
Have a look at Lee Brimelow's Sprite Sheet tutorials (part 1,2 and especially 3). They're really easy to follow and useful.
Also, it might be worth having a look at the have a look at the GPU accelerated IsoHill
library.
I've used IsoHill for a project before and it's quite fast, but it's best to get comfortable with the basics first, otherwise this might seem a bit much.

can't add an event listener to a mask AS3 Flash

New to AS3. Trying to do a simple mask exercise, but for some reason when I add event listener to 'myMask', the event doesn't trigger. I tried turning both 'myMask' and 'theMaskee' as sprites and movie clips, but no luck. It does trigger if I don't assign 'myMask' as a mask to 'theMaskee'. It also works if I add the listener directly to the stage, but eventually I want to put many things on the stage, and I'm afraid there will be conflict if it has to listen to the same event but perform several functions... especially if I need them one at a time. I looked through textbooks and the API and mask-related questions other people had, but I can't find anything relating to my specific situation.
(this code is written directly in the timeline)
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.sampler.NewObjectSample;
import flash.events.MouseEvent;
var temp = new backGround();
var myBG:Bitmap = new Bitmap(temp);
temp = new splashMaskee();
var theMaskee:Bitmap = new Bitmap(temp);
var myMask = new MovieClip();
myMask.graphics.beginFill(0x000000, 0);
myMask.graphics.drawRect(0, 0, 800, 600);
myMask.graphics.endFill();
myMask.cacheAsBitmap = true;
theMaskee.cacheAsBitmap = true;
theMaskee.mask = myMask;
addChild(myBG);
addChild(theMaskee);
addChild(myMask);
myMask.addEventListener(MouseEvent.CLICK, myMaskClick);
//stage.addEventListener(MouseEvent.CLICK, myMaskClick);
function myMaskClick(e:MouseEvent):void
{
trace("click");
myMask.graphics.beginFill(0x000000, 1);
myMask.graphics.drawCircle(mouseX, mouseY, 30);
}
Thank you for taking the time
You need to add the listener to theMaskee instead, not your mask.
The mask in AS3 does not implement IEventDispatcher therefore can not catch and dispatch events.
Do this:
theMaskee.addEventListener(MouseEvent.CLICK, myMaskClick);
And it should work. :)
Masks dont take any mouse/keyboard events as it is just a mask and not actually present in the display list.

Ways to animate bezier curves with AS3?

I've been trying to find the best way to animate bezier curves with AS3. By this far following has been the best solution:
import flash.display.*;
import flash.display.Sprite;
import flash.geom.*;
import com.greensock.TweenMax;
import com.greensock.easing.*;
public class Waves extends MovieClip
{
public var piste:Number = stage.stageHeight;
public var piste2:Number = 0;
var a:Sprite = new Sprite();
var coord:Vector.<Number> = new Vector.<Number>();
var com:Vector.<int> = new Vector.<int>();
public function Waves()
{
addChild(a);
coord.push(0, 30);
com.push(1);
coord.push(260, piste, stage.stageWidth, 30);
com.push(3);
tweenNumbers();
}
public function tweenNumbers():void {
TweenMax.to(this, 0.45, {piste:piste2, repeat:-1, yoyo:true, immediateRender:true, ease:Expo.easeOut, onUpdate:draw});
}
public function draw():void {
coord[3] = piste;
a.graphics.clear();
a.graphics.lineStyle(1,0x990000,1);
a.graphics.drawPath(com, coord);
}
}
Do I really have to use graphics.clear to animate curves? Is there more efficient way? If I tween faster than 1 second, rendering lags and you can see the previous line, is there way to get rid of it?
Hmm. Perhaps you should post your used version of TweenMax to properly debug the issue. There seem to be several of them, some use asynchronusly dispatched "update" events, some employ an enterframe listener, thus making sure each update routine is called each frame. So, graphics jittering can occur in an asynchronus scenario.
On the other questions:
Yes, you have to redraw the graphics object in question, this involves calling graphics.clear(). See, the Graphics object is a blackbox entity, you can't directly reach a control point of a curve to tween it somehow. So, in order to change a point on a curve, you have to redraw it.
A more efficient way would be emulating a tween on your Sprite directly, via an enterframe listener and a function similar to Strong.easeOut used in tweening to interpolate coordinates. You will then get rid of the all extra framework included in TweenMax library and will get full control of the event and code flow. This, however, is some work to both emulate yoyo behavior, time setting behavior, framerate behavior (you can switch to "time=frame" approach, eliminating one of the issues) and easing behavior. The tweenNumbers will look like this:
var isYoyo:Boolean=false;
var currentFrame:int;
var maxFrame:int;
function easingFunction(frame:int,maxframe:int,a:Number,b:Number):Number {
var x:Number=Number(frame)/maxframe;
return a+(b-a)*(x*x*(3-2*x)); // 3x^2-2x^3, a double-easing Perlin function
// recreate your needed function here!
}
var piste1:Number=0; // storing start coordinate
private function tweenNumbers():void {
maxFrame=Math.round(0.45*stage.frameRate); // from seconds to frames
currentFrame=0;
isYoyo=false;
a.addEventListener(Event.ENTER_FRAME,onUpdate);
}
private function onUpdate(e:Event):void {
if (!isYoyo) {
currentFrame++;
if (currentFrame==maxFrame) isYoyo=true;
} else {
currentFrame--;
if (currentFrame==0) isYoyo=false;
} // advance time
coords[3]=easingFunction(currentFrame,maxFrame,piste1,piste2);
// tween the coords[3] manually
a.graphics.clear();
a.graphics.lineStyle(1,0x990000,1);
a.graphics.drawPath(com, coord);
// draw updated path
}
No guarantee of desynching, though, but will normally work. Also a desynch (seeing previous line) can possibly happen if you have set stage framerate too high, so the video subsystem of a target device can't draw as many frames at once.

Convert drawn lines into bitmapdata - Bitmap.draw() + Sprite.graphics - wont work

EDIT: The convertation / copy process it self works, I just cant figure out how to tell the bitmapdata, which part of the stage to copy - I tried to solve that problem by movie the canvas to x=0 y=0 didnt show anychanges.
The only thing that showed a change was that I did move the canvas BEFORE drawing to zero, but this is totally buggy because the part of the drawing which has negativ coordinates wont be copied since the coordinate change only affect the bitmap if you do it before you start to paint
OLDER ENTRY:
I want to convert the Sprite.graphics into bitmapData, because I have a drawTool which allowes the user to paint lines, which are located inside the Sprite.grahpics I think.
I need to convert these lines to bitmapdata, because this allows me to deform them later on, but I cant use this
Bitmapdata.draw(Sprite.graphics);
And using only the Sprite instead of Sprite.graphics doesnt show any result =\
help is needed!
Use a matrix if you want to draw only a certain portion and from an origin other than (0,0). There's plenty in the Adobe docs on this, or a good example here:
http://www.quasimondo.com/archives/000670.php
Only use graphics when drawing. The actual Sprite object contains what you want, so following your convention, simply do:
BitmapData.draw(Sprite);
Although for a literal example:
var mySprite:Sprite = new Sprite();
// add lines etc using mySprite.graphics
var myBitmapData:BitmapData = new BitmapData(mySprite.width, mySprite.height);
myBitmapData.draw(mySprite);
I think, you've not completely understand usage BitmapData.draw().
BitmapData.draw() is a All DisplayObject(Sprite, MovieClip, Shape, Text, Video...) drawing possible. because they are have a IBitmapDrawable.(more information refer a adobe document Is the best teacher.)
If you want implement Paint Board. refer a below code. very simple Paint Board. but some help you.
try copy & paste.
import flash.display.Sprite;
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.MouseEvent;
var isDraw:Boolean = false;
var brush:Sprite =new Sprite();
brush.graphics.beginFill(0x0000ff);
brush.graphics.drawCircle(0,0,5);
brush.graphics.endFill();
var canvasData:BitmapData = new BitmapData(600,400, false);
var canvas:Bitmap = new Bitmap(canvasData);
addChild(canvas);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDrawStart);
stage.addEventListener(MouseEvent.MOUSE_UP, onDrawStop);
stage.addEventListener(Event.ENTER_FRAME, render);
function onDrawStart(e:MouseEvent):void
{
isDraw = true;
}
function onDrawStop(e:MouseEvent):void
{
isDraw = false;
}
function render(e:Event):void
{
if(!isDraw) return;
brush.x = mouseX;
brush.y = mouseY;
canvasData.draw(brush,brush.transform.matrix);
}

Accurate BPM event listener in AS3

I'm trying to sync animation to music at a specific BPM. I've tried using the Timer but it isn't accurate when dealing with small intervals in milliseconds. I did some reading and found an alternate method that uses a small silent audio file and the SOUND_COMPLETE event as a Timer.
I used 167ms long sound file with this code.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public class BetterTimer extends EventDispatcher
{
private var silentSound:Sound;
public function BetterTimer(silentSoundUrl:String):void {
super();
silentSound = new Sound( new URLRequest(silentSoundUrl) );
silentSound.addEventListener(Event.COMPLETE, start);
}
public function start():void {
this.timerFired( new Event("start") );
}
private function timerFired(e:Event):void {
dispatchEvent( new Event("fire") );
var channel:SoundChannel = silentSound.play();
channel.addEventListener(Event.SOUND_COMPLETE, timerFired, false, 0, true);
}
}
}
This still doesn't stay on beat. Is the Flash Player capable of accuracy with sound?
You can also use the new Sound API with the SampleDataEvent and basically play your MP3 manually using Sound.extract(). In that case you know the latency up front and can even count up to the sample when your (delayed) event should happen.
This is what we do in the AudioTool and it works very well.
This is very tricky to get right! There's a small lib called BeatTimer that tries to do this. I haven't tried this code myself, but if it does what it claims it should be exactly what you need.
Setting the frame rate so that the event interval is a multiple of the frame rate might help (for example, 167ms equals 6 fps; 12, 18, 24 etc. are then also ok).
If I understood correctly, better solution would be to use the enterframe event. Instead of determining the position of the animation by counting the events, calculate it using elapsed time (getTimer or sound position). This would also make the animation work on slower computers that have lag.
I was looking through the popforge library's AudioBuffer and tried using one of the approach. That's the create a sync sound. The following is what i did.
var syncSamples:ByteArray = new ByteArray();
syncSamples.length = (2646000 / _bpm) << 1; /*44100*60=2646000*/
SoundFactory.fromByteArray(syncSamples, Audio.MONO, Audio.BIT16, Audio.RATE44100, soundInit);
The ms delay is pretty close, eg: at 120 BPM, it's between 509 - 512ms. The question is, am I going in the right direction?