We're making a Flash browser game with a few reasonably complex animations. Our designer is making the animations in Flash Professional while I'm wiring everything up and adding some logic through AS3 (using FlashDevelop).
In one of our more complex animations a "bonus item" moves around the screen. It tweens hither and tither, there special effects and as such, it disappears for a few frames and then reappears later.
From AS3 we want to be able to dynamically decide which bonus item (say a mushroom or a star) to include in the animation. We don't want to have to ask our designer to replicate the entire animation for each of our bonus items.
This is what we've tried:
Created a two frame (1 mushroom frame, 1 star frame) "BonusItem" movieclip in FlashPro and Exported for ActionScript.
Created the complex animation movieclip in FlashPro and added the BonusItem movieclip to the relevant frames. Gave the BonusItem instance an instance name on all necessary KeyFrames. Exported entire movieclip for ActionScript (exported as "ComplexAnimation").
Intention:
The intention was to be able to do this:
var complexAnimation:ComplexAnimation = new ComplexAnimation();
complexAnimation.bonusItem.gotoAndStop("star"); // Frame labels have been added in FlashPRo.
this.addChild(complexAnimation);
This would play the complex animation with the star and we could easily call gotoAndStop("mushroom") to play the same animation with the mushroom.
Problems:
The first problem was that complexAnimation.bonusItem was null on line 02 above. I solved this by handling ADDED_TO_STAGE for complexAnimation and putting line 02 above in the handler.
The next problem was that each time the bonusItem movieclip started tweening, or if it was not present in some frames and was subsequently re-added the complexAnimation.bonusItem attribute/reference was reassigned to a new bonusItem instance. I then had to find a way to know when this was happening and call gotoAndStop("star") on the new instance.
I've found two ways to do this:
1) Listen for ADDED events on complexAnimation with a target.name of "bonusItem". It's a bit crap in a strongly typed language to have to resort to matching strings, but this works. Btw, when the ADDED event is fired new frame object references are still null.
2) Listen for FRAME_CREATED events. This happens later than ADDED at a point where new frame references have been initialized. As such I can check if complexAnimation.bonusItem is non-null at then call gotoAndStop("star") on it. One problem with this is that calling gotoAndStop actually triggers another FRAME_CREATED event to fire, so I need to guard against infinite looping. Again, it works but I don't have a great feeling about it.
Conclusion:
Well I don't really have a conclusion other than I feel like I'm working really hard to do something relatively simple. I'm hoping there's an easier & more robust approach. I have a strong feeling that I'm going crazy. Anyone know a better way to do this?
If you have an object that exists on a timeline and it has an instance name, and you need to be able to maintain a reference to it through the duration of the timeline, then it must exist on every frame (and in the same layer!) of the movieclip. I grant you, your workarounds get the job done but you have already experienced the pain involved in doing so.
The path of least resistance is to just have the object exist at all times. If the user shouldn't "see" it, just hide it offscreen somewhere. Just make sure it always exists, contiguously, on that timeline layer from frame 1 all the way to the final frame.
The other thing I'd suggest is to stop deeply nesting movieclips in the hopes of using those nested clips as state representations. This is one of the things that sadly was very easy to do in the AS2 days, but has been rendered impractical to the point of madness in AS3. Anything deeper than 1 layer is getting into some dicey territory. 3 layers deep and you need to rethink your strategy. Perhaps instantiating different movieclip instances from the library and adding/removing dynamically instead of relying on frames.
I thought I'd update this post with our current (and hopefully long-term) solution.
First of all, I made an error in the above post:
The next problem was that each time the bonusItem movieclip started tweening ... the complexAnimation.bonusItem attribute/reference was reassigned to a new bonusItem instance.
This was incorrect. Flash was indeed assigning a new instance of BonusItem, but it was caused by a Mask layer keyframe rather than the tween.
I had been keen to try to avoid having any logic which relied on string comparisons, but in the end I swallowed my pride to make life easier.
Our designer gives all relevant objects (stuff that we'll need to access from AS3) instance names on each keyframe in the timeline. If the object is nested within other objects our designer must assign those parent objects instance names too. We must coordinate those instance names so that dev know what the accessors are called - we would've had to do all this anyway. Our designer also still has to "Export For Actionscript" a class for each relevant movieclip (e.g. BonusItem).
In AS3 we're using Robotlegs for dependency injection and as the basis of our MVC framework. Robotlegs suggests that application-specific logic should be separated from view-specific logic. It allows us to specify a logic class (called a Mediator) to be associated with each and any of our views. As such we can do the following mapping:
BonusItem -> BonusItemMediator
This means that every time Flash creates a BonusItem on the timeline Robotlegs somehow knows about it and creates a new instance of BonusItemMediator (which we write ourselves and have full control over). In addition, Robotlegs can easily give us a reference from our BonusItemMediator to its associated view instance (the BonusItem instance). So inside my BonusItemMediator I can ask the view reference what its instance name is. I also walk up its parents to the stage and record each of their names to generate a resultant string of instance names that uniquely specified this instance of the BonusItem.
e.g.
"game.complexAnimation.bonusItem"
Once I know this I can ensure that the bonusItem is showing the correct image (star or mushroom) with the following code:
var frameLabelName:String myGameModel.whatTheHellShouldThisBeShowing("game.complexAnimation.bonusItem");
this.view.gotoAndStop(frameLabelName); // where view is the BonusItem instance
So now regardless of how or when Flash seemingly randomly decides to destroy and recreate my bonusItem I'll hear about it and can ensure that that new BonusItem instance is displaying on the correct frame.
The main weakness with this solution is that we're relying on string comparisons. Our designer could easily mistype an instance name and we wouldn't hear about it until that code was hit at runtime. Of course tests mitigate that risk, but I still feel that it's a shame that I'm using a strongly typed language, but then not making use of the compile time type checking.
Related
Simple question: how can I reuse instance names for different objects (obviously on different frames)? I really want to be able to use the same instance name for all my scenes, "ground", even though these ground instances are of different classes. I really don't want to have to go about naming them "ground0", "ground1", etc.I realize that there are ways around this, but I would hate to fool around with all that extra work. So that being said, how do I reuse the instance name "ground" without having this warning thrown at me?:
Menu, Layer 'ground', Frame 1 Warning: The instance name 'ground' is declared on an object of type Ground but there is a conflicting use of the instance name 'ground' on an object of type Ground2.
And by the way, I realize that an instance is supposed to be an occurrence of a specific object, but to be honest I don't quite see the point in not being allowed to reuse instance names when appropriate. Any help would be much appreciated.
If I recall how finicky Flash was, it may have something to do with how you created the "ground" instance on those frames.
By that, I mean:
Did you create one instance first, and then created new keyframes afterwards (meaning: the same instance would be used in all keyframes), or...
Did you begin by creating your empty keyframes first, and then drag-dropping / pasting one "ground" instance in each frames? (meaning: in this case, it would likely be treated as 3 separate instances).
I'm not sure if this assumption is correct, but Flash doesn't necessarily tie instances to layers, as in "Oh, ground is on Layer 1, therefore every frames should reuse the same instance...". In a perfect world, I agree this would make sense. But from the early versions of Flash when it was mostly targeted to animators, where anything goes (from having multiple shapes and instances on the same frame, from re-arranging the depth of groups / instances on one frame/layer), it's still making the assumption that if you drag / copy an item from the library to the stage, it doesn't necessarily mean they are the same instance. Instead, you have to babysit Flash by creating more keyframes (or tween keyframes depending on your need) after the frame with the existing asset instance you want to reuse.
Again, the above is an assumption based on my experience - but if you did enter the "ground" instance-name manually for each instances found on each frames, chances are you previously copy/pasted or dragged new instances to those frames.
Does it sound like something you may have done?
EDIT:
If you have "ground" assets across a few frames, which are instances of different Symbols, then that definitely would trigger those Warnings you've been getting. I'm not sure where you can turn those off (Preferences? Project Publish settings? Advanced AS3 settings maybe?).. but regardless, here's a way that may work for you, programmatically.
Since each frames have a unique instance, with each instances named "ground", you could create a helper function to work like the .getChildByName("ground") method (and to be honest, I'm not sure if that method would work right-off the bat, you could try). You would need to for-loop through the given MovieClip's children (In your Menu's children, in your case I believe?), and check if(child.name==theNameInQuestion) return child;.
That being said, I can't guarantee this is THE proper solution (didn't test), as I don't know how you're navigating the frames at runtime (play/stop/gotoAndPlay/gotoAndStop/etc), and that may affect which "ground" instance is available at a given time. Internally, Flash does addChild/removeChild to swap out those various "ground" instances as it cycles through the frames, it's not a simple visible=true/false toggle (AFAIK).
EDIT 2:
This could be what you need?
If any programming language if you want to reuse instance name variables you simply type them with the top most superclass type. In your case that would be:
var ground:DisplayObject;
ground = new Ground!();
ground = new Ground2();
//etc ....
Edit: If using property panel you can't use the same name for 2 objects as there will be no way to differentiate between them. So an error will be correctly thrown.
Now if you want to use in code only one name same principle as shown above applies:
var ground:DisplayObject;
ground = ground0;
ground = ground1;
//etc ....
Then you can safely use in code the variable ground.
I started using AS3's Tween function but noticed some strange behavior every once in awhile. Basically, it freezes before it finishes the tween.
This website seems to offer the solution:
http://www.rgbeffects.com/blog/actionscript/tween-freeze-frustrations-avoid-actionscript-tweens-stalling-out/
My program requires that the rotation tween works precisely every time. I like to use the functions adobe put in place but this makes me nervous since it has such a big issue.
Should I be using AS3's tween or some external tween like greensock.com's TweenLite?
Which would be more reliable?
Most likely your tween instance is being prematurely garbage-collected, because you don't store a reference to it somewhere. This is a common mistake which you can easily Google for.
Solution: store a reference to Tween instance while it is performing animation to save it from GC.
Follow the link for detailed explanation: AS3 Garbage Collection, the reason your tweens are ending early.
Official article on adobe.com also mention this problem (see note at the bottom of article):
Note: Consider variable scope when using the Tween class. If a tween is created in a function, it is important that the variable's scope exists beyond the function itself. If a tween is stored to a variable of local scope, ActionScript garbage collection removes the tween as the function completes, which will likely be before the tween has even begun.
And here are some links on garbage collecting if you would like to learn more on GC logics.
I have a question how can I clear/free memory in flash? For example I am finishing game and I want to start from beginning and if I will just jump to the first frame all the objects there are still in this memory, is any possibility to force cleaning memory?
Can I free memory for an object? for example I removeChild(something) - and I want to free memory for an object as I will reuse it?
Can anybody explain me how the engine works?
Thanks
I would encourage you to read Chapter 14, Garbage Collection in the "Moock Book" (Essential ActionScript 3.0 by O'Reilly Publishing).
The short answer to your question is that you're not in control of de-allocation, the garbage collector is. In a garbage-collected language like AS3 or Java, you don't have manual control over allocation and de-allocation of memory like you do in lower level languages; there are no AS3 equivalents to things like delete in C++ or free in C. Your goal should not be controlling when you destroy things, but rather not forgetting to remove references to things you no longer need around and making sure you disable things that you intend for garbage collection.
Memory leaks in AS3 typically come from a mixture of newbie misunderstanding (like thinking removeChild or setting a reference to null destroys objects), and from not keeping good track of references to objects--especially where strong listeners are involved.
A previous respondent posted this:
myObject = null;
What this does is remove a reference to the object that the variable myObject was holding. Nothing more. You need to know a lot more about the situation in order to be able to say whether this assignment even makes the object in question eligible for garbage collection, especially how many other variable are holding references to the object. And the object might already be eligible for garbage collection even if you didn't set the reference to null (i.e. if myObject has no connection to a GC root).
Suffice to say, the entire GC mechanism is more complex than can be satisfactorily explained in a StackOverflow post. That's why it has a whole chapter in the Moock Book, and even that book does not go into implemenation detail or great detail about when exactly the Flash Player does its ref counting deletions or mark and sweep passes.
The most important things to remember are, IMHO:
When you intend to "kill" an object, give it a cleanUp() or destroy() function where you do things like stop all its timers, sounds, remove listeners, etc. An object will continue to exist and execute code until it gets GC'd. And the Flash Player defers GC as long as it can--it's usually triggered when the Player needs to allocate more RAM from your system, because allocating memory is about the only thing that's more time consuming than doing the mark and sweep GC pass.
Read about weak vs strong listeners. Basically, when you have a weak listener, the listener reference is one that is ignored by mark-and-sweep GC, so it alone will not prevent an object from getting collected. But don't listen to anyone who tells you "always use weak" or "always use strong listeners and manually remove them" because there are times where each is appropriate, and that's why the choice is yours.
removeChild() will remove object from stage, but will still keep it in memory. You will have to null the object like this myObject = null if you wish to get rid of it entirely. You might not need to do that thought. Just removing it from stage and removing all associated events will be sufficient in most cases.
Clearing memory is a tricky thing with Flash, the proper way ow implementing it setting up objects properly in the first play for easy clearing, rather than forcing deletion. When you want to remove an object from memory, you do it by removing any reference to it, and then flash marks it for garbage collection. Then Flash at a later point removes the object from memory.
In order for the object to be ready for data collection it cannot have any connection to another object.
so if you have an object that has a single connection to a MovieClip and the movie clip has no other relation, then if you set it to null, you will remove it.
if you, however, have two objects that point to it, if you remove one link by setting it to null, the MovieClip will not be removed.
furhtermore, if you have a 2 or more movie clips that have a network of connections, removing those objects requires these connections be broken as-well. For example if you have a level with many characters and listeners set up, removing the lavel movieClip will not clear it.
one way of breaking these connections is adding onRemovedFromStage Events, that remove further children, listeners and objects. I've started using the casaLib extension of movieclip - CasaMovieClip, that has a function called removeChildrenAndDestroy. this makes it a bit easier, but would take a while to implement on an older project.
Anyhow, you'll find there are many sites discussing this, a good place to start is grant skinner's blog
My professor gave my class an assignment today based on object oriented programming in Pygame. Basically he has said that the game that we are to create will be void of a main game loop. While I believe that it is possible to do this (and this question has stated that it is possible) I don't believe that this is required for adherence to the Object Oriented paradigm.
In a diagram that the professor gave, he showed the game initializing and as the objects were instantiated the control flow of the program would be distributed among the objects.
Basically I believe it would be possible to implement a game this way, but it would not be an ideal way nor is it required for Object Oriented adherence. Any thoughts?
EDIT: We are creating an asteroids clone, which I believe further complicates things due to the fact that it is a real time action game.
Turn based games or anything event driven would be the route to go. In other words, take desktop GUI apps. They'll just tick (wait) over until an event is fired. The same could be done for a simple game. Take Checkers for example. Looping each game cycle would be overkill. 90% of the time the game will be static. Using some form of events (the observer design pattern would be nice here) would provide a much better solution. You're using Pygame, so there may be support for this built in, through due to my limited use I cannot comment fully. Either way, the general principles are the same.
All in all it's a pretty rubbish assignment if you ask me. If it's to teach you event driven programming, a simple GUI application would be better. Even the simplest of games us a basic game loop, which can adhere to OO principles.
Hmm. In the general case, I think this idea is probably hokum. SDL (upon which PyGame is implemented), provides information to the program via an event queue, and consuming that queue requires some sort of repeatedly checking the queue for events, processing them, and waiting until the next event arrives.
There are some particular exceptions to this, though. You can poll the mouse and keyboard for their state without accessing the event queue. The problem with that is it still requires something like a loop, so that it happens over and over again until the game exits.
You could use pygame.time to wait on a timer instead of waiting on the event queue, and then pass control to the game objects which poll the mouse and keyboard as per above, but you are still 'looping', but bound by a timer instead of the event queue.
Instead of focusing on eliminating a main loop, how about instead think about using it in an object oriented way.
For instance, you could require a 'root' object, which actually has its own event loop, but instead of performing any action based on the incoming events, it calls a handler on several child objects. For instance when the root object recieves a pygame.event.MOUSEBUTTONDOWN event, it could search through it's children for a 'rect' attribute and determine if the event.pos attribute is inside that rect. if it is it can call a hypothetical onClick method on that child object.
I think it might qualify as event driven programming? Which can still be object oriented. You see this in Flash a lot.
There's a difference between a main loop in a main class. You can still have a game class initialize all of your objects, and then rely on inputs to move the game onward.
Kinda hard to say exactly without knowing the exact parameters of your assignment, the devil is in the details.
You might look at how python utilizes signals. A decent example I found is at: http://docs.python.org/library/signal.html
So, I've been toying around with Flash, browsing through the documentation, and all that, and noticed that the ENTER_FRAME event seems to defy my expectation of a deterministic universe.
Take the following example:
(new MovieClip()).addEventListener(Event.ENTER_FRAME,
function(ev) {trace("Test");});
Notice this anonymous MovieClip is not added to the display hierarchy, and any reference to it is immediately lost.
It will actually print "Test" once a frame until it is garbage collected. How insane is that? The behavior of this is actually determined by when the garbage collector feels like coming around in all its unpredictable insanity! Is there a better way to create intermittent failures? Seriously.
My two theories are that either the DisplayObject class stores weak references to all its instances for the purpose of dispatching ENTER_FRAME events, or, and much wilder, the Flash player actually scans the heap each frame looking for ENTER_FRAME listeners to pull on.
Can any hardened Actionscript developer clue me in on how this works? (And maybe a why - the - f**k they thought this was a good idea?)
Short answer: you get what you ask for.
The fact that your MovieClip is "anonymous", as you call it, doesn't change how garbage collection works.
Both of your theories are wrong. It's simpler. You asked your MovieClip instance to notify you whenever a frame is entered. Quote from the docs:
Dispatched when the playhead is
entering a new frame. If the playhead
is not moving, or if there is only one
frame, this event is dispatched
continuously in conjunction with the
frame rate. This event is dispatched
simultaneously to all display objects
listenting for this event.
And this is what is happening.
Objects such as MovieClips are heap allocated. If you store a reference to it in a local variable, the reference lives in the stack and will get out of scope when you return from your function. You'll have no way to refer to the object, but the object will still be alive, in the heap. So, whether you store a reference to the mc instance or not, that doesn't change this basic fact of how heap and garbage collection works.
Now, since the are no strong refs to this object, this object is elligible for collection. This does not mean it will be collected when the function returns.
Since the object is still alive, it will continue to dispatch the event, because you asked for it. Once it's collected, naturally, your code in the handler will no longer be executed.
Edit
I said that both of your theories are wrong. I think that's the case from an Actionscript perspective. However, at the player level, it seems evident that the player has to keep track of objects on which it must fire certain events, so yes, it must be storing an internal reference to said objects. This is no different to how it works for other objects that are globally handled by the player, though, such as for instance, loaders. There's one exception to this that I'm aware of: running Timers. As long as a Timer is running, it won't be collected, even though you don't have any Actionscript reference to it.