AS3: repeating function in sigle frame timeline - actionscript-3

I'm very new in action script. I have single frame timeline and there is function that moves movie clip verticaly. i want to repeat this only three times.
The code works, I'm just not sure if this is the correct way or if it's too complicated.
var pocet:Number = 0;
pruh.addEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
function fl_AnimateVertically(event:Event)
{
if (pruh.y >= stage.stageHeight) {
pocet++;
}
if (pruh.y < stage.stageHeight) {
pruh.y += 3;
}
else {
pruh.y = 0 - pruh.y;
}
if (pocet == 3) {
pruh.removeEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
}
}
thanx

Congratulations on achieving your goal.
Your code could be improved in terms of readability. You have fl_AnimateVertically as a descriptive name, but other than that it's kind of hard to figure out what's going on exactly. I mean sure it adds 3 to y which probably results in movement, but it's not trivial to understand the exact behaviour.
That's why you want to use abstraction or more of a top down approach as it often called..
What you are doing at the moment is adding a value to the coordinate, which as a result creates an animation. What you actually want is to create an animation, without going into details what that actually means.
And sure enough, people created animations with code before. That's why you can create an animation in the abstract sense: An animation is the change of a property of an object over time. In the realm of flash an animation is called a tween and there's a class doing exactly that..
Let's take the example code there:
var myTween:Tween = new Tween(myObject, "x", Elastic.easeOut, 0, 300, 3, true);
And apply it to your situation.
var verticalAnimation:Tween = new Tween(pruh, "y", Elastic.easeOut, pruh.y, stage.stageHeight, 3, true);
You have to adjust the duration to your liking. I hope you see how this is easier to read and maintain, because you specify properties of the animation like duration. You can also specify easing, which makes the motion more interesting.
Ok, this is only one animation, but you want 3, right?
More precisely, you want to do that same animation again, when it finished.
And you can do exactly that:
var animationCount:uint = 0;
var verticalAnimation:Tween = new Tween(pruh, "y", Elastic.easeOut, pruh.y, stage.stageHeight, 3, true);
verticalAnimation.addEventListener(TweenEvent.MOTION_FINISH, onMotionFinish); // wait for the animation to be finished
function onMotionFinish(e:TweenEvent):void
{
animationCount++; // add 1 to the counter
if(animationCount >= 3) // check how many times the animation finished so far
{
// if it was the last one, remove the listener
verticalAnimation.removeEventListener(TweenEvent.MOTION_FINISH, onMotionFinish);
}
else
{
// otherwise rewind and start again
verticalAnimation.rewind();
verticalAnimation.start();
}
}
There are other libraries than this built in Tween class that are far more powerful.
The one from greensock is very popular and easy to use you can find the documentation for the flash version here

Try this
var pocet:Number = 0;
pruh.addEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
var startY:int=pruh.y;
function fl_AnimateVertically(event:Event)
{
if (pruh.y >= stage.stageHeight) {
pocet++;
pruh.y=startY;
}
if (pruh.y < stage.stageHeight) {
pruh.y += 3;
}
else {
pruh.y = 0 - pruh.y;
}
if (pocet ==3) {
pruh.removeEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
trace("done");
}
}

Related

AS3 tween object not working with .hitTestObject()

