symbols placed on the timeline become undefined if stepping backwards - actionscript-3

I am using the frames in the timeline of a .swf as pages in a flash app. The user can advance to the next page by clicking a button that takes her to the next frame. Similarly, it is possible to navigate to the previous frame/page as well.
Most of the content is placed on the stage (i.e. created by dragging an instance of a library symbol to the stage) but properties of those instances, such as .visible might be changed via actionscript. Also, some objects are loaded from external flash files and displayed programmatically with addChild / addChildAt.
The problem is, if I am on Frame N+1 and there is an object displayed on the stage programmatically (i.e. with addChild, not by having it placed on the stage) and navigate to Frame N where there is an object that is placed on the stage (i.e. dragged from the library),
then the instance of that object is undefined/null and throws an error if I try to set its properties (like .visible).
The error does not occur if I am moving to the NEXT frame, only if I am moving to the PREVIOUS one. Therefore I assume that some kind of initialization is not getting called while going one frame back.
I was also thinking that the objects would just not "live" to the next timeframe, that is, their value would be lost and re-initialized because of scope, but if there is no dynamically created object on the stage, I can navigate back and forth just fine.
Is there a way to ensure that the objects created on the stage do not disappear while navigating back to the previous frame?

The first, and more useful, part of the answer is this: timeline keyframes and scripts can give conflicting information about display objects - whether they should exist, where they should be, and so on. For example, when you add an item by playing into its frame, and then delete it with script, and then play into its frame again. When this happens, there's no unambiguously correct thing for Flash to do, so it tends to be unpredictable. I believe what generally happens is that once you fiddle with a given object via script, it's considered to no longer pay attention to the timeline - but your mileage will vary.
Having said that, the reason things are different when you play backwards is the second and more arcane part of the answer. Internally Flash functions differently when seeking forward and backwards on the timeline. Flash internally treats keyframes as changes to be applied in the forward direction, so as you play forward, it applies those changes in sequence. When you move backwards, however, from frame N+X to frame N, it doesn't scan through the intervening X frames reversing those changes - it jumps back to frame 1 and fast-forwards along to frame N. Normally, it amounts to the same thing and you don't need to worry about it, but when you get into the twitchy area where scripts and the timeline have a different idea of what should be on the stage, you're liable to see things behave differently depending on which way you jump (as you are now).
The super-short version is, for things to work predictably, try to ensure that any given object gets added, updated, and removed the same way - either all via script, or all via the timeline. When that seems impossible, fiddle with your content structure - usually, the best solution is to change your object into two nested ones, so that the things you want to do with script occur one level higher or lower than the things you want to do with the timeline.

I'm not sure I got your question right, but as3 does not instantiate elements on the timeline as soon as you gotoAndSomething, but later that frame.
That is, you can't
this.gotoAndPlay(10)
this.elementOnTimelineFrame10.DoSomething()
without errors.

I remember using this chunk of code in the past to work around this problem. It uses the Stage.Invalidate() function to wait for an Event.RENDER before trying to access and children, more info (although vague as hell) is here
private function init():void
{
stage.addEventListener(Event.RENDER, stage_renderHandler);
}
private function stage_renderHandler(evt:Event):void
{
// Run your code here
updateChildren();
}
private function enterFrameHandler(evt:Event):void
{
// triggers the RENDER event
stage.invalidate();
}
This also might me very costly (performance wise). I would strongly advise against dynamically adding/removing objects to an existing timeline, is there any way in which you can place an empty Sprite above the timeline animation and use that for all your dynamic content?
Hope this helps

Related

Cocos2D-X nodes loading in wrong positions have RemoveAllChildren()

I'm only using one scene object for my entire 2d sidescrolling platformer with two layers: There is a hudLayer for the controller, and the gameLayer for everything in the game.
When I control my character to walk into a door (sprite with a physicsBody that has a callback that takes me to the next level), the next level loads by these instructions:
remove all event listeners
stop all actions for both layers (the gameLayer typically has been doing a Follow action that follows the player, and is limited to the size of the backgroundSpriteNode)
remove all children from the gameLayer
set gameLayer position to Vec2(0,0)
load level 2 (a complete copy of what level 1 should look like... load the background sprite to gameLayer, playerSprite, add event listeners)
add event listeners back to the hudLayer
The only problem is that for whatever reason, the level only partially loads the way it should... For instance, coins appear in the spots they should, but platforms don't seem to. Neither that or the player, or other characters. Not sure really why these objects are specifically not loading in the correct positions, but maybe someone who has experienced this sort of problem before can help?
Most nodes (such as platforms) are appearing below the bottom of the screen when they should be fully shown at the bottom of the screen. Characters are appearing down there too when they should be appearing above the platforms.
P.S. After testing a bit more, I've come to realize that SOMETIMES some of the platforms are loading in the correct positions, but sometimes they aren't. Not sure why. A fix for this may be to load things one at a time with a delay between them. But I'd rather not have to put delays and just figure out what the deal is.
Figured it out: for whatever reason, I needed to add a delay in there. I think for whatever reason, by calling removeAllChildren(), something got messed up with the positions. It was my suspicion since SOME of the time, parts of the next level would load, but only some parts, and only sometimes.
Here's the code I added to run an action on the gameLayer once all children were removed, etc...
runAction(Sequence::createWithTwoActions(DelayTime::create(0.1), CallFunc::create( [&] ( ) {
loadLevel2(this);
})));

