Can multiple mouse actions be added to a button? How? - actionscript-3

So i have a ton of button on stage and id like to assign the same function to all the buttons.
I want the function itself to evaluate whether ot not the event received was a MOUSE_CLICK or a MOUSE_HOVER and based on the event do something...
What i have is something like this....
btn1.addEventListener(MouseEvent.CLICK, makeListener('option1'));
btn2.addEventListener(MouseEvent.CLICK, makeListener('option2'));
btn3.addEventListener(MouseEvent.CLICK, makeListener('option3'));
btn4.addEventListener(MouseEvent.CLICK, makeListener('option4'));
btn5.addEventListener(MouseEvent.CLICK, makeListener('option5')); // and so on for about 100 buttons
btn1.addEventListener(MouseEvent.MOUSE_HOVER, makeListener('option1'));
btn2.addEventListener(MouseEvent.MOUSE_HOVER, makeListener('option2'));
btn3.addEventListener(MouseEvent.MOUSE_HOVER, makeListener('option3'));
btn4.addEventListener(MouseEvent.MOUSE_HOVER, makeListener('option4'));
btn5.addEventListener(MouseEvent.MOUSE_HOVER, makeListener('option5')); // and so on for about the same 100 buttons
function makeListener(option: String): Function {
return function (event: Event): void {
// DO something
}
}
What I would like is something like this... (obviously it doesnt work)
btn1.addEventListener(MouseEvent.CLICK || MouseEvent.CLICK, makeListener('option1'));
btn2.addEventListener(MouseEvent.CLICK || MouseEvent.CLICK, makeListener('option2'));
btn3.addEventListener(MouseEvent.CLICK || MouseEvent.CLICK, makeListener('option3'));
btn4.addEventListener(MouseEvent.CLICK || MouseEvent.CLICK, makeListener('option4'));
btn5.addEventListener(MouseEvent.CLICK || MouseEvent.CLICK, makeListener('option5')); // and so on for about 100 buttons
function makeListener(option: String): Function {
return function (event: Event): void {
if (event = MOUSE_CLICK){
// DO something
} else if(event = MOUSE_HOVER){
// Do Something else
}
}
}
Any ideas?

I'd add CLICK and MOUSE_HOVER listeners in button class and in event handler for them dispatch CustomEvent.
Next on the stage where you are listening for all this CLICK and HOVER events I'll addEventListener for this custom Event and in one of the parameters pass which action you want to do.
So 1st you need ButtonCustomEvent class with const and extra param:
public class ButtonCustomEvent extends Event
{
public static const CUSTOM_EVENT:String = "ButtonCustomEvent::CUSTOM_EVENT";
private var _customParam:String;
public function ButtonCustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
public function get customParam():String
{
return _customParam;
}
public function set customParam(value:String):void
{
_customParam = value;
}
}
Next in your button class:
public class MyButtonClass extends Sprite
{
public function MyButtonClass ()
{
super();
addEventListener(MouseEvent.CLICK, handleEvent);
addEventListener(MouseEvent.MOUSE_OVER, handleEvent);
}
public function handleEvent(event:MouseEvent):void
{
var myEvent:ButtonCustomEvent = new ButtonCustomEvent(ButtonCustomEvent.CUSTOM_EVENT, true);
myEvent.customParam = event.type;
dispatchEvent(myEvent);
}
}
And then if class where you are adding listeners for ~100 buttons you can do now:
addEventListener(ButtonCustomEvent.CUSTOM_EVENT, handleEvent);
public function handleEvent(event:ButtonCustomEvent):void
{
if(event.customParam == MouseEvent.CLICK)
{
//DO something
}
else if(event.customParam = MouseEvent.MOUSE_OVER)
{
//Do Something else
}
}

Related

Is it possible to make use of a value returned from a method called by a delegate?