I am having a major problem in my new browser app.
Okay so I made game where different cubes (squares) spawn at the top of the screen and I use the Tween class to make them go down the screen and then disappear.
However I want to detect a collision when a cube hits the player (that is also a flying cube).
I tried everything, truly everything but it does not seem to work. The problematic thing is that when I remove the "Tween" function it does detect collision with the hitTestObject method but when I add the "Tween" line collision won't be detected anymore.
It looks like this:
function enemiesTimer (e:TimerEvent):void
{
newEnemy = new Enemy1();
layer2.addChild(newEnemy);
newEnemy.x = Math.random() * 700;
newEnemy.y = 10;
if (enemiesThere == 0)
{
enemiesThere = true;
player.addEventListener(Event.ENTER_FRAME, collisionDetection)
}
var Tween1:Tween = new Tween(newEnemy, "y", null, newEnemy.y, newEnemy.y+distance, movingTime, true);
}
And the collision detection part:
private function collisionDetection (e:Event):void
{
if (player.hitTestObject(newEnemy))
{
trace("aaa");
}
}
I am desperate for some information/help on the topic, it's been bugging me for days.
Thanks for your time, I would be very happy if someone could help me out^^
First, make sure the "newEnemy" instance and the "player" instance are within the same container. If they are not, their coordinate systems might not match up and could be the source of your problem.
Otherwise, you need to keep a reference to each enemy instance you create. It looks like you are only checking against a single "newEnemy" variable which is being overwritten every time you create a new enemy. This might be why you can successfully detect collision between the player and the most recent "enemy" instance.
So... you need a list of the enemies, you can use an Array for that.
private var enemyList:Array = [];
Every time you create an enemy, push it to the Array.
enemyList.push(newEnemy);
In your "collisionDetection" function, you need to loop through all of the enemies and check if the player is touching any of them.
for(var i:int = 0; i < enemyList.length; i++)
{
var enemy = enemies[i];
if (player.hitTestObject(enemy))
{
trace("Collision Detected!");
enemy.parent.removeChild(enemy); // remove the enemy from the stage
enemies.splice(i, 1); // remove the enemy from the list
}
}
I'd suggest that you move to TweenMax, it just might solve your problem, and in my experience it's much better in every possible way.
Scroll down the following page to see a few variations of this library, I myself use TweenNano, they're completely free of charge:
https://greensock.com/gsap-as
I think some plugins cost money, but I doubt you'll ever need them.

AS3: Fast hovering doesn't execute rollOut

I'm having a serious problem that is getting me nervous:
I've made a button _btn that includes ROLLOVER and ROLLOUT animations with coding (an nested movieclip instance called barra that increases to half alpha when you hover over and decreases when you hover out).
[Here it should go a descriptive image but I'm new and I need 10 reputation. I'll appreciate your help]
This works perfectly but the problem occurs when I move my cursor very quickly from one point to another, with the button in between. It seems that the ROLLOUT function is not detected, so the ROLLOVER animation keeps working (and if you look carefully, the animation stops for a few seconds and then continues).
[Here it should go another descriptive image too]
This is the code in the Actions layer:
//Funciones ROLL OVER
function _btnOver(event:MouseEvent):void {
_btn.buttonMode = true;
_btn.addEventListener(Event.ENTER_FRAME,_btnFadeIn);
}
function _btnFadeIn(event:Event):void {
_btn.barra.alpha += 0.1;
if (_btn.barra.alpha >= 0.5)
{
_btn.removeEventListener(Event.ENTER_FRAME,_btnFadeIn);
}
}
_btn.addEventListener(MouseEvent.ROLL_OVER,_btnOver);
//Funciones ROLL OUT
function _btnOut(event:MouseEvent):void {
_btn.addEventListener(Event.ENTER_FRAME,_btnFadeOut);
}
function _btnFadeOut(event:Event):void {
_btn.barra.alpha -= 0.1;
if (_btn.barra.alpha <= 0.2)
{
_btn.removeEventListener(Event.ENTER_FRAME,_btnFadeOut);
}
}
_btn.addEventListener(MouseEvent.ROLL_OUT,_btnOut);
Click here if you want to download the FLA and SWF files, so you can see the problem clearly.
I barely know how to use ActionScript 3 (my only programming knowledge is Processing) and I don't have time now to learn it from head to toe, but I've researched about the problem and it's still not clear.
With tutorials and guides, I managed to learn how to create and understand this code, and I think the problem might be in the functions of the events ROLL_OVER and ROLL_OUT, which contain the addEventListener of the ENTER_FRAME events (where the animations actually are), respectively. But I don't know exactly what I have to do to fix it, what should I add or change.
I would be really glad if someone could help with this, I'm frustrated! What do you recommend me to do?
Thanks in advance
(PD: I don't understand most of the programming language. If you can be as clear and direct as possible, I'll really appreciate it)
Apparently your troubles lay in incoherent animation sequence by using enter frame listeners. You are running two independent listeners, both altering alpha of a single object, this creates a conflict, only one will work (you can determine which if you add both at once and trigger events, the resultant alpha value will indicate which listener changes it last) and you apparently expect one to do a fade in while the other to do a fade out. Instead, you should use one listener (probably even persistent) and give your object "target alpha" property as well as delta to change alpha per frame. An example:
var bbta:Number=0.2; // btn.barra's target alpha
_btn.addEventListener(Event.ENTER_FRAME,_btnFade);
function _btnFade(e:Event):void {
var a:Number=_btn.barra.alpha;
if (Math.abs(a-bbta)<1e-8) return;
// no sense of setting alpha with minuscule difference
const delta:Number=0.1; // how fast to change per frame
if (a>bbta) {
a-=delta;
if (a<=bbta) a=bbta;
} else {
a+=delta;
if (a>=bbta) a=bbta;
}
_btn.barra.alpha=a;
}
function _btnOver(event:MouseEvent):void {
_btn.buttonMode = true; // move this elsewhere, if you don't cancel buttonMode
bbta=0.5; // set target alpha, the listener will do a fade-in
}
function _btnOut(event:MouseEvent):void {
bbta=0.2; // set target alpha, the listener will do a fade-out
}
I edited some code in here, basically i am checking hover state onLoop function, so you can change your settings on here
import flash.events.Event;
var isRolledOver:Boolean = false;
//Funciones ROLL OVER
function _btnOver(event:MouseEvent):void {
isRolledOver = true;
}
function _btnOut(event:MouseEvent):void {
isRolledOver = false;
}
_btn.addEventListener(MouseEvent.ROLL_OVER,_btnOver);
_btn.addEventListener(MouseEvent.ROLL_OUT,_btnOut);
this.addEventListener(Event.ENTER_FRAME,onLoop);
function onLoop(e){
if(this.isRolledOver){
if(_btn.barra.alpha < 0.5) _btn.barra.alpha += 0.1;
}
else{
if(_btn.barra.alpha > 0.5 || _btn.barra.alpha > 0) _btn.barra.alpha -= 0.1;
}
}
I added the sample fla in case

