views:

54

answers:

3

Here's the deal: I'm working on a personal portfolio in AS3 and I've run into a problem which I can't seem to find a logical answer to. I want everything (well, most of it) to be editable with an XML file, including my menu. My menu is just a Sprite with some text on it and a Tweener-tween, no big deal. But, I forgot to think of a way how I can determine which menu-item I have clicked.

This is in my Main.as

private function xmlLoaded(e:Event):void {
    xml = e.target.xml;
    menu = new Menu(xml);
    menu.x = 0;
    menu.y = stage.stageHeight / 2 - menu.height / 2;
    addChild(menu);
}

In Menu.as

public function Menu(xml:XML) {
    for each (var eachMenuItem:XML in xml.menu.item) {
        menuItem = new MenuItem(eachMenuItem);
        menuItem.y += yPos;
        addChild(menuItem);
        yPos += menuItem.height + 3;
    }
}

and in my MenuItem.as, everything works - I have a fancy tween when I hover over it, but when I click a menu-item, I want something to appear ofcourse. How do I know which one I clicked? I've tried with pushing everything in an array, but that didn't work out well (or maybe I'm doing it wrong). Also tried a global counter, but that's not working either because the value will always be amount of items in my XML file. Also tried e.currentTarget in my click-function, but when I trace that, all of them are "Object Sprite".. I need something so I can give each a unique "name"?

Thanks in advance!

+1  A: 

you can just set a name variable in the MenuItem class and use that. Then, in the event handler you can use (e.currentTarget as MenuItem).name (or just plain e.currentTarget.name)

By the way, you do know that for each .. in does not guarantee to traverse through the xml in order right?

jonathanasdf
When I use the name property (if that's what you mean), I get "instance4", "instance9", "instance14" in my trace in the Click handler. Even when I put menuItem.name = "item" + counter;in my Menu.as, I still get instanceX. When I tried to cast it to MenuItem (with as MenuItem), I get "null" (like jolyonruss explained below). When I use a hard cast, I get #1034: Can not convert Sprite to MenuItem ...(Should I work with a counter? I'm completely lost here)Thanks for your reply!
Nick
wait... what exactly are you attaching the event listener to? jolyonruss and I had assumed you were attaching it to the menu item, but obviously that's not the case. Setting the name will work, but e.currentTarget is pointing to the wrong thing thus it doesn't find the updated name variable. If you tell us what the event listener is attached to maybe we can help. You most likely won't need to use a Dictionary.
jonathanasdf
In my MenuItem.as, I make a "bar" (a Sprite with black background), add the text from my XML on it (in my Menu.as it makes a new instance of that bar, and leaves 3px margin). So, in my MenuItem.as, I have a couple of eventListeners. One of them is bar.addEventListener(MouseEvent.CLICK, mouseClickHandler). Others are MOUSE_OVER/MOUSE_OUT to make that fancy tween I was talking about. This works (on each of the 3 items I have now), but the CLICK is making me pull all my hair out. Thanks for your reply already, Jonathan :)
Nick
here you go.`e.currentTarget.parent.name`Either that, or you set the .name attribute to the bar, so in the constructor of MenuItem, you set bar.name = whatever, then you can use`e.currentTarget.name`
jonathanasdf
That did the trick, wow! I just had to put the .name in the constructor of the bar, ofcourse ... How stupid of me. Thank you so much for your help Jonathan, and the others too ofcourse!!
Nick
A: 

I've got a similar answer but I would use a hard cast MenuItem( e.currentTarget ) instead of a soft cast using the as keyword. The reason being that if you accidentally cast to the wrong type a soft cast will only return null where as a hard cast will give you a proper run-time error and a line number in your code where the problem is.

I'd also suggest adding a public function to MenuItem to get a unique reference or index so you can do something like this in the MenuItem's CLICK event handler

private function onMenuItemClick( event : MouseEvent ) : void{

// this could be a int, ie. the node index in the XML

var uniqueSectionReference : String = MenuItem( event.currentTarget ).uniqueReference;

dispatchEvent( new MenuEvent( MenuEvent.CHANGE, false, false, uniqueSectionReference ) );

// or if you have a reference in Menu for whatever it is thats controlling your content you could do this

contentHolder.showSection( uniqueSectionReference );

}

jolyonruss
A: 

You might also look into using the Dictionary class - I find myself using it more and more for stuff like this. Basically it acts sort of like an Array, but instead of associating your values with index keys it allows you to map object references to other object references. So for instance, you could set up something like this:

// create your buttons
var button1:MyButton = new MyButton();
var button2:MyButton = new MyButton();
var button3:MyButton = new MyButton();

// declare and instantiate a dictionary object
var links:Dictionary = new Dictionary();

// populate your dictionary object, mapping values to your buttons.
links[button1] = "http://www.button1URL.com";
links[button2] = "http://www.button2URL.com";
links[button3] = "http://www.button3URL.com";

// group buttons into an array for convenience
var buttons:Array = [button1, button2, button3];

// add event listener to your button
for each (var b:MyButton in buttons) {
     b.addEventListener(MouseEvent.CLICK, clickHandler);
}

// handle the button click by getting the relevant information for the clicked button from
// your dictionary object
function clickHandler(e:MouseEvent):void {
     navigateToUrl(new URLRequest(links[e.currentTarget]));
}

Your example is obviously a bit more complex - you'll need to include your URL value or whatever into your XML and then process it that way. Depending on what you're doing, you may not need the dictionary at all - e.currentTarget.MyValue may work just fine for you if the data is included within the button itself.

Hope that helps!

Myk
Thanks for your extended response, Myk. I've did some research on the Dictionary class, but I can't seem to understand it fully (probably because my situation is a little bit more complex, yeah). I tried dict[menuItem] = "item" + counter; in my Menu.as, when I trace that (in Menu.as), I get item1-2-3 so thats great. Back in my MenuItem.as, the Clickhandler's e.currentTarget is a Sprite. So, how can I let the Dictionary and the clicked Sprite "communicate" with each other? I'd like to have something like if(e.currentTarget == Menu.dict["item1"])... Any help would be appreciated. Thanks!
Nick
Glad you sorted it out above. Dictionary is maybe not quite what you need here - I guess the point I was trying to make is that even without a unique name every instance you create is still recognized by Flash as a unique object. Dictionary tends to make that fact obvious, since you don't have to name anything - you can link one abstract reference to another, one array index point to another and know nothing about the details of what those instances actually are. Rather than if (e.currentTarget.name == x) then y, write code that does (e.currentTarget.y) and you're set. Does that make sense?
Myk