Movieclips freeze during tween - actionscript-3

I've written this thing that displays little star graphics randomly, tweens them down the screen, and then removes them. I'm having a problem though where after maybe 10 seconds some of the stars freeze in place mid tween and then just stay there.
Here's my code:
// Create Random Variables.
var xPosition:int;
var yPosition:int;
// Animate Stars.
function stars ():void {
//Defines random starting position for stars.
xPosition = Math.floor(Math.random()*(540))+5;
yPosition = Math.floor(Math.random()*(2))+5;
//Add and position stars.
var newStar:star = new star();
newStar.x = xPosition;
newStar.y = yPosition;
addChild(newStar);
//Tween stars.
var tweenStar:Tween = new Tween(newStar, "y", None.easeOut, yPosition, stage.stageHeight, 4, true);
//Event listener checks star tween.
tweenStar.addEventListener(TweenEvent.MOTION_FINISH, removeStar);
//Remove stars when tween is complete.
function removeStar(e:TweenEvent):void {
removeChild(newStar);
tweenStar.removeEventListener(TweenEvent.MOTION_FINISH, removeStar);
}
}

Your tweens are being picked up by the garbage collector, what you need to do is create a global array to store your tweens after you create them so the garbage colletor does not get them.
var tweens:Array = [];
and then add tweens to it
var tweenStar:Tween = new Tween(newStar, "y", None.easeOut, yPosition, stage.stageHeight, 4, true);
tweens.push(tweenStar)
Also if possible use TweenLite, it's a lot better then Adobe's standard tween, and you won't have to worry about losing tweens to the garbage collector.

Related

How can I create a simple "slider" with Flash AS3

I'm trying to create a jQuery-like slider in Flash. I'm currently using the tween class to move an object's x position, but it's slightly buggy. I have to use flash because it's being incorporated into a brochure software that works best with Flash.
When I click on the right arrow button, the object moves to a new x position. When I click on the button again, it moves, but sometimes it jumps back to the current position. When I click on the left button, it sometimes overshoots the destination.
I can create a simple "click to go to next frame" type of scroller, but it wouldn't provide the same tween/scrolling effect.
This is the code I'm using:
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;
var scrollTweenRight:Tween = new Tween(mc_scroll, "x", Strong.easeOut, mc_scroll.x, mc_scroll.x-1940, 3, true);
scrollTweenRight.stop();
var scrollTweenLeft:Tween = new Tween(mc_scroll, "x", Strong.easeOut, mc_scroll.x, mc_scroll.x+1940, 3, true);
scrollTweenLeft.stop();
// functions
function scrollRight(e:MouseEvent){
scrollTweenRight.start();
}
function scrollLeft(e:MouseEvent){
scrollTweenLeft.start();
}
// listeners
btn_right.addEventListener(MouseEvent.CLICK, scrollRight);
btn_left.addEventListener(MouseEvent.CLICK, scrollLeft);
Use some library like TweenLite/TweenMax/Tweener, the built in one is not too good (and pretty slow as well).
If you want to move in regular 'steps', do not use mc.x to determine the final position as that will be taken upon each click on the button. Always store the 'final' (desired) position in a var. Example:
var shouldMoveToX:Number = 0; //initial position value
var step:Number = 500;
function scrollLeft(e:MouseEvent):void {
shouldMoveToX += step;
//kill the tween if any - I dunno if this is necessary, I haven't used Tween class in ages
//start the tween, pass the final position as shouldMoveToX
}
EDIT:
Oh I see what the problem is now - you are passing the mc's X pos into the constructor of Tween and you actually never update it. The tween will always begin from this point. Put that into your scrollLeft/Right functions:
function scrollRight(e:MouseEvent){
scrollTweenRight.begin = mc_scroll.x;
scrollTweenRight.start();
}
What I wrote above still applies, always use the shouldMoveToX as final position, otherwise it will have irregular movement when fast clicking.
...so your full code should look something like this:
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;
var scrollTweenRight:Tween = new Tween(mc_scroll, "x", Strong.easeOut, mc_scroll.x, mc_scroll.x, 3, true);
scrollTweenRight.stop();
var scrollTweenLeft:Tween = new Tween(mc_scroll, "x", Strong.easeOut, mc_scroll.x, mc_scroll.x, 3, true);
scrollTweenLeft.stop();
var step:Number = 100;
var shouldMoveToX:Number = mc_scroll.x;
// functions
function scrollRight(e:MouseEvent){
scrollTweenRight.begin = mc_scroll.x;
shouldMoveToX -= step;
scrollTweenRight.finish = shouldMoveToX;
scrollTweenRight.start();
}
function scrollLeft(e:MouseEvent){
scrollTweenLeft.begin = mc_scroll.x;
shouldMoveToX += step;
scrollTweenLeft.finish = shouldMoveToX;
scrollTweenLeft.start();
}
// listeners
btn_right.addEventListener(MouseEvent.CLICK, scrollRight);
btn_left.addEventListener(MouseEvent.CLICK, scrollLeft);