Asynchronous Entity Rendering in Cesium 1.7+

Good Day all,
I've been working with Cesium for a bit now and I started when Primitive Collections were the thing to use. I had click and drag primitive rendering working, but now I want to upgrade Cesium and move on to entities. I moved over the code, refactored, and can click and drag to draw shapes; however, before I was able to flip the asynchronous flag and it would render as I moved the mouse. Now, I'm unable to do that. I tried setting 'allowDataSourcesToSuspendAnimation' on the viewer to false, but to no avail. Any help would be extremely appreciated.
In my naivety I forgot to add a code snippet to my question. This is in a clock tick event listener that only fires when mouse down is happening(Boolean value set to true)
var radius = Cesium.Cartesian3.distance(cartesianStartMousePosition, cartesianMousePosition);
if (radius > 0) {
if (currentEntity && currentEntity.id) {
currentEntity.position = cartesianStartMousePosition;
currentEntity.ellipse = {
semiMinorAxis: radius,
semiMajorAxis: radius,
material: new Cesium.ColorMaterialProperty(myColor)
};
currentEntity.label = {
text: 'New Overlay',
scale: 0.35
};
overlayEntities.resumeEvents();
}
else {
currentEntity = new Cesium.Entity({
position: cartesianStartMousePosition,
ellipse: {
semiMinorAxis: radius,
semiMajorAxis: radius,
material: new Cesium.ColorMaterialProperty(myColor)
},
label: {
text: 'New Overlay',
scale: 0.35
},
isSaved: false
});
overlayEntities.add(currentEntity);
}
bDrewPrim = true;
}
It looks to me like you're doing too much work to update the entity. You only need to set the values that have changed, and you should only do that if the change was substantial enough to warrant a graphics update. Try replacing the top half of your if statement with something like this:
var lastRadius = 0;
...
if (radius > 0 && !Cesium.Math.equalsEpsilon(radius, lastRadius, Cesium.Math.EPSILON2)) {
lastRadius = radius;
if (currentEntity && currentEntity.id) {
currentEntity.ellipse.semiMinorAxis = radius;
currentEntity.ellipse.semiMajorAxis = radius;
} else {
// Same as before...
I believe the ellipsoid primitive is being built on a worker thread, so this code tries to avoid setting the new radius every tick unless a real change has been applied to it.
Also, you don't show your mouse down handler, but make sure that you're setting this flag, if you aren't already setting it:
viewer.scene.screenSpaceCameraController.enableInputs = false;
This stops the globe from spinning while you drag-select the ellipse. You can reset this to true on mouse up.

Actionscript 3: Back History Button

Sorry in advance. I am new in Actionscript and trying to implement a history back button, not a go to previous frame button; in a flash movie, not in a flash web page). My approach is to use an array and push the currentframe to it on any visited frame. My code is not working and it is giving back an error #1502.
Please help me to fix the code I have by now:
var back:Array = new Array(); //global variable
stop();
back.push(MovieClip(this.root).currentFrame); // in every visited frame
back.addEventListener(MouseEvent.CLICK, backGoTo);
function backGoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "framelabel":
for (var i:int = 0; i < back.length; i++)
{
gotoAndStop(back[i]); // expected to go to the last visited frame.
}
break;
}
}
thanks a lot in advance.
To get the last element of an array, use back[back.length-1], or if you want to remove the last element and get it back at the same time then back.pop();. So in your case, that for cycle has no use, it's all wrong.. nonsense to check all the elements of an array in this case.
function Softkey3GoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "m_1":
case "m_2":
case "reply":
case "messages":
gotoAndStop(back[back.length-2]); // -2 because the current one is -1, the previous frame is -2.
break;
}
}
After exploring the Array .pop method I could find the solution to my back button. here is my code for a global back history button. in every frame you must push it to the array. call the last element of the array and delete it at the same time:
var back:Array = new Array(); //global variable
stop();
back.push(MovieClip(this.root).currentFrame); // in every visited frame
back.addEventListener(MouseEvent.CLICK, backGoTo);
function backGoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "framelabel":
back.pop();
gotoAndStop(back.pop());
break;
}
}

