I'm trying to create a function (called by a mouseover event) which will play the movie backwards while the mouse is over the instance, and stop while it's not.
This is what I have so far:
var b2:rightButton = new rightButton(); //new instance
b2.X = 550; //instances position
addChild(b2); //add instance to stage
b2.alpha = .4; // set instances alpha
var num = 0; // new variable called 'num'
b2.addEventListener(MouseEvent.ROLL_OVER, rightScroll); //mouse event for roll over
function rightScroll(event:MouseEvent) { //the function
num = 1; //set num to 1
b2.alpha = .8; //set alpha to 80%
}
b2.addEventListener(MouseEvent.ROLL_OUT, no_rightScroll); //event for roll out
function no_rightScroll(event:MouseEvent){ //roll- out function
num = 0; //set num back to 0
b2.alpha = .4; //set alpha back to 40%
}
while (num == 1){ // while num =1 (while mouse is over)
prevFrame(); //goto previous frame
}
Does anyone knows how to fix that, or a better way to do this?
The problem with your code at the moment is that loops do not execute in line with the framerate of your movie, so your code will call prevFrame() many, many times in a single frame. This can result in endless loops, which will cause your program to crash, and is completely useless in terms of animation.
The best approach is to listen to the ENTER_FRAME event of your movie, and move the playhead back one frame each time the event is dispatched. By adding and removing the listener in your button actions, you will get the effect you're looking for:
var b2:rightButton = new rightButton(); //new instance
b2.X = 550; //instances position
addChild(b2); //add instance to stage
b2.alpha = .4; // set instances alpha
b2.addEventListener(MouseEvent.ROLL_OVER, rightScroll); //mouse event for roll over
function rightScroll(event:MouseEvent):void { //the function
stage.addEventListener(Event.ENTER_FRAME,moveBackwards); //add stage listener
b2.alpha = .8; //set alpha to 80%
}
b2.addEventListener(MouseEvent.ROLL_OUT, no_rightScroll); //event for roll out
function no_rightScroll(event:MouseEvent):void { //roll- out function
stage.removeEventListener(Event.ENTER_FRAME,moveBackwards); //remove stage listener
b2.alpha = .4; //set alpha back to 40%
}
function moveBackwards(evt:Event):void {
prevFrame();
}
You'd probably find it much easier to write and read if you use the Greensock TweenLite library. Tweenlite can tween frames just as easily as other numeric values.
Inside your event listener, try adding this:
TweenLite.to( targetMC, 1, { frame:1, ease:fl.transitions.easing.None.easeNone } );
It will tween your movieclip (targetMC in this example) to frame 1 linearly with a duration of 1 second. You can play around with it using any of the other Tweenlite parameters as well.
Related
I've spent 14 hours on this problem. This is a basic collision checker that sets a MovieClip animation to play when a collision occurs. The clip is
currentBallObject.clip.
It works. The clips plays. But it repeats over and over.
private function checkCollisions():void
{
var i = balls.length;
while (i--)
{
var currentBallObject = balls[i];
if (currentBallObject.contact)
{
//condition hits ground
if (currentBallObject.ground)
{
currentBallObject.body.SetPosition(new b2Vec2(currentBallObject.startX / PIXELS_TO_METRE, currentBallObject.startY / PIXELS_TO_METRE));
currentBallObject.body.SetLinearVelocity(new b2Vec2(0, 0));
currentBallObject.body.SetAngularVelocity(0);
//currentBallObject.texture.pop();
}
else // it hit player
{
// assign clip texture to current position
currentBallObject.clip.x = currentBallObject.body.GetPosition().x * PIXELS_TO_METRE;
currentBallObject.clip.y = currentBallObject.body.GetPosition().y * PIXELS_TO_METRE;
// whisk old object away
currentBallObject.body.SetPosition(new b2Vec2(currentBallObject.startX / PIXELS_TO_METRE, currentBallObject.startY / PIXELS_TO_METRE));
currentBallObject.body.SetLinearVelocity(new b2Vec2(0, 0));
currentBallObject.body.SetAngularVelocity(0);
currentBallObject.contact = false;
}
}
}
}
I added this code to delete the MovieClip or somehow get rid of it after it has played through once. (42 frames). I also tried to add a frameListener and at least a dozen other suggestions. When I add
stop()
The animation doesn't play. It just loads the last frame. The code I have now is:
private function updateClips():void
{
var i = balls.length;
while (i--)
{
var currentBallObject = balls[i];
if(currentBallObject.clip)
{
var frame:int = currentBallObject.clip.currentFrame;
//trace(currentBallObject.clip.currentFrame);
if(frame == 42)
{
currentBallObject.clip._visible = false;
currentBallObject.clip.removeMovieClip();
currentBallObject.clip.enabled = -1;
}
}
}
}
I've tried counting the frames, putting it a run-once function, a frame exit listener, I am out of ideas. I just want to make a MovieClip run one time through. I also tried putting stop() in the timeline and then the animation didn't play. It just loaded the last frame.
Right now the collisions work but the animations stay on the screen forever, looping forever.
Edit: I got the code by Patrick to run without errors.
I added the event listener with the others like this:
_input = new Input(stage);
...
addEventListener(Event.ENTER_FRAME, oEF);
addEventListener(Event.ENTER_FRAME, update);
time_count.addEventListener(TimerEvent.TIMER, on_time);
time_count.start();
}
And then created a function:
private function oEF(e:Event):void{
var i = balls.length;
while (i--)
{
var currentBallObject = balls[i];
if (currentBallObject.clip.currentFrame >= currentBallObject.clip.totalFrames)
{
currentBallObject.clip.stop();
currentBallObject.clip.removeEventListener(Event.ENTER_FRAME, oEF);
if (currentBallObject.clip.parent) currentBallObject.clip.parent.removeChild(currentBallObject.clip);
}
}
}
But I still get the same problem as any other result. The MovieClip disappears on contact without the animation happening.
Through debugging I've learned more. The value of currentFrame starts off going 1-40 then stays at 40 for the rest of the execution.
So the MovieClip is always on the last frame for every object.
if clip is a MovieClip then you can use clip.totalFrames in an enterframe listener.
function oEF(e:Event):void{
if (this.currentFrame >= this.totalFrames){
this.stop();
this.removeEventListener(Event.ENTER_FRAME, oEF);
if (this.parent) this.parent.removeChild(this);
}
}
this.addEventListener(Event.ENTER_FRAME, oEF);
I figured it out.
if (currentBallObject.clip.currentFrame == currentBallObject.clip.totalFrames)
{
trace("Frame before kill: ", currentBallObject.clip.currentFrame);
currentBallObject.clip.x = 0;
currentBallObject.clip.y = 0;
}
The above block goes right after
var currentBallObject = balls[i];
It checks if the movieClip is on the last frame and then gets rid of the clip by setting clip.x & clip.y to 0. I could find no other way to stop the movie in this particular case.
I need to do simple thing, by clicking button 1 time, slowly move item1 by 100 px up and move down by 100 px. I've tried this, but item1 immediately increasing by 50px and immediately decreasing by 50px, I need to make It slower.
var moving:Boolean = false;
if(!moving){
item1.y -= 50;
moving = true;
}
else {
item1.y += 50;
moving = false;
}
You could set up a max moving value like 50, and then move the item1 on the Y with 1, and decrease that max value by 1. When the max value reaches 0, the item reached it's destiny.
if(!moving){
item1.y-=1;
maxValue--;
if(maxValue==0){
//reached final position
}
}
Use a Timer object. Declare that object inside of your class. Then, when the button has been clicked, set the object to a new instance of a Timer that will run very quickly and for many iterations, add an event listener to your new Timer, and in the event listener, apply much smaller increments or decrements to item1.y. For example:
private var m_tmr:Timer;
private function buttonClickHandler(pEvent:MouseEvent):void
{
// This is the function that's called when the button's clicked.
if (m_tmr == null)
{
m_tmr = new Timer(200, 0);
m_tmr.addEventListener(TimerEvent.TIMER, onTimer);
m_tmr.start();
}
}
private function onTimer(pEvent:TimerEvent):void
{
// The first several times this function is called (should be around every
// 200 milliseconds), increment item1.y by 1 or 2 or something else small.
// After the first many times, start decrementing item1.y by the same amount.
// Then call m_tmr.removeEventListener(TimerEvent.TIMER, onTimer);
}
You have to tell Flash to re-draw the screen after each incremental move. An excellent way to do this is with an ENTER_FRAME loop:
var moving:Boolean = false;
const initY = item1.y; // your starting y value
const limitY = initY - 100; // your move will end here
if(!moving){
moving = true;
addEventListener(Event.ENTER_FRAME,moveit)
function moveit(e)
{
item1.y -=1
if (item1.y < limitY)
removeEventListener(Event.ENTER_FRAME,moveit)
}
you have to removeEventListener(...) once you've got to where you want to be, otherwise the loop will go on and hog memory and performance.
UPDATE
So, to move up on a mouse click, you'd do this:
var moving:Boolean = false;
const initY = item1.y; // your starting y value
const limitY = initY - 100; // your move will end here
stage.addEventListener(MouseEvent.CLICK, moveUp)
function moveUp(e)
{
stage.removeEventListener(MouseEvent.CLICK, moveUp)
if(!moving){
moving = true;
addEventListener(Event.ENTER_FRAME,moveit)
function moveit(e)
{
item1.y -=1;
if (item1.y < limitY)
{
removeEventListener(Event.ENTER_FRAME,moveit);
item1.y = limitY;
moving = false;
}
}
}
Instead of targeting stage you may just want to target your button when you use the addEventListener method to register the listener function with the mouse click.
To move back to the start position, apply the same idea to another button or another MouseEvent. For instance you could move up on MOUSE_DOWN and move down on MOUSE_UP.
There are more sophisticated things you can do inside the listener functions (in this case the moving functions). You could apply "easing" to the beginning and ending of the moves so that the motion seems more natural. But, you'll have to read up on that - this answer is too long already!
I would like participate in this conversation. My version of object movement without If statements. Movement is based on trigonometric function:
var objectToAnimate:Shape = new Shape();
objectToAnimate.graphics.beginFill(0x009900);
objectToAnimate.graphics.drawCircle(0, 0, 20);
addChild(objectToAnimate);
//Place it somewhere
objectToAnimate.x = objectToAnimate.y = 200;
//Config for movement
var step:Number = 1; //really slow... 1° per frame
var maxOffsetY:Number = -100; //Move object maximum on 100px top
var cursor:Number = -90;
var position: Number = objectToAnimate.y; // catch current position
var timer:Timer = new Timer(30, 180);
timer.addEventListener(TimerEvent.TIMER, updateAnimation);
timer.start();
function updateAnimation(e:TimerEvent):void {
objectToAnimate.y = position + Math.cos(cursor * Math.PI / 180) * maxOffsetY;
cursor += step;
}
Hey guys I am having a problem with Event Listeners in my AS3 file. I am trying to make this object that lasts for 83 frames to appear in a different location every time the parent (83 frame) movie clip resets. The problem is I have a function that places the object at a random y value which works great once. When it resets the ojbect appears on the same Y point. This is because I removeEventListener the function otherwise the object goes shooting off the screen when it loads. How do I call that event listener again without causing a loop that will shoot the object off screen?
Here is my code:
import flash.events.Event;
stop();
addEventListener(Event.ENTER_FRAME, SeaWeedPostion);
//stage.addEventListener(Event.ADDED_TO_STAGE, SeaWeedPostion);
function SeaWeedPostion(e:Event):void{
// if(newSeaWeed == 1) {
var randUint:uint = uint(Math.random() *500 + 50);
this.seaweedSet.y += randUint;
trace(randUint);
stopPos();
//}else{
//nothing
// }
}
function stopPos():void{
removeEventListener(Event.ENTER_FRAME, SeaWeedPostion);
//var newSeaWeed = 0;
}
function resetSeaWeed():void{
addEventListener(Event.ENTER_FRAME, SeaWeedPostion);
}
I have some // code in there from trying different things.
Anyone have any suggestions?
Thanks!
ENTER_FRAME event is triggered every frame, so rather than changing position on each frame maybe it's better to create a counter, count frames and if it reaches 82 change position of SeaWeed.
var counter:uint = 0;
Now add ENTER_FRAME listener
addEventListener(Event.ENTER_FRAME, onEnterFrame);
function SeaWeedPostion(e:Event):void {
counter++;
//Are we there yet?
if(counter < 83) {
//Nope, carry on
}
else {
//Yey!
changePosition();
counter = 0;
}
}
//Reset SeaWeed position when called
function changePosition() {
var randUint:uint = uint(Math.random() *500 + 50);
this.seaweedSet.y += randUint;
trace(randUint);
}
I am making a small game from a tutorial where you hit small tofu's with your cursor and gather up points and accuracy - I have implemented a start button that works and goes to frame(2), where the game starts.
I would like to create a stop button but when i make a button in the scene - the button does not function (cant click on it) and when i hit a tofu it gets the 1009 error? (Cannot access a property or method of a null object reference.)
The game works when i have no stop button but i can't exit the game.
How can i create a stop button or a menu inside the game that allows the user to go back to a previous scene or stop the game?
// define some global variables which let you track the player's statistics.
var hits:int = 0;
var misses:int = 0;
var shots:int = 0;
var cDepth:int = 100;
var level:int = 1;
// define some runtime variables which are used in calculations.
var xSpeed:Number = 3;
var stageWidth:Number = 480;
var stageHeight:Number = 580;
/* attach the crosshair_mc movie clip instance from the Library onto the Stage.
This clip is used as a custom mouse cursor. */
var crosshairClip:MovieClip = new crosshair_mc();
crosshairClip.mouseEnabled = false;
addChild(crosshairClip);
// hide the mouse cursor
Mouse.hide();
/* every time the mouse cursor moves within the SWF file,
update the position of the crosshair movie clip instance on the Stage. */
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
function mouseMoveHandler(event:MouseEvent):void {
crosshairClip.x = event.stageX;
crosshairClip.y = event.stageY;
};
/* when the mouse button is clicked, check to see if the cursor is within the boundaries of the Stage.
If so, increment the number of shots taken. */
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(event:MouseEvent):void {
if (bg_mc.hitTestPoint(event.stageX, event.stageY, false)) {
shots++;
updateStats();
}
};
// define a TextFormat which is used to format the stats_txt text field.
var my_fmt:TextFormat = new TextFormat();
my_fmt.bold = true;
my_fmt.font = "Arial";
my_fmt.size = 12;
my_fmt.color = 0xFFFFFF;
// create a text field to display the player's statistics.
var stats_txt:TextField = new TextField();
stats_txt.x = 10;
stats_txt.y = 0;
stats_txt.width = 530;
stats_txt.height = 22;
addChild(stats_txt);
// apply the TextFormat to the text field.
stats_txt.defaultTextFormat = my_fmt;
stats_txt.selectable = false;
updateStats();
// add an onEnterFrame event to the main timeline so new tofu is constantly added to the game.
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(event:Event):void {
// randomly add new target's to the Stage.
if (randRange(0, 20) == 0) {
var thisMC:MovieClip;
// attach a new instance of the tofu instance from the library onto the Stage, and give it a unique depth.
var randomTofu:Number = randRange(1, 3);
switch (randomTofu) {
case 1:
thisMC = new tofu1_mc();
break;
case 2:
thisMC = new tofu2_mc();
break;
case 3:
thisMC = new tofu3_mc();
break;
default:
return;
break;
}
cDepth++;
// set the starting postition of the current target movie clip so it is just off to the left of the Stage.
thisMC.x = -thisMC.width;
/* create a random number between 80 and 100.
This is used to set the current movie clip's scale,
alpha and speed that it moves across the Stage. */
var scale:int = randRange(80, 100);
/* set the _xscale and _yscale properties of the current movie clip.
This allows for some minor variations of the targets within the game. */
thisMC.scaleX = scale / 100;
thisMC.scaleY = scale / 100;
thisMC.alpha = scale / 100;
thisMC.speed = xSpeed + randRange(0, 3) + level;
/* set a random _y value for the target.
Now, instead of all targets flying along the same path,
they vary their vertical position slightly. */
thisMC.y = Math.round(Math.random() * 350) + 65;
thisMC.name = "tofu" + cDepth;
/* create an onEnterFrame handler that executes a couple dozen times per second.
Update the target's position on the Stage. */
thisMC.addEventListener(Event.ENTER_FRAME, tofuEnterFrameHandler);
thisMC.addEventListener(MouseEvent.CLICK, tofuClickHandler);
addChild(thisMC);
// swap the custom cursor to the higher depth
swapChildren(thisMC, crosshairClip);
}
};
/* create a function to update the player's statistics on the Stage.
You're displaying number of shots taken, number of targets "hit",
number of targets "missed", the percentage of hits vs misses,
overall accuracy (number of shots taken vs number of hit targets). */
function updateStats() {
var targetsHit:Number = Math.round(hits/(hits+misses)*100);
var accuracy:Number = Math.round((hits/shots)*100);
if (isNaN(targetsHit)) {
targetsHit = 0;
}
if (isNaN(accuracy)) {
accuracy = 0;
}
stats_txt.text = "shots:"+shots+"\t"+"hits: "+hits+"\t"+"misses: "+misses+"\t"+"targets hit: "+targetsHit+"%"+"\t"+"accuracy: "+accuracy+"%"+"\t"+"level:"+level;
}
/* create a function that returns a random integer between two specified numbers.
This allows you to add some subtle differences in size and speed for the movie clips on the Stage. */
function randRange(minNum:Number, maxNum:Number):Number {
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
function tofuEnterFrameHandler(event:Event):void {
var tofuMC:MovieClip = event.currentTarget as MovieClip;
/* move the target horizontally along the Stage.
Currently all targets will move from left to right. */
tofuMC.x += tofuMC.speed;
/* slightly decrement the _y position of the current target movie clip.
This makes it appear like the targets are flying slightly higher as they move across the Stage. */
tofuMC.y -= 0.4;
/* if the current position of the target is no longer on the Stage,
count the target as a "miss" and delete the instance.
If the instance wasn't deleted from the Stage,
the user's computer would eventually slow to a crawl. */
if (tofuMC.x > stageWidth) {
misses++;
updateStats();
removeChild(tofuMC);
tofuMC.removeEventListener(Event.ENTER_FRAME, tofuEnterFrameHandler);
}
}
// when the target movie clip instance is pressed, count it as a "hit".
function tofuClickHandler(event:MouseEvent):void {
var tofuMC:MovieClip = event.currentTarget as MovieClip
// update the player's stats
hits++;
if ((hits%40) == 0) {
level++;
}
updateStats();
/* go to the movie clip's label named "hit"
(which allows you to show a clever animation when the instance is hit.) */
tofuMC.gotoAndPlay("hit");
// create an onEnterFrame event for the current movie clip instance.
tofuMC.addEventListener(Event.ENTER_FRAME, tofuHitEnterFrameHandler);
/* delete the onPress event handler.
This makes it so the target cannot continually be clicked while it is falling from the sky. */
tofuMC.removeEventListener(MouseEvent.CLICK, tofuClickHandler);
tofuMC.removeEventListener(Event.ENTER_FRAME, tofuEnterFrameHandler);
}
function tofuHitEnterFrameHandler(event:Event):void {
var tofuMC:MovieClip = event.currentTarget as MovieClip;
// set some local variables that you'll use to animate the target falling from the sky.
var gravity:int = 20;
var ymov:int = tofuMC.y + gravity;
// ***** xmov *= 0.5;
// increment the rotation of the current movie clip clock-wise by 5 degrees.
tofuMC.rotation += 5;
/* set the _x and _y properties of the movie clip on the Stage,
this allows us to make the target look like it is semi-realistically
falling from the sky instead of just dropping straight down. */
tofuMC.x += xSpeed;
tofuMC.y = ymov;
/* after the _y position is off of the Stage,
remove the movie clip so that the coordinates aren't continually calculated */
if (tofuMC.y > stageHeight) {
removeChild(tofuMC);
tofuMC.removeEventListener(Event.ENTER_FRAME, tofuHitEnterFrameHandler);
}
}
The stage is confused, because some objects don't exist when you change frames, so you have to remove all of your events from the stage:
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
And then you can call the gotoAndStop() function to whatever frame you want...
Help!!
I have a piece of code on a mc that when the mouse is dragged it plays through that movie clip, giving a 360 spin of a product.
Inside this movieclip on different increments of the 360 spin i have child movieclips with various other animations to relate to each angle of the product.
exp..
Scene 1 > spinY_mc > AWComplete_mc
My code for the spin is written within the actions in scene1 and controls spinY_mc but once im in AWComplete_mc i do not want you to be able to drag the mouse and spin?
Im sure this is simple but im a noob at all this and am taking on a mammoth project!
Here is the code used on the movieclip (spinY_mc) I dont want this code to work when inside of its child mc (AWComplete_mc).
// Rotation of Control Body Y
spin_Y.stop();
spin_Y.buttonMode = true;
var spin_Y:MovieClip;
var offsetFrame:int = spin_Y.currentFrame;
var offsetY:Number = 0;
var percent:Number = 0;
spin_Y.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
spin_Y.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
function startDragging(e:MouseEvent):void
{
// start listening for mouse movement
spin_Y.addEventListener(MouseEvent.MOUSE_MOVE,drag);
offsetY = stage.mouseY;
}
function stopDragging(e:MouseEvent):void
{
// STOP listening for mouse movement
spin_Y.removeEventListener(MouseEvent.MOUSE_MOVE,drag);
// save the current frame number;
offsetFrame = spin_Y.currentFrame;
}
// this function is called continuously while the mouse is being dragged
function drag(e:MouseEvent):void
{
// work out how far the mouse has been dragged, relative to the width of the spin_Y
// value between -1 and +1
percent = (mouseY - offsetY) / spin_Y.height;
// trace(percent);
// work out which frame to go to. offsetFrame is the frame we started from
var frame:int = Math.round(percent * spin_Y.totalFrames) + offsetFrame;
// reset when hitting the END of the spin_Y timeline
while (frame > spin_Y.totalFrames)
{
frame -= spin_Y.totalFrames;
}
// reset when hitting the START of the spin_Y timeline
while (frame <= 0)
{
frame += spin_Y.totalFrames;
}
// go to the correct frame
spin_Y.gotoAndStop(frame);
}
I'm pretty sure you just want to stop the propagation of an event from the child so it doesn't make it out to the parent. If you add a listener for the event you want to block (lets say it's mouseDown you want to block).
child_mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownBlocker);
private function mouseDownBlocker(event:MouseEvent):void
{
event.stopImmediatePropagation();
}
The way events work is they start at the "deepeest" child which the mouse has a hit event with, then they "bubble" up through all the parents. By stopping the propagation you block the bubbling from occurring, so the parents never get the event.