Enemy move randomly

To make things quick, I have an arrangement of tiles that a player and an enemy are on.
public static var floor1:Array = new Array(7);
floor1[0] = [0,1,1,1,1,1,0];
floor1[1] = [1,1,1,1,1,1,1];
floor1[2] = [1,1,1,0,1,1,1];
floor1[3] = [1,1,0,0,0,1,1];
floor1[4] = [1,1,1,0,1,1,1];
floor1[5] = [1,1,1,1,1,1,1];
floor1[6] = [0,1,1,1,1,1,0];
public function Main()
{
var tilew:int = 60;
var tileh:int = 60;
for (var i:int=0; i<floor1.length; i++)
{
for (var u:int=0; u<floor1[i].length; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(floor1[i][u]);
cell.x = ((u-i)*tileh);
cell.y = ((u+i)*tilew/2);
addChild(cell);
cell.addEventListener(MouseEvent.ROLL_OVER, mouseover);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseout);
cell.addEventListener(MouseEvent.CLICK, mouseclick);
cell.addEventListener(Event.ENTER_FRAME, beginfloor1);
}
}
var player:Player = new Player();
addChild(player);
player.mouseEnabled = false;
player.x = 5 * (tileh);
player.y = 5 * (tilew/2);
var enemy:Enemy = new Enemy();
addChild(enemy);
enemy.mouseEnabled = false;
enemy.x = 9 * (tileh);
enemy.y = 9 * (tileh/2);
My goal is to have the enemy move randomly on tiles in his range. What I did was create a square graphic called enemyVisionArea that checks which tile is hitting the enemy, which is basically surrounding tiles.
I have a timer function that tells the enemy to move every 5 seconds if the player isn't near him and if he's next to an available tile.
function timerenemy (event:TimerEvent){
if (enemy.enemyVisionArea.hitTestObject(enemyMover) && !player.visionPoint.hitTestObject(enemyMover.tileMiddle))
{
enemy.x = (enemyMover.x)+55;
enemy.y = (enemyMover.y)+20;
trace("moved");
}
}
enemyMover is a variable that I made equal to the tile objects.
function beginfloor1(event:Event)
{
enemyMover = event.currentTarget as Tile;
}
It just stays where it is. I'm just want to have the enemy move on its own on any tile that its enemyVisionArea is hitTesting a nearby tile. The beginfloor1 function doesn't seem to be working. Is there any way I can declare enemyMover = event.currentTarget as Tile and have the enemy move on a random tile that its enemyVisionArea is hitTesting?
If this is confusing, I can post the full code.
You are assigning 49 enterframe listeners which are called in sequence, and they ALL change one single variable to the cell they are attached to. Of course, the last tile is what's always assigned.
I expect that you want an enemy to check if there's a tile available for it to move to. You are essentially checking for one tile which is enemyMover - how do you determine what's that tile? You have to check all available tiles that are around the enemy, make a list of them and select one out of that list that's not the current tile, then move the enemy there.
So, first you need a complete tileset to be addressable from somewhere. The best way will be to declare a class-wide var tileset:Array and fill it where you make new tiles. Drop the Event.ENTER_FRAME listener from the code there, as it's useless. Then, in your timerevent that's for the enemy you do check all of the tileset if they are within your enemy's vision area (you use hitTestObject, I'd use clear distance grid-wise or coordinate-wise - it's a whole lot faster), if so, you add them to the TEMPORARY array you create within that function. Of course, if your enemy is at the currently processed cell, you ignore it - you have to move your enemy, not make him stand in place. Then, select (somehow, it's up to you) what cell your enemy should move to, and execute a move. Yes, if you want your enemy to move randomly, select a cell at random by its index via Math.floor(Math.random()*selectedcells.length).

