I found a quite strange problem while making two classes in AS3. Let's call them ParentClass and ChildClass. In order to make both of them you need a Sprite object, then the ParentClass makes it visible in the stage. ChildClass inherits the ParentClass, too.
ParentClass.as:
package myStudio.basic {
import flash.display.MovieClip;
import flash.display.Sprite;
public dynamic class ParentClass extends MovieClip {
public function ParentClass(mc:Sprite=null) {
addChild(mc);
}
}
}
ChildClass.as:
package myStudio.containers {
import myStudio.basic.ParentClass;
import flash.display.MovieClip;
import flash.display.Sprite;
public class ChildClass extends ParentClass {
public function ChildClass(mc:Sprite=null) {
addChild(mc);
}
}
}
Then, I write this code on Frame 1, Layer Actions of the FLA file:
var mc:MovieClip = new childMC;
var vig:ChildClass = new ChildClass(mc);
addChild(vig);
However, I got run-time error #2007:
TypeError: Error #2007: The value of the parameter child must not be null.
at flash.display::DisplayObjectContainer/addChild()
at myStudio.basic::ParentClass()
at myStudio.containers::ChildClass()
at myStudioComicAnimator_fla::MainTimeline/frame1()
I tried overriding the ChildClass constructor function, but it still doesn't work.
So here's my question: Is there another workaround to solve this problem?
The reason for that is that you are not calling super. You can check what's happening in the error stack (down to top):
you instantiate ChildClass, and you pass the previously created childMC to the constructor
ChildClass extends ParentClass, so when instantiated it always calls the constructor
the constructor of ParentClass tries to add something as a child
The problem is that you cannot add null as a child. But because the constructor is called internally, there is no param that is being passed to it. so mc variable is always null. But as we said - null cannot be added.
Use the super by yourself:
public function ChildClass(mc:Sprite=null) {
super(mc);
}
This way the ParentClass will get reference to the mc object and will be able to add it.
Another option is not to use addChild in the ParentClass, but only in ChildClass. Then it doesn't matter if you pass anything to super, or even if you are calling super at all.
Edit: I forgot to say that this is not a bug, but a standard behavior and works exactly like it should work. The reason for this is that each class can have a whole different override of the constructor. It can take more or less parameters, so the chain for calling parent's constructor is your job to handle.
Related
So im trying to understand how I can call a function from one class from another class. Im getting a few errors and am wondering if someone can explain what im doing wrong here.
Main file:
package code {
import flash.display.MovieClip;
import flash.events.*;
import code.*;
import code.functions.*;
public class Main extends MovieClip {
public var _playerHP:Number;
public var _enemyYellow:EnemyYellow;
public function Main() {
_enemyYellow = new EnemyYellow;
_playerHP = 10;
_playerHPdisplay.text = _playerHP.toString();
trace("loaded")
}
public function lowerHP ():void
{
_playerHP -= 1;
_playerHPdisplay.text = _playerHP.toString();
trace(_playerHP)
}
}
}
Second File:
package code.functions {
import flash.display.MovieClip;
import flash.events.*;
import code.Main;
public class EnemyYellow extends MovieClip {
public var _main:Main;
public function EnemyYellow() {
_main = new Main;
_main.lowerHP();
trace ("test")
}
}
}
It will then load with a blackscreen and the following error:
Error: Error #2136: The SWF file file:///test/Main.swf contains invalid data.
at code.functions::EnemyYellow()[test\code\functions\EnemyYellow.as:15]
at code::Main()[test\code\Main.as:16]
Error opening URL 'file:///test/Main.swf'
However, If I remove _enemyYellow = new EnemyYellow; from the Main file it loads but the second file is not loaded.
If I remove _main = new Main; from the Second file, the game again loads but it does not call the lower HP function, and I get the following error
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at code.functions::EnemyYellow()[test\code\functions\EnemyYellow.as:16]
at code::Main()[test\code\Main.as:16]
If anyone could help me it would be appreciated. Im just trying to get my head around how to call a function from another file..
_playerHPdisplay.text is also a text box on the stage when the game loads.
If you do not assign a value to _main, it is null. That's why you receive the #1009 if you do not assign new Main() to it.
However, you do not want to create a new Main object either.
The main class represents the application and generally speaking you do no explicitly instantiate it in your project.
To make your code work, you have to pass a reference of Main to the enemy class.
A better approach to this is to let the enemy class dispatch events, so that the Main class can be notified "some damage was dealt". This however will not work from within the constructor of enemy.
Think about whether your package names make sense. Pretty much all packages contain code, which makes "code" a not very informative name. The package "functions" contains the class EnemyYellow, which doesn't seem to be a good fit.
I have a class which extends MovieClip. This class has an update() function which needs to be called every new frame with the deltaTime in the arguments. This works if the class has been declared but not if it has just been added to the display list.
Code in the main class:
package packageFoo{
import flash.display.MovieClip;
import packageFoo.customMovieclip;
public class Main extends MovieClip{
public function Main():void{
var testMc:customMovieClip = new customMovieClip();
addChild(testMc);
testMc.update(dt);
}
}
}
This outputs the correct values where as if I just added it without referencing it:
package packageFoo{
import flash.display.MovieClip;
import packageFoo.customMovieclip;
public class Main extends MovieClip{
public function Main():void{
addChild(new customMovieclip());
this.getChildAt(0).update(dt);
}
}
}
This makes the compile time error: 1061: Call to a possibly undefined method update through a reference with static type flash.display:DisplayObject.
I can't really reference the 'customMovieclip's because I am wanting multiple ones.
It looks like this.getChildAt(0) is not customMovieClip. This can arise if your Main has pre-places components at design time. To check, do trace(this.numChildren) as the first line of Main() constructor. And also, to address any subclass methods properly, you need to typecast your DisplayObject returned by getChildAt() to a proper type.
(this.getChildAt(0) as customMovieClip).update(dt);
Still, using a class-wide variable is better if you want to address that custom MC in more than one function of main class.
If you're trying to avoid a reference to the custom class in the document class, you can call it like this:
this.getChildAt(0)["update"](dt);
THE SOLUTION!
Ensure that you're not mistakenly importing the wrong class.
In my case, I was explicitly importing TabButton, but there was
another TabButton class that was being imported because it existed in
the same directory as TabButtonSubclass. This happened because
TabButtonSubclass exists in a folder specific to one application. This
folder also contains a horribly generically named class, TabButton,
which is completely unrelated and incompatible to the other TabButton
class. The application-specific TabButton class was renamed so it's
obvious it belongs to that application.
Had I written 'public class MBTabButtonSprited extends [specific
location].TabButton', it would have worked fine. But it seems that
since 'extends TabButton' was not prefaced by a specific path, the
compiler defaulted to the local path.
I've been banging my head on my desk for a while now, trying to figure this one out.
My issue is that I've extended Sprite to create a TabButton class. I then extended the TabButton class (we'll call it TabButtonSubclass) for some additional functionality. When I try to cast an instance of TabButtonSubclass as TabButton, I get null.
The bizarre part though, is that I can cast an instance of TabButton or TabButtonSubclass as a Sprite, and it works fine.
Here is what my TabButton looks like:
import flash.display.Sprite;
public class TabButton extends Sprite {
public function TabButton():void {
super();
// code
}
}
And here is what my TabButtonSubclass looks like:
import ...TabButton;
public class TabButtonSubclass extends TabButton {
public function TabButtonSubclass( bitmap:BitmapData ):void {
// code
super();
}
}
To clarify the TabButtonSubclass class: The purpose of extending the TabButton was so that you could instantiate a TabButtonSubclass with a supplied Bitmap. This Bitmap would be used for various purposes. I have extended classes in the past and added parameters to the constructor, and it worked fine. Is this not a good idea?
Some sample code that illustrates my issue:
import flash.display.Sprite;
import ...TabButton;
import ...TabButtonSubclass;
var btn1:TabButton = new TabButton();
trace( "btn1:", btn1 ); // btn1: [object TabButton]
trace( "btn1 as Sprite:", (btn1 as Sprite) ); // btn1 as Sprite: [object TabButton]
var btn2:TabButtonSubclass = new TabButtonSubclass( new GangTabMail() );
trace( "btn2:", (btn2) ); // btn2: [object TabButtonSubclass]
trace( "btn2 as TabButton:", (btn2 as TabButton) ); // btn2 as TabButton: null
trace( "btn2 as Sprite:", (btn2 as Sprite) ); // btn2 as Sprite: [object TabButtonSubclass]
As you can see, I'm able to cast btn1 as Sprite with no issues. I can also cast btn2 as a Sprite, and it returns the expected results. However, if I try casting an instance of TabButtonSubclass as TabButton, it returns null.
I'm interested in casting an instance of TabButtonSubclass as TabButton because the application utilizes a couple of classes with that require objects of type TabButton to be passed.
Here's an example of some code that allows you to type a class as a super class:
import ...TabButton;
import flash.display.Sprite;
var myBtn:TabButton = new TabButton();
doStuff( myBtn ); // btn: [object TabButton]
function doStuff( btn:Sprite ):void {
trace( "btn:", btn );
}
Here's an example of the code failing if you go one level deeper:
import ...TabButton;
import ...TabButtonSubclass;
import flash.display.Sprite;
var myBtn:TabButtonSubclass = new TabButtonSubclass( new BitmapData( ... ) );
doStuff( myBtn ); // Returns error: Implicit coercion of a value of type ...:TabButtonSubclass to an unrelated type ...:TabButton
doStuff( myBtn as TabButton ); // btn: null
function doStuff( btn:TabButton ):void {
trace( "btn:", btn );
}
Can anybody offer any help as to why I get a null response when I attempt to trace it out? Or why I get that error? Am I missing something in regards to subclasses and type casting?
I'm not sure why it would return null, but the way I would handle it would be to rather define the variable using the base class and cast up when needed, eg.
import ...TabButton;
import ...TabButtonSubclass;
import flash.display.Sprite;
var myBtn:TabButton = new TabButtonSubclass( new BitmapData( ... ) );
doStuff( myBtn ); // No need to case here since myBtn is already defined as TabButton
function doStuff( btn:TabButton ):void {
trace( "btn:", btn );
}
Then if you need TabButtonSubclass functionality you cast it up:
var myBtnSub:TabButtonSubclass = myBtn as TabButtonSubclass;
Your code should work perfectly. However this is not what other call the "best practice" when you declare a type of a function parameter as a Class. You should have interface for your TabButton.
You can pass tons of different objects to your function with one exception: objects must have all methods implemented, that your interface has. That's it. See Polymorphism
One possible answer for your question is:
public class TabButtonSubclass extends TabButton {
the TabButton above and the TabButton below are different classes
trace( "btn2 as TabButton:", (btn2 as TabButton) ); // btn2 as TabButton: null
A key difference between casting and the as operator is the behavior on failure. When a cast fails, a TypeError is thrown. With the as operator whenever a cast fails the default value for the datatype is returned. read more...
I'm not an expert in AS3 and still learning, but perhaps this can help:
In your last code snippet, when you do
doStuff( myBtn );
and
doStuff( myBtn as TabButton);
what you are doing there is you are passing a variable with type TabButtonSubclass when the function is expecting a variable with a type of TabButton. So that's why you get the first error about implicit coercion.
Now the reason why you get a null if you try to cast your variable as TabButton when passing it into the function, I'm not really sure but I think it has to do with that if a cast can't be "honored" (i.e, you tell the compiler that the cast is valid, but it turns out that it actually is not, like if you tried to cast XML data as a movie clip, for example), then the result will be a null.
Hope that helps, but I could be totally wrong!
Ensure that you're not mistakenly importing the wrong class.
In my case, I was explicitly importing TabButton, but there was another TabButton class that was being imported because it existed in the same directory as TabButtonSubclass. This happened because TabButtonSubclass exists in a folder specific to one application. This folder also contains a horribly generically named class, TabButton, which is completely unrelated and incompatible to the other TabButton class. The application-specific TabButton class was renamed so it's obvious it belongs to that application.
Had I written 'public class MBTabButtonSprited extends [specific location]. TabButton', it would have worked fine. But it seems that since 'extends TabButton' was not prefaced by a specific path, the compiler defaulted to the local path.
I am trying to call a function which is set inside a class..
How can I call that?
Here is my source, and I would like to call 'processLogin' from outside this class.
Link to source: http://pastebin.com/aFygyXKZ
You can create a new instance of your class main.
Try
var m:main = new main();
m.processLogin();
Also, AS3 best practices state that classes should begin with an Uppercase letter.
You also should extend Sprite instead of MovieClip for DisplayObject classes that do not need timeline functionality.
If you know you are only going to have one instance of the class main in your application, what you can do is:
Implement class main as a singleton class, in which case you
can access the processLogin method using
main.getInstance().processLogin or
Just add a public static
variable to your main class containing the instance of your main
class. In this instance your code would look something like:
package actions {
import flash.display.MovieClip;
import flash.events.*;
import flash.net.*;
import flash.text.*;
public static var instance:main;
public function main(){
instance = this;
}
//The rest of your main class code...
}
That way, you can access your processLogin function using main.instance.processLogin().
However, if your application is set to possibly have more than one instance of your main class, then the best approach would be to instantiate main and use that instance, as f-a suggested.
I have an AS3 class on my Flex project:
package system
{
import mx.managers.PopUpManager;
import ui.Eula;
public class Dialogs
{
public function Dialogs(){}
public static function showEula():void {
var eulaWindow:Eula = new Eula;
PopUpManager.addPopUp(eulaWindow,MyMainMXML,true);
}
}
}
MyMainMXML is my base MXML file. It won't let me reference to it via my class. How do I do that? The compiler error goes as follows:
1067: Implicit coercion of a value of type Class to an unrelated type flash.display:DisplayObject.
The main MXML file is a spark WindowedApplication so I assumed it's part of the DisplayObjects.
EDIT:
I tried using FlexGlobals like the one below but it gives off an error that says 1118: Implicit coercion of a value with static type Object to a possibly unrelated type flash.display:DisplayObject.
package system
{
import mx.core.FlexGlobals;
import mx.managers.PopUpManager;
import ui.Eula;
public class Dialogs
{
public function Dialogs(){}
public static function showEula():void {
var eulaWindow:Eula = new Eula;
PopUpManager.addPopUp(eulaWindow,FlexGlobals.topLevelApplication,true);
}
}
}
Using FlexGlobals.topLevelApplication returns you an object of type Object (yeah I know, that sounds redoundant :P). However addPopUp 2nd parameter if a DisplayObject. Hence, this should do the trick :
PopUpManager.addPopUp(eulaWindow,FlexGlobals.topLevelApplication as DisplayObject,true);
I'm not 100% sure about why FlexGlobals.topLevelApplication does not return a DisplayObject, might be a low-level issue.
You can got main application refference from
FlexGlobals.topLevelApplication
mx.core.FlexGlobals.topLevelApplication: The top-level application object, regardless of where in the document tree your object executes. This object is of type spark.components.Application or mx.core.Application.