Cycle array of functions with TimerEvent AS3 - arrays

i'm working on a movieclip that features a small array of functions that i'd like to execute at certain intervals.
Here is the code:
var Tmr:Timer = new Timer(1000, 3);
var funcArray:Array = [move1, move2, move3];
var i:Number = 0;
funcArray[i]();
function kick(){
if (i < 2) {i++; funcArray[i](); trace("GO NEXT");}
else {i = 0; funcArray[i](); trace("FROM-START");}
}
function timerX():void{
Tmr.start();
Tmr.addEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMERSTART");
}
function outro(e:TimerEvent):void{
e.currentTarget.removeEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMEREND");
kick();
}
function move1(){
item1.x = 100;
item1.y = 100;
trace("MOVE1");
timerX()
}
function move2(){
item1.x = 300;
item1.y = 400
trace("MOVE2");
timerX();
}
function move3(){
item1.x = 800;
item1.y = 600
trace("MOVE3");
timerX();
}
As you see i've filled it with trace commands because... The dark magic happens after move1() has been played: apparently it starts looping the functions EVERY SECOND instead of 5, like it is depending on an Enter_Frame event.
The trace sequence in output is: MOVE1, TIMERSTART, TIMEREND, MOVE2, TIMERSTART, GO NEXT, TIMEREND, MOVE3, TIMERSTART, GO NEXT, TIMEREND, MOVE1, TIMERSTART, FROM-START...
instead of: MOVE1, TIMERSTART, TIMEREND, GO NEXT, MOVE2, TIMERSTART, TIMEREND, GO NEXT etc.
I also tried to move the kick() function up in the outro one
function outro(e:TimerEvent):void{
kick();
e.currentTarget.removeEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMEREND");
}
And the effect is that it doesn't go past move2().
Where do you think is the error? What was wrong in the logic i used to write down this?
Thanks for your kind help!

Per the timer firing every second, you've defined the Timer to dispatch timer events every 1-second; although, repeating 3 times before firing TIMER_COMPLETE.
var Tmr:Timer = new Timer(1000, 3);
If you want the timer to fire every 5-seconds, you could just:
var Tmr:Timer = new Timer(5000);
Next up, you never stop or reset the timer. You can stop() and start() the timer; or, if you're depending on a specific number of times for the timer to repeat, then reset() the timer.
Since it seems like you simply want an event to fire every 5-seconds infinitely, there's really no need to start / stop / reset / add new listeners - just start the timer and listen for timer events:
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
function timerHandler(event:TimerEvent):void {
// timer fired
}
Putting this all together, if you just want to go from: move1, move2, move3 infinitely repeating:
import flash.utils.Timer;
import flash.events.TimerEvent;
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
var actions:Array = [move1, move2, move3];
var i:uint = 0;
function timerHandler(event:TimerEvent):void {
actions[i]();
if(++i >= actions.length)
i = 0;
}
function move1():void {
trace("move1");
}
function move2():void {
trace("move2");
}
function move3():void {
trace("move3");
}
function exit():void {
timer.reset();
timer.removeEventListener(TimerEvent.TIMER, timerHandler);
timer = null;
}

Related

Differentiate between click and hold & click

I have an html element that can get drag and dropped & also it can be clicked to do another action, I want to differentiate between these two actions so I thought I'd use ng-mousedown & ng-mouseup and count the time difference between them and based on that I'd be able to tell if that was a click or a click and hold (aka drag & drop).
So the element would look something like this:
<a ng-mousedown="mouseDownStudent()" ng-mouseup="mouseUpStudent()"> {{student.name}} </a>
And what I want to do is something along the lines of:
var isMouseDown = false;
var clickAndHoldTime = 0;
$scope.mouseDownStudent = function(){
isMouseDown = true;
while(isMouseDown){
clickAndHoldTime++;
}
}
$scope.mouseUpStudent = function(){
isMouseDown = false;
// If clickAndHoldTime > 100 ... it's a click
}
Of course this won't work and the while loop won't stop, I thought about using $interval but not sure how it would fit in that scenario.
You are very close in your implementation. What you want to do is capture the current time on mousedown and then on mouseup, find the elapsed time. You can do this by capturing the current Date and then finding the difference once you mouseup.
let startTime = 0;
$scope.mouseDownStudent = function() {
// Capture current time
startTime = new Date();
}
$scope.mouseUpStudent = function() {
// Get current time
let currentTime = new Date();
// Get elapsed time in ms
let elapsedTime = currentTime - startTime;
if (elapsedTime > 100) { ... }
}

Arrays / loops with midi.js

