I posted yesterday about how to communicate to one class from another that I wanted to delete an instance of it, and I got the dispatcher working today. However, I think I've painted myself into a corner. Even though the dispatcher is working, I A:feel like it's running through too many functions on the way to actually deleting the object, and B: still can't manage to get it to actually delete. I don't have any formal CS training, so it's one of those situations where my mind is going in circles and I can't "see" what I'm doing wrong. I figure if I post my classes here, at the very least people can have a chuckle at my amateur code, and if I'm lucky, some kind soul will point out what I'm doing wrong. So here goes:
Background.as:
//Background class. Singleton? Sets up/maintains the application.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Background extends flash.display.MovieClip {
private var slate:MovieClip;
private var slateBounds:Rectangle = new Rectangle(100,-260,0,280);
private var _toolbox:MovieClip;
private var _elementArray:Array = new Array();
public function Background() {
//attach movieclips to stage
slate = new mc_slate();
slate.x = 100;
slate.y = 20;
addChild(slate);
_toolbox = new Toolbox();
_toolbox.x = 750;
_toolbox.y = 20;
addChild(_toolbox);
//set draggables
//slate.addEventListener(MouseEvent.MOUSE_DOWN, dragSlate);
//slate.addEventListener(MouseEvent.MOUSE_UP, releaseSlate);
slate.addEventListener(MouseEvent.MOUSE_UP, dropNewElement);
}
private function dragSlate(event:MouseEvent) {
slate.startDrag(false, slateBounds);
}
private function releaseSlate(event:MouseEvent) {
slate.stopDrag();
}
private function dropNewElement(event:MouseEvent) {
var _elementType:String = _toolbox.currentTool;
var _x:Number = event.target.x;
var _y:Number = event.target.y;
var _newElement:MovieClip;
var _latestIndex:Number;
//case switch to choose element based on _elementType
//add new element to stage
_newElement = new PageElement(_elementType, event.localX, event.localY);
_latestIndex = _elementArray.push(_newElement);
_newElement.addEventListener("closeWindow", deleteElement);
slate.addChild(_newElement);
}
private function deleteElement(event:Event) {
trace("trying to remove element.");
slate.event.target.removeChild(_elementArray[0]);
}
}
}
Toolbox.as:
//Toolbox class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Toolbox extends flash.display.MovieClip {
private var _toolboxback:MovieClip;
private var _tool01:MovieClip;
private var _tool02:MovieClip;
private var _tool03:MovieClip;
private var _tool04:MovieClip;
private var _tool05:MovieClip;
private var _currentTool:String = 'none';
public function Toolbox() {
_toolboxback = new ToolboxBack();
_toolboxback.x = 0;
_toolboxback.y = 0;
_toolboxback.alpha = .5;
addChild(_toolboxback);
_tool01 = new TextTool();
_tool01.x = 10;
_tool01.y = 10;
addChild(_tool01);
//_tool01.addEventListener(MouseEvent.MOUSE_DOWN, dragTool);
_tool01.addEventListener(MouseEvent.MOUSE_UP, switchTool);
_tool02 = new ImageTool();
_tool02.x = 10;
_tool02.y = 54;
addChild(_tool02);
_tool02.addEventListener(MouseEvent.MOUSE_UP, switchTool);
}
private function dragTool(event:MouseEvent) {
event.target.startDrag(false);
}
private function releaseTool(event:MouseEvent) {
event.target.stopDrag();
}
private function switchTool(event:MouseEvent) {
_currentTool = event.target.toolname;
//trace(_currentTool);
}
public function get currentTool():String{
return _currentTool;
}
}
}
Tool.as (any class with "Tool" at the end of it simply extends this class and adds a name)
//Tool class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Tool extends flash.display.MovieClip {
private var _toolname:String;
public function Tool(toolname) {
_toolname = toolname;
}
public function get toolname():String{
return _toolname;
}
}
}
PageElement.as:
//Page element class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
public class PageElement extends flash.display.MovieClip {
private var _elementname:String;
private var _elementback:MovieClip;
private var _elementmenu:MovieClip;
private var _title:TextField;
private var _formatter:TextFormat = new TextFormat();
public function PageElement(elementname, x, y) {
_elementname = elementname;
_elementback = new ElementBack();
_elementback.x = x;
_elementback.y = y;
_elementback.alpha = .5;
_elementback.addEventListener(MouseEvent.MOUSE_DOWN, dontBubble);
_elementback.addEventListener(MouseEvent.MOUSE_UP, dontBubble);
_elementmenu = new ElementMenu();
_elementmenu.x = x + _elementback.width - 5;
_elementmenu.y = y - 5;
_elementmenu.addEventListener(MouseEvent.MOUSE_OVER, showElementMenu);
_elementmenu.addEventListener(MouseEvent.MOUSE_OUT, retractElementMenu);
_elementmenu.addEventListener(MouseEvent.MOUSE_DOWN, dragElement);
_elementmenu.addEventListener(MouseEvent.MOUSE_UP, releaseElement);
_formatter.font = "Helvetica";
_formatter.size = 10;
_title = new TextField();
_title.text = elementname;
_title.x = x;
_title.y = y;
_title.textColor = 0xffffff;
_title.setTextFormat(_formatter);
addChild(_title);
addChild(_elementback);
addChild(_elementmenu);
}
public function get elementname():String{
return _elementname;
}
public function set elementTitle(newTitle) {
}
public function showElementMenu(event:MouseEvent) {
_elementmenu.expandMenu();
}
public function retractElementMenu(event:MouseEvent) {
_elementmenu.retractMenu();
}
public function hideElementMenu() {
_elementmenu.alpha = 0;
}
private function dragElement(event:MouseEvent) {
event.target.parent.parent.startDrag(false);
event.stopPropagation();
}
private function releaseElement(event:MouseEvent) {
event.target.parent.parent.stopDrag();
event.stopPropagation();
}
private function dontBubble(event:MouseEvent) {
event.stopPropagation();
}
}
}
DeleteBack.as:
//Element menu back class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class DeleteBack extends flash.display.MovieClip {
public function DeleteBack() {
}
public function closeElement(event:MouseEvent) {
dispatchEvent(new Event("closeWindow", true));
trace("event dispatched.");
}
}
}
ElementMenu.as:
//Element menu class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import caurina.transitions.Tweener;
public class ElementMenu extends flash.display.MovieClip {
private var _elementmenuback:MovieClip;
private var _deletebutton:MovieClip;
public function ElementMenu() {
_elementmenuback = new ElementMenuBack();
_elementmenuback.x = 0;
_elementmenuback.y = 0;
_elementmenuback.width = 100;
_elementmenuback.height = 5;
_elementmenuback.alpha = .5;
addChild(_elementmenuback);
_deletebutton = new DeleteBack();
_deletebutton.x = -5;
_deletebutton.y = 10;
_deletebutton.width = 10;
_deletebutton.height = 10;
_deletebutton.alpha = .2;
_deletebutton.visible = false;
addChild(_deletebutton);
_deletebutton.addEventListener(MouseEvent.MOUSE_DOWN, closeElement);
}
public function expandMenu() {
Tweener.addTween(_elementmenuback, {height:30, time:.2, transition:"easeOutBack"});
_deletebutton.visible = true;
}
public function retractMenu() {
Tweener.addTween(_elementmenuback, {height:5, time:.1, transition:"easeInBack"});
_deletebutton.visible = false;
}
public function closeElement(event:MouseEvent) {
//check that the user really wants to close the element before sending the destroy signal
//perform any closing animations
//this.parent.destroy();
_deletebutton.closeElement(event);
}
}
}
That's it for the meaningful classes. Anything else is either an empty class that's only in there to help make the library object accessible to ActionScript, or a trivial extension of something else.
The code puts a new element onto the stage, gives it a cool little dropdown menu that makes it draggable and has a delete button on it, and should link that button to a function that closes the element.
I've got everything but the closing.
General code criticism also very welcome. Like I said, I have no training, I've been figuring this stuff out for myself, and feedback of any kind from people who know what they're doing is valuable.
Thanks!
SS
PS. In response to Daniel's comment, here are the steps the code takes:
The Background class puts everything on the stage and creates the toolbox.
The toolbox creates the tools, which are like Photoshop's tools. You click on them to select an element you want to add to the stage, then you click inside the "slate" to drop a new instance of that object on top of it. The background creates the instance and saves it in an array of all instances created at runtime.
The new element makes its own dropdown menu, which is the draggable portion of the element and holds the delete button. This menu places an eventListener on the delete button.
When the delete button is clicked, the eventListener placed on it by its parent class calls an event dispatcher inside the delete button class itself.
This dispatched event is caught by the background class (I figured the best class to remove the element is the same class that made it, right?) and triggers the actual code to remove the element.
This code, "deleteElement," is where I'm stuck. I have all the instances in an array, but the event has gone through so many intermediary classes, the MouseEvent, and thus, I suspect, the MouseEvent target, has fallen by the wayside. So the only way to know which element to delete is to find its array index. I have no idea how this would work. Any ideas?
let's do this a bit at a time...
slate.event.target.removeChild(_elementArray[0]); in background.as
why are you using slate.event?
you are passing an event object to the function, but looks like you're using a different event's target, which I don't know where it's coming from or why it's not giving you an error.
it should just be event.target, which should give you the PageElement(formerly known as _newElement)
what I don't know also is why you are removing a child from it which is _elementArra[0] - which really is another PageElement and likely itself if you only have one.
so it looks to me that there are a bunch of things that should have thrown errors. What are you using to compile your code? What about debugger? are you using any?
If you look at your previous question, I added some code there about how to get the parent. So I adjusted it a bit
function deleteElement($e:MouseEvent):void{
var parentMC:MovieClip = $e.target.parent;
parentMC.removechild($e.target);
}
however the problem is that you're not passing a MouseEvent but a blank event
dispatchEvent(new Event("closeWindow", true)); in DeleteBack.as
so this will not pass anything under target, and you can't get it. (target is read only, so new Event(etc) will always have a null target. So essentially that's a bit of a lost cause.
you could set an onject in your singleton and pass which mc is to be deleted, and then the deleteElement would just grab that object. The other option is to look into the signals class which will let you do some better/more efficient event handling.
finally (sort of, there's more but for now) I'd say look into using CASAlib, in particular, use CasaMovieClip instead of MovieClip for extending, as it will delete your movie clips better. If you have a lot of event listeners and you don't clear them properly, they'll end up staying in memory even after you delete them.
of course looking into other frameworks like RobotLegs is a good idea too, it gets you into better practices.
GL
Edit ...
frameworks/micro-architectures:
http://www.robotlegs.org/
http://swizframework.org/
http://puremvc.org/
and many more
I think the important thing is to not get stuck on a framework (though I mention the word often). And the best framework is the framework that is best for you, and to me that means offering a good communication backbone for the app and staying out of the way.
My setup for writing code is this:
FlashDevelop with the Flex Compiler. FlashDevelop is for PC only so if you're on Mac you might want to consider other options like flex. FlashDevelop and the Flex compiler(the compiler only) are both free so you can't go wrong, and once you start using it you won't want to go back to coding in Flash - guaranteed!!
debugging:
Trace is the simplest form of debugging, and it can be quite difficult to understand the problem.
You can use the flash debugger by pressing Ctrl-Shift-Enter to compile and run. You will need to set the break points ahead though.
FlashDevelop has a debugger that works just like the Flash and Flex debuggers and I use it quite often.
But my favorite debug tool has to be de monster debugger
it takes a bit to more to implement, and you need to add some code, but it found issues for me that I couldn't get to using the default debugger only. Definitely worth a look.
Related
I am programming in AS3 on flash develop. I am using a program called flashpunk that adds in multiple presets for classes that i can use to make it easier to program. I have an if statement that is supposed to add a new graphic when a variable equals 1. If the user presses one on their keyboard then that variable does increase by 1. But when the variable increases by 1 and the if statement is supposed to check if it is one it doesn't add a new graphic like it's supposed too. I do have the if statement and the variable in different classes and i am not sure if i can do that, here is the code. The if statement that doesn't work is the one that is supposed to add a new background1.
Chapter
package
{
import net.flashpunk.Entity;
import net.flashpunk.World;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
public class Chapter extends World
{
public var mainmenu:MainMenu;
public var background1:Background1;
public function Chapter()
{
mainmenu = new MainMenu();
add(mainmenu);
background1 = new Background1();
if(mainmenu.YesNo == 1)
{
add(background1);
}
}
}
}
MainMenu
package
{
import flash.events.TimerEvent;
import flash.utils.Timer;
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
import net.flashpunk.FP;
public class MainMenu extends Entity
{
[Embed(source = "net/MainScreen.png")]
private const SPRITE1:Class;
public var YesNo:int = 0
private var sprite1:Image = new Image(SPRITE1);
public function MainMenu()
{
graphic = sprite1;
sprite1.centerOrigin();
x = 200
y = 150
layer = 150
}
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
YesNo = YesNo + 1;
}
trace(YesNo);
}
}
}
The problem is that your code is in a World class constructor. Flashpunk's idea of game objects is that Entities are added to Worlds. Once added, World runs update() method every frame in which it also runs every Entity's update() as well. Yours if statement in a constructor will always yield false as update wasn't yet executed.
If I understand correctly you want to add background entity after user will press 1. You can do it like this:
// in MainMenu class
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
world.add(new Background1());
}
}
If you want to hold reference to this entity you could:
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
Chapter(world).background1 = new Background1(); // note the casting of World to Chapter
world.add(Chapter(world).background1);
}
}
I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.
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.
I have a small project I'm trying to help learn as3. It is a variation from the book Foundation Game Design with Actionscript 3.0. I am using an fla only to have a document class. All art is loaded within the as files. In the book, he just put all the code in the document class, I followed along and it worked as expected. I am trying to break out the code into separate classes to get a handle on OOP. One class makes a background - Background.as, one makes a character - Character.as, and one makes a button, which I instantiate 6 times for 6 different buttons - GameButton.as. And of course there is GameWorld.as which is the document class. Everything loads and shows up as expected. However when I try and add an eventListener for the buttons, I don't get any response. I have tried putting the eventListener in the GameButton.as and also tried it in the GameWorld.as neither of which has worked. Also I pass a reference to the stage when instantiating the various classes, because when I tried to addChild in the GameWorld.as, nothing would show up. I searched the site and found something similar, but it didn't seem to help. Thank you in advance for any advice you my have. Here is the code:
GameWorld.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.DisplayObject
import flash.events.MouseEvent;
import GameButton;
import Character;
import Background;
[SWR(width = "550", height = "400", backgroundColor = "#FFFFFF", frameRate = "60")]
public class GameWorld extends Sprite
{
//public variables
//Background
public var gameBackground:Background;
//Character
public var catCharacter:Character;
//Buttons
public var upButton:GameButton;
public var downButton:GameButton;
public var growButton:GameButton;
public var shrinkButton:GameButton;
public var vanishButton:GameButton;
public var spinButton:GameButton;
public function GameWorld ()
{
//Add the background to the stage
gameBackground = new Background("../images/background.png", stage);
//Add the character(s) to the stage
catCharacter = new Character("../images/character.png", stage);
//Set initial character position
catCharacter.CharacterPos(225, 150);
//Add the buttons to the stage
upButton = new GameButton("../images/up.png", stage, 25, 25);
downButton = new GameButton("../images/down.png", stage, 25, 85);
growButton = new GameButton("../images/grow.png", stage, 25, 145);
shrinkButton = new GameButton("../images/shrink.png", stage, 425, 25);
vanishButton = new GameButton("../images/vanish.png", stage, 425, 85);
spinButton = new GameButton("../images/spin.png", stage, 425, 145);
//Button event handlers
upButton.addEventListener(MouseEvent.CLICK, upButtonHandler);
}
public function upButtonHandler(event:MouseEvent)
{
trace("You clicked the up button!");
catCharacter.CharacterMove(15);
}
}
}
GameButton.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class GameButton extends Sprite
{
//public variables
public var stageRef:Stage;
public var urlRequest:URLRequest;
public var gameButtonLoader:Loader;
public var gameButtonSprite:Sprite;
//Constructor
public function GameButton (urlRequest:String, stageRef:Stage, xPos:Number, yPos:Number)
{
this.stageRef = stageRef
this.urlRequest = new URLRequest();
gameButtonLoader = new Loader();
gameButtonSprite = new Sprite();
this.urlRequest.url = urlRequest;
gameButtonLoader.load(this.urlRequest);
gameButtonSprite.addChild(gameButtonLoader);
this.stageRef.addChild(gameButtonSprite);
gameButtonSprite.buttonMode = true;
gameButtonSprite.x = xPos;
gameButtonSprite.y = yPos;
}
}
}
Character.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Character
{
//private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var characterLoader:Loader;
private var characterSprite:Sprite;
//public variables
public var character_x_pos:Number;
public var character_y_pos:Number;
//Constructor
public function Character (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
characterLoader = new Loader();
characterSprite = new Sprite();
this.urlRequest.url = urlRequest;
characterLoader.load (this.urlRequest);
characterSprite.addChild (characterLoader);
this.stageRef.addChild (characterSprite);
characterSprite.mouseEnabled = false;
}
//Set the position of the character
public function CharacterPos(xPos:Number, yPos:Number):void
{
characterSprite.x = xPos;
characterSprite.y = yPos;
}
//Move the position of the character
public function CharacterMove( yPos:Number):void
{
characterSprite.y -= yPos;
}
}
}
Background.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Background
{
//Private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var backgroundLoader:Loader;
private var backgroundSprite:Sprite;
//Constructor
public function Background (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
backgroundLoader = new Loader();
backgroundSprite = new Sprite();
this.urlRequest.url = urlRequest;
backgroundLoader.load (this.urlRequest);
backgroundSprite.addChild (backgroundLoader);
this.stageRef.addChild (backgroundSprite);
backgroundSprite.mouseEnabled = false;
}
}
}
All art is loaded within the as files.
This is not an approach I recommend. There's a reason God gave us the Flash IDE--and it's not to write code! Any time you're spending on layout and viduals in code is just wasted, unless you have an actual requirement to change the visuals at runtime. The fact that your paths are all hard-coded suggests that you don't have that requirement.
So let's step back and imagine that you have a Symbol that contains 6 Symbols that you've created as just Flash buttons (when you select Button as the Symbol type). These will be SimpleButtons, but in the Class below we're just going to type them as DisplayObject. The Class doesn't care what they are, but using Simplebutton gives them up, over, down and hit states that require no code.
Note that the below assumes you have "automatically declare stage instances" off, which is IMO the best way to do things.
package view {
public class NavBar extends Sprite {
//because you put these on stage in the Symbol, they will be available in the constructor
public var upButton:DisplayObject;
public var downButton:DisplayObject;
public var growButton:DisplayObject;
public var shrinkButton:DisplayObject;
public var rotateButton:DisplayObject;
public var vanishButton:DisplayObject;
//makes it easier to do the same setup on all buttons
protected var allButtons:Vector.<DisplayObject> = <DisplayObject>([upButton, downButton, growButton, shrinkButton, rotateButton, vanishButton]);
public function NavBar() {
super();
for each (var btn:DisplayObject in allButtons) {
btn.buttonMode = true;
btn.mouseChildren = false;
btn.addEventListener(MouseEvent.CLICK, onButtonClick);
}
}
protected function onButtonClick(e:MouseEvent):void {
switch (e.target) {
case upButton:
dispatchEvent(new CharacterEvent(CharacterEvent.UP));
break;
case downButton:
dispatchEvent(new CharacterEvent(CharacterEvent.DOWN));
break;
case growButton:
dispatchEvent(new CharacterEvent(CharacterEvent.GROW));
break;
case shrinkButton:
dispatchEvent(new CharacterEvent(CharacterEvent.SHRINK));
break;
case rotateButton:
dispatchEvent(new CharacterEvent(CharacterEvent.ROTATE));
break;
case vanishButton:
dispatchEvent(new CharacterEvent(CharacterEvent.VANISH));
break;
default:
break;
}
}
}
}
Note that there's zero layout code. This code is dependent on a custom Event Class. I'm going to write that Event Class so that it always bubbles. That way, it can be dispatched anywhere on the display list and received at the top level:
package control {
class CharacterEvent extends Event {
public static var UP:String = 'characterUp';
public static var DOWN:String = 'characterDown';
public static var GROW:String = 'characterGrow';
public static var SHRINK:String = 'characterShrink';
public static var ROTATE:String = 'characterRotate';
public static var VANISH:String = 'characterVanish';
public function CharacterEvent(type:String) {
super(type, true, true);//always bubbles
}
public function clone():Event {
return new CharacterEvent(type);
}
}
}
Now, if you want to manually handle instantiation of the Symbol that has view.NavBar as its base class, it will look like this:
package {
public var navBar:NavBar;
class GameWorld {
public function GameWorld() {
try {
var navClass:Class = getDefinitionByName('NavBarSymbol') as Class;
} catch (e:Error) {
trace('You need to have a Library symbol called NavBarSymbol');
}
if (navClass) {
navBar = new navClass() as NavBar;
//unnecessary layout code here
//Note that this is NOT the responsibility of the child Class!
addChild(navBar);
}
//instantiate character
...
//by listening to the whole Document, you can add other things that
//dispatch Character events on the display list, like a keyboard listener
addEventListener(CharacterEvent.UP, moveCharacterUp);
//listeners for the rest of the character events...
}
public function moveCharacterUp(e:CharacterEvent):void {
//character move logic
}
//other handlers
}
}
Personally, I'd just add the navBar to the stage, and then there's no need to manage it at all (not even reference it with a variable), simply add the event listeners for the various character events.
The root of your problem doesn't seem to be the character code. However, I'm going to give you a few "best practice" pointers about it.
The convention in AS3 is for Class members (properties and methods) to be camel case starting with a lower case letter. So, characterPos() and characterMove().
Your Class already contains character in the name, so really these should just be pos() and move() (though there's no need now to shorten position()).
The only thing your child Classes are doing with their references to the parent are adding themselves. They don't need and shouldn't have a reference to the parent for this purpose. It is the parent's responsibility to add the Children (or the responsibility of the Flash Player if you use the stage).
That said, you could give your Character a reference to the parent Class typed as IEventDispatcher and allow the Character to listen to this channel. This concept is called an event bus.
Note that the reason that so many people do what you're doing is that Adobe failed to document how to properly use OOP with the timeline. Unfortunately, by the time a few of us started documenting that around late 2009/early 2010, the damage was done and everyone assumed that if you wanted to write good code you had to pretend the timeline and stage didn't exist.
I know I've covered a lot of ground, and probably most of what I said directly contradicts what you thought you knew, so please don't hesitate to ask any questions you might have.
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.