Scenes in Actionscript 3 - actionscript-3

Is there a way to set up scenes in AS3, without using the Flash tool? Basically when someone clicks a button I want to present a completely different screen. Right now if I try to draw a bitmap the bitmap starts at the upper left corner of the button rather than at the upper left corner of the program display.
This is easy using Flash, you just use gotoAndPlay(1, scenename). However, I want this program to be entirely in AS3 without using Flash.
Suggestions?

You'll have to create your own collection of classes that represent scenes. This also involves some type of foundation to run your scenes ontop of, managing loading and unloading those scenes and their content.
In a simple scenario, this would begin as something along the lines of:
class SceneManager extends Sprite
{
private var _currentScene:Scene;
public function loadScene(scene:Scene):void
{
unloadCurrentScene();
_currentScene = scene;
_currentScene.load(this);
addChild(_currentScene.view);
}
public function unloadCurrentScene():void
{
if(hasScene)
{
_currentScene.unload(this);
_currentScene.view.parent === this && removeChild(_currentScene.view);
}
_currentScene = null;
}
public function get currentScene():Scene{ return _currentScene; }
public function get hasScene():Boolean{ return _currentScene !== null; }
}
With the Scene class:
class Scene
{
private var _view:Sprite;
public function Scene()
{
_view = new Sprite();
}
public function load(sceneManager:SceneManager):void
{
// Abstract.
// Load children, initialize components, etc.
}
public function unload(sceneManager:SceneManager):void
{
// Abstract.
// Remove children, event listeners, references, etc.
}
public function get view():Sprite{ return _view; }
}
Used like (assuming you have a class MainMenuScene extending Scene):
var sceneManager:SceneManager = new SceneManager();
addChild(sceneManager);
sceneManager.loadScene( new MainMenuScene() );

Related

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

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.

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
}

How to improve this AS3 code structure to be more effective?

I have made an AS3 code to be a function. But I think my code is too lengthy. Could you help to improve it? Thank you!
I created test.fla first and added 5 grey block(external pictures from PSD) to stage. My function is to display different pictures when hovering mouse on corresponding grey block.
I converted those 5 grey blocks to Movie Clip and set instance name as sp1, sp2, sp3, sp4 and sp5. Then I created a document class, test.as and set 5 EventListener.
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse1);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse2);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse3);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse4);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse5);
So my first question is can I have any method to combine those 5 EventListener to be one? Because in my mind, so many EventListener will cost much more resource of PC.
My second question is I set 5 target pictures as 5 class.
In test.as I created code below:
public class EuroCup extends Sprite{
var arr:Array=new Array();
var Res1:Result609=new Result609();
var Res2:Result610=new Result610();
var Res3:Result611=new Result611();
var Res4:Result612=new Result612();
var Res5:Result613=new Result613();
var i:int=0;
public function EuroCup() {
arr[1]=Res1;
arr[2]=Res2;
arr[3]=Res3;
arr[4]=Res4;
arr[5]=Res5;
}
}
I think that is too lengthy. Is there any way to simplify it?
Here is the test.fla and test.as:Download
Whatever, thank u guys!
Restructuring:
public class EuroCup extends Sprite {
private var arr:Array;
public function EuroCup() {
arr = [ new Result609(), new Result610(),
new Result611(), new Result612(), new Result613()
];
}
}
Then use results as arr[0], arr[1] and so on. Also, if you have several sprites to listen clicks on, with similar listeners, you can connect all such sprites to single listeners and use event.target to distinguish them, where event is MouseEvent. Or place them into container and create one listener to that container - again, event.target will tell what sprite is clicked.
And yet two things - every time you see new Array(), replace it with [] - its faster and shorter. And place all code into constructor, not class body - it will be compiled to be executed faster.
You can/should use a Dictionary for associations between the grey rects and the images to display.
package {
public class EuroCup {
private var _children:Array, _current:Sprite, _map:Dictionary;
public function EuroCup() {
super();
initialize();
}
protected function initialize():void {
_children = [];
_map = new Dictonary();
// i don't know the image's symbol name.
// _map[_children[_children.length] = new Result609()] = new SYMBOL_NAME();
for each(var child:Sprite in _children) {
child.addEventListener(MouseEvent.CLICK, click_handler);
}
}
private function click_handler(event:MouseEvent):void
{
if (_current) {
_current.visible = false; // or use fading, etc
}
_current = _map[event.currentTarget] as Sprite;
if (_current) {
_current.visible = true; // or use fading, etc
}
}
}
}
One option for simplifying the code would be to associate the sp and Res instances with each other by identity, using a Dictionary. That allows you to avoid the work of tracking array indices, which is half of the reason you have separate event handler methods. Once the instances are associated by identity, then you can use the currentTarget property of a dispatched event to determine which element in the Dictionary you want to show on the stage.
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Dictionary;
public class test extends Sprite
{
var dict:Dictionary = new Dictionary();
var visibleResult:MovieClip;
public function test()
{
dict[sp1]=new Result609();
dict[sp2]=new Result610();
dict[sp3]=new Result611();
dict[sp4]=new Result612();
dict[sp5]=new Result613();
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
}
private function clickmouse(evt:MouseEvent):void
{
if(visibleResult)
{
removeChild(visibleResult);
}
var Res:MovieClip = dict[evt.currentTarget] as MovieClip;
addChild(Res);
Res.x=300;
Res.y=400;
visibleResult=Res;
}
}
}
If you expect to have more than 5 sp instances in the application, then you could use a loop to assign the event listeners. But for less than 10 instances, you probably don't gain much from a loop.
I would go for a more simple version; add only one event listener and use Event.target to determine on which item is clicked, using a switch-statement.
This is helpful if the buttons should do different things.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public var sp1:Sprite;
public var sp2:Sprite;
public var sp3:Sprite;
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
trace("Clicked on: " + event.target)
switch (event.target)
{
case this.sp1:
{
// do something here
break;
}
case this.sp2:
{
// do something here
break;
}
case this.sp3:
{
// do something here
break;
}
default
{
trace("No handler defined for: " + event.target)
}
}
}
}
}
However, you can also make smart use of it's type. Let's say all you buttons extend a custom class called CustomButton, and they all need to do the same (like call a function), but with a parameter based on it's id.
This is helpful if the buttons should basically do the same thing.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
if (event.target is CustomButton)
{
var button:CustomButton = event.target as CustomButton; // you're now sure it's a CustomButton
this.showById(button.id); // let's say CustomButton has a public var 'id'
}
}
private function showById(id:int):void
{
// do something
}
}
}
Hope that helps.
Tip: Always start your class+filename with a capital. Variables start with capitals. This is very common in the actionscript world.

