Oie... Your class needs some work. I'll run through my list as I see them. Some are minor suggestions, others are major errors.
A) The class name "CreateButtons" isn't too hot. This is a minor point, but these are general rules of OOP for good reasons... Class names should be nouns given that you're creating a "thing", not (directly) performing an action. A class name should tell what the "thing" is and give some indication of what it does; for example: "ButtonManager" is good. "MainNavManager" is even better, given that its indicating what set of buttons that it manages. A class then has many methods which are typically named with verbs, indicating all the specific actions that this "thing" DOES. Naming convention might seem like a nit-picky thing, but believe me... as your application grows, it becomes essential!
B) I'm not digging this whole "_i" property thing. Try to limit your properties to the bare essentials... which does NOT include loop iterators. In that scenario, the proper way to spawn buttons would be to pass in a parameter:
private function createButton(index:int):void
{
// do stuff
}
// later, down in the loop...
for (var i:int = 0; i<_getXML.portfolioData.length; i++)
{
createButton(i);
}
C) Data typing... this is another nit-picky thing, but OH GOSH is it ever important. You're only shooting yourself in the foot when you don't declare a data type for every single variable you create. The flash compiler hashes everything together when it builds your application, and assuming you have provided datatypes for everything, it will -*test*-('") every single variable and method against one another to see if they all line up... It catches things like a string where a number is expected, etc. So, the compiler gets incredibly anal with incredibly specific error messages as you include more and more data-typing information; which is GOOD!! While you may think you're slipping one past the compiler by removing data typing information so it doesn't know to throw errors, it doesn't mean that the errors aren't still there, and will cause problems when the program runs. So in fact, the joke is on you because you're ripping your hair out over really dumb things that the compiler would have caught for you had you just given it data-type information. So, datatype EVERYTHING. All variables, all properties, all method parameters, and all method return values.
I'll proceed with the actual errors in a follow-up post. This is getting long.
bigmac- 04-14-2008
D) As for why you're only getting one button... haha, instantiation strikes again! Look at where you're creating your buttons text field: you're creating it ONCE as a property of the class. Now look at how you're trying to display it: you're trying to repeatedly display that ONE text field as MULTIPLE buttons, which (logically) does not work. You need a separate text field instance for EACH button you create. So, move the "new TextField();" line into the createButton method so that you create a new text field for each. That also clears out a non-essential property from your class :D
E) Rollover styles... this goes back to that MVC thing: the controller shouldn't govern how a display (view) behaves. Instead of doing all your visual styles in your controller class, just make the button a new class that sets up all the display styles. The only thing the controller would register then would just be a mouse CLICK event to know when the button was clicked. Other than that, it doesn't care about the button's appearance or behavior.
And finally... antialiasing. You need to embed fonts in order for antialiasing to work. That whole thing is a pain in the ass with dynamically created text fields, so I'll defer to Flash help on how to do it. Look it up. :P
fatbuoy1- 04-14-2008
heh, whenever I read through your posts I spend my time going 'doh!'... it always makes perfect sense when you explain it! :D
Thanks for all that, i'll try it again with a separate class for the buttons themselves. One thing though, how do I get the class to add the buttons as children of ANOTHER class, or even the main stage? like I want the buttons to be added to the menu_mc movie.. and when I click a button I want it to add the photos as children of the main timeline, rather than the buttons class... does that make sense?
bigmac- 04-14-2008
heh, whenever I read through your posts I spend my time going 'doh!'... it always makes perfect sense when you explain it! :D
Excellent! I've suspected as much, which is why I've been writing pretty detailed explanations. Not to imply that I'd give up on a lost cause, but uh... I wouldn't bother if you didn't get it. Some people just don't get this stuff, or don't care to get it. However, you've got both the knack and the interest. Truth be told, you're doing exceptionally well, and that's not all because of my help. You can only be as good as you make yourself, so kudos! You're getting into some rather advanced stuff. We're quite a ways into intermediate level AS3 here.
As for the management scenario... It's not uncommon or bad form to construct a class with references to the object scopes that it should work within. For example:
// our class constructor....
private var _buttonScope:DisplayObjectContainer;
private var _slideScope:DisplayObjectContainer;
public function ManNavManager(buttonScope:DisplayObjectContainer, slideScope:DisplayObjectContainer):void
{
_buttonScope = buttonScope;
_slideScope = slideScope;
}
// later, when we instance this class...
var navManager:ManNavManager = new ManNavManager(menu_mc, this);
What we've done there is to create an instance of this class with references to the two display scopes in which the child objects will be placed. This nav manager now acts as a bridge between to the two scopes which are unaware of each other. Also note that we're citing the objects as DisplayObjectContainers, which is the rawest form of object that can receive children. While the menu clip and the main stage are probably really Sprites or MovieClips (or custom classes!) in actuality, they descend from DisplayObjectContainer so this reference is valid. And by citing them as DisplayObjectContainers we've only given our MainNavManager the most bare-bones information about these objects. Again, this goes back to dependency... an object wants to know as little about other objects as possible. As long as it knows those other two objects accept children, then it has enough information to do its job.
fatbuoy1- 04-14-2008
hmm.. ok so when I say DisplayObjectContainer, will it automatically go to the top of the tree and start working down till it finds a DisplayObjectContainer sub class called menu_mc?
fatbuoy1- 04-14-2008
We're quite a ways into intermediate level AS3 here.
Ah.. that would be why my heads starting to spin a little bit... :D
bigmac- 04-15-2008
hmm.. ok so when I say DisplayObjectContainer, will it automatically go to the top of the tree and start working down till it finds a DisplayObjectContainer sub class called menu_mc?
Erm... not exactly. The fact that you've given "menu_mc" an instance name means that you've created a direct reference to that object, so Flash isn't searching for anything. The issue at hand here is how specific we are in the information we provide about the object. Remember the inheritance principal of "IS"? A Sprite IS a DisplayObjectContainer (becasue it descends from it), although a DisplayObjectContainer is not necessarily a Sprite. When in doubt, put this in context of something relevant to you... say, YOURSELF. You, Chris, are you a Human? I hope the answer is yes. Now, is a Human "Chris"? Not necessarily... I'm a Human and I'm Greg. So, is it valid to refer to you as Chris? Yes, it is. Is it also valid to refer to you, Chris, as a Human? Yes, it is. So in OOP, Human would be a super-class of Chris because you descend from the Human race. What else applies to you? Hmm... you're Northern Irish. That would be another super class of Chris. So, let's use this inheritance as an example...
Human > NorthernIrish > Chris
Now, let's say that you go to some random website online that you've never heard of that has every square inch of spare screen space filled with neon colored flashing ads on it. There's also a form on there that asks you to provide your name and email "for best experience". Do you make them aware of YOU, Chris, a specific object? probably not. You don't want to give them that specific of information because they'll probably spam you. However, if you go there and they ask you what country you are from (for best experience), would you tell them? Sure, why not? Tell them you're from Northern Ireland, what's the worst that could happen?
You see where I'm going with this? If an object, like that website, doesn't actually need SPECIFIC information about another object, like you, don't provide it.... give more general information that runs less risk of being misused. In your case, all your controller needs to know is that it has a reference to two objects that will receive children. Whether those objects are Sprites, MovieClips, or MyCoolCustomClass, your controller does not need to know. All it needs to know is that they're DisplayObjectContainers that will hold children.
fatbuoy1- 05-19-2008
Ok, quick problem for you to run your experienced eye over if you have a minute...
Im making a timetable for a festival, and Im trying to make a button that will toggle certain items visible/invisible, so i'm using an array to contain the instance names of the objects i want to appear/disappear.
Heres my code var musicVisible:Number = 1;
var musicEvents:Array = ["skibunny_btn","rudi_btn"];
trace(musicEvents.length);
function setMusicVisible():void {
musicVisible = 1;
var i:Number = 0;
for (i; i<musicEvents.length; i++) {
var eventTitle:String = musicEvents[i];
timetable_mc.eventTitle.visible = true;
trace(eventTitle);
}
}
function setMusicInvisible():void {
musicVisible = 0;
var i:Number = 0;
for (i; i<musicEvents.length; i++) {
timetable_mc.musicEvents[i].visible = false;
trace(i + musicEvents[i]);
}
}
function toggleMusicVisible(evt:MouseEvent):void {
if (musicVisible==1) {
setMusicInvisible();
} else {
setMusicVisible();
}
}
music_btn.addEventListener(MouseEvent.CLICK, toggleMusicVisible);
The problem seems to be in the timetable_mc.musicEvents[i].visible = false; part of both setMusicInvisible and setMusicVisible functions, I reckon its where I try to slot in the instance name from the array. just not sure how I should do it.
See anything amiss? Would appreciate any advice :D
bigmac- 05-19-2008
Yeah, you're not referencing the stored string value AS A PROPERTY of the object. You're just referencing it as a string. So in effect, you're trying to adjust the visible property of a string (which is invalid). So, here's how to reference a property name with a string:
timetable_mc["prop_name"].visible = false;
// or in your scenario:
timetable_mc[musicEvents[i]].visible = false;
So, that fixes your code up, although this in and of its self is kind of a backwards way to do it. Keep in mind that you can dump ANYTHING into an array, even object references. So instead of putting string values of your object names that you then reference, just drop direct references to the objects into the array instead. Then when you toggle the visible property, you can do it directly to the object reference...
var musicEvents:Array = [this.skibunny_btn, this.rudi_btn];
// then later:
musicEvents[i].visible = false;
This approach has a couple advantages. The biggest is that it gives you compiler validation! When you publish your movie, the compiler will throw an error if it tries to set up those object references and finds that one of them is invalid. With a string, you're going to be creating a run-time reference that the compiler can't check. If your string value doesn't line up to reference an object correctly, then you're on your own for figuring it out.
Also, using object references leverages the power of AS3. To be entirely honest, the "visible" property is kind of useless these days since ActionScript 3 has added support for orphaned display objects... ie: display objects that do not exist on the stage with a parent display object. ActionScript 3 lets you create display objects and just keep them stored in memory until you actually want to display them, at which time you can add them to the stage display. The benefit of this is that it doesn't bog the stage down with all kinds of garbage the it has to keep track of even though the assets aren't visible. So, what you're doing was the best thing that could have been done in AS2: All display objects had to exist on stage, so you would switch off the visible properties to save as much display memory as possible. But now with AS3, just add and remove stuff from stage. Writing bullet-proof display object management code takes a little practice but it really isn't all that hard. If you give it a go, you'll probably hit a few errors early on as you get used to how adding/removing display objects works, but once you get the hang of it... it's a much more efficient approach.
fatbuoy1- 05-19-2008
Ah right, thanks! Its a short wee site im working on, nothing too impressive and it needs done quick so I'll not have time to do things the right, but harder, way this time... but i'll bear that in mind next time.
I've hit one more snag, I have movie clips in my library I want to create on the stage when I press the corresponding button. Rather than rewrite the code for each button I thought i'd try and create a function that all the buttons could use... but it aint working (surprise surprise! :D)
var eventTitle:String = new String;
function loadEvent():void{
var eventInfo:[eventTitle] = new [eventTitle]();
eventInfo.x = 200;
eventInfo.y = 200;
addChild(eventInfo);
}
function loadConstructive(evt:MouseEvent) {
eventTitle = "constructive_mc";
loadEvent();
}
The bit giving me trouble is var eventInfo:[eventTitle] = new [eventTitle]();
I thought it was the same sort of situation as earlier on, where i'm trying to place a string into a path, but square brackets don't seem to be working... Any ideas?
fatbuoy1- 05-19-2008
Hmm... im also having trouble putting a preloader into those movie clip symbols I want to add to the stage. They each have two frames, the first contains a preloader movie clip and the second contains the content. Each of them use the same movie clip in the first frame, which has the following scriptstop();
this.parent.addEventListener(Event.ENTER_FRAME,checkProgress);
function checkProgress(evt:Event):void {
var filesize:Number = this.parent.loaderInfo.bytesTotal;
var amountLoaded:Number = this.parent.loaderInfo.bytesLoaded;
var percent:Number = amountLoaded/filesize;
percentLoaded_txt.text = Math.round(percent*100)+"%";
if (percent >= 1) {
this.parent.removeEventListener(Event.ENTER_FRAME, checkProgress);
this.parent.gotoAndStop(2);
}
}
But i'm getting this error
1061: Call to a possibly undefined method gotoAndStop through a reference with static type flash.display:DisplayObjectContainer. this.parent.gotoAndStop(2);
iv used the path 'this.parent' throughout because I want it to look to the movie clip it is contained within an when it is loaded, move it's parent mc on to the next frame (the content frame). Im not sure whether its just a problem with my syntax or whether my whole logic is flawed...
Would really appreciate any input on these two problems if you get a moment
bigmac- 05-19-2008
Whoa! We got some problems here, my friend. I'll reply inline...
var eventTitle:String = new String;
First off, the above doesn't work (for two reasons). You're calling a "new" operation without executing the object constructor, which would throw an error. All constructors must be called with open/close parenthesis, such as "new String();". However, even then this is a bad example because String is a privative data type. Primitive data types include String, Boolean, and Number (including integers and floats/decimals) and do not require their constructors to be run. Now don't get me wrong, your above example might (and probably does!) work without errors (so is technically correct). However, it is extremely atypical to construct a blank privative. Generally you just declare a value, such as:
var str:String = "";
var bln:Boolean = true;
var num:Number = 1.03456;
var inum:int = 3;
Also know that it is totally fine to declare a variable without giving it a value, too. If you just want to have a string place holder sitting around to collect a value, then it's totally cool just to write this:
var str:String;
One handy thing about not declaring a value for a variable is that you can always -*test*-('") the variable against "null" to see if it has been assigned a value.
function loadEvent():void{
var eventInfo:[eventTitle] = new [eventTitle]();
eventInfo.x = 200;
eventInfo.y = 200;
addChild(eventInfo);
}
function loadConstructive(evt:MouseEvent) {
eventTitle = "constructive_mc";
loadEvent();
}
As for this... hmmm. First off, I'll ask why "eventTitle" is being set as an object property for exchange between two methods. Or don't I want to know? :D Make it a parameter!!!! Such as:
function loadEvent(eventTitle:String):void{
// do stuff
}
loadEvent("constructive_mc");
Next up, this line of code is a real Frankenstein-ization of ActionScript:
var eventInfo:[eventTitle] = new [eventTitle]();
I totally see where you're going with this idea, and I admit it: back in my days of OOP innocence I tried this kind of thing too! Your logic is very rational to make this assumption; however, this does fall miles outside the realm of OOP reality. Here's why... first, the data typing:
var eventInfo:[eventTitle];
This doesn't work. Collection property accessors (brackets) are a run-time-only reference. That means that collection accessors can only pull values from a collection with the final, compiled application is running. For example, lets look at a valid and invalid scenario:
var mc:MovieClip = new MovieClip();
trace(mc.sfoo); // compiler throws an error
trace(mc["sfoo"]); // no compiler error
The first throws an error because we're telling the script to look for a "sfoo" property on a movieClip. A MovieClip has no sfoo property, so the compiler yells at us. However, in the second case we're just telling the compiler that we'll be accessing a collection value from the movieClip. The value will be evaluated at runtime, so the compiler skips over this. While we slipped this past the compiler, it doesn't make it any the less wrong, and it WILL cause a run-time error which we (the developer) then have to figure out. So, in general you want to use collection references as seldom as possible. But back to the issue at hand, you can't declare a compiler value through a collection reference. And since a variable's data type is set mostly for the benefit of the compiler, I think you can guess why this is a problem.
Oh incidentally, AS3 has introduced the "*" data type as a wildcard for a value that can be of any data type. While it's nice to have a catch-all default, DO NOT start using a "var myVar:*;" declaration in place of stringent data typing. In the past six months, I've encountered MAYBE five scenarios where the "*" wildcard was appropriate.
And finally, this:
new [eventTitle]();
No go. You're trying to access a class definition like you would an object property, which is not one and the same. A document's class architecture composes the framework of the entire application. To do what you're trying to do is like... well, imagine a store; say an electronics store. You walk up to the counter and ask for three TV's and a stereo, and they go into the back, browse their inventory collection, and pull out the products that you've requested. But to do this with a class is like walking in and asking to buy their loading dock. It doesn't work, it's part of the store as a whole. You can't buy it and take it with you.
So, dynamic class access was totally off limits in AS2. Thankfully, Adobe extended an olive branch in AS3 and gave us the ApplicationDomain class that can tap into the document object model and extract references to source classes for dynamic instantiation. This is the foundation of the new library system in Lassie. However, this is some really advanced stuff. And it's a pain in the ass. My word of advice: don't go there. At least not yet. Even later, don't do it unless it is a vital and pivotal component of a dynamic application.
When you're just working with a series of five to ten possible class values, I'd just hard-code the instance script using a switch statement, such as:
function loadEvent(type:String):void
{
var eventInfo:Object;
switch(type)
{
case "type1": eventInfo = new ObjectType1(); break;
case "type2": eventInfo = new ObjectType2(); break;
case "type3": eventInfo = new ObjectType3(); break;
}
}
Oie, this got long. sorry! I blame Wes, he called in the middle of my post so I lost track of how much I'd written :P
bigmac- 05-19-2008
As for this:
stop();
this.parent.addEventListener(Event.ENTER_FRAME,checkProgress);
function checkProgress(evt:Event):void {
var filesize:Number = this.parent.loaderInfo.bytesTotal;
var amountLoaded:Number = this.parent.loaderInfo.bytesLoaded;
var percent:Number = amountLoaded/filesize;
percentLoaded_txt.text = Math.round(percent*100)+"%";
if (percent >= 1) {
this.parent.removeEventListener(Event.ENTER_FRAME, checkProgress);
this.parent.gotoAndStop(2);
}
}
But i'm getting this error
1061: Call to a possibly undefined method gotoAndStop through a reference with static type flash.display:DisplayObjectContainer. this.parent.gotoAndStop(2);
Yep, makes sense that you're getting the error. I'll do my usual of explaining the problem and then offer a better way to do it in the first place.
First of all, you're getting that error because you're calling the "gotoAndStop()" method on a generic "parent" reference. This goes back to that discussion we had about you, Chris, as an OOP object. Your lineage was:
Human > NorthernIrish > Chris
Remember this discussion? The driving point was that Chris is a specific type (or sub-set) OF Human. So to call Chris a Human is always valid. However, to call Human a Chris is not always valid... I'm Human and I'm Greg. So, the bottom lines is that we can reference objects at different levels of specificity. In the case of a display object's native "parent" reference, parent objects are data typed as DisplayObjectContainers since that is the most generic type of object that can have children. In the case of your scenario, your parent just so happens to be a MovieClip (which is a specific type of DisplayObjectContainer). However, you could potentially have added that loader clip to a Sprite or the Stage object, which are NOT movieClips. So, "parent" references are super generic by default so that they'll be valid in all scenarios.
Why the error? Because "gotoAndStop()" is a specific method of a MovieClip, meanwhile the compiler is just seeing the parent object reference as a generic DisplayObjectContainer (which does not have a "gotoAndStop()" method). So, the solution here is to wrap the parent reference in a MovieClip declaration that tells the compiler: "Hey, this generic DisplayObjectContainer is actually a MovieClip, so please treat it as such". This practice is also known as "decorating". It looks like this:
MovieClip(parent).gotoAndStop(2);
Okay, now after yet another longer-than-necessary explanation, I'm going to tell you that it would be better if you didn't do it like this in the first place. You kind of limit yourself when you start making these specific declarations... lets say you want to use this loader clip somewhere else in your application where its parent object will not be a MovieClip. Suddenly you've got a new error: your original error occurred because the parent reference was too generic, now you'd get an error because the parent reference is too specific. In general, this is nullifying the flexibility that OOP theory is designed to offer. Using this reference creates a dependency on the parent object – and dependency is the root of all evil in OOP. So, just use an event. In fact, this would be a prime place for using a bubbling event: you can have the parent add a listener to itsself for the load-complete event. Then, have the child loaders fire off bubbling events of that type. A bubbling event passes up through the display hierarchy, registering with every object along the way. So the parent doesn't even need to listen specifically to the children for events so long as they bubble.
fatbuoy1- 05-20-2008
No I appreciate the in-depth explanations, I find myself learning more when I get something wrong than when it just works! :D
So... a bubbling event you say? Sounds like fun! :D Surely that means replicating code though if I have to put code into each of the 26 parent mc's? Or maybe im getting the wrong end of the stick.
bigmac- 05-20-2008
26 parent MC's? I don't follow. Either way, there should never be a reason to write something 26 times. All you need to do is create a class for the parent object and apply that class to the object's library symbol. Then every instance of that symbol – everywhere – will be constructed with the code that is in that class. Write a script once, instance it as many times as you'd like. That's the premise of a scalable application.
And yes, look into bubbling events! They ROCK. They are, hands down, one of THE handiest things that was added in AS3.
Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.