I have a frame-by-frame animation. I want to be able to click and drag on the stage back and forth and traverse through the animation. I.e. I want to click and drag from left to right to make the animation go forwards and right to left to make the animation go backwards.
How would I achieve this?
I am making an assumption that there will be some maths involved in calculating mouse position and traversing to the correct frame, but how would I do this?
Here you are (edited version)
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
var clickedMouseX:int;
var clickedFrame:uint;
var backgroundClip:Sprite = getChildByName("background") as Sprite;
var clip:MovieClip = getChildByName("animation") as MovieClip;
clip.stop();
clip.mouseEnabled = false;
backgroundClip.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
backgroundClip.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
function onMouseDown(event:MouseEvent):void
{
backgroundClip.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
clickedMouseX = backgroundClip.mouseX;
clickedFrame = clip.currentFrame;
}
function onMouseUp(event:MouseEvent):void
{
backgroundClip.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
function onMouseMove(event:MouseEvent):void
{
var delta:int = backgroundClip.mouseX - clickedMouseX;
var wantedFrame:uint = (clickedFrame + delta * clip.totalFrames / backgroundClip.width) % clip.totalFrames;
while (wantedFrame < 1)
{
wantedFrame += clip.totalFrames;
}
clip.gotoAndStop(wantedFrame);
}
Cheers!
It should be a matter of mapping the length of your drag area to the length of the timeline:
stage.addEventListener(MouseEvent.MOUSE_MOVE, updateAnimation);
function updateAnimation(event:MouseEvent):void {
animation.gotoAndStop(Math.floor(mouseX/stage.stageWidth * animation.totalFrames));
}
Here's a commented version:
stage.addEventListener(MouseEvent.MOUSE_MOVE, updateAnimation);
function updateAnimation(event:MouseEvent):void {
//take the ratio between the mouse position and stage width -> //a number from 0.0 to 1.0
var mouseRatio:Number = mouseX/stage.stageWidth;
//'scale'/multiply that number to fit the animation frames -> from a maximum of 1.0 to animation's total frames
//also, we 'round down' because we need an integer for the frame number, not a fractional number
var frame:int = Math.floor(mouseRatio * animation.totalFrames);
animation.gotoAndStop(frame);
}
Also, not that MOUSE_MOVE gets triggered several frames per second. You could update on ENTER_FRAME and since you mentioned dragging, you could also have a variable to keep track if the mouse is pressed or released:
var mousePressed:Boolean;
stage.addEventListener(MouseEvent.MOUSE_DOWN, togglePressed);
stage.addEventListener(MouseEvent.MOUSE_UP, togglePressed);
stage.addEventListener(Event.ENTER_FRAME, update);
function togglePressed(event:MouseEvent):void {
mousePressed = event.type == MouseEvent.MOUSE_DOWN;
}
function update(event:Event):void {
if(mousePressed) animation.gotoAndStop(Math.floor(mouseX/stage.stageWidth * animation.totalFrames));
}
HTH
Related
I am new to ActionScript 3 and yes it is quite interesting. But I have got a problem with touch event.
I have already coded for the character (bird) to move forward and down at each frame and now I need to insert a function to move the bird upwards when tapping on the screen. I have tried it with an example in this url - https://help.adobe.com/en_US/as3/dev/WS1ca064e08d7aa93023c59dfc1257b16a3d6-7ffe.html
But still nothing happens. Please help me.
My code is as below.
package{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.TouchEvent;
import flash.ui.Multitouch;
Multitouch.inputMode=MultitouchInputMode.TOUCH_POINT;
public class MyBirdy extends MovieClip{
public var bird: Birdy;
public var sky: Background;
public var sky2: Background;
public var birdinsky: MovieClip;
public var skyBreadth:Number;
public function MyBirdy(){
bird = new Birdy();
sky = new Background();
sky2 = new Background();
skyBreadth = 1453.15;
sky.x = 730;
sky.y = 360;
bird.x = 100;
bird.y = 340;
sky2.x = sky.x + skyBreadth;
sky2.y = sky.y;
birdinsky = new MovieClip();
birdinsky.addChild(sky);
birdinsky.addChild(sky2);
birdinsky.addChild(bird);
stage.addChild(birdinsky);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(e:Event):void{
bird.x += 4;
birdinsky.x = 100 - bird.x;
bird.y += 2
if(sky.x + skyBreadth + birdinsky.x < 700){
sky.x = sky.x + (2 * skyBreadth);
}
if(sky2.x + skyBreadth + birdinsky.x < 700){
sky2.x = sky2.x + (2 * skyBreadth);
}
birdinsky.addEventListener(TouchEvent.TOUCH_TAP, onTap);
}
function onTap(e:TouchEvent): void {
bird.y -= 2;
//I want my bird to fly up when tapped!
}
}
The reason it doesn't work, is because your tap movement is always going to be negated by the movement in the enter frame handler. e.g. you tap, which moves the bird up 2 pixels, but then on the next frame tick of your application (when you'd see the visual change) you move the bird down again 2 pixels in onEnterFrame - which runs every frame tick.
Here is a way to do what you'd like:
Take this line, and remove it completely (or if you actually want a tap and not a hold, move it out of the onEnterFrame method and into your constructor - you don't want to add a listener over and over again every frame)
birdinsky.addEventListener(TouchEvent.TOUCH_TAP, onTap);
If you don't need multiple simultaneous touch support, it would be simpler to use mouse events. remove this line if switching to mouse events instead of touch (though you could still use touch if you'd like):
Multitouch.inputMode=MultitouchInputMode.TOUCH_POINT;
In your constructor function (MyBirdy) add the following lines to listen for the mouse up and down events:
As suggested in the comments, you should listen on the stage for the mouse down (or touch begin) if you want a press anywhere on the screen to work
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); //or TouchEvent.TOUCH_BEGIN
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); //or TouchEvent.TOUCH_END
Then create the handler functions for those listeners, as well as a var to track the mouse button state:
private var isMouseDown:Boolean = false;
private function onMouseDown(e:Event):void {
isMouseDown = true;
}
private function onMouseUp(e:Event):void {
isMouseDown = false;
}
With the above you now have a var (isMouseDown) that will be set to true when the mouse is down, and false when the mouse is up. Remember that mouse here is also the same of a finger press/hold.
Now, inside your enter frame handler (onEnterFrame), add the following:
remove the line bird.y += 2.
Add:
if(isMouseDown){
bird.y -= 2; //move the bird up, as the screen is being pressed/clicked
}else{
bird.y += 2 //move the bird down
}
Now, instead of a single tap event, any frame where the mouse is down (or a press and hold) the bird will move up instead of down.
i would apreciate if someone help me with some coding.
i have this code that i previously get to rotate a movieclip with the mouse, and get to another frame, for a college work (we only did the tweens, just learned the basic coding) but i cant get no result.
import flash.events.Event;
import flash.events.MouseEvent;
knob_mc.addEventListener(MouseEvent.MOUSE_DOWN, rotate);
stage.addEventListener(MouseEvent.MOUSE_UP, endrotate );
var angle:Number=0
function rotate(e:Event):void
{
stage.addEventListener(MouseEvent.MOUSE_MOVE,rotate);
var position:Number = Math.atan2(mouseY - knob_mc.y,mouseX - knob_mc.x);
angle=(position/Math.PI) *180;
knob_mc.rotation = angle;
}
function endrotate(e:MouseEvent):void
{
knob_mc.removeEventListener(MouseEvent.MOUSE_DOWN, rotate);
stage.removeEventListener(MouseEvent.MOUSE_UP, menu);
stage.removeEventListener(MouseEvent.MOUSE_MOVE,rotate);
knob_mc.addEventListener(MouseEvent.MOUSE_DOWN,rotate);
}
function menu(e:MouseEvent):void
{
if ( angle >=1 && angle <= 100 )
{
gotoAndPlay(2);
}
else if (angle >=100 && angle < 340) {
gotoAndPlay(2);
}
You will need to put the code that adds the event listener for the rotate function somewhere outside the rotate callback. As it is, it is never being called.
function rotate(e:Event):void
{
var position:Number = Math.atan2(mouseY - knob_mc.y,mouseX - knob_mc.x);
angle=(position/Math.PI) *180;
knob_mc.rotation = angle;
}
stage.addEventListener(MouseEvent.MOUSE_MOVE,rotate);
So I'm workin on a flash project where I want keyboard input. In the stage there's an instance "Car" seen from above which is supposed to be rotate and drive direction of rotation. This is what I've put together so far in AS3:
//Required stuff
import flash.ui.Keyboard;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.display.Stage;
import flash.display.MovieClip;
Car.addEventListener(Event.ENTER_FRAME, this.RunGame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
//Variables
var keys:Array = []
var vDrive:Number = 3; //Car's current base speed
var vx:Number = 0; //Speed along x axis
var vy:Number = 0; //Speed along y axis
var vMax:Number = 30; //Top speed
var vRot:Number = 3; //Rotation speed
var vAcc:Number = 1.1; //Factor for acceleration
var vDeAcc:Number = 0.90; //Factor for de-acceleration
//Game Loop
RunGame();
function RunGame():void
{
// Drive forwards
if (keys[Keyboard.UP])
{
if (vDrive < vMax)
vDrive += vAcc;
}
// Reverse
if (keys[Keyboard.DOWN])
{
if (vDrive > vMax)
vDrive *= vAcc;
}
// Turn right
if (keys[Keyboard.RIGHT])
{
Car.rotation += vRot;
}
// Turn left venstre
if (keys[Keyboard.LEFT])
{
Car.rotation -= vRot;
}
//Movement
// Friction
vDrive *= vDeAcc;
//Calculating movement vector
vx = vDrive * Math.cos(toRad(Car.rotation));
vy = vDrive * Math.sin(toRad(Car.rotation));
//Update car position
Car.x -= vx ;
Car.y -= vy;
}
However, when I run the program, the arrow keys don't seem to do anything.
I also get the following compiler warnings for both "onKeyDown" and "onKeyUp":
Migration issue: The onKeyDown event handler is not triggered
automatically by Flash Player at run time in ActionScript 3.0. You
must first register this handler for the event using addEventListener
( 'keyDown', callback_handler)
Trying to add what it suggested just makes errors saying callback_handler ain't defined.
I'm now stuck trying to figure out how to make the keyboard input work. Anyone know?
You are currently missing the functions for the key listeners. You have added the listeners to the stage here:
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
Now you just need to create the functions:
function onKeyDown( e:KeyboardEvent ):void {
//add our key to the keys array
keys[e.keyCode] = e.keyCode;
}
function onKeyUp( e:KeyboardEvent ):void {
//if the key is in the keys array, set the value to null
keys[e.keyCode] = null;
}
But there is another problem here:
Car.addEventListener(Event.ENTER_FRAME, this.RunGame);
You do not need the this.RunGame, just RunGame will do, but you should get an error this method needs a parameter of type Event, so your RunGame() definition should look like this:
function RunGame(e:Event):void
Then you wouldn't call RunGame(), it is called each frame while tied to the event listener.
Edit: Please note that there are many ways to handle key events, my answer will work with your current implementation.
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'm using Flash Professional CS5.5 and I need to make an app where there is a ball (symbol) that moves using the accelerometer and I want that, when the ball coordinates A reach this coordinates B it goes to frame 2 (gotoAndPlay(2)). I have to find the ball coord first, right? How do I make this?
Here is the code I've now
c_ball.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void{
c_ball.startDrag();}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void{
c_ball.stopDrag();}
would it work if, after retriving the coordinates?
function f_level (e) if (c_ball.x==100 && c_ball.y==100) {
gotoAndStop(2);}
MOUSE_UP and MOUSE_DOWN are not what you need if you're looking for Accelerometer data. You want the Accelerometer class and associated events.
Try something like this:
import flash.sensors.Accelerometer;
import flash.events.AccelerometerEvent;
var accel:Accelerometer = new Accelerometer();
accel.addEventListener(AccelerometerEvent.UPDATE, handleAccelUpdate);
Update handler:
function handleAccelUpdate(e:AccelerometerEvent):void{
//inside this function you now have access to acceleration x/y/z data
trace("x: " + e.accelerationX);
trace("y: " + e.accelerationY);
trace("z: " + e.accelerationZ);
//using this you can move your MC in the correct direction
c_ball.x -= (e.accelerationX * 10); //using 10 as a speed multiplier, play around with this number for different rates of speed
c_ball.y += (e.accelerationY * 10); //same idea here but note the += instead of -=
//you can now check the x/y of your c_ball mc
if(c_ball.x == 100 && c_ball.y == 100){
trace("you win!"); //fires when c_ball is at 100, 100
}
}
Now this will let you "roll" your MC off the screen so you're probably going to want to add some kind of bounds checking.
Check out this great writeup for more info:
http://www.republicofcode.com/tutorials/flash/as3accelerometer/
An easy and save way is to use colission detection, instead of testing for exectly one position ( what is hard to meet for users) you go for a target area :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Hittester extends Sprite
{
var ball:Sprite = new Sprite();
var testarea:Sprite = new Sprite();
public function Hittester()
{
super();
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,0,10);
testarea.graphics.beginFill(0x00ff00);
testarea.graphics.drawRect(0,0,50,50);
testarea.x = 100;
testarea.y = 100;
// if testarea should be invisble
/*testarea.alpha = 0;
testarea.mouseEnabled = false;
*/
ball.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
addChild(testarea);
addChild(ball);
}
private function startDragging( E:Event = null):void{
ball.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
private function stopDragging( E:Event = null):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
ball.stopDrag();
test();
}
private function test():void{
if( ! ball.hitTestObject(testarea) ){
ball.x = 10;
ball.y = 10;
}
else{
// here goes next frame command ;)
}
}
}
}