upcasting in actionscript 3

Hi I have a custom class that extends Sprite, that controls positioning on the stage. I would like the class that manages the stage to be able to take any sprite that is added to the stage and convert it to custom class type, ie wrap it so that the managing class can position a native sprite accordingly.
Does anyone know if it is possible to do something like the following
var managed:managedSprite = new managedSprite(nativeSprite);
where the managedSprite extends the Sprite class? I don't want to have to composite in a reference to the sprite if at all possible in order to prevent the overhead associated with the managedSprite already extending Sprite.
Is there anyway using reflection (perhaps the commons.reflection library)?
Thanks
You can add an event listener to the stage and use Event.ADDED to get a reference to any display object added anywhere in the display list.
Then simply type cast if the added item is a subclass of ManagedSprite (BTW, the convention is to start your class names with an uppercase letter):
stage.addEventListener (Event.ADDED, onAdded);
function onAdded( ev:Event ):void {
if (ev.target is ManagedSprite) {
var managed:ManagedSprite = ManagedSprite( ev.target );
doStuffWith( managed );
}
}
EDIT
I think I only just understood your question: You are not trying to do an actual type cast - which would require your class hierarchy to be already set up, i.e. you'd have to have extended the ManagedSprite class already - but to add functionality at runtime!
I would strongly discourage you from trying to do deep copies or such - it will be heavy on performance, depending on how many sprites you are going to add, and you will no longer have your compiler to help you prevent errors.
Rather, see if you can't favor composition over inheritance: Create a kind of "proxy" class for the sprite, let's call it ManagedSpriteProxy, which implements all the methods you would call on ManagedSprite, but forwards all the actual manipulations to its `managedSprite' property. Then use the event handler I outlined above to create the proxy objects and attach the respective sprites:
public class ManagedSpriteProxy {
private var _managedSprite:Sprite;
public function ManagedSpriteProxy( managedSprite:Sprite ) {
this.managedSprite = managedSprite;
}
public function get managedSprite():Sprite {
return _managedSprite;
}
public function set managedSprite( managedSprite : Sprite ):void {
_managedSprite = managedSprite;
setUpAnyHandlersOrWhatever();
}
private function setUpAnyHandlersOrWhatever():void {
// Many wonderful things happening
}
// Many more wonderful things happening via public API
}
// somewhere else
public class SpriteManager {
private var _managedSprites:Array = [];
public function SpriteManager() {
addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
}
private function onAddedToStage( ev:Event ):void {
removeEventListener( Event.ADDED_TO_STAGE );
stage.addEventListener( Event.ADDED, onAdded );
}
private function onAdded( ev:Event ):void {
if( ev.target is Sprite ) {
addWithProxy( ev.target as Sprite );
}
}
private function addWithProxy( sprite:Sprite ) : void {
var proxy:ManagedSpriteProxy = new ManagedSpriteProxy( sprite );
_managedSprites.push( proxy );
}
// Insert here whatever methods used to manage the sprites,
// all of them call the proxies instead of the sprites!
}
You may want to use the 'Decorator' pattern.
It seems a little bit complex at the first sight but it's quite easy to understand and use.
http://www.as3dp.com/2009/04/actionscript-30-easy-and-practical-decorator-design-pattern/

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.