How to make a class file apply an eventhandler to instance or object on main stage asides from itself? - actionscript-3

I'm pretty new with ActionScript 3 and I'm stuck with trying to perform the following above in a class file. I tried to look for a solution online, but I can't find a suitable answers, perhaps because I'm not looking for the right search terms, etc.
Anyway, I'm trying to make a basic animation in Adobe Animate CC in an FLA file, "Campfire.FLA", where pressing the mouse down on a Campfire causes a piece of Coal attaching to a levitating stick to glow, and cooldown upon letting go of the mouse button. On the main timeline, I can execute it fine, but I want to transfer the information to a class file/ document file, but to no avail.
The code is what I used on the FLA's main timeline, Frame 1, and it works below works perfectly fine:
stop();
/* Instance names:
Fire = Instance of "FireButton"; simplebutton.
STween = Instance of "Stick Tween"; MovieClip, simple tween animation the object, "MarshmallowStick" moving.
Stick = Instance of "Marshmallow Stick"; MovieClip.
CoalRock = Instance of "Coal"; MovieClip.
*/
Fire.addEventListener(MouseEvent.MOUSE_DOWN, RockHot)
function RockHot(e: MouseEvent): void {
stopPlayReverse();
// Causes Coal and Stick to play their animation upon clicking Fire.
STween.Stick.play();
STween.Stick.CoalRock.play();
}
Fire.addEventListener(MouseEvent.MOUSE_UP, RockCold)
function RockCold(e: MouseEvent): void {
STween.Stick.CoalRock.addEventListener(Event.ENTER_FRAME, playReverse, false, 0, true);
STween.Stick.gotoAndPlay(1);
// Upon letting go of mouse button, it causes the coal to cool down/ play reverse. Stick resets to Frame 1.
}
function playReverse(e: Event): void {
if (STween.Stick.CoalRock.currentFrame == 1) {
stopPlayReverse();
// If Coal is back on Frame 1, it stops.
} else {
STween.Stick.CoalRock.prevFrame();
// If Coal is not on Frame 1 it continues going reverse where it left off.
}
}
function stopPlayReverse(): void {
if (STween.Stick.CoalRock.hasEventListener(Event.ENTER_FRAME)) {
STween.Stick.CoalRock.removeEventListener(Event.ENTER_FRAME, playReverse);
// Stops the function playreverse()
}
}
However, when trying to migrate the information into an ActionScript File I ran into a couple of problems. First I tried making an ActionScript 3 class file for each of the objects above much of the information is blank because I had no idea how to communicate eventhandlers between them. Much of the information for the MovieClips have no information, "MarshmallowStick" is below:
package {
import flash.display.MovieClip;
public class MarshmallowStick extends MovieClip {
public function MarshmallowStick() {
// Empty, no constructor code.
}
}
}
For the "Fire" Class file I tried something like:
package {
import flash.display.*;
import flash.events.*;
import Coal;
public class FireButton extends SimpleButton {
public var CoalRock = Coal;
public function FireButton() {
Coalrock = new Coal ();
this.addEventListener(MouseEvent.CLICK, RockHot)
function RockHot(e: MouseEvent): void {
CoalRock.play();
trace("OK");
trace(CoalRock);
}
}
}
}
However, it turned out that upon testing, The file only appeared to create a new object named CoalRock, and is not related to the one on the mainstage. So clicking the FireButton causes only the new object to play.
I tried making a document class as seen below in a file named "Main.as":
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
public var Fire: FireButton;
public var CoalRock: Coal;
public var Stick: MarshmallowStick;
public var STween: StickTween;
public function Main() {
CoalRock = new Coal();
Fire = new FireButton();
Stick = new MarshmallowStick();
/*
addChild(Fire);
addChild(CoalRock);
addChild(Stick);
addChild(STween);
*/
// RIP, well it's pretty much the same code as above. Just without the nested symbols/ objects.
Fire.addEventListener(MouseEvent.MOUSE_DOWN, RockHot)
function RockHot(e: MouseEvent): void {
stopPlayReverse();
//Eye + Emblem glow
Stick.play();
CoalRock.play();
trace("OK");
}
Fire.addEventListener(MouseEvent.MOUSE_UP, RockCold)
function RockCold(e: MouseEvent): void {
CoalRock.addEventListener(Event.ENTER_FRAME, playReverse, false, 0, true);
Stick.gotoAndPlay(1);
}
function playReverse(e: Event): void {
if (CoalRock.currentFrame == 1) {
stopPlayReverse();
} else {
CoalRock.prevFrame();
}
}
function stopPlayReverse(): void {
if (CoalRock.hasEventListener(Event.ENTER_FRAME)) {
CoalRock.removeEventListener(Event.ENTER_FRAME, playReverse);
}
}
}
}
}
But it only turned out that it only affects objects added via the addChild() well as far as I have tested. But the main point of this is for the script to affect objects that already exist on the main stage/ scene.
If you want to see how it plays/ suppose to play out, you can take the main timeline code and paste it into an FLA file with instances of the ones provided.
I don't know how Stack will format it. / / is suppose to be multi-line comments.