I am looking to use a playsound function in midi.js to loop an array, with chords that i select, maybe 4 diff ones. But i can't figure it out. I can get it to do a single array, but not multiple, and it does not loop, only plays the amount of time I set it to (now 8).
window.onload = function () {
MIDI.loadPlugin({
soundfontUrl: "../MIDI.js/examples/soundfont/",
instrument: "acoustic_grand_piano",
onprogress: function(state, progress) {
console.log(state, progress);
},
onsuccess: function () {
for (var i = 0; i < 9; i++){
playsound([37,59,61,71,80])}
}});
var delay =1;
function playsound($chords)
{
var velocity = 127;
MIDI.setVolume(0, 127);
MIDI.chordOn(0, $chords, velocity, delay);
MIDI.chordOff(0, $chords, delay+1);
delay += 1;
}
Your code should work, except that for the timing to work predictably, I found you have to wait a bit after the success callback is called. If you call right after load, notes are played irregularly and out of sequence.
I recommend using a function like playChords below and testing well after load by calling the function with a button press. For example, this function plays three different chords at 1/2 second intervals, a total of 9 times.
chords = [[37,59,61,71,80],[38,60,62,72,81],[39,61,63,73,82]];
function playChords() {
for (var i = 0; i < 9; i++){
playChord(i/2, chords[i%chords.length]);
}
}
function playChord(delay, chord) {
MIDI.chordOn(0, chord, 127, delay);
MIDI.chordOff(0, chord, delay+1);
}

Angularjs freezes in loop suing $timeout to call function multiple times sequential after or less than a second?

I want to be able to call a function repeatedly every second (or faster).
Assuming "startdate" contains the starting date in the text... In my controller:
scope.arrayoftimes=[{time:XXXX},{time:XXXXXX}]
scope.startdate = $('#startingdate'); //start date comes from text in an element
scope.speed = 1000;
scope.index = 0;
scope.timer = $timeout(scope.doSomething(), scope.speed);
scope.doSomething = function() {
var currentTime = scope.startDate.html();
for (var i=scope.index; i < scope.arrayoftimes.length; i++) {
if(currentTime == roundTo(scope.arrayoftimes[i].time)) {
$rootScope.$emit('timeMatch', scope.index);
scope.index++;
break;
}
}
$timeout(scope.doSomething(), scope.speed);
}
The above code frezes my browser on execution. HOWEVER if I stick in an "alert" anywhere after the scope.index++ or inside the if condition, then it works fine (as long as I check "Prevent opening multiple dialog boxes")
scope.arrayoftimes=[{time:XXXX},{time:XXXXXX}]
scope.startdate = "//Insert Starting Date Here"
scope.speed = 1000;
scope.index = 0;
scope.timer = $timeout(scope.doSomething(), scope.speed);
scope.doSomething = function() {
var currentTime = scope.startDate;
for (var i=scope.index; i < scope.arrayoftimes.length; i++) {
if(currentTime == roundTo(scope.arrayoftimes[i].time)) {
$rootScope.$emit('timeMatch', scope.index);
scope.index++;
alert("alert"); // the existence of this alert message and checking prevent from opening more dialog boxes in chrome allows this to execute fine without freezing
break;
}
}
$timeout(scope.doSomething(), scope.speed);
}
What is going on? How can I get around this? Is there a better way to do this?
Your timeout call probably needs to be:
$timeout(function() { scope.doSomething(); }, scope.speed);
or
$timeout(scope.doSomething, scope.speed);
Otherwise you're executing doSomething() immediately which causes an infinite loop.

AS3 Array items not splicing properly

I'm attempting to make a small game where the user mouses over the circles that fall from the ceiling for points. The circles are added to a container and pushed into an array to hold them, and are removed and spliced when they are mouse-over'd or go off stage.
Everything works fine, until two circles are removed at nearly the same time, whether it be from falling off stage at the same time or mousing over two of them extremely fast. When this happens, the child on stage is removed, but the object is still left in the array, meaning another circle cannot take its place, leaving one less circle spawning every time the issue happens.
Code on main timeline:
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
var ballContainer:Sprite = new Sprite();
addChild(ballContainer);
var maxBalls:uint = 10;
var balls:Array = [];
var ballTypes:Array = [GreenBall];
var ballChances:Array = [800];
var ballVelocities:Array = [1.5];
var ballAccelerations:Array = [1.02];
stage.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
while (balls.length < maxBalls){
addBall();
}
}
function addBall():void {
var ballType = ballTypes[0];
var ball = new ballType;
ball.x = Math.ceil(Math.random()*(stage.stageWidth - ball.width));
ball.y = 0 - (ball.height*1.5);
ballContainer.addChild(ball);
balls.push(ball);
}
Code in GreenBall:
import flash.events.Event;
var mainStage = Sprite(root);
var index = mainStage.balls.indexOf(this);
var velocity:Number = mainStage.ballVelocities[0]*randomNumber(0.5, 1.5);
var acceleration:Number = mainStage.ballAccelerations[0];
this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
function onMouseOver(e:MouseEvent):void {
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
this.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
this.y += velocity;
velocity = velocity*acceleration;
if (this.y > stage.stageHeight + this.height){
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
}
function removeBall():void {
mainStage.balls.splice(index, 1);//doesn't get spliced if balls are removed too quickly
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
function randomNumber(min:Number, max:Number):Number {
return Math.random()*(max - min) + min;
}
So what's going on? Did I set something up incorrectly? How can I go about fixing this issue?
Any help would be appreciated greatly.
Your logic is flawed - the index should be calculated when the removal occurs. When you remove objects from an array via splice, the index of all the elements after the one you removed is decreased by one.
This means that if you have 10 balls and remove the first, the index value you have for every other ball will be incorrect and you'll be removing the wrong ball from your array on subsequent removals.
Moving the indexOf statement to the removeBall method should solve the issue:
function removeBall():void
{
var index:int = mainStage.balls.indexOf(this);
if(index >= 0)
{
mainStage.balls.splice(index, 1);
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
}
To make it easy on yourself, you could extend Array and make a remove function:
public dynamic class List extends Array
{
public function remove(item:*):void
{
var i:int = indexOf(item);
if(i >= 0) splice(i, 1);
}
}

Mouse Events for objects in an array in Flash

I am not sure how to make this clear but : Is there a way to allow mouse event register with objects in an array? I have multiple objects being added to stage from an array and i would like to call different functions after said objects are clicked on ? I have this:
function makeEnemies():void
{
//create humans
var tempEnemy:MovieClip;
var wolf:MovieClip;
tempEnemy = new Enemy2();
tempEnemy.cacheAsBitmap = true;
tempEnemy.speed = 20;
tempEnemy.x = Math.round(Math.random() * 800);
tempEnemy.y = Math.round(Math.random() * 480);
addChild(tempEnemy);
enemies.push(tempEnemy);
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy = enemies[i];
if (tempEnemy.x > stage.stageWidth)
{
tempEnemy.x = stage.stageWidth;
}
if (tempEnemy.y > stage.stageHeight)
{
tempEnemy.y = stage.stageHeight;
}
tempEnemy.x += Math.round(Math.random() * tempEnemy.speed);
tempEnemy.y -= Math.round(Math.random() * tempEnemy.speed);
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
makeBite(tempEnemy.x, tempEnemy.y);
removeEnemy(i);
score++;
score_txt.text = String(score);
}
function removeEnemy(idx:int)
{
removeChild(enemies[idx]);
enemies.splice(idx,1);
}
And i get an error
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at veinsVtest_fla::MainTimeline/removeEnemy()
at MethodInfo-67()
You won't need an Array for this job. Like Man of Snow said. Use event.currentTarget will point to the "clicked on Enemy"
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
var clickedOnEnemy:MovieClip = event.currentTarget as MovieClip;
//Now you have your enemy, do whatever you please with him.
makeBite(clickedOnEnemy.x, clickedOnEnemy.y);
//And farewell, my enemy ... time to remove him.
removeChild(clickedOnEnemy);
clickedOnEnemy = null;
score++;
score_txt.text = String(score);
}
* EDIT **
There are several ways to implement "removeAllEnemies".
One way to do it is to use have another MovieClip to hold all enemies created.
So create a movieClip and add it to stage.
var enemiesWrapper : MovieClip = new MovieClip();
addChild(enemiesWrapper);
And then instead of add enemy to root
addChild(tempEnemy); //Instead of doing this
Add them to this MovieClip instead.
enemiesWrapper.addChild(tempEnemy); //Do this instead
Note that your removeChild has to be updated accordingly
enemiesWrapper.removeChild(clickedOnEnemy);
And for "removeAllEnemies" function
function removeAllEnemies() {
while(enemiesWrapper.numChildren > 0) {
enemiesWrapper.removeChildAt(0);
}
}
Replace removeEnemy(i); with removeEnemy(event.currentTarget);
You cannot remove an integer, because it isn't a child. However, I'm assuming you want to remove the MovieClip that was clicked, and it looks like removeEnemy calls removeChild() on the parameter. If not, do you mind showing your removeEnemy function?

Resources