how to remove a child object by removeChild()?

I'm trying to make a custom animated/shooter, from this tutorial: http://flashadvanced.com/creating-small-shooting-game-as3/
By custom, I mean customized, ie, my own version of it.
In it's actionscript, there's a timer event listener with function: timerHandler()
This function adds and removes child "star" objects on the stage (which the user has to shoot at):
if(starAdded){
removeChild(star);
}
and :
addChild(star);
Code works great, but error occurs on scene 2.
The code works great, and I even added some code while learning via google and stackflow to this flash file. I added Scene 2 to it too, and had it called after 9 seconds of movie time. But when it goes to Scene 2, it still displays the star objects and I've been unable to remove these "star" object(s) from Scene 2.
Here's the code I added:
SCENE 1:
var my_timer = new Timer(5000,0); //in milliseconds
my_timer.addEventListener(TimerEvent.TIMER, catchTimer);
my_timer.start();
var myInt:int = getTimer() * 0.001;
var startTime:int = getTimer();
var currentTime:int = getTimer();
var timeRunning:int = (currentTime - startTime) * 0.001; // this is how many seconds the game has been running.
demo_txt.text = timeRunning.toString();
function catchTimer(e:TimerEvent)
{
gotoAndPlay(1, "Scene 2");
}
SCENE 2:
addEventListener(Event.ENTER_FRAME,myFunction);
function myFunction(event:Event) {
timer.stop();
timer.removeEventListener(TimerEvent.TIMER, timerHandler);
stage.removeChild(star);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, cursorMoveHandler);
my_timer.stop(); // you might need to cast this into Timer object
my_timer.removeEventListener(TimerEvent.TIMER, catchTimer);
Mouse.show();
}
stop();
=====================================================
I'm quite new as3, and have just created an account on StackOverflow... even though I've known and read many codes on it since quite some time.
Here's the Edited New Complete Code:
//importing tween classes
import fl.transitions.easing.*;
import fl.transitions.Tween;
//hiding the cursor
Mouse.hide();
//creating a new Star instance
var star:Star = new Star();
var game:Game = new Game();
//creating the timer
var timer:Timer = new Timer(1000);
//we create variables for random X and Y positions
var randomX:Number;
var randomY:Number;
var t:int = 0;
//variable for the alpha tween effect
var tween:Tween;
//we check if a star instance is already added to the stage
var starAdded:Boolean = false;
//we count the points
var points:int = 0;
//adding event handler on mouse move
stage.addEventListener(MouseEvent.MOUSE_MOVE, cursorMoveHandler);
//adding event handler to the timer
timer.addEventListener(TimerEvent.TIMER, timerHandler);
//starting the timer
timer.start();
addChild(game);
function cursorMoveHandler(e:Event):void{
//sight position matches the mouse position
game.Sight.x = mouseX;
game.Sight.y = mouseY;
}
function timerHandler(e:TimerEvent):void{
//first we need to remove the star from the stage if already added
if(starAdded){
removeChild(star);
}
//positioning the star on a random position
randomX = Math.random()*500;
randomY = Math.random()*300;
star.x = randomX;
star.y = randomY;
//adding the star to the stage
addChild(star);
//changing our boolean value to true
starAdded = true;
//adding a mouse click handler to the star
star.addEventListener(MouseEvent.CLICK, clickHandler);
//animating the star's appearance
tween = new Tween(star, "alpha", Strong.easeOut, 0, 1, 3, true);
t++;
if(t>=5) {
gotoAndPlay(5);
}
}
function clickHandler(e:Event):void{
//when we click/shoot a star we increment the points
points ++;
//showing the result in the text field
points_txt.text = points.toString();
}
And on Frame 5 :
//timer.stop();
//timer.removeEventListener(TimerEvent.TIMER, timerHandler);
// uncomment lines above if "timer" is something you've made
stage.removeChild(star);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, cursorMoveHandler);
timer.stop(); // you might need to cast this into Timer object
timer.removeEventListener(TimerEvent.TIMER, timerHandler);
Mouse.show();
stop();
There's no Scene 2 now, in this new .fla file...
Here's a screenshot of the library property of my flash file...: http://i.imgur.com/d2cPyOx.jpg
You'd better drop scenes altogether, they are pretty much deprecated in AS3. Instead, use Game object that contains all the cursor, stars and other stuff that's inside the game, and instead of going with gotoAndPlay() do removeChild(game); addChild(scoreboard); where "scoreboard" is another container class that will display your score.
Regarding your code, you stop having a valid handle to stage that actually contains that star of yours, because your have changed the scene. So do all of this before calling gotoAndPlay() in your catchTimer function.
function catchTimer(e:TimerEvent)
{
//timer.stop();
//timer.removeEventListener(TimerEvent.TIMER, timerHandler);
// uncomment lines above if "timer" is something you've made
stage.removeChild(star);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, cursorMoveHandler);
my_timer.stop(); // you might need to cast this into Timer object
my_timer.removeEventListener(TimerEvent.TIMER, catchTimer);
Mouse.show();
gotoAndPlay(1, "Scene 2");
}
And the code for Scene 2 will consist of a single stop() - until you'll add something there. Also, there should be no event listeners, especially enter-frame, without a code to remove that listener! You add an enter-frame listener on Scene 2 and never remove it, while you need that code to only run once.
Use getChildAt.
function catchTimer(e:TimerEvent)
{
var childCount:int = this.numChildren - 1;
var i:int;
var tempObj:DisplayObject;
for (i = 0; i < childCount; i++)
{
tempObj = this.getChildAt(i);
this.removeChild(tempObj);
}
gotoAndPlay(1, "Scene 2");
}
This will remove all the children you added on scene 1 before going to scene 2.