That's one elaborate question.
The thing you (probably) need the most is some feudal hierarchy so that objects tend to their own and their direct children, but not deeper and totally not upwards:
Container → (interface method) → Child → (tends to own children)
Container ← (status event) ← Child
Normally, a child must not know of its parent and should communicate with status events. This way you can put such a child into any container with no risk of that child possibly reaching upwards expecting some parent structure that is not there.
So, your problems.
First. Accessing the objects that already exist on the main timeline. Well, the only trick is not to create new ones but to get references to the existing ones.
package
{
// Imports.
public class Main extends MovieClip
{
// If you have an instance of MarshmallowStick with the
// instance name "Stick" on the main timeline,
// you don't need to do anything else.
//
// Default publish settings are to auto-declare
// the main timeline instance into the
// appropriately named variable.
public var Stick:MarshmallowStick;
P.S. Figure the Stick out of Tween by yourself.
Second. To access Coal inside Stick you need to declare an appropriate variable inside the correspondent class. Also, it makes a lot of sense to put the things needed to play things forward and backward as close to the objects they operate as it is possible.
package
{
import flash.events.Event;
import flash.display.MovieClip;
public class MarshmallowStick extends MovieClip
{
// You don't seem to need a separate class for Coal.
// Having it as a regular MovieClip will suffice.
public var Coal:MovieClip;
// Call this to play animation forward.
public function playNormal():void
{
removeEventListener(Event.ENTER_FRAME, onReverse);
gotoAndPlay(1);
Coal.play();
}
// Call this to play animation backward.
public function playReverse():void
{
Coal.stop();
gotoAndPlay(1);
addEventListener(Event.ENTER_FRAME, onReverse);
}
// This method does not need to be seen from the outside
// so declare it as private rather than public.
private function onReverse(e:Event):void
{
if (Coal.currentFrame > 1)
{
Coal.prevFrame();
}
else
{
playNormal();
}
}
// Call this to stop playing into any direction.
public function stopPlaying():void
{
stop();
Coal.stop();
removeEventListener(Event.ENTER_FRAME, onReverse);
}
}
}
Third. Avoid declaring functions inside functions. You can read up what closures are and how to handle them in AS3, but for the time being (and probably for the foreseeable future) you won't need them.
package
{
// Imports.
public class Main extends MovieClip
{
public var Fire:SimpleButton;
public var Stick:MarshmallowStick;
public function Main()
{
Fire.addEventListener(MouseEvent.MOUSE_DOWN, makeHot);
// The rest of the code.
}
function makeHot(e:MouseEvent):void
{
Stick.playNormal();
trace("OK");
}
Fourth. Do not subclass buttons. It is pretty enough to create a simple class-free button and subscribe to its MouseEvent.CLICK or MouseEvent.MOUSE_DOWN events to process all the necessary actions in some place that knows what this buttons is for. I said that above already. Feudal hierarchy. Button should fire an event, then its holder should capture that event and react. The button itself should not know where it is or what its purpose is, even less to try doing things.

Related

Eventlisteners Not Working On Main Sprite AS3

I have been looking around at a few articles and SO questions to get a grasp on what exactly I am experiencing with my project, but have not found an answer.
I am creating a main sprite that acts as my root for this particular project (I am using terminology as I currently understand it. If I am in error please correct me). In that sprite class, I add a MouseEvent listener so that I can operate in some way with the mouse. The problem is, the main sprite acts as if it either can not read my mouse events or it has no size and therefore can not register mouse events. Here are my classes and the tests I have preformed.
============
Main Class =
============
package
{
import flash.display.Sprite;
import Fu;
[SWF(backgroundColor = "0xffffff", width = "550", height = "400")]
public class Main extends Fu
{
public function Main():void
{
super();
addMouseListener();
}
}
}
==========
Fu Class =
==========
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Fu extends Sprite
{
private var display_height:uint;
private var display_width:uint;
private var default_background_color:uint;
public function Fu(display_height:uint = 400, display_width:uint = 550, default_background_color:uint = 0x2e2e2e)
{
this.display_height = display_height;
this.display_width = display_width;
this.default_background_color = default_background_color;
if (stage) {
init();
}
else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
graphics.beginFill(default_background_color, 1);
graphics.drawRect(0, 0, display_width, display_height);
}
public function addMouseListener():void {
trace("Added Mouse Listeners");
addEventListener(MouseEvent.CLICK, onMouseEvent);
// Also tried MouseEvent.MOUSE_OVER, MouseEvent.ROLL_OVER
// with no results either.
}
private function onMouseEvent(e:Event):void {
trace("Mouse Event Fired.");
}
}
}
Everything compiles ok and runs just fine until I try to activate a MouseEvent such as a click, in which case, nothing is output to the console.
================
Console Output =
================
Added Mouse Listeners
//Event messages should appear here...
If I then add a second sprite (s) to the Main class with the following code...
===========================
Added to Main constructor =
===========================
var s:Sprite = new Sprite();
s.name = "s";
s.graphics.beginFill(0x000000);
s.graphics.drawRect(0, 0, 200, 200);
addChild(s);
and click on s I get the "Mouse Event Fired." in the console window, but not when I click outside of s' 200x200 box.
I also tried adding the listener to the stage (which is not ideally what I want since I want these listeners to only affect the main sprite of this specific SWF file) and tracing out...
trace(e.target, " ", (e.target as DisplayObject).name);
The areas not covered by sprite s display "[object Stage] null" in the console and "[object Sprite] s" is displayed when s is clicked.
An obvious fix would be to use s as my "stage" and call it a day, but I an curious why event listeners do not work on the main sprite and why it does not have a default instance name like other sprites do.
Any ideas?
You document class cannot extend Fu since Fu require two parameters and a document class cannot have one. Besides it makes no sense for a document class to extend anything other than Sprite or MovieClip since there cannot be any object higher in the hierarchy of your app. But if you have to extend a custom super class that super class cannot take parameter. Instead create a separate init method that you call explicitly yourself to register your events and draw your content.
Document classes are a special case. When used as the main entry in your app (as opposed to an externally loaded swf), they are set to not receive interactive event directly and behave much like a Singleton. They are also set as the root of your app and are the only DisplayObject that has the stage directly available in constructor. The design idea is that it should be a container for your app and not a part of it directly. The hierarchy is stage -> root (document class) -> your app elements.
In your case for example if you were to add a Fu instance as a child of the document class instance then it will receive mouseevent normally and the document class would also catch those event as they propagate.

Actionscript 3.0 method keeps repeating, cant figure out why

I'm having difficulty debugging my audio slider. I'm pretty sure my problems lies in the fact that one of my methods, changeVolumeRedFireball is just constantly repeating at a very fast rate. I get a glitchy sound every once in a while in my game, so it seems to correlate. I traced "output" inside the method and quickly found out it's repeating at a high rate.
Problem is, I cannot figure out WHERE this is coming from! One other note. This only starts repeating once I hold down my slider, hence activating the changeVolumeRedFireball from dragSliderRedFireball
I do have other methods from other classes referencing methods in this class. They only access playSoundRedFireball and stopSoundRedFireball though, so I don't see why that would have any effect. Also, this class is instantiated by my document class upon start up of the game. I suppose I'll put in the relevant code from the document class if requested, but I just didn't think it would affect this problem at all.
package {
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.geom.Rectangle;
public class VolumeRedFireball extends Sprite {
public var redFireballSnd:Sound = new Sound();
public var redFireballChannel:SoundChannel = new SoundChannel();
//URLRequest=new URLRequest("solitude.wav");
//Make sure you pass URLRequest an audio file on your computer.
public var reqRedFireball:EnemyAppearSound = new EnemyAppearSound();
public var boundaryRedFireball:Rectangle;
public var spriteRedFireball:Sprite;
public var sliderRedFireball:Sprite;
public var xPosRedFireball:Number;
public var yPosRedFireball:Number;
public static var volRedFireball:Number = 1;
public function VolumeRedFireball() {
this.addEventListener(Event.ADDED_TO_STAGE, onStageRedFireball,false,0,true);
volRedFireball=1;
redFireballChannel.soundTransform=new SoundTransform(volRedFireball)
}
public function onStageRedFireball(e:Event):void
{
//We remove it immediately so that it doesn't get called multiple times
//As the instance is added to the display list tree
this.removeEventListener(Event.ADDED_TO_STAGE, onStageRedFireball);
xPosRedFireball = 320;
yPosRedFireball = 170;
initRedFireball();
}
public function initRedFireball():void {
spriteRedFireball = new Sprite();
redFireballChannel.stop();
spriteRedFireball.graphics.beginFill(0x999999);
spriteRedFireball.graphics.drawRect(xPosRedFireball,yPosRedFireball,100,5);
spriteRedFireball.graphics.endFill();
addChild(spriteRedFireball);
spriteRedFireball.x-=spriteRedFireball.width/2;
sliderRedFireball = new Sprite();
sliderRedFireball.graphics.beginFill(0xFF0000);
sliderRedFireball.graphics.drawCircle(xPosRedFireball+50,yPosRedFireball, 15);
sliderRedFireball.graphics.endFill();
addChild(sliderRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
stage.addEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
boundaryRedFireball=new Rectangle(-100,0,100,0);
}
public function dragsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.startDrag(false,boundaryRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.CLICK, dragsliderRedFireball);
sliderRedFireball.addEventListener(Event.ENTER_FRAME, changeVolumeRedFireball);
}
public function stopsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.stopDrag();
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
}
public function changeVolumeRedFireball(event:Event):void {
volRedFireball=1+Math.round(sliderRedFireball.x)/100;
redFireballChannel.soundTransform=new SoundTransform(volRedFireball);
trace("output");
}
public function playSoundRedFireball():void
{
redFireballChannel = reqRedFireball.play();
}
public function stopSoundRedFireball():void
{
redFireballChannel.stop();
}
}
}
Changing a SoundTransform during every frame isn't good, as you are essentially undermining the audio channel. It's better if you use MouseEvent.MOUSE_MOVE to trigger volume change, as if mouse is moved, and volume slider is being dragged, then the SWF user apparently wants the volume to change. But if a user starts dragging the slider but does not move it, why changing the volume?
public function dragsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.startDrag(false,boundaryRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_MOVE, changeVolumeRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
}
public function stopsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.stopDrag();
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_MOVE, changeVolumeRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
}
Also, you have messed up your listeners. First, you are not removing the enterframe listener after you stop dragging the fireball. Second, you are not adding a start-drag listener back after the fireball has been released. And third, in your initRedFireball you are adding stopsliderRedFireball as listener to stage, for a really strange reason, but you are attempting to remove it from sliderRedFireball. Please pay precise attention on where your listeners go, what do they listen and where do you remove them and from which objects. Misuse of an enterframe listener can build up pretty quickly, and spoil you all the fun.

AS3: if-function doesn't listen to a boolean in another class

So... I'm working on a chess-game, and trying to make it so that a "public static boolean" (turn) dictates which player can make a move. This boolean is in a class (Board.as) which imports all the classes for all the chess-pieces (e.g. QueenW.as (for the White Queen)).
I've tried multiple ways: Trying to make functions not run anymore, and replacing the pieces (which are buttons) to other objects (non-clickable movieclips). Decided to go with the latter. I've traced the boolean in a chess-piece class, as well as the Board-class, in an ENTER_FRAME function. Both seem to trace it correctly when the value changes.
Problem is: Flash doesn't remove the chess-pieces and replaces them with a non-clickable object, even though the class in which it should happen (Board.as) does listen to the boolean when tracing. Anybody knows a solution?
A little piece of my code, which is relative to the problem:
Board class (which is the Documentclass for my .fla file)
package
{
import QueenWclass; //imports the class used for example.
public class Board extends MovieClip
{
public static var turn:Boolean = new Boolean; //creates public static bool.
var queenW:QueenWclass = new QueenWclass(); //creates aforementioned chess-piece.
var queenWnoturn:QueenWnoturn = new QueenWnoturn; //creates a non-clickable object.
}
public function Board()
{
turn = true;
this.addEventListener(Event.ENTER_FRAME, frameEnter);
addChild(queenW); //adds pieces to the screen.
}
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
}
And my QueenWclass.as class:
package
{
public class QueenWclass extends MovieClip
{
var queenW:QueenW = new QueenW();
}
public function QueenWclass()
{
addChild(queenW);
this.addEventListener(MouseEvent.CLICK, CLICKqueenW);
}
function CLICKqueenW(event.MouseEvent):void
{
Board.turn = false;
}
}
I hope I wrote this example correctly and understandably. There's no real timelimit to my project as I already had to turn it in an hour ago (but still got a 6/10 because of effort and how far I've come with this rather complex game). I just want to finish it for myself... Thanks in advance!
Maybe the code has not been copied correctly or there is a small problem.
This code:
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
Will only run once, when "Board" is created, it will not run when the state of "turn" changes.
Well, you have nothing that's listening for the boolean's change. The code that's checking the boolean is located in constructor, while the actual change is done in a MouseEvent.CLICK event listener. You have to either implement a function that's called repeatedly via Event.ENTER_FRAME listening, SetInterval(), or TimerEvent.TIMER (with a timer), or implement a publicly available property as a function, that would check which turn is it and do corresponding actions. The latter is a little better, as it works only when something is changed.
private static var _turn:Boolean=false;
public static function get turn():Boolean { return _turn; } // getter part
public static function set turn(value:Boolean):void // setter part
{
if (_turn==value) return; // no need to change turn
_turn=value;
if (_turn) YouGetATurn(); else EnemyGetsATurn();
// this part is what will get called when you change Board.turn
}

