as3 Variables changing impossibly - actionscript-3

I'm having an issue with a "Point" type variable velocity changing without any calls to change it.
private function framecode(e:Event) {
trace(getVelocity().y);
tracks.gotoAndStop(2);
trace(getVelocity().y);
}
This code is part of a class called "tank" which extends the one that velocity is used in (my moving object class). velocity is a private point type variable and getVelocity() is a public access method. tracks is a named movieClip contained inside the one linked with tank. The event listener is ENTER_FRAME. There is no coding on the frames of tracks.
Somehow these two traces give different values (the first one being correct) and I can't figure out how the gotoAndStop() can possibly effect it (and hence how to fix it).
I've found that play() does not reproduce the bug but prevFrame() and nextFrame() do. As the variable is private this class should not even have access to it to change it.
Another strangeness is that if the event listener is changed to FRAME_CONSTRUCTED or EXIT_FRAME, there is massive lag and my movieClip randomly disappears after a few seconds.
Thank you for reading, any help would be appreciated.

Your velocity variable is private, so one one can access that outside of the class.
However, getVelocity() is returning a reference to your velocity variable. Once someone has that reference, they can change the values of it's properties: getVelocity().y = 3. So it's not impossible for this to happen.
One way to trouble shoot this is to add a trace() statement to/set a breakpoint in getVelocity() so you can see where it's being used.
You could do something similar w/the Point class, but you'll have to extend it, add getter/setter methods for y (that traces out when they are called), and modify your code to use the getter/setter. This might be worthwhile (its simple enough), and the act of modifying your code to use the getter might help you discover where the problem is.

Related

AddChild with GetChildByName

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.

What happens to a Movieclip when casted as a Sprite?