While experimenting with a basic coding stuff, I wondered, if a value returned by a method called by a delegate, could be used or captured. In other words, where will the return value will go ?
For example :
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener( "myClick" , myClick);
}
function myClick(e:Event):String //returning a string
{
return "What happens to this return value ???"
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener( MouseEvent.CLICK , onClick);
}
function onClick(e:Event):String //returning a string
{
dispatchEvent(new Event("myClick"));
}
}
As I know it's not possible to do, but, there are at least some ways to implement the logic similar to what you've told about.
For example, you may call a method of a dispatcher, from a listener method:
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener("myClick" , myClick);
}
function myClick(e:Event):void
{
mc.specialMethod("some string");
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener(MouseEvent.CLICK , onClick);
}
function onClick(e:Event):void
{
dispatchEvent(new Event("myClick"));
}
public function specialMethod(param:String):void
{
// Do something to the param
}
}
Also, you may think about dispatching an event from the Main class, and listen to it in the MyMc class, or pass a callback, which returns a string, from Main to the MyMc.
It's according to you and your needs to return something from the listener function because normally it must return nothing :
... This function must accept an Event object as its only parameter and must return nothing, ...
but you can of course get the returned value(s), take a look on this little example :
var light_on:Boolean = false;
btn_light_on.addEventListener(MouseEvent.CLICK, btn_on_onPress);
btn_light_off.addEventListener(MouseEvent.CLICK, btn_off_onPress);
function btn_on_onPress(e:MouseEvent): Boolean {
light_on = true;
if(e.target === btn_light_off){
light_on = false;
}
return light_on;
}
function btn_off_onPress(e:MouseEvent): void {
trace('The light is', btn_on_onPress(e) ? 'on' : 'off');
}
Hope that can help.

AS3 Mouse_Over Getting Stuck

I have many buttons being populated on the stage. They are all movieclips with an on and off state on frame one and 2. The problem is when you mouse over the buttons quickly sometimes it gets stuck on the over state. Is there something i am missing?
public class SimpleRollOverButton extends MovieClip
{
private var _selected:Boolean;
public function SimpleRollOverButton()
{
// EVENTS
this.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);
this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
this.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
enable();
}
//
// PUblic functions
//
public function enable():void
{
this.selected = false;
this.gotoAndStop(1);
this.mouseEnabled = this.mouseChildren = true;
this.buttonMode = true;
}
public function disable():void
{
this.mouseEnabled = this.mouseChildren = false;
this.buttonMode = false;
}
public function onState():void
{
this.disable();
this.selected = true;
this.gotoAndStop(2);
}
public function offState():void
{
this.enable();
}
//
// Private Functions
//
protected function onClick(e:MouseEvent):void
{
onState();
}
protected function onMouseOver(e:MouseEvent):void
{
this.gotoAndStop(2);
}
protected function onMouseOut(e:MouseEvent):void
{
this.gotoAndStop(1);
}
//
// ACCESSORS
//
public function get selected():Boolean
{
return _selected;
}
public function set selected(value:Boolean):void
{
_selected = value;
}
}
You could add a listener to the stage or to the MovieClip, which contains the buttons (if it has a background, and its not transparent):
stage.addEventListener(MouseEvent.ROLL_OVER, turnThemOff);
function turnThemOff(evt:MouseEvent):void {
for (var i:int=0; i<yourButtons.length; i++) yourButtons[i].gotoAndStop(1);
}
If you move the mouse quickly away from the SWF movie, this could help:
stage.addEventListener(Event.MOUSE_LEAVE, turnThemOff);

How to correctly use KeyboardEvent on Flash