Controlling/initializing external Class [AS3]

Before firing away, I know there are many questions here on SO that are quite similar. Yet, none of the solutions given were of any help to me, probably because my case is a little different.
I have a main class which loads an external class (separate .as file). In this external class, there are several objects which have tweens and time events bound to them.
What I want to do, is starting the animations when a certain function is called in my Main class. However, I've tried numerous things to stop and/or reset the animations in the external class, so it will start from the beginning if the required function in Main is called.
Main.as:
package {
//required imports
public class Main extends MovieClip {
var myClass:MyClass = new MyClass; //this is the external class
var button:Button = new Button; //movieclip in the library
public function Main() {
addChild(myClass); //I want to do this here so the objects show from the start
//try 1: myClass.gotoAndStop(1);
//try 2: myClass.stop();
button.addEventListener(MouseEvent.MOUSE_CLICK, playAnimation);
}
function playAnimation (e:MouseEvent) {
//try 1: myClass.gotoAndPlay(1);
//try 2: myClass.start();
//try 3: controlling the startTweening() function in MyClass, I tried different ways
}
}
}
The problem starts in the Main class above. I don't want to animate yet!
MyClass.as:
package {
//required imports
public class MyClass extends MovieClip {
//vars
public function MyClass() {
startTweening();
}
function startTweening() {
//tween event
//calling next function (with use of a TimerEvent) after tween is done. This is repeated several times.
}
}
}
Everything in this class works fine, so that's not the problem.
If this makes any difference, I used TweenMax in MyClass for tweening. I didn't use the timeline in the .fla.
Any help would greatly appreciated!
If you don't want to animate at creation of MyClass remove startTweening(); call from the constructor of MyClass.
Make startTweening(); a public function and call it whenever your need with myClass.startTweening().
Here the MyClass
public class MyClass extends MovieClip {
//vars
public function MyClass() {
}
public function startTweening() {
//tween event
//calling next function (with use of a TimerEvent) after tween is done. This is repeated several times.
}
}
and here the Main class
public class Main extends MovieClip {
var myClass:MyClass;
var button:Button = new Button; //movieclip in the library
public function Main() {
myClass = addChild(new MyClass()) as MyClass;
button.addEventListener(MouseEvent.MOUSE_CLICK, playAnimation);
}
function playAnimation (e:MouseEvent) {
myClass.startTweening();
}
}