(Note that there is nothing really 'wrong' here with my code (in the sense that it works), but more wondering on how it is working and what is happening under the hood)
Currently I have two libraries, each with one object. One is set with the class "Apple" and the other is "Pear", They are located in separate external swfs.
Apple's base class is MovieClip as content-wise it is a movieclip: has frames + animation
And here is the code I used to create and display an Apple movieclip object:
function getClip(inputName, spriteLibrary:Loader):MovieClip {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (MovieClip) (new aClass());
}
this.addChild(getClip("Apple", referenceToTheLoadedSwfThatHasAppleInIt));
The above works just fine and Apple appears on the stage and plays.
However, Pear's base class is a Sprite (has no animation, frames, etc). So the above fails, since the method is supposed to return a MovieClip.
this.addChild(getClip("Pear", referenceToTheLoadedSwfThatHasPearInIt));
I thought for a moment I would have to have two versions of the above method, one for Sprite and one for MovieClip. But just to see, I changed it to Sprite and tried to create Apple:
function getClip(inputName, spriteLibrary:Loader):Sprite {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (Sprite) (new aClass());
}
this.addChild(getClip("Apple"), referenceToTheLoadedSwfThatHasAppleInIt);
this.addChild(getClip("Pear"), referenceToTheLoadedSwfThatHasPearInIt);
Now both will work, but interestingly, I found that even though the method returns a Sprite, Apple still seems to work fine and plays it's animation on the stage. I can cast this to a MovieClip and access all the MovieClip related properties and so forth.
My question is then, when Apple "existed" as a Sprite, what happened to all of it MovieClip related "stuff" and is this actually a normal thing to do when having to work with MovieClips and Sprites (by pretending you only have Sprites and cast to MovieClip only when you need it?)
A Sprite class provides more basic functionality than a MovieClip, but everything that's a MovieClip can be manipulated by using the functionality of a Sprite class. In fact, your method can return as low a class as DisplayObject if your only intention is to do an addChild(). The typecast will not strip the typecasted object of any functionality, it will instead restrict the available calls for its properties. Say, a DisplayObject has x and y properties, a Sprite can be used to add objects to itself (the addChild() method) which the DisplayObject does not have, and a MovieClip has internal animation and a say gotoAndStop() method which a Sprite does not have. So, if you typecast an Apple to a Sprite, you cannot make a call to the reference's gotoAndStop(), because you've told the program that the reference is just Sprite. If you typecast the Apple or the Pear object to DisplayObject, you cannot call its addChild() method to say add a health bar (weird thing for apples to have health bars, but why not?), because the reference does not know that the underlying object supports this functionality. But actually nothing will happen to either object, no matter how you typecast them, you will just restrict yourself from applying more advanced actions via received reference.
Actually it's good practice to limit the functionality for yourself via typecasts, because you are then protected from making "crutches" over working code which can probably spoil its purpose. Say, your code will not be surprised in case you would decide to turn Apple into an advanced Sprite class (say, public class Apple extends Sprite {...}) with tailored properties, embedded event listeners, color-changing maybe, but if you typecast the newly created Apple to Sprite or DisplayObject, you are abstracting from any extra abilities of the instance and only use its basic interface (properties and methods of a typecasted class) to perform the actions the Sprite or DisplayObject are intended for. If you have some overridden properties in the advanced Apple, they will work as you have intended while making the Apple class, even if addressed from the superclass description - this is actually what override is for.
In short, don't worry about losing functionality, but try typecasting to least possible class with used functionality.
About "what happens under the hood": Each class has its own table of properties and methods, if a class is extending another class, that class's table is identical to the superclass's up to its end, and the extra space is occupied with information on properties and methods implemented by the class. If there is an override, the overridden method's info replaces the info of the corresponding method in the table for the class, not for the superclass though. Each instance has a memory block allocated to it, there is a reference to the class's properties and methods table, which is then used to execute correct code, if a method is called via instance reference. So, when you call a method, or a property with a getter or setter assigned to it, out of an instance, the following happens:
The correct set of parameters, including "this" pointer to that instance, is pushed into the stack. The correctness of the order and the type of parameters is ensured by Flash compiler.
Then, the class that's actually the proper class of that instance is referenced via prototype property of the instance. Every class in AS3 has this property. The instance is accessed via "this" pointer previously pushed into the stack. (Technically it's stored in more than just stack, so the reference is just the same that's put in the stack)
Then, the correct offset (determined by the compiler) is returned from that table, that is the address of the called method of the class. Overridden or not, no matter here, because an improper override is detected at the compile time, and will not happen here.
Then, code execution is transferred to the returned address. The method's code then parses the parameters in stack, does some more data protection and proceeds with its implementation.
The most important thing to understand here is that nothing happens to the object itself, but the way the compiler treats the object will differ.
For example, the compiler will treat the object returned by this function as a Sprite, even if we actually return a MovieClip:
function makeSprite():Sprite
{
return new MovieClip();
}
var test:MovieClip = makeSprite();
// 1118: Implicit coercion of a value with static type flash.display:Sprite
// to a possibly unrelated type flash.display:MovieClip.
So what we need to do here (as you understand currently) is tell the compiler that the result is actually a MovieClip via typecasting:
var test:MovieClip = makeSprite() as MovieClip;
Another thing to take notice of is that if you were to trace() the result, you would get [object MovieClip] rather than [object Sprite]:
trace( makeSprite() ); // [object MovieClip]
And using is to check if it's a MovieClip will return true:
trace( makeSprite() is MovieClip ); // true
Even if you used a more primitive type for test, your object would truly be a MovieClip:
var test:Object = makeSprite();
trace(test); // [object MovieClip]
Casting a MovieClip to a Sprite doesn't strip it of its MovieClip implementation; it simply tells the calling code (in this case, this.addChild()) that "this object is a Sprite; please treat it as such." The calling code doesn't know — or care — that it's really a MovieClip, because as long as it's a DisplayObject (which a Sprite, and in turn a MovieClip, derives from), the addChild() method will happily accept it.
Since a MovieClip is a Sprite anyway, it doesn't make any difference to the calling code. As for the Apple object itself, though, it's still a MovieClip at heart, and so will continue to function on its own like one.
As an analogy, think of going to the grocery store, buying some goods, and paying at the counter. You're a person with many different roles in various aspects of life, but as far as the cashier is concerned, you're just a customer making a purchase. That doesn't make you any less of a person, but the cashier doesn't have to be interested in who you are or what you do, beyond just a customer making a purchase.
TL;DR:
Your MovieClip will stay a MovieClip, but the reference that is down casted to a Sprite will be able to access only methods and variables that are available to Sprite.

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

hitTestObject, stopDrag stops drag on two movieclips even though function states one movieclip to stop drag

I have a function that states when movieclip1 is dragged and hits a line then it stops the drag, however it seems to stop the entire drag function in the swf on the other movieclips even though they arent called in the function. Can somebody please help me with this.
Regards
T
Here is the code:
function hitTest(event:Event):void
{
if (movieclip1.hitTestObject(line))
{
movieclip1.stopDrag();
}
else
{
}
}
Are you absolutely positive you only have one instance of movieclip1 on your stage? Definitely double check. Are you creating them dynamically, or are they preloaded when your SWF loads?
If they're preloaded:
Perhaps during testing you made some quick copies of it, and now those copies have the same name and they're all responding the same. That's my first guess.
If they're loaded dynamically:
Check the function where they're being created. If you're naming them in a loop (with a number on the end like the above), be sure that you're properly increasing the numeric value used on the end.

symbols placed on the timeline become undefined if stepping backwards

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