AS3 Tween a variable inside an array - actionscript-3

I have the following code which works perfectly:
updaterVariable = 0;
Tweener.addTween(this, {time:2, transition:"linear", updaterVariable:1000, onUpdate:tweenerUpdate});
I will get the value of updaterVariable tweened from 0 to 1000 in 2 seconds. My question is whether there is a similar way to tween a variable in an array, for example:
updaterVariable[10] = 0;
Tweener.addTween(this, {time:2, transition:"linear", updaterVariable[10]:1000, onUpdate:tweenerUpdate});
I tried the above code, and its not working. Can anyone help?

You can pass your array to the tweener and use index as a field to be changed:
updaterVariable[10] = 0;
Tweener.addTween(updaterVariable, {time:2, transition:"linear", '10':1000, onUpdate:tweenerUpdate});
Not sure which tweener you are using, however it works with TweenMax:
private var arr:Array;
public function Main() {
arr = [];
arr.push(0);
arr.push(0);
arr.push(t);
TweenMax.to(arr, 1000, { '2' : 1000 } );
addEventListener(Event.ENTER_FRAME, onframe);
}
private function onframe(e:Event):void {
trace(arr[2]);//outputs expected numbers
}

Related

AS3 Update Variable Reports Null.. in a pattern

I'm making a space game in AS3 - I have an enemy class named EnemyShip.
I've already registered the Event.ENTER_FRAME and it works correctly - the problem is that my variable, rowXY of type Array, reports both null and a non-null value.. in a pattern.
How can I keep this from happening (or is there an easier way to animate the ships to move in Flash Professional?).
The pattern is as follows:
EnemyShip:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class EnemyShip extends Sprite
{
internal var id:int;
internal var rowOrder:int;
internal var rowXY:Array;
private var dirUp:Boolean = false;
public function EnemyShip()
{
// add event listeners
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
addEventListener(Event.ENTER_FRAME, update);
}
private function update(e:Event):void
{
moveUpAndDown();
trace(rowXY);
function moveUpAndDown():void
{
if (dirUp)
y -= C.ENEMY_SPEED;
else
y += C.ENEMY_SPEED;
}
}
private function onAddedToStage(e:Event):void
{
// get row XY
if (rowOrder == 1)
rowXY = C.ENEMY_ROW_1;
if (rowOrder == 2)
rowXY = C.ENEMY_ROW_2;
if (rowOrder == 3)
rowXY = C.ENEMY_ROW_3;
// set XY position
x = rowXY[0];
y = rowXY[1];
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
internal function destroy():void
{
rowOrder = null;
rowXY = null;
// remove event listeners
removeEventListener(Event.ENTER_FRAME, update);
// remove from display list
parent.removeChild(this);
}
}
}
C.as (Constants):
package
{
// C stands for constants
public class C
{
// ship constants
public static const FIRE_DELAY:int = 500; // milliseconds
// laser constants
public static const LASER_SPEED:int = 30;
public static const POINTS_KILL:int = 10;
// mcScore constants
public static const SCORE_LOC_X:Number = 345;
public static const SCORE_LOC_Y:Number = -120;
// enemy ship constants
public static const ENEMY_ROW_1:Array = [485, -45];
public static const ENEMY_ROW_2:Array = [485, 25];
public static const ENEMY_ROW_3:Array = [485, 95];
public static const ENEMY_SPEED:int = 5; // 5 pixels
public static const ENEMY_Y_MIN:int = -10;
public static const ENEMY_Y_MAX:int = 10;
}
}
If you're trying to animate using Flash Professional, I would take advantage of their built in Motion Tween feature. You could also animate the ship in your code by taking advantage of the Tween class.
I reproduced the behavior that you've got using this for loop ( you can get the same result by instantiating 3 objects and insert 3 others directly to the stage ) :
for(var i:int = 1; i < 7; i++){
var enemy:EnemyShip = new EnemyShip();
enemy.rowOrder = i;
addChild(enemy);
}
here we can see very clear that where i is 4, 5 or 6, rowOrder will be null which also will fire some #1009 errors for all the instances which has the rowOrder greater that 3 from this line :
x = rowXY[0];
So to avoid that, you can, for example, restrict the value of rowOrder to be between 1 and 3, like this, for example :
enemy.rowOrder = 1 + int(Math.random()*3);
you can also set that value inside the EnemyShip class itself.
...
Hope that can help.
The Solution
It was to my knowledge that the event Event.ENTER_FRAME was only passed to objects on the stage, but this is not true.
The problem was in my EnemyShip class' constructor method - the ENTER_FRAME event was being listened to by both on- and off-stage instances - the off-stage instances had not been assigned a rowXY (this happens in the event ADDED_TO_STAGE).
Old Code:
New Code:
So what's the lesson to be learned here?
Event.ENTER_FRAME happens no matter if the object is displayed or not.
In the screenshot you posted you have this code:
if(rowOrder > 3)
rowOrder = 0;
But when you check rowOrder in onAddedToStage() you don't have a case for 0 -- only 1, 2, or 3. So rowXY never gets set when rowOrder is 0.
Either change that code to set rowOrder to 1 instead of 0, or change your other code to be zero indexed.

