ActionScript Basic Question - actionscript-3

i've only ever created external .as files that extended a class such as sprite. now i just want to create one that doesn't extend anything and call it from a frame script.
package
{
public class Test
{
public function Test(val:Number, max:Number)
{
trace(val, max);
}
}
}
from my frame script of an .fla that is in the same folder as Test.as, i'll write this:
Test(50, 100);
this produces the following error:
1137: Incorrect number of arguments. Expected no more than 1.

Your code will be interpreted as cast to Test. It makes no sense to cast 2 numbers as a Test object.
What you want is an instance (an object) of the class Test.
For this, you need the new operator.
var testInstance:Test = new Test(50,100);
Then, you can use your object as needed, for example, calling methods, setting or getting values, etc.
testInstance.someMethod("hello");
testInstance.someNumber = 10;
var n:Number = testInstance.someNumber;
// etc...

Related

Can I still create Global variables in AS3

Following the answer here, I have created a file called MyGlobals.as and placed some global variables and functions so that I can access it from anywhere within my project just like AS3 buil-in functions such as trace() method.
This is MyGlobals.as which is located in the src folder (top level folder)
package {
public var MessageQueue:Array = new Array();
public var main:Main;
public var BOOKING_STATUS_DATA:Object;
public function postMessage(msg:Object):void {
MessageQueue.push(msg);
}
public function processMessage():void {
var msg:Object = MessageQueue.pop();
if (msg) {
switch (msg.type) {
}
}
}
Looks like my IDE (FD4) is also recognizing all these functions and variables and also highlighting the varibles and functions just like any other built-in global functions. However, I am getting compilation errors "Accessing possibly undefined variable xxx". The code is as simple as trace(MessageQueue) inside my Main (or another classe).
I am wondering if there was any change Adboe has done recently that it can't be done now or am I missing something? I am not sure if I need to give any special instructions to FD to include this MyGlobals.as?
I am using FD4, Flex SKD 3.1, FP12.0
I am aware of the best practices which suggests to avoid using this type of method for creating global variables but I really need it for my project for my comfort which I feel best way (right now) when compared to take any other path which involves daunting task of code refactoring. I just want do something which can be done in AS3 which I guess is not a hack.
I've done some playing around; it looks like you can only define one (1) property or method at package level per .as file. It must be the same name (case-sensitive) as the .as file it is contained in.
So no, nothing has changed since the older Flash Versions.
In your case that would mean you need five separate ActionScript files along the lines of:
MessageQueue.as:
package
{
public var MessageQueue:Array;
}
main.as:
package
{
public var main:Main;
}
...etc. As you can see this is very cumbersome, another downside to the many others when using this approach. I suggest using the singleton pattern in this scenario instead.
package{
public class Singleton{
private static var _instance:Singleton=null;
private var _score:Number=0;
public function Singleton(e:SingletonEnforcer){
trace(‘new instance of singleton created’);
}
public static function getInstance():Singleton{
if(_instance==null){
_instance=new Singleton(new SingletonEnforcer());
}
return _instance;
}
public function get score():Number{
return _score;
}
public function set score(newScore:Number):void{
_score=newScore;
}
}
}
then iin your any as3 class if you import the singleton class
import Singleton
thn where u need to update the global var_score
use for example
var s:Singleton=Singleton.getInstance();
s.score=50;
trace(s.score);
same thing to display the 50 from another class
var wawa:Singleton=Singleton.getInstance();
trace(wawa.score)

ActionScript 3 - use [brackets] instead of getChildByName

I have a MovieClip inside library, linkaged to MyObject and it contains a textField.
I don't know how I can access this textField without using the getChildByName method.
Apparently, the 3rd section works when object is on stage (without using addChild). But when using addChild I think there has to be some kind of casting; which I don't know how.
var childElement: MyObject = new MyObject();
childElement.name = "theChildElement";
container.addChild(childElement);
btn.addEventListener(MouseEvent.CLICK, changeText);
function changeText(event: MouseEvent): void
{
var targetBox:MovieClip = container.getChildByName(childElement.name) as MovieClip;
targetBox.textField.text = "hello"; // THIS WORKS
// This works too:
// MovieClip(container.getChildByName("theChildElement"))["textField"].text = "hello"; // THIS WORKS TOO.
// THIS DOESN'T WORK. why?
// container["theChildElement"]["textField"].text = "hello";
}
As confusing as it may seem, instance name, and name are not the same. From your code you should always be able to get to your MC by it's variable name. To get your last like to work you could just use this.
childElement["textField"].text = "hello";
There is a difference between Symbols created by the Flash IDE, which aggregate other DisplayObjects and programmatically created DisplayObjects.
When a DisplayObject is created in the Flash IDE, it's instance name can be used to resolve the instance as a property - which means it can be accessed via []. The [] can be used to access properties or keys of dynamic declared classes - like MovieClip. This necessary because you'll most likely down cast to MovieClip instead of using the symbol class created by Flash. That is not possible when simply using addChild, addChildAt or setChildAt from the DisplayObjectContainer API.
It is always the save way to access it via getChildByNameand check for null because otherwise your app, website or whatever is doomed for 1009 errors as soon as someone is changing the symbols.
I'd create a bunch of helper methods, like
// not tested
function getChildIn(parent:DisplayObjectContainer, names:Array):DisplayObject {
var child:DisplayObject, name:String;
while (names.length > 0) {
name = names.shift();
child = parent.getChildByName(name);
if (!child) {
// log it
return null;
}
if (names.length == 0) {
return child;
}
}
// log it
return null;
}
function getTextFieldIn(parent:DisplayObjectContainer, names:Array):TextField {
return getChildIn(parent, names) as TextField;
}
function getMovieClipIn(parent:DisplayObjectContainer, names:Array):MovieClip {
return getChildIn(parent, names) as MovieClip;
}
Your third method doesn't work because you are trying to call the ChildElement by it's name
without using getChildByName method. On the other hand, you shouldn't call your textField textField, because that's already an actionScript property.
Your should rather call it 'displayText' for example.
For a textField called 'displayText' contained in childElement :
function changeText(event:MouseEvent): void
{
childElement.displayText.text = "hello";
}

How to choose which child class to instantiate dynamically

My current project is in as3, but this is something I am curious about for other languages as well.
I'm attempting to use a factory object to create the appropriate object dynamically. My LevelFactory has a static method that returns a new instance of the level number provided to the method. In the code calling that method, I am able to dynamically create the buttons to call the levels like so:
for (var i:int = 1; i < 4; i++) {
var tempbutton:Sprite = createButton("Level " + i, 25, 25 +(60 * i), start(i));
_buttons.push(button);
}
This code just creates a simple button with the given arguments (ButtonText, x, y, function). It's working fine. The buttons are created, and clicking on one of them calls this method with the appropriate argument
private function start(level:int):Function {
return function(e:MouseEvent):void {
disableButtons();
newLevel = LevelFactory.createLevel(level);
addChild(newLevel);
}
}
This is all working fine; I'm just providing it for background context. The question I have is this: Is it possible to dynamically choose the type of object that my static function returns? Currently, I have am doing it as follows
public static function createLevel(level:int):Level {
var result:Level;
switch(level) {
case 1: result = new Level1(); break;
case 2: result = new Level2(); break;
//etc
}
return result;
}
I should note that all of these Level1, Level2, etc. classes extend my base level class. (Yay polymorphism!) What I would like to do is be able to do something along the lines of
public static function createLevel(level:int):Level {
var result:Level;
var levelType:String = "Level" + level;
return new levelType();
}
Obviously it's not going to work with a string like that, but is there any way to accomplish this in as3? What about other languages, such as Java or Python? Can you dynamically choose what type of child class to instantiate?
Update:
import Levels.*;
import flash.events.*;
import flash.utils.*;
public class LevelFactory
{
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.' + 'Level' + level) as Class;
var result:Level = new ref();
return result;
}
}
Update/Edit: getDefinitionByName seems to be what I'm looking for, but it has a problem. It seems that the compiler will strip unused imports, which means that unless I declare each subclass in the code ahead of time, this method will get a reference error. How can I get around the need to declare each class separately (which defeats the purpose of dynamic instantiation)?
Yes, you sure can, and it's very similar to the string thing that you've provided. The only thing that you are missing is the getDefinitionByName method: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()
You can generate whatever class name you want, and what this method does is that it searches for that class in it's namespace, and if it finds it - it returns it as a class:
var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
This piece of code will instantiate a Sprite. This way you can instantiate your classes without all those switches and cases, especially when you have to make a hundred levels :)
Hope that helps! Cheers!
Edit:
In your case, the code should be:
var ref:Class = getDefinitionByName('com.path.Level' + index) as Class;
var level:Level = new ref(); // it will actually be Level1 Class
Since Andrey didn't quite finish helping me out, I am writing up a more complete answer to the question after much research.
getDefinitionByName definitely has the use I am looking for. However, unlike its use in Java, you HAVE to have a hard reference to the class you want instantiated somewhere in your code. Merely imported the class is not enough; the reason for this is that the compiler will strip the reference from any unused import to save space. So if you import the package of classes you want to choose dynamically but don't have a hard reference to them, the compiler will de-reference them. This will lead to a run-time error when the program cannot find the appropriate reference to your class.
Note that you don't actually have to do anything with the reference. You just have to declare a reference so that it can be found at run-time. So the following code will work to eliminate the switch-case statement and allow me to dynamically declare which class I am using at run-time.
{
import Levels.*;
import flash.events.*;
import flash.utils.*;
/**
*
* Returns the requested level using the createLevel class
* ...
* #author Joshua Zollinger
*/
public class LevelFactory
{
Level1, Level2, Level3, Level4, Level5, Level6, Level7;
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.Level' + level) as Class;
var result:Level = new ref(); // it will actually be the correct class
return result;
}}}
The obvious downside to this is that you still have to have a hard-coded reference to every class that can be instantiated like this. In this case, if I try to create a Level8 instance, it will through a run-time error because Level8 is not referenced. So every time I create a new level, I still have to go add a reference to it; I can't just use the reference dynamically.
There are supposedly ways around this that I have not tested yet, such as putting the code for the classes in a separate SWF and importing the SWF at run-time or using outside libraries that will have different functionality. If anyone has a solid way to get a truly dynamic reference that doesn't require a hard coded reference anywhere, I would love to hear about it.
Of course, it's still a lot cleaner this way; I don't have a extensive switch case statement to pack all the levels. And it's easier and faster to add a reference to the list than creating a new case in a switch. Plus it is closer to dynamic programming, which is usually a good thing.