I have a problem making a KeyboardEvent work in the game I'm starting. I have three classes, one for handling the levels, one that is the actual level and one to represent the avatar:
Level
import flash.display.MovieClip;
import flash.events.Event;
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
}
Avatar
public class Avatar extends MovieClip
{
public function Avatar()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
//stage.focus=this;
this.addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function apertou(event:KeyboardEvent)
{
trace("o");
if(event.keyCode == Keyboard.LEFT)
{
this.x++;
}
}
}
I have all the packages on both classes an all works if I use the stage.focus=this on the Avatar, but if I click somewhere else during game excecution the focus is lost and it doesn't work anymore. Please can anyone help me?
Thanks in advance
Keyboard events only trigger when the object they're assigned to are the current focus.
Fortunately, the stage always has focus by default. This means you can add your event listeners to the stage to always have the keyboard events trigger as expected:
stage.addEventListener(KeyboardEvent.KEY_DOWN, apertou);
You can move the key handler from the avatar to the level or stage and then move your avatar in there.
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
public function apertou(event:KeyboardEvent)
{
if(event.keyCode == Keyboard.LEFT)
{
avatar.x++;
}
}
}

Action Script 3 Static Method

I'm new to Action-script OOP and i need to know how to chain methods like this example i have
I.$(button).bind('click',clickButton).bind('rollover',overButton).bind('rollout',outButton)
First i need to remove the I. to use dollar sign only like jQuery :) to select MovieClip and apply any action on it second issue that i have because this way i'm using static Methods Action-script restrict's me to use only static property saving the last one who called the action here is the class code to know what i mean:
package com.MAIN
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class I extends Sprite
{
private static var cSelector:Sprite;
public static function $(selector:Sprite)
{
cSelector = selector
return I;
}
public static function alpha(val:Number)
{
cSelector.alpha = val;
return I;
}
// bind mouse event to the element
public static function bind(EventStr,func:Function)
{
var func1:Function = function(e:MouseEvent){
func(cSelector);
}
// select the event from the list
if(typeof(EventStr) == 'string'){
// map the events in lowercase
var events:Object = {click:'CLICK',rollover:'ROLL_OVER',rollout:'ROLL_OUT',dblclick:'DOUBLE_CLICK',mousedown:'MOUSE_DOWN',mousemove:'MOUSE_MOVE',mouseout:'MOUSE_OUT',mouseover:'MOUSE_OVER',mouseup:'MOUSE_UP',mousewheel:'MOUSE_WHEEL'};
// check if the event exists in the list
if(events[EventStr] && MouseEvent[events[EventStr]]){
cSelector.addEventListener(MouseEvent[events[EventStr]],func1);
}
}else if(typeof(EventStr) == 'object'){
// add the event
cSelector.addEventListener(EventStr,func1);
}
return I;
}
public static function remove()
{
cSelector.parent.removeChild(cSelector);
return I;
}
}
}
Here you go, some steps in the right direction. However, this is a really, really, really crappy idea.
//$.as
package
{
import flash.display.DisplayObject;
//NOTE: there's NO class definition
public function $( selector : DisplayObject ) : IDisplayObject
{
//traverse displaylist to find <code>selector</code>
//and return an instance of IDisplayObject that holds the reference
}
}
//IDisplayObject.as
package
{
public interface IDisplayObject{
function alpha( value : Number ) : IBinding;
}
}
//IBinding.as
package jquery
{
public interface IBinding{
function bind( eventName : String, callback : Function, ...parameters ):void;
}
}
Once you've created concrete implementations of these you can do:
$( someMC ).alpha( .5 ).bind( 'click', function(){ trace( 'what a miraculously crappy idea !!!!' ) } );
You could try it like this:
interface Test {
function doBla(): Test
function moreBla(): Test
}
public class StaticTest {
private static const instance: Test = new InternalTest()
public static doBla() : Test {
return instance.doBla();
}
public static moreBla() : Test {
return instance.moreBla();
}
}
internal class InternalTest implements Test {
function doBla(): Test {
trace("bla");
return this;
}
function moreBla(): Test {
trace("more bla");
return this;
}
}

ActionScript - Global Custom Events?