Removing MC from array AS3

I am having a problem removing a MovieClip from an array.
I am getting this error :
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Spiker/loop()".
The spike (Spiker) is getting removed, but the error still annoys me.. Any idea how to fix this?
Here is my code :
package {
import flash.display.MovieClip;
import flash.events.Event;
public class Spiker extends MovieClip {
public var spikeDirection:int = 1;
private var removed = false;
public function Spiker() {
// constructor code
trace("Spiker added");
addEventListener(Event.ENTER_FRAME, loop);
}
private function loop (evt){
this.x += spikeDirection * 10;
for(var i:int = 0; i < this["parent"].enemyList.length; i++){
if(removed == false){
if(this["parent"].enemyList[i].hitTestObject(this)){
trace("Spikerhit");
removed = true;
remove(evt);
}
}
}
}
public function remove (evt){
removeEventListener(Event.ENTER_FRAME, loop);
this["parent"].removeChild(this);
}
}
Your problem is coming from your for loop, this line exactly :
for(var i:int = 0; i < this["parent"].enemyList.length; i++){
because when your current object has touched an enemy it is removed from its parent, and for the next iteration of the for loop (if there is one), this["parent"] is null and that's why that error is fired as the for loop is verifying every iteration the conditional statement that determines when the looping ends (this["parent"].enemyList.length in your case).
To avoid that, you can use a variable, for example, to store the number of enemies to be used in the for loop.
You can also use the break statement inside your for loop after removing your object to exit that loop.
Hope that can help.

ActionScript: How to Re- setInterval Each Time Function Works?

I have a function that draws a rectangle on the screen (see createInfoPanel())
While drawing rectangle, I am adding 2 text fields on it.
But as you may guess, it is adding those immediately.
I want to delay adding these text fields, then I want to remove these panels after a while.
The problem is, when I set an interval or timer, they won't work after I using once (I had to stop them by clearing/removing, it didn't set them again).
Since my panel is being created each time image changes, I need them to work every time image changes.
So, I have 2 questions:
1- How can I re-set interval each time my createInfoPanel() function works? It won't work anymore after setting and claring once.
2- You can see infoPanel.addChild(titleField); line in addInfoPanel() function. How can I work a smooth animation here? I mean, text appears slowly?
Thanks in advance.
public class ImageRotator extends Sprite
{
private var ... ; //Some variables
public function ImageRotator(xmlPath:String = "images.xml", interval:int = 8301):void
{
timer = new Timer(interval);
loadXML(xmlPath);
}
private function loadXML(file:String):void
{
urlLoader = new URLLoader(new URLRequest(file));
urlLoader.addEventListener(Event.COMPLETE, parseXML);
}
private function parseXML(e:Event):void
{
xml = new XML(e.target.data);
loadImages();
}
private function loadImages():void
{
for (var i:int = 0; i < xml.children().length(); i++)
{
var loader:Loader = new Loader();
loader.load(new URLRequest(xml.children()[i].#src));
imagesVector.push(loader);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, sortImages);
}
}
private function sortImages(e:Event):void
{
imagesCounter++;
for (var i:int = 0; i < imagesVector.length; i++)
{
imagesVector.reverse();
addChild(imagesVector[i]);
}
//I have only 3 images, I needed to set indexes because
//they were covering each other
this.setChildIndex(imagesVector[2], 0);
this.setChildIndex(imagesVector[1], 0);
this.setChildIndex(imagesVector[0], 0);
if (imagesCounter == imagesVector.length)
{
createInfoPanel();
timer.addEventListener(TimerEvent.TIMER, autoChange);
timer.start();
}
}
private function createInfoPanel():void
{
infoPanel.graphics.beginFill(0x000000, 0.0);
infoPanel.graphics.drawRect(0, 0, 967, 138);
infoPanel.graphics.endFill();
////Here I want to run addInfoPanel() function with 2 seconds delay,
////After it starts, I want to run removeInfoPanel() function with 2 more seconds delay
addChild(infoPanel);
}
private function addInfoPanel():void {
titleField.text = xml.children()[infoCounter]. # title;
titleField.x = 425;
titleField.y = 0;
description.text = xml.children()[infoCounter]. # description;
description.x = 427;
description.y = 26;
infoPanel.y = 300;
infoPanel.addChild(titleField);
infoPanel.addChild(description);
}
private function removeInfoPanel():void {
infoPanel.removeChild(titleField);
infoPanel.removeChild(description);
}
private function addActions():void
{
//Some function
}
private function changeImage(e:MouseEvent):void
{
//Image changing function
}
private function changeDepth(e:TweenEvent):void
{
//Some function
}
private function autoChange(e:TimerEvent):void
{
//Some function
}
}
Edit: How I used to work the intervals:
private function createInfoPanel():void
{
//lines above code sample
intervalInfoPanel = setInterval(addInfoPanel,2000);
addChild(infoPanel);
}
private function addInfoPanel():void {
//lines above code sample
clearInterval(intervalInfoPanel);
intervalInfoPanelRemove = setInterval(removeInfoPanel,3500);
}
private function removeInfoPanel():void {
//lines above code sample
clearInterval(intervalInfoPanelRemove);
}
1- How can I re-set interval each time my createInfoPanel() function
works? It won't work anymore after setting and claring once.
how exactly are you resetting your interval? you don't show the code here.
but normally you can reset + re-use a timer like this:
timer.reset();
this will stop and reset the timer's currentCount to 0.
you can then later say timer.start(); and everything should work like it never ran before.
2- You can see infoPanel.addChild(titleField); line in addInfoPanel()
function. How can I work a smooth animation here? I mean, text appears
slowly?
use a tween. add the TextFields with txt.alpha = 0; and then tween the alpha value slowly to 1.0. TweenLite (http://www.greensock.com/tweenlite/) is a great tweening-engine.
import com.greensock.*;
txt.alpha = 0.0;
TweenLite.to(txt, 1, {alpha:1.0, delay:0.4});

Fading out volume on a movieClip

I've looked around the net on this issue, and came up with the following code to fade out the volume on my movieclip:
var myTransform = new SoundTransform();
myTransform.volume = 1;
loaderClip2[indexNumber].soundTransform = myTransform;
audioTween = new TweenLite(myTransform, 2, {volume:0});
My movie clip is stored in the Array loaderClip2 at index position determined by the variable indexNumber. This code does not produce the desired fade. Can anyone see what is the problem here?
var myTransform:SoundTransform = new SoundTransform(1);
TweenLite.to(myTransform, 1, {volume:0, onUpdate:updateChannel, onUpdateParams:[indexNumber]});
function updateChannel(index:int):void {
loaderClip2[index].soundTransform = myTransform;
}
Try this code:
private function updateChannel() : void {
var st : SoundTransform = new SoundTransform(loaderClip2[indexNumber].soundTransform.volume, 0 );
loaderClip2[indexNumber].soundTransform = st;
}
TweenLite.to(loaderClip2[indexNumber], 4, { volume:.5, ease:Strong.easeInOut, onUpdate:updateChannel } );
Set your own parameters
Alright guys, after trying everything possible with tweenlite, I figured out another solution using good-old-fashioned ENTER_FRAME events. This is as straight-forward as possible, wish I had thought of it before:
so in a previous function I just do this:
myClip.addEventListener(Event.ENTER_FRAME, fadeAudio);
and then later flush out the event function (or whatever it is called):
var audioshift = 1;
function fadeAudio(e : Event) : void {
audioshift -= .05;
if (audioshift <= 0) {
audioshift = 0;
trace("fadeAudio complete");
e.target.removeEventListener(Event.ENTER_FRAME, fadeAudio);
}
var st : SoundTransform = new SoundTransform(audioshift, 0);
e.target.soundTransform = st;
}
Easy as pie.

Why can't my class see an array on the timeline?

I have a class that controls an enemy. From within that class, it checks for collisions with an array on the main timeline. I've done it this way before and it works fine, so I have no idea what I've done wrong this time. It keeps giving me an
ReferenceError: Error #1069: Property
bulletArray not found on
flash.display.Stage and there is no
default value.
error from within the enemy class.
Here's my code (shortened to remove the unimportant parts):
On timeline:
var bulletArray:Array = new Array();
function shoot(e:TimerEvent)
{
var bullet:MovieClip = new Bullet(player.rotation);
bullet.x = player.x;
bullet.y = player.y;
bulletArray.push(bullet);
stage.addChild(bullet);
}
In class:
private var thisParent:*;
thisParent=event.currentTarget.parent;
private function updateBeingShot()
{
for (var i=0; i<thisParent.bulletArray.length; i++) {
if (this.hitTestObject(thisParent.bulletArray[i]) && thisParent.bulletArray[i] != null) {
health--;
thisParent.bulletArray[i].removeEventListener(Event.ENTER_FRAME, thisParent.bulletArray[i].enterFrameHandler);
thisParent.removeChild(thisParent.bulletArray[i]);
thisParent.bulletArray.splice(i,1);
}
}
Any help would be greatly appreciated! Thanks.
My guess is that event.currentTarget is the instance where you declared the bulletArray variable. Using event.currentTarget.parent will refer to stage outside your scope. I donĀ“t know how you declare the listeners. Try using event.target instead of event.currentTarget and see if you get the same error.
My advice is that you put all your code in a class.
If you are going to do it this way you need to pass in a reference to the timeline.
private var _timeline:Object;
// constructor
public function YourClass(timeline:Object) {
_timeline = timeline;
}
private function updateBeginShot() {
// ..
trace(_timeline.bulletArray); // outputs [array contents]
// ..
}