as3 how can i prevent that a new instance is created by entering a frame?

i am working with several nested movieclip objects in a project. but i get into trouble with the buttons i created and implemented in the nested movieclips:
to describe it in a simple way:
I have a main movieclip with five frames, including two buttons with listeners to browse between the frames. Then inside of one Frame I have another movieclip with its own buttons. i instanciated it by hand not through code and gave it a specific name like "nestedMc".
Now I dont want to build the Listeners for those buttons inside the class of the nested movieclip class but in its parent class, which works fine until i then goto another frame in the main movieclips timeline and come back.
obviously every time flash enters a frame its contents get created anew (and therefore get new instance names). I could now try solve this through filling the frames via code.
But maybe there is another way to make sure the frame contains the same instance everytime i enter?
Timeline scripting is a dirty business, and really, a carry-over compatibility layer for Actionscript 2 projects. Whenever possible, I highly recommend not doing it, and simply keeping all of your code in your document class. As you're experiencing, timeline code causes headaches.
Consider instead just creating both states of your Stage (it sounds like that's what your two buttons are jumping between) and simply hiding them offstage or setting their alpha to zero and their mouseEnabled state to false. Furthermore, if the purpose of your frames is to play animation (a tween), consider instead switching to a much more powerful suite such as TweenLite. Moving an object over a hundred pixels (smoothly) can be as easy as:
TweenLite.to(redBall, 3, {x:100});
Now, if you're manually adding these items to the stage, as long as the object is a dynamic one, you can assign an instance name to it which will be saved between frame loads. Be aware the object name is not the same as the instanced name. For example:
var redBall:Ball = new Ball();
redBall.name = "bubbles";
The object's name is Ball, but it's represented as a variable called redBall. Its actual DisplayList name will likely be ambiguous (such as "Instance71"), and I can manually define it as "bubbles". 3 different names for the same object, all very different and necessary.
Even if you give the object a displayList name, you may not be able to reference it through code unless you enable Automatically declare stage instances, which basically creates on each object a pointer to the displayList object.
That said, you can always fetch the object by other means. Obviously, your buttons are always appearing, but you're trying to find a very specific object on the stage. At this point, we can use getChildByName() or getChildAt().
Hope that helps.
-Cheers

AS3 How to communicate between frames

I have been writing a game in timeline code. I want the different frames (rooms) in the game to be able to share information between each other. Of course, timeline code is limited to the frame it is written in.
After doing quite a bit of reading ("Foundation Game Design with Flash" and a number of articles, tutorials, forums etc) I decided to employ a document class. This did not work either. It seems it only works for frame one but not the rest of the frames (I have four).
How can I have frame four respond to something that happpened in frame one? For example, if the player achieves something in frame one, I want a movie clip in frame four to be visible.
If You are writing your code on the timeline, My suggestion would be to create two layers in the timeline, one for 'frame-actions' - in this layer you insert the code specific to a single frame (will work when the movieclip is stopped on that particular frame).. And also create one more layer called global-actions (for the entire timeline). Only the first frame will be a key frame and there should be empty frames till the end of the timeline.
In this layer actions write the code that you want to access from any keyframe in the same timeline.
If you define a variable in the actions which are written for the whole timeline (global-actions) then that will be available on all the frames.
Now if you want to go to a different frame based on some action, just write some functions in the layer which contains global actions and call that particular function through the frame actions. To go to a different frame use the 'gotoAndStop(frameNumber)' function of flash.
I want to tell you that while it will work, I would not recommend using it in this way.
HTH.
You can use static variables - these are variables which are linked to a class, rather than an instance of it.
Suppose your document class was called Document.as, and you wanted a variable, playerLives, to be visible from any part of the program.
Declare it inside Document.as:
public static var playerLives:int = 3;
You can then reference this directly from anywhere else in your code with:
Document.playerLives
(note that the variable is a member of the class itself, not an instance of it).
You could use a dedicated Statics class to hold these variables if you want to keep your document neat, or attach them to the relevant classes (eg Player.lives)
I've not used timeline/frames for some years but I believe this is how I used to do it!
NB Statics will be fine for your purposes but they are, in some ways, an equivalent to the _global variable in AS2 (at least, they can be used in the same manner) - many would not approve of their use, or over-use, as they are freely accessible from anywhere in your program (thus anathema to the OO concept of encapsulation), but personally I try not to worry about it in small cases - the most important thing to know about the rules of any design pattern is when they can be broken!
They are also slightly slower to access than instance members, but you won't notice this unless you are constantly accessing/changing them (making things like player velocity, which will need to be referenced/changed every frame, static, is not a good idea).
Hope this helps.
You may find the simplest way to link everything with the document class is to move your four frames into a movieclip together and have that on the first frame, then interact with that movieclip.
E.g. in the document class, where the movieclip instance on the timeline is called 'game'.
game.gotoAndStop(4);
game.objectToDisplay.visible = true;
If you encounter reference errors in the IDE then you can avoid these by using [] notation to refer to the properties of game, e.g. game["objectToDisplay"].visible = true;
Note that it's not really best practice to do this, but it will at least help you to finish that first game which is really more important at this stage in your learning. Afterwards, if you want to learn more then I'd recommend "The Essential Guide to Flash Games" by Jeff Fulton from 8bitrocket.com - it will teach you how to use the document class effectively.

Why would a keyframe in Flash fail to update an object's position as defined in the keyframe when the play head is sent to that frame?

I have a background MovieClip in a custom button class, which moves the play head to a different frame (via gotoAndStop("framename")) depending on which mouse events it receives.
When the mouse up event is received, it sends the play head back to the "release" frame, where the background should shift back to its original location, but instead nothing happens and the background remains where it was. It's as though Flash is not honoring the background's position defined by the key frame it enters.
The only workaround is to add a frame script to each frame which manually sets the x and y position to what it should be, but this defeats the purpose of using keyframes for the position.
What is going on?
Try calling event.updateAfterEvent(), it's supposed to
Instructs Flash Player or Adobe AIR to render after processing of this event completes, if the display list has been modified.
from here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/MouseEvent.html#updateAfterEvent%28%29
This appears to have been a bug in the Flash IDE, specifically some kind of corruption in a timeline layer.
The corruption originated in Flash CS4, but persisted when opened and compiled in Flash CS5, which is why I suspect it was some kind of content-generation bug, rather than something specific to either IDE version. The IDE probably gets confused when you construct the timeline out of order or drag things around, rename things, copy things in from other files, etc. I think, specifically... it was the fact that I copied this particular object in from another file. And it wasn't even a complex object, it was just a MovieClip that contained a rounded rectangle and had a drop shadow applied, seriously.
Steps I took to resolve the issue were to remove any classes associated with the library item, reverting it back to a standard MovieClip. I then completely removed the problematic layer. I then reassigned the class to the library object and exported it for ActionScript. Finally, I rebuilt the layer from the first frame, adding subsequent keyframes as necessary. I reapplied filters, and adjusted the clips location on each frame.
Now, everything is functioning as expected. The clip's position, filters, etc. are all updated as expected when the playhead moves to any frame. It's working now as expected, so I wasn't doing anything wrong or not possible. It simply must have been some kind of incrementally constructed key framing confusion within the data structures of the IDE. Glad it's working now.

Has anyone experienced side effects (including performance issue) of using getObjectsUnderPoint?

Before I go making major change in my ongoing game project, I just want to hear from others if anyone has found any issues with getObjectsUnderPoint() function of the DisplayObject?
Update:
Not just the performance issue but any other limitations of using it (like it doesn't detect certain type of UIelements (just as example))
I will have three layers in my application (which an Isometric game)
Background -- This is just a background which stays in the bottom, has nothing to do with game
Middle Layer -- This is the playable area, Here all my game elements will be placed on this layer
Top Layer -- This is one dummy transparent layer covers entire playable area which interrupts all the mouse events. This is where I want to use the getObjectsUnderPoint()
So, player wants to click on the element, the top layer will interrupt the mouseevent and then check if there is something placed or just a plain background and take appropriate action like, notify the underneath object.
This really doesn't require to be done this way because I could simply add moues events for all those items placed on the map directly but because I would be using getObjectsUnderPoint() anyway to check if there is anything beneath the item.
If anyone can explain how this function works then it would be little easy for me to make a decision.
There was one annoying problem though. I don't know if they fixed it or not. At least it was there in 10.1 times.
If you have a container and you scaled it container.getObjectsUnderPoint will return wrong result. All the time. So everywhere where I needed getObjectsUnderPoint I had to call it from stage to get proper result.
It's an incomplete function. It returns graphical objects under the mouse, NOT all potential mouse targets for event or interaction purposes. It actually requires complex logic to examine the array returned by getObjectsUnderPoint to determine the mouse target, because the appropriate target (the one Flash would choose if you actually clicked that point) may not be in the list.
First you'd have to examine the object array in reverse, since the items are ordered back to front. You'd have to examine each object's entire parent chain, looking for a parent with mouseChildren = false that would cause it to intercept the event and become the target. Whether or not such an object is found, this final object you arrive at must have its mouseEnabled property set to true, otherwise you must skip it and move on to the next object in the array, which would be, for example, the next sprite or shape behind the one you initially checked. While going through the list, you must notice when the parent changes, at which point you need to assume that all children of that common parent had their mouseEnabled property set to false, in which case the parent would become the next candidate. This is actually extremely complicated, because you're working backwards in a bottom-up approach with an incomplete set of objects that was generated from the top-down.
To get actual potential mouse event targets, consistent with the default dispatching logic... it is actually easier to start from the stage in a top-down manner and walk backwards through the display hierarchy in a depth-first search, checking mouseChildren to determine whether you need to step into children, and checking mouseEnabled if it's to be a target, otherwise stepping into the container's children and repeating the process from back to front again. This is much more accurate, complete, and staightforward. The only problem is you have to code it yourself.