AS3 - Recording a click with playHeadTime and using the time

I am fairly new to AS3 so any help would be appriciated.
Basically I am trying to make something similar to the Hazard Perception test, where you click and it records weather you clicked at the right time or not.
What I have so far is this:
import flash.events.Event;
videoOverlay.addEventListener(MouseEvent.CLICK,doClick)
function doClick(e:Event):void
{
trace(myVideo.playheadTime)
}
I have managed to make a clickable area and then display click times, what I now need to do is to be able to tell if a click was in a certain time frame then add 1 point, and then at the end of the video clip I want to display a score.
I am not after just code, if anyone could maybe suggest a way of doing this it would be appriciated.
You can store the 'right moments' in an array, xml, whatever. Let's say something like this:
var moments:Array = [{start: "1:01", end: "1:16"}, {start: "1:25", end: "1:26"}, {start: "1:39", end: "1:51"}];
//time is in minutes, so you need to convert it to seconds
function doClick(e:Event):void
{
for (var i:int = 0; i < moments.lenght; i++)
{
var moment:Object = moments[i];
if (myVideo.playheadTime => toSeconds(moment.start) && myVideo.playheadTime <= toSeconds(moment.end))
{
trace("that's the right moment");
break; //we do not need to check further
}
}
}
The best way I can think of to do this is a timer. Setup a timer class and on the click event display rather or not the click is within a certain range of time - if so then success if not so then failure.
Try something like this:
counter:Number = 0;
videoOverlay.addEventListener(MouseEvent.CLICK,doClick)
public function doClick(e:Event):void
{
if (counter < 10)
trace("Success, your quick!");
else
{
trace("Failure!");
}
}
addEventListener(Event.ENTER_FRAME, timed_event);
public function timed_event(event:Event) : void
{
counter++;
}
Of course, the time rate depends upon the frame rate of the application as well.