Controlling objects of another class that are already on the stage

I'm quite ashemed to ask this question here because I'm sure that I'm missing something very basic. I'm not even sure what should be the correct title for this question.
Let's say that I've a button object (instance of Flip) and a coin object (instance of Coin) on the stage. The coin object has two frames: one showing Heads and one for Tails.
MyCoin class is as following:
package
{
import flash.display.MovieClip;
public class Coin extends MovieClip
{
protected var _coinFace:uint;
public function Coin()
{
stop();
}
public function get coinFace():uint {
return _coinFace;
}
public function set coinFace(value:uint):void {
_coinFace = value;
}
public function show():void {
gotoAndStop(_coinFace);
}
}
}
Objective: When user clicks the button, the coin should flip and show a random coinFace. I've added an eventListener to the Flip class as follows:
public function Flip()
{
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
Problem: How do I reach the coin object on the screen via onMouseClick function? Let's say that the object on the stage has instance name of myCoin. I suppose that had I not done this with an external class and simply used actions from the frame I could just use the instance name as a variable. I couldn't figure to do the same it in an external class. Do I first create the object which is already on the stage?
Where you create the instance of each, the flip object needs to be passed an instance of the coin object.
var myCoin:Coin = new Coin();
var myFlip:Flip = new Flip(myCoin);
Then inside the Flip class:
private var _coin:Coin;
public function Flip(coin:Coin) {
_coin = coin;
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
private function onMouseClick(e:MouseEvent):void {
_coin.gotoAndStop(2); // Or what ever needs to be done to the coin on click
}
Alternatively, depending on the complexity of the overall structure, you can a build a control class that acts as a link between the two.