Own drag function in AS3 - actionscript-3

I need to develop my own drag function in AS3 (instead of using startDrag) because I'm resizing a MovieClip.
I'm doing this:
public class resizeBR extends MovieClip {
var initialScaleX, initialScaleY;
public function resizeBR() {
this.addEventListener(MouseEvent.MOUSE_DOWN, initResize);
this.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = e.target.scaleX;
initialScaleY = e.target.scaleY;
e.target.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
e.target.x += e.localX;
e.target.y += e.localY;
e.target.parent.parent.width += mouseX;
e.target.parent.parent.height += mouseY;
// Keep its own scale
e.target.scaleX = initialScaleX;
e.target.scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}
But the drag feature is not working fluently. I mean, when I drag a MovieClip from class resizeBR I need to move slowly my mouse cursor or it's not going to work propertly.
resizeBR is a MovieClip as a child of another MovieClip; the second one is which I have to resize.
What am I doing wrong?
Thanks!

Thanks all for your answers, but I found a great classes to do what I want.
http://www.senocular.com/index.php?id=1.372
http://www.quietless.com/kitchen/transform-tool-drag-scale-and-rotate-at-runtime/

I'm not really sure if I completely understand what you mean. But I think your problem lies with your MOUSE_MOVE handler.
In your current example you're resizing your target only when moving your mouse over the target. When you're moving your mouse fast enough it's possible your mouse leaves the target, casuing it to stop resizing. When I'm writing my own drag handlers I usually set the MOUSE_MOVE and MOUSE_UP listeners to the stage.
Your class would end up looking something like this:
public class resizeBR extends MovieClip
{
var initialScaleX, initialScaleY;
public function resizeBR()
{
addEventListener(MouseEvent.MOUSE_DOWN, initResize);
addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = scaleX;
initialScaleY = scaleY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
x += e.localX;
y += e.localY;
parent.parent.width += mouseX;
parent.parent.height += mouseY;
// Keep its own scale
scaleX = initialScaleX;
scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}

There are a couple reasons the resizing is jumpy. First, like rvmook points out, you'll need to make sure you support the mouse rolling off of the clip while its being resized. Since there is not an onReleaseOutside type of event in AS3, you have to set listeners to the stage, or some other parent clip. If you have access to the stage, that is best. If not, you can use the root property of your resizable clip, which will reference the highest level display object you have security access to. Setting mouse events to the root is a little wonky, because for them to fire, the mouse needs to be on one of the root's child assets - whereas the stage can fire mouse events when the mouse is over nothing but the stage itself.
Another reason you might be seeing some strange resizing behavior is because of using the localX/Y properties. These values reflect the mouseX/mouseY coordinates to the object being rolled over - which might not necessarily be your clip's direct parent.
I tend to avoid having classes access their parent chain. You might want to consider placing the resizing logic in the clip you want resized, and not in one of its children. Here is simple self resizing example:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class ResizableBox extends MovieClip {
public function ResizableBox() {
addEventListener(MouseEvent.MOUSE_DOWN, startResize);
}
private function startResize(evt:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function stopResize(evt:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function handleResize(evt:MouseEvent):void {
this.scaleX = this.scaleY = 1;
this.width = this.mouseX;
this.height = this.mouseY;
}
}
}
ResizableBox is set as the base class of a MC in the library.

Related

ActionScript classes reference

I have a class Square and a class Circle.
This my class Circle:
public class Circle extends MovieClip
{
var growthRate:Number = 2;
public function Circle()
{
addEventListener(Event.ENTER_FRAME, grow);
}
function grow(e :Event):void
{
e.target.width +=growthRate;
e.target.height +=growthRate;
}
}
I need to stop growing the circle inside a function from Shape.
public function Square() {
buttonMode = true;
addEventListener(MouseEvent.MOUSE_DOWN, down);
}
protected function down ( event: MouseEvent):void
{
//here i need to stop the circle
}
I don't know how to make a relation with the Circle class in order to stop the circle growing.
Thank you in advance.
I don't know how to make a relation with the Circle class in order to stop the circle growing.
That's because you cannot with the code you have right now. There's nothing in your class that's accessible from outside (public), that stops the growth. But there's not even something private in your class that does this. The functionality simply is not there.
So first of all, create the desired functionality. and make it available to public.
Here's how your Circle class could look like:
public class Circle extends Sprite
{
private var growthRate:Number = 2;
public function Circle()
{
// nothing here
// this is just to create a circle graphic, if you have artwork in your library symbol, you do not need this
graphics.beginFill(0xffffff * Math.random());
graphics.drawCircle(0, 0, 10 + 30 * Math.random());
graphics.endFill();
}
public function startGrowing(rate:Number = 0):void
{
if(rate != 0)
{
growthRate = rate;
}
addEventListener(Event.ENTER_FRAME, grow);
}
public function stopGrowing():void
{
removeEventListener(Event.ENTER_FRAME, grow);
}
private function grow(e:Event):void
{
width += growthRate;
height += growthRate;
}
}
Pay attention to
the constructor: I create a circle graphic there with code. As the comment says, if Circle is a class associated to a library symbol, you do not need this, because you already created the artwork in the symbol.
the super class: It's Sprite. This should be your default superclass. The only real reason to use MovieClip is if you have a timeline animation. It doesn't look like you have any of that from what you posted, so I recommend Sprite.
the two new public methods: startGrowing and stopGrowing, which do exactly what their names imply. startGrowing has an optional parameter to to start growing at a different growth rate.
the lack of e.target: which is unnecessary here.
A simple demo of that code looks like this:
var circle:Circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing();
//circle.startGrowing(1); // grow slowly
//circle.startGrowing(5); // grow fast
To stop the growth, stop listening for the ENTER_FRAME Event.
So far so good, now to your actual question:
how to make a relation with the Circle class
protected function down ( event: MouseEvent):void
{
//here i need to stop the circle
}
You think that you should make this connection in your Square class, but you are wrong about that. It's very bad practice to connect two classes this way. You want the classes to be as individual as possible.
Think about it like phones. Does your phone have a direct way to a specific other phone? No. It has the ability to connect to any phone, which makes it a lot more universally useful than a phone hard wired to another phone.
You make the connection outside both classes with events. That's like your phone making a call to the network with a number it wants to call. The network then figures out how to find the other phone with that number and how to establish the connection.
As a short interlude and so that we are on the same page about it, here's the Square class that I'm using:
public class Square extends Sprite
{
public function Square()
{
// nothing here
// this is just to create a circle graphic, if you have artwork in your library symbol, you do not need this
graphics.beginFill(0xffffff * Math.random());
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}
}
As you can see, it only has a constructor in which I programmatically draw a rectangle. Again, if you have the desired artwork in your library symbol, there's no need for this. In that case, the constructor would be empty and in turn the entire class file would be empty. In this case, you do not even need a class file. Just associate the library symbol with the name. The Square is only a graphic asset without any code attached to it.
Here's a full fledged document class using both classes:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Main extends Sprite
{
private var circle:Circle;
public function Main()
{
circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing(1);
var square:Square = new Square();
addChild(square);
square.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(e:MouseEvent):void
{
circle.stopGrowing();
}
}
}
As you can see, the event listener is added in the document class and also the function that is executed when the event occurs is in Main.
Here's a variation of that without the square. This time you have to click on the circle to stop it growing:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Main extends Sprite
{
private var circle:Circle;
public function Main()
{
circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing(1);
circle.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(e:MouseEvent):void
{
circle.stopGrowing();
}
}
}
As you can see, making the connection outside both classes with events gives you a lot of flexibility to wire things up in a different way. Just like having a phone that connects to a network instead of another phone directly.

as3 - How to add a movieclip into the parent's parent?

I have a gun child inside of a movieclip called "player" and "player" is inside another movieclip called "level one".
So inside the gun class, the code spawns a bullet. Which has to spawn in the parent's parent. So the bullet can shoot into the level.
private function fire(m: MouseEvent)
{
//when bullet fired
var b = new Bullet;
MovieClip(MovieClip(parent).parent).addChild(b);
}
However, the bullet never appears in the parent's parent. What could be the issue here?
UPDATED CODE:
In gun class:
function fire(e:MouseEvent):void
{
dispatchEvent(new Event('fire!', true));
}
In player class:
protected function fire(e: Event)
{
var b: Bullet = new Bullet();
// bullet must be in same position and angle as player.gun
b.rotation = player.gun.rotation;
b.x = player.gun.x; + player.gun.width * Math.cos(player.gun.rotation / 180 * Math.PI);
b.y = player.gun.y + player.gun.width * Math.sin(player.gun.rotation / 180 * Math.PI);
addChild(b);
}
You missed to strong typing the variable and add parentheses
Try
var b:Bullet = new Bullet();
Why don't you keep a reference to the parent.parent?
var b:Bullet = new Bullet(parent)
...
public function Bullet(spawnTarget:MovieClip)
{
_spawnTarget = spawnTarget;
}
private function fire(m: MouseEvent)
{
//when bullet fired
var b = new Bullet;
_spawnTarget.addChild(b);
}
Using parent.parent is risky because you'd always need to make sure that the gun class is instantiated in a child of the game class. Also, you can't create guns in any other position in the hierarchy which can be a problem if your game grows bigger.
You can solve this with listeners (as the comments state, and this is an example of passing a game reference. Each instance receives a reference to game, so each instance can call public function on game:
Game.as
myPlayer = new Player(this);
public function addBulletToWorld(){
}
Player.as
public function Player(game){
myGun = new Gun(game);
}
Gun.as
public function Gun(game){
game.addBulletToWorld();
}
If you absolutely insist on using the parent.parent thing, just set a break point at that line and see what parent.parent actually is, then adjust as needed to match your actual structure. But aside from the fact that this is just bad practice, it's going to limit you to where you can't use this Class anywhere but at that one depth.
I'd recommend just generating a custom event when you want to fire the bullet, and letting the grandparent handle it.
In Gun:
function fire(e:MoudeEvent):void {
dispatchEvent(new Event('fire!', true));//setting it true lets it bubble up to the parent
}
In Game
protected var bullets:Vector. = new Vector();
public function Game() {
addEventListener('fire!', fire);
}
//note I worked with Flex too long to use private methods very often
protected function fire(e:Event) {
var bullet:Bullet = new Bullet();
bullet.x = MoveClip(e.target).x);
bullet.y = MoveClip(e.target).y);
addChild(bullet);
bullets.push(bullet);//I assume you're going to want to move this or something, so we need to store it to manage it
}

making a symbol move by keyboard not showing result and when published it not reads stop(); but replays it again and again

I am new to actionscript ,
My document class is ,
package
{
//list of our imports these are classes we need in order to
//run our application.
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
public class engine extends MovieClip
{
// moved ourShip to a class variable.
private var Circle:circle = new circle()
//our constructor function. This runs when an object of
//the class is created
public function engine()
{
addFrameScript(0, frame1);
addFrameScript(1, frame2);
}
// frame 1 layer 1 --------------------------------------------------
public function frame1()
{
stop();
}
//-------------------------------------------------------------------
// frame 2 layer 1 --------------------------------------------------
public function frame2()
{
Circle.x = stage.stageWidth / 2;
Circle.y = stage.stageHeight / 2;
addChild(Circle);
}
//-------------------------------------------------------------------
}
}
i made two frames first contains button and the other circle which i want to move but it not moves and it stays in the middle on second frame
My button class is
package
{
//imports
import flash.events.MouseEvent;
import flash.display.SimpleButton;
import flash.display.MovieClip;
//-------
public class start extends SimpleButton
{
public function start()
{
addEventListener(MouseEvent.CLICK, onTopClick);
addEventListener(MouseEvent.MOUSE_OVER, onBottomOver);
}
function onTopClick(e:MouseEvent):void
{
MovieClip(root).gotoAndStop(2)
}
function onBottomOver(e:MouseEvent):void
{
}
}
}
And my as of circle movieclip is
package
{
//imports
import flash.display.MovieClip;
import flash.display.Stage;
import flash.ui.Keyboard;
import flash.events.Event;
import flash.events.KeyboardEvent;
public class circle extends MovieClip
{
private var speed:Number = 0.5;
private var vx:Number = 0;
private var vy:Number = 0;
private var friction:Number = 0.93;
private var maxspeed:Number = 8;
public function circle()
{
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
public function loop(e:Event) : void
{
addEventListener(KeyboardEvent.KEY_DOWN, keyHit);
x+=vx;
y+=vy
}
function keyHit(event:KeyboardEvent):void {
switch (event.keyCode) {
case Keyboard.RIGHT :
vx+=speed;
break;
case Keyboard.LEFT :
vx-=speed;
break;
case Keyboard.UP :
vy-=speed;
break;
case Keyboard.DOWN :
vy+=speed;
break;
}
}
}
}
I am sorry to post so much for you guys to read but stackoverflow is the only website where anyone helps me !
You have made several major errors. First, addFrameScript() isn't a proper way to place code on frames, use Flash's editor to place code on timeline. (IIRC you will have to make a single call out of your two in order to have all the code you add to function) And, whatever code you added to a frame of a MC is executed each frame if the MC's currentFrame is the frame with code. Thus, you are adding a function "frame2()" that places the Circle in the center of the stage each frame! You should instead place it at design time (link it to a property) into the second frame, or in a constructor, or you can use one single frame and Sprite instead of MovieClip, and instead of using frames you can use container sprites, adding and removing them at will, or at an action.
The other major mistake is adding an event listener inside an enterframe listener - these accumulate, not overwrite each other, so you can have multiple functions be designated as listeners for a particular event, or even one function several times. The latter happens for you, so each frame another instance of a listening keyHit function is added as a listener. The proper way to assign listeners is either in constructor, or in any function that listens for manually triggered event (say, MouseEvent.CLICK), but then you have to take precautions about listening for more than once with each function, and listening only with those functions you need right now.
EDIT:
Okay. Your code was:
addFrameScript(0, frame1);
addFrameScript(1, frame2);
The more correct way should be:
addFrameScript(0,frame1,1,frame2);
The reason is, the call to addFrameScript replaces all the timeline code with what you supply within here. The function is undocumented, perhaps by the reason of its affects on the stage and AS3 environment. The closest thing to the documentation on addFrameScript() so far is this link.
Next: Your code is:
public function circle()
{
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
public function loop(e:Event) : void
{
addEventListener(KeyboardEvent.KEY_DOWN, keyHit);
x+=vx;
y+=vy
}
The correct way of writing this is as follows:
public function circle()
{
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event=null):void
{
removeEventListener(Event.ADDED_TO_STAGE,init);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyHit);
}
public function loop(e:Event) : void
{
x+=vx;
y+=vy
}
The listeners should be assigned in constructor, if they are permanent or you want them to be active as soon as you create an object. The KeyboardEvent listeners are separate case, as in order for them to function you have to assign them to stage, which is not available right at the time of creating the object, so you need an intermediate layer - the init() function, that is only called when the object is added to stage. At this point stage is no longer null, and you can assign an event listener there. Note, if you want to make your circles eventually disappear, you have to remove the listener you assigned to stage at some point of your removal handling code.
Next: Your code:
public function frame2()
{
Circle.x = stage.stageWidth / 2;
Circle.y = stage.stageHeight / 2;
addChild(Circle);
}
Correct code should be:
public function frame2():void
{
if (Circle.parent) return; // we have added Circle to stage already!
Circle.x = stage.stageWidth / 2;
Circle.y = stage.stageHeight / 2;
addChild(Circle);
}
See, you are calling this every time your MC is stopped at second frame, thus you constantly reset Circle's coordinates to stage center, so you just cannot see if it moves (it doesn't, as you have assigned the keyboard listener not to stage).
Perhaps there are more mistakes, but fixing these will make your MC tick a little bit.

Make Flash Parent Fade Out & adding sound BrickBreaker Game

Hi i am trying to get this brick to fade out when the ball hits it in my brickbreaker game in flash AS3. Here is the code. At the moment there is just a removechild function which makes it just dissapear i want to know how to make it fade out instead. Also i have a breaking sound i would like to add when the ball hits the brick and wonder how i would add this aswell?
EDIT: I have managed to add sound by using Var & Play after the remove child line
package {
import flash.display.*;
import flash.events.*;
public class Brick extends MovieClip {
private var _root:MovieClip;
public function Brick(){
addEventListener(Event.ADDED, beginClass);
addEventListener(Event.ENTER_FRAME, enterFrameEvents);
}
private function beginClass(event:Event):void{
_root = MovieClip(root);
}
private function enterFrameEvents(event:Event):void{
if(this.hitTestObject(_root.Ball)){
_root.ballYSpeed *= -1;
this.parent.removeChild(this);
removeEventListener(Event.ENTER_FRAME, enterFrameEvents);
}
}
}
}
No need for any tweener pack for just one tween.
You can use the Tween class provided in AS3 itself. Try this :
new Tween(mc,"alpha",
Strong.easeIn,
mc.alpha,
0,
2,
true).addEventListener(
TweenEvent.MOTION_FINISH,
function() { removeChild(mc); },
false, 0, true);
Note:
mc is the movieclip (or the brick).
The code removes the movieclip from stage after the tween completes.
You may play the sound as soon as the ball touches the brick & put
this code after that.
The last three parameters (false, 0, true) set the motion finish listener to be garbage collected.
How I would do it would be to first create a variable hit:Boolean and set it to true when it gets hit and change your code inside enterFrameEvents function to something like this
if(!hit && this.hitTestObject(_root.Ball)){
hit = true;
_root.ballYSpeed *= -1;
//this.parent.removeChild(this);
//removeEventListener(Event.ENTER_FRAME, enterFrameEvents);
}
if(hit){
this.alpha -= 0.1; //change value to preference
if(this.alpha <= 0){
this.parent.removeChild(this);
removeEventListener(Event.ENTER_FRAME, enterFrameEvents);
}
}

Click event outside MovieClip in AS3

Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.