Value Will Set properly, but Get receives Null

So, I have successfully grabbed a value out of an XML document and set it into a separate class called "AddCommas." The trace functions have shown me that it sets properly.
For more details, my objective is to take the language indicator ("fr" for french or "en" for english), set it inside the appropriate class and into a variable I will use. Now, I am using this variable to be used in an if statement; which will help me format a number properly (commas, decimals, spaces) per the clients request.
However, my problem is when I try to get the value to use it. It always comes back as Null. I have placed traces all over my program trying to pinpoint when this happens, but I cannot find it. Here's the code...
The pull from the XML file and into the set (this works fine, but I am adding it for your benefit in case I missed something)
public var commaHold = new AddCommas();
localLanguage = xmlObj.localLanguage;
trace("localLanguage + " + localLanguage);
commaHold.setLanguage(localLanguage); // Set Language
//More code follows...
This is the set function istelf...
public function setLanguage(localLanguage:String){
langHold = localLanguage;
trace("Set Language = " + langHold); //This always shows a successful set
}
Now am I wrong in thinking that in AS3, once langHold in my AddCommas class has been set I should be able to use it without calling a get within the function I am using the If Statement in, right? Such as this?
var language = langHold;
if (language == "en"){
trace("Language is = " + language); // More code follows afterwards and as of now, this shows NULL
Now, I have attempted plenty of Get functions to add the language variable in the call itself to this function and it's always the same. Am I missing some fundamentals here?
Thank you very much for your time.
If you expect a string comparison you need to use quotes, unless en is a String variable since langHold is a String, like:
if (language == "en"){
Consider modifying the set function to use the as3 keyword like:
private var _language:String;
public function set language(value:String):void {
_language = value;
//do other stuff here if necessary, put a breakpoint on the line above
}
public function get language():String{
return _language;
//put a breakpoint on the line above
}
You should be able to see when any instance of your class has the property changed. The only other issue I can think of is it is not the same instance of the class and therefore doesn't share the property value you set earlier. In the debugger you can check the "hashCode" or "address" it shows for this to see if it changes when it hits the breakpoints.
Here's a sample Singleton structure in AS3 (this all goes in one file):
package com.shaunhusain.singletonExample
{
public class SingletonExample
{
private static var instance:SingletonExample;
public static function getIntance():SingletonExample
{
if( instance == null ) instance = new SingletonExample( new SingletonEnforcer() );
return instance;
}
/**
*
* #param se Blocks creation of new managers instead use static method getInstance
*/
public function SingletonExample(se:SingletonEnforcer)
{
}
}
}
internal class SingletonEnforcer {public function SingletonEnforcer(){}}
using this single shared instance from any other class would look something like this:
private var singletonInstance:SingletonExample = SingletonExample.getInstance();
ShaunHusain's theory of using a Singleton was the perfect solution I needed. However, his code gave me a bizarre 1061 error and my format and code appeared to be error free. Regardless, I looked up another way to use a Singleton as follows that worked perfectly for me. Honestly, Shaun's code should work for anyone and I have no idea why it wasn't. I am perfectly willing to admit that it was probably a typo on my end that I just did not see.
I ended up embedding the Set and Get within the Singletons class and used it as an intermediary to hold the information I needed. It worked perfectly.
package chart {
import chart.*;
//
public class StaticInstance {
private static var instance:StaticInstance;
private static var allowInstantiation:Boolean;
private var language:String;
public static function getInstance():StaticInstance {
if (instance == null) {
allowInstantiation = true;
instance = new StaticInstance();
allowInstantiation = false;
}
return instance;
}
public function StaticInstance():void {
if (!allowInstantiation) {
throw new Error("Error: Instantiation failed: Use StaticInsance.getInstance() instead of new.");
}
}
public function setLanguage(_language:String):void{
language = _language;
trace("language set = " + language);
}
public function getLanguage():String{
return language;
}
}
}
This code allowed me to hold the data and call upon it again from two different classes. It's a very hack job instead of just being able to pass on the variable from function to function, but in my case we didn't create this file, we are modifying it and attempting to do things beyond the original scope of the project.
Thanks again for your help Shaun! I hope this helps other people!

Library design quandary

Ok so I am writing an open source library. A section of this library deals with moving an entity in a two and three dimensional space so it will have functions that manipulate the rotation, position etc.
Now ideally I would like my library to work well with other libraries, in particular things like Papervision3D and other Flash 3D engines, but not forgotting basic image objects like a Sprite or Movieclip.
So this is my quandary. The functions that will manipulate the entity will also need to modify the values of the underlying model data (so either a Sprite, Papervision object etc). What is the best way to make my library flexible so that it can support multiple data models. Performance is also important aspect too.
Currently I am thinking of something like this:
//this is the public function that I expose in my library
public function rotate(val:Number,func:Function,objData:*):void
{
func(val,objData);
}
//example of a function that could be passed in
//this one will rotate a MovieClip
private function modelFunction1(rot:Number,objData:*):void
{
var myMov:MovieClip = objData as MovieClip;
myMov.rotation = rot;
}
//second example of a function that could be pass in
//this one will rotate a point
private function modelFunction2(rot:Number,objData:*):void
{
//yes I know this piece of code makes no sense :P
var p:Point = objData as Point;
p.x = Math.cos(rot);
p.y = Math.sin(rot);
}
so then it could be used like:
rotate(4,modelFunction2,myPoint)
//or
rotate(4,modelFunction1,mySprite);
I should add that in reality I, as the client code, won't be able to directly call the rotate function. Instead the rotate function that I want to pass in would need to be stored somewhere as a class member and then be called by the rotate function. Its just less code for me to write it out like above.
This to me seems quite flexible although the performance implications of casting and passing functions concerns me (but might be ok). Can anyone else suggest an alternative or is what I have the most logical solution. Thanks :)
I suggest the adapter pattern.
In your case you could define interfaces which offer type safe definitions for what your library expects instead of having function arguments.
then you need to write adapter classes which implement your librarys interfaces and wrap for instance a papervision object and delegate the function calls to your interface methods to the papervision object.
interface IRotatatable {
function rotate(deg : Number) : void
}
class YourLibraryClass {
public function rotate(r : IRotatatable, val : Number):void {
r.rotate(val)
}
}
class P3DAdapter implements IRotatable {
public function P3DAdapter(p3d : SomePaperVisionObject) {
_p3d = p3d;
}
public function rotate(r :Number):void {
p3d.rot = r;
}
}
function someClientCode():void {
var adapter : IRotatable = new P3DAdapter(p3d)
new SomeLibraryClass().rotate(adapter, val));
}