up until now, the way i've been needing to handle my own custom events is by adding an event listener to the object that was dispatching the custom event. while this method of event handling works just fine, i've come to the point where i would like my custom events to be globally accessible, where the listening object does not need to be the same object that is dispatching the event.
in this example, my main Controller class is instantiating and adding to the display list 2 sprite classes: Square and Triangle. the 4th and final class is a custom event called ColorChangeEvent.
i'm attempting to dispatch a new ColorChangeEvent from the Square class, which uses a timer to dispatch a new random color once every second, while Triangle will listen for the dispatched event and change its fill color to the color that was dispatched by Square.
Controller.as:
package
{
import flash.display.Sprite;
public class Controller extends Sprite
{
public function Controller()
{
var sq:Square = new Square();
sq.x = sq.y = 100;
var tr:Triangle = new Triangle();
tr.x = tr.y = 250;
addChild(sq);
addChild(tr);
}
}
}
Square.as:
package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Square extends Sprite
{
public function Square()
{
graphics.beginFill(0x999999);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
var myTimer:Timer = new Timer(1000);
myTimer.addEventListener(TimerEvent.TIMER, dispatchNewColor);
myTimer.start();
}
private function dispatchNewColor(evt:TimerEvent):void
{
var randomColor:Number = Math.random() * 0xFFFFFF;
trace("Square Class Dispatched: " + randomColor);
dispatchEvent(new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor));
}
}
}
Triangle.as:
package
{
import flash.display.Sprite;
import flash.geom.ColorTransform;
public class Triangle extends Sprite
{
public function Triangle()
{
graphics.beginFill(0x999999);
graphics.moveTo(0, 0);
graphics.lineTo(100, 50);
graphics.lineTo(-50, 150);
graphics.endFill();
addEventListener(ColorChangeEvent.CHANGE, changeColor);
}
private function changeColor(evt:ColorChangeEvent):void
{
var ct:ColorTransform = new ColorTransform;
ct.color = evt.color;
transform.colorTransform = ct;
trace("Triangle Class Received: " + evt.color);
}
}
}
ColorChangeEvent.as:
package
{
import flash.events.Event;
public class ColorChangeEvent extends Event
{
public static const CHANGE:String = "change";
public var color:Number;
public function ColorChangeEvent(type:String, color:Number)
{
super(type);
this.color = color;
}
override public function clone():Event
{
return new ColorChangeEvent(type, color);
}
}
}
needless to say, this isn't working.
of course, i could add the event listener to the Square instance in the Controller class, who's event handler could pass that value to Triangle via a public function to change the color, but this is exactly the kind of limitation i'm trying to avoid.
it's not always easy to access and pass a value to a class from where the custom event is dispatched, which is why i'm looking for an actual global solution to handling custom events.
I have been using this class for some time now. To use it you would do this in square:
data.EventManager.instance.publish("someName", randomColor);
and then in your Triangle:
data.EventManager.instance.subscribe("someName", handleColorChange);
private function handleColorChange(color:Number):void {
// implementation here
}
You can even pass the ColorChangeEvent instead of just the color.
data.EventManager.instance.publish(ColorChangeEvent.CHANGE, new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor);
And then
data.EventManager.instance.subscribe(ColorChangeEvent.CHANGE, handleColorChange);
private function handleColorChange(colorChangeEvent:ColorChangeEvent):void {
// implement here
}
I removed a lot of code that is specific to my projects, so I am not 100% it is usable exactly as-is. But, you should be able to modify it to get it working correctly. If not, let me know and I can try to work it out with you.
This class handles additional things that I will not go into, though you are free to explore. Be aware, however, that anything that subscribes for event notification has a strong reference by the EventManager. That means that if you want to destroy something for garbage collection, you need to call EventManager.instance.cancel(ColorChangeEvent.CHANGE, handleColorChange) before the Triangle instances can be collected.
package data {
import flash.utils.*;
public class EventManager extends Object {
private var _subscribers:Dictionary;
private var _calls:Dictionary;
private var _feeds:Dictionary;
private var _requests:Dictionary;
private var _notify:Dictionary;
private var _services:Dictionary;
private static var __instance:EventManager;
public function EventManager() {
if (__instance) {
trace("EventManager is a Singleton class which should only be accessed via getInstance()");
}
_feeds = new Dictionary(true);
_subscribers = new Dictionary(true);
_requests = new Dictionary(true);
_services = new Dictionary(true);
_notify = new Dictionary(true);
}
public function getFeedData($name:String) {
if (_feeds[$name]) {
return _feeds[$name];
}
return undefined;
}
public function unpublish($name:String) {
var _post:* = _feeds[$name];
delete _feeds[$name];
return _post;
}
public function cancel($name:String, $subscriberFunc:Function, ...args): void {
var _cnt:Number;
var _subscriberArray:Array;
if (_subscribers[$name]) {
for (_cnt = 0; _cnt < _subscribers[$name].length; _cnt++) {
if (_subscribers[$name][_cnt] == $subscriberFunc) {
_subscribers[$name].splice(_cnt, 1);
}
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name];
_cnt = _subscriberArray.length;
while (_cnt > 0) {
if (_subscriberArray[_cnt] == $subscriberFunc) {
_subscriberArray.splice(_cnt, 1);
}
_cnt--;
}
}
}
public function subscribe($name:String, $subscriber:Function, ...args): void {
var _funcArray:Array;
var _func:Function;
if (_feeds[$name]) {
$subscriber(_feeds[$name]);
}
if (! _subscribers[$name]) {
_subscribers[$name] = new Array();
}
_subscribers[$name].push($subscriber);
if (_notify[$name]) {
_funcArray = _notify[$name];
for each (_func in _funcArray) {
_func();
}
delete _notify[$name];
}
}
public function request($name:String, $feedFunction:Function): void {
var _requestArray:Array;
var _request:Function;
if (! _feeds[$name]) {
if (! _requests[$name]) {
_requests[$name] = new Array();
}
_requests[$name].push($feedFunction);
} else {
$feedFunction(_feeds[$name]);
}
if (_notify[$name]) {
_requestArray = _notify[$name];
for each (_request in _requestArray) {
_request();
}
delete _notify[$name];
}
}
public function publish($name:String, $data:*, $args:Object = null): void {
var _subscriberArray:Array;
var _func:Function;
var cnt:Number = 0;
_feeds[$name] = $data;
if (_subscribers[$name] != undefined) {
_subscriberArray = _subscribers[$name].slice();
_cnt = 0;
while (_cnt < _subscriberArray.length) {
_func = _subscriberArray[_cnt] as Function;
if ($args) {
_func($data, $args);
}else {
_func($data);
}
_cnt++;
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name].slice();
delete _requests[$name];
_cnt = 0;
while (_cnt < _subscriberArray.length) {
if (_subscriberArray[_cnt] != null) {
_subscriberArray[_cnt]($data);
}
_cnt++;
}
}
}
public function notify($name:String, $subscriber:Function): void {
if (_requests[$name] || _subscribers[$name]) {
$subscriber();
}else {
if (! _notify[$name]) {
_notify[$name] = new Array();
}
_notify[$name].push($subscriber);
}
}
public static function getInstance(): EventManager {
if (! __instance) {
__instance = new EventManager();
}
return __instance;
}
public static function get instance(): EventManager {
return getInstance();
}
}
}
I got this to work by creating a singleton: EventDispatchSingleton that extends EventDispatcher. It's basically an empty singleton that provides the dispatchEvent and add/removeEventListener methods (these are automatically provided by extending EventDispatcher).
Anywhere I want to dispatch an event I import EventDispatchSingleton and then call EventDispatchSingleton.instance.dispatchEvent(<someEvent>);.
Then, wherever I want to listen to that event, I just import EventDispatchSingleton and call EventDispatchSingleton.instance.addEventListener(eventName, callback);
You should look into event bubbling, specificly I think you will find the Capturing phase of the event propagation useful. Take a read of Event propagation from Adobe LiveDocs. It's in the Flex docs, but it is about AS3 Events.
Also Senocular has a good post on Flash Event Bubbling.