I'm working on an interactive map in Actionscript-3 (Adobe Flash CS6).
What I'm trying to do is, with a single button, to show multiple objects (movieclips) with each mouse click.
I'm currently working with this code, but I can't manage to find out how to show multiple movieclips, I can only show ONE:
btn_ally_unit.addEventListener(MouseEvent.CLICK, mostrar_ally_unit2);
function mostrar_ally_unit2(event:MouseEvent):void
{
map_editor.ally_unit.visible = true;
}
How do I extend this to apply to any number of movieclips?
I'm sure by infinite you mean indefinite.
Target the unit clicked by using the target property of the Event class. Looks something like this:
btn_ally_unit.addEventListener(MouseEvent.CLICK, mostrar_ally_unit2);
function mostrar_ally_unit2(event:MouseEvent):void
{
event.target.visible = true;
}
You see? (event:MouseEvent) is saying that this function expects one argument (a MouseEvent) which you are giving the variable name of event. That's a convention but I like to use me as an abbreviation for mouse event. Others just use the letter e. Ok. Now event has a property, target, which is the thing receiving the event. In this case it will be one of your units. Your units have a property of visible which you can toggle off like you have been doing but by using the relative mouse event target, you can use the same line of code for all units.
note
Of course you must add the event listener to each unit. You could make it part of the class or just add it when a new unit is instantiated.
note
Using the event flow in actionscript 3 can be tricky. Seek out a tutorial on this. Here is one link related to event flow from Adobe.
Related
I'm developing a canvas game that happens to have several scenes. Each scene might end up being a static final frame after having finished. The circumstances are that the ticker and the listener for the "tick" event are still running and keep on rendering full speed - which is asking for cpu usage.
I have tried to remove the listeners at the end of scene and add them back wenn the user interacts and starts the next scene.
I wonder what would be the "createJS" way of doing this.
I see some other options but am a bit lost how to proceed:
Caching the "whole" last frame. Will it make the ticker do "absolutely nothing" performance-wise?
Pause the ticker and check for the paused attribute in the handleTick method: Seems to not take the CPU usage completely down.
Can somebody recommend a way?
On a side note: I need my real "this" object inside the tick function that is bound to the ticker. How can I achieve this? Right now I use this code:
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick(event) {
// Actions carried out each tick (aka frame)
if (!event.paused) {
// Actions carried out when the Ticker is not paused.
}
}
Inside handleTick "this" is not my object that added the listener.
A simple createjs.Ticker.removeEventListener("tick", handleTick); should do just fine as long as handleTick exists in your current scope. See this example.
There are a couple ways to access the scope of the object that assigned the tick listener. For example, you could simply assign this to a local variable like so:
var _this = this;
function handleTick(){
//"_this" now refers to the scope that defined handleTick.
}
Or you can use a delegate. In this example I'm using jQuery's proxy function to scope handleTick to this.
var handleTick = $.proxy(function(){
//"this" refers to the scope that defined handleTick.
}, this);
createjs.Ticker.addEventListener("tick", tickHandler);
Complete AS3 noob here - I've tried Googling this, but I can't seem to find what I'm looking for (I stumbled across this, http://ub4.underblob.com/as3-naming-elements-dynamically/, but it doesn't weem to work for me).
I'm trying to dynamically add a Movieclip inside another Movieclip through an external AS3 class
Something like this:
var bullet:Bullet = new Bullet(x, y, "right");
var stageBackground:MovieClip = (stage.getChildByName("back") as MovieClip);
stageBackground.addChild(bullet);
However, while this compiles correctly, at run time, I get error #1009 - Cannot access a property or method of a null object reference.
The debug panel tells me the problem is with this line:
stageBackground.addChild(bullet);
But I can't seem to figure out what's wrong with it. I've tried recasting stageBackground as a Sprite, but that didn't change anything. I know the MovieClip back exists - when I reference it through near identical code in my document class, it works perfectly.
You are accessing stage here to find your container, which is very likely the problem.
You are probably thinking that the stage property refers to "the stage" in Adobe Flash authoring environment.
That's not true.
If you placed a MovieClip on "the stage" in the Flash IDE, it ends up on the main time line, this however, is not the thing the stage property is referencing. stage is the topmost DisplayObjectContainer in the display list. It only exists at runtime. It more or less represents the FlashPlayer window, the runtime environment that executes your .swf file.
In short: you are simply looking for your back MovieClip in the wrong place.
The property of a container that represents the main timeline is root.
Do not use root either.
As you can see, your code becomes dependent on the display list
structure of your application. You are already struggling to find the
container that you are looking for. If you change the structure, your code breaks. Even changing the name of the container (for example to something like "background") will wreak havoc.
Instead, use Events.
You are in another class and you want to fire a bullet.
So you create that bullet same as you do now:
var bullet:Bullet = new Bullet(x, y, "right");
Next, dispatch an Event to notify the rest of your code that a bullet was created and it should be placed in the appropriate container:
dispatchEvent(new BulletEvent(BulletEvent.CREATED, bullet));
(Create a custom event class BulletEvent that extends Event, with the apropriate setter and getter for a Bullet object)
Register a listener on the object of your class that creates the bullets, you will catch this event and place the bullet in the container:
var object:YourClass = new YourClass();
object.addEventListener(BulletEvent.CREATED, addBulletToContainer);
function addBulletToContainer(e:BulletEvent):void
{
// adding the bullet to the container
back.addChild(e.bullet);
}
This code would be placed in the parent of your back MovieClip.
The Flash IDE automatically creates variables behind the scenes that have the same names as the instance names. That's why the variable back is available here.
Using events here allows you to literally fire the bullet into your code with somebody else taking care of it, where it's easy to figure out the container it belongs into.
I'm working on game made with libgdx that needs some GUI above my game screen. Something like FrameLayout in Android.
I have GameScreen where everything is happening.
What I want now is to add a "pause" button, highscore information etc.
I've tried to combine a Stage object with regular sprite drawing.
But I had some problems with handling inputs: how to manage if user clicked pause button in stage, or clicked game area (where I should add some bullets)...
You should be able to use a Stage to manage your UI. To get input working correctly, you'll need to add an InputMultiplexer
so that the Stage and then your current input scheme will both get the inputs.
To set it up, you'll do something like this:
InputMultiplexer multiplexer = new InputMultiplexer();
multiplexer.addProcessor(stage);
multiplexer.addProcessor(gameScreenInputProcessor);
Gdx.input.setInputProcessor(multiplexer);
(Code sample based on code from https://code.google.com/p/libgdx/wiki/InputEvent)
Note that the order is important (I'm guessing you'll want the stage to get events first to see if the UI is being touched or not). Also, the boolean return value from input event handlers are more important with a multiplexer, as "handled" events will not be propagated by the mutliplexer. UI events inside the Stage have their own "handled" flag (mostly it does the right thing but there are some subtle differences).
One alternative to the InputMultiplexer would be to create a "GameScreenActor" (a new subclass of Actor) that contains your current game screen that you plug into the global Stage. You'd have to move your input processing to the scene2d approach, though. This probably isn't the right choice for you, but it is a viable one.
I'm working on something similar to the Angry Birds "rollout" for options, etc., but I'm running into a fairly substantial problem.
The rollout itself is just a toggle button, with several other buttons added to the display list that move when you touch the toggle button. Each of these buttons is a class that extends Sprite and contains individual methods for touch events, begin, end and out. When these buttons get initialized (NOT instantiated), the touch begin listener is added. Something like this:
public function Initialize():void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
}
private function OnTouchBegin(e:TouchEvent):void
{
this.removeEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin);
this.addEventListener(TouchEvent.TOUCH_END, OnTouchRelease, false, int.MAX_VALUE);
this.addEventListener(TouchEvent.TOUCH_OUT, OnTouchOut, false, int.MAX_VALUE);
}
private function OnTouchRelease(e:TouchEvent):void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
}
private function OnTouchOut(e:TouchEvent):void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
}
Then, when these buttons get hidden from the screen, a method is called to remove any of the listeners that are currently active on them:
public function Deactivate():void
{
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin);
}
This is just for the standard button functionality (up/down texture and sound), on top of this, when I make the game, in my rollout class, I have an additional method that will add another event listener for custom logic that should occur when the button is touched (the button itself is created elsewhere).
public function AddRolloutButton(listener:Function):void
{
if (listener != null)
{
_buttons[index].addEventListener(TouchEvent.TOUCH_BEGIN, listener);
}
The buttons in the rollout itself are removed from the display list until they are to be shown. When the rollout is closed, the buttons are deactivated (removed from display list and the 3 button listeners within the button class are removed).
Everything works perfectly fine the very first time I open and close the rollout. After that, the event dispatching system just inexplicably dies. Every single InteractiveObject on the screen, regardless of position or type, becomes unusable. I traced out whether or not the listeners were still there on the rollout toggle button, and it was. I also confirmed that the rollout button itself was the only thing on the display list.
What I've noticed is that if I comment out the listener removal in the deactivate method of the button for the touch begin listener, or pass in null for the listener method in the AddRolloutButton method, everything works just fine. The issue seems to stem from having multiple listeners of the same type on the rollout buttons, and then removing one or all of them.
If anyone has any ideas of just what is going on, that would be very helpful. I was under the impression that adding multiple listeners of the same type to an InteractiveObject was perfectly valid.
UPDATE:
It appears that only TouchEvents get broken by this issue I'm having. I just tried using a mouse click listener on the stage after opening and closing the rollout, and that still works. So, only touch events are getting broken, if that helps at all.
Seems like there is something going wrong in your AddRolloutButton method. Are you sure you are assigning the correct listener function?
In your example the listener:Function argument should be equal to _buttons[index]. OnTouchBegin.
Or, since whatever class that owns the AddRolloutButton method seems to be in control of the buttons, you could potentially scrap the listener argument altogether since you know what method that needs to be triggered.
For example like this:
public function AddRolloutButton():void {
var currentButton:MyButtonClass = _buttons[index] as MyButtonClass;
currentButton.addEventListener(TouchEvent.TOUCH_BEGIN, currentButton.OnTouchBegin);
[...]
}
However, what you could do is to never remove the TouchEvent.TOUCH_BEGIN in the Deactivate function. The touch events will never be triggered when the DisplayObject isn't on the Display List. This means you don't have to worry about adding the listener every time you want to add the button to the Display List again.
If you set the listener to use a weak reference it will not hinder the button from being garbage collected when it's not needed anymore. To make your event listener use a weak reference, set the fifth argument of the addEventListener method to true.
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
public function Initialize():void {
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE, true);
}
Do you call the Initialize method again at some point after closing the rollout?
From your code it looks as though you remove all of the event listeners in Deactivate and then never add the TouchEvent.TOUCH_BEGIN listener again.
I'm not sure what you're trying to accomplish by removing the event listeners. Since the Button has a reference to the callback, it has a reference to that callback whether the listeners are attached or not. Are you thinking that adding a listener creates a reference from the callback to the button? It doesn't--the reverse is true.
If you really want to have the Button release the callback when it is removed from the display list, then it can't hold that reference after it removes the listeners. Consider using something like Supervising Presenter or a RobotLegs Mediator to manage those dependencies.
I seriously doubt that the entire event system is being borked by what you're doing.
I'd be more inclined to believe that this:
if (listener != null)
{
_buttons[index].addEventListener(TouchEvent.TOUCH_BEGIN, listener);
}
is failing, either because the button that's referenced is not the one that's actually on stage, or because listener is null..
I have seen times where adding and removing events with priorities can cause things to fail, but typically this is with events that aren't propagating on the display list.
One way that you can test this is simply to listen to the main document or the stage for touch events and see if you're getting those events. If the entire event system is borked, you won't get them, but if your listener logic is wrong, you will.
You may also want to check the willTrigger property on the button and event.
Is there something like the java 'glasspane' in as3?
The glass pane is useful when you want to be able to catch events or paint over an area that already contains one or more components. For example, you can deactivate mouse events for a multi-component region by having the glass pane intercept the events. Or you can display an image over multiple components using the glass pane. http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html
Why do this? While some animations are underway in flash, I want to prevent any mouseevents from firing. I could remove all listeners systematically, then re-add them after the animation, but if there is something like a glasspane, it might be an easier way to achieve the same effect.
My current thinking is to:
add a sprite to the stage
stretch to width and height of the stage,
give the sprite the highest z-order,
grab all events on this sprite, and stop their propagation?
if you set
enabled=false;
mouseChildren=false;
on to the top most DisplayObject it should disable all mouse events for your app. I've used it and it works a treat.
For a more specific approach, e.g. only block clicks but let mouse down etc. through I use this approach. It uses a 'clickBlocker' stage event handler during capture phase, stopping propagation to any other object.
public function blockClicks():void{
if(!stage) return;
stage.addEventListener(MouseEvent.CLICK, clickBlocker, true); //useCapture!
}
private function clickBlocker(event:MouseEvent):void{
trace("Me (the stage) gets the "+event.type+" first, and I block it #"+event.stageX+"/"+event.stageY);
event.stopImmediatePropagation();
}