Drawing a bitmap based on a transformed movieclip

I'm drawing bitmaps of movieclips which I then feed into my hittest function to test for collisions. However, I'm not quite sure how i would add to the code below to take into account and draw bitmaps for movieclips which have been scaled and/or rotated. The below code obviously only works for non-transformed movieclips. I've included in comments code which i've already tried but to no success.
When adding the drawn bitmap to the stage, no matter whether the movieclip in question is transformed or not, the drawn bitmap is "cut off" and incorrectly drawn - it appears to only draw a section of it. However, this does not particularly affect the collision testing for the non-transformed movieclips, but has an adverse effect on transformed movieclips.
All of the movieclips I want to be drawn have been created through the graphics property.
//for example:
var mymc:MovieClip = new MovieClip();
var g:Graphics = mymc.graphics;
g.moveTo(0,0);
g.lineTo(17.5,0);
g.lineTo(8.75,17.5);
g.lineTo(-8.75,17.5);
g.lineTo(0,0);
main code:
for each(var mc:MovieClip in impassable) {
//var bMatrix:Matrix = new Matrix();
//bMatrix.scale(mc.scaleX, mc.scaleY);
//bMatrix.rotate(mc.rotation * (Math.PI/180));
var bData:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
//bData.draw(mc, bMatrix);
bData.draw(mc);
var bitmap:Bitmap = new Bitmap(bData);
bitmap.x = mc.x;
bitmap.y = mc.y;
var HitTest:Number = newCollision(bitmap, centerX, centerY, 13.7);
Any thoughts? thanks
This function will create a BitmapData clone of a DisplayObject, taking into account its transform matrix, though it doesn't take into account bitmap filters. (Based on this answer.)
function createBitmapClone(target:DisplayObject):BitmapData {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
return cloneData;
}
When you call successive transforms on a Matrix, the ordering is very important and can really mess things up.
Luckily there is a helper method that allows you to specify translation, rotation and scaling in one go and avoid those issues - createBox
For your case, something like this:
var matrix:Matrix = new Matrix();
matrix.createBox(mc.scaleX, mc.scaleY, mc.rotation*Math.PI/180, 0, 0);
(the two zeros are for x and y translation)

As3 How to remove or update bitmapdata for a new level?

I'm making a maze game. The character can't walk through the walls of the maze (because of a collition detection between the bitmapdata from the character and the bmd from the walls). When the character arrives at a door, the next level/frame should appear with a new maze (new bounds)
For the next level (next frame), I made a new maze with different walls. But the bitmapdata from the first maze is still 'active'. So even though there's a new maze, the bitmapdata from the previous walls is invisible but still drawn on the stage.
My question to you is:
I want to change the bounds/maze every frame, how can I remove the previous bitmapdata so the character won't walk through the bounds of the next maze? Or is it possible to make an array from the different 'bounds'?
stop();
var isRight:Boolean=false;
var isLeft:Boolean=false;
var isUp:Boolean=false;
var isDown:Boolean=false;
var speed:int = 10;
var mazeRect:Rectangle = bounds.getBounds(this);
var charRect:Rectangle = char.getBounds(this);
var boundsBmpData = new BitmapData(mazeRect.width, mazeRect.height, true, 0);
var charBmpData = new BitmapData(charRect.width, charRect.height, true, 0);
boundsBmpData.draw(bounds);
charBmpData.draw(char);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
stage.addEventListener(Event.ENTER_FRAME, moving);
function keyPressed(event:KeyboardEvent):void
{
if(event.keyCode==39){
isRight=true}
if(event.keyCode==37){
isLeft=true}
if(event.keyCode==38){
isUp=true}
if(event.keyCode==40){
isDown=true}
}
function keyReleased(event:KeyboardEvent)
{
if(event.keyCode==39){
isRight=false}
if(event.keyCode==37){
isLeft=false}
if(event.keyCode==38){
isUp=false}
if(event.keyCode==40){
isDown=false}
}
function moving(e: Event): void
{
var newx: Number = char.x - (isLeft ? speed : 0) + (isRight ? speed : 0);
var newy: Number = char.y - (isUp ? speed : 0) + (isDown ? speed : 0);
if(!boundsBmpData.hitTest(new Point(bounds.x, bounds.y),
255,
charBmpData,
new Point(newx, newy),
255))
{
char.x = newx;
char.y = newy;
}
if(char.hitTestObject(door))
{
onHitTest();
}
}
function onHitTest() : void
{
nextFrame();
}
Maybe try calling dispose() on old BitmapData first and then create new one?
After looking at the FLA, there were a few issues.
the main one is that though you switched frames, you did not reset your pointers to the bounds object, the door object, and the char object. So you were still tied to the old ones programmatically, though not visually.
I put the declarations into a method called setupFrame(), and call it from your onHitTest() method.
I added a check in onHitTest() to make sure that the bounds object exists in the current frame before setting up the frame. If not, the game stops.
The actions and char layers now extend across the entire game timeline, since they are reused.
char object is now repositioned each frame using points found in the startPts array, instead of having to recreate it each time.
removed the event listeners during the frame setup, and add them at the end of the frame setup. This prevents possible errors from listening to the events.
This is a pretty good effort at creating a simple game engine. Just fyi, gamedev.stackexchange.com is a place devoted to all levels of game development, and you can ask more theoretical questions there.
HTH!