views:

445

answers:

5

I notice in older version of flash you can create an instance of a dynamic class. I am creating a game that will have many different classes that can be displayed on the stage but can very from time to time. How would I go about calling them dynamically. for example

var newObject = new ["DynamicObject"]();

??

Is this possible in As3 ??

+1  A: 

Occasionally, I still make calls along the lines of:

parent["MovieClipName"].someMethod();

from time to time. So I imagine your code above will work.

However, whenever I find myself using that syntax, it usually means I need to re-think my approach and use a better, more object-oriented solution.

So I would suggest that maybe what you really need to do is re-think the factors that are leading you to attempt to use the solution above. Chances are you can reach the same goal in a more object-oriented way.

Perhaps by using inheritance? Could all of your objects extend from some common parent Object?

Perhaps by using an interface? If it doesn't make sense for your objects to share the same parent, maybe they all can agree to share some key functions?

Maybe code along the lines of:

var tank:Enemy = new Tank();
var soldier:Enemy = new Soldier();

soldier.attack(target);
tank.attack(target);

Wnere the tank and soldier classes both extend from (or implement) Enemy like this

public class Tank extends Enemy

or like this

public class Tank implements Enemy

and the Enemy class/interface contains the attack method

gmale
I see where you getting at. I should use that. But looking at my code, I surely didnt design it to be used that way. Im creating a game like BreakOut. Where you must hit the blocks with the ball. I gave the brick class the repsponsiblity of detecting the collision with the ball, removing itself when it is hit. putting points to the score board before doing so. all I have to do is call on it. But there will be other classes that inherit from it. stronger bricks that take more hits before it removes itself. Of course I will over ride those methods.But what about when it comes time to adding them
numerical25
In order for me to add those new bricks, I could either create another method that generates those different types of bricks, plus the old ones. but that would be tedious. So I would use the same method to generate different types of bricks depending on the values given in my array. so say array number 4 is a new brick.How would I would I use interfaces to supply that different type of brick or any type of brick, given the value from the array ?
numerical25
Well, first, I typed in what you had above into Flex Builder and it compiled. So my guess is that it will work. So you can probably go with that.However, I would suggest taking a close look at your code to see if there's a clean way to redesign it so you don't have to instantiate objects based on the contents of an array. There are probably a couple fundamental, high-level design changes you could make to clean things up so it works without having to do that.Or not... only a deep understanding of what you're doing can answer that. If it turns out that you need the array then...
gmale
... the solution I would use (without knowing much about what you're doing) is exactly what you suggested. To have some sort of factory method that builds the object I need, based on the parameters I send it or the state of the application.For example a method that returns red bricks as long as certain conditions are true (like current row of bricks <= 3) and then blue bricks when things change.
gmale
as in BrickBuilder.createBrick(currentLevel);
gmale
I tried doing it like var newObject = new ["DynamicObject"]();It doesnt work
numerical25
I will look into it. thanks
numerical25
Are the bricks really different classes? From what I understand so far it is more like the bricks have different values in their properties (like scoreWhenHit) and different states like isNew or isHit. Or do they really have different behavior?
Stefan
they for the most part have same behaviors. but some are stronger bricks then others. Honestly, I could bound all the different types of bricks to one class. But for learning purposes and good practice, I would rather they both be seperate classes. But it looks like that wont be happening. for there is no reasonable way to make my subclasses loosely typed. Even if I did do gmale way of making a factory class. That method that gets the specified class would only be limited to 1 return type. What i am looking for is true polymoprhism. does as3 have this ?
numerical25
A: 

Interfaces is good. I am using an array that maps out how the bricks will be put down on the stage

 mapArry =  [
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]], 
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]],
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]],
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]],
      ];

1 specifies brick type Number 1. but what if the next map has brick type 2 or 3

 mapArry =  [
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]], 
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]],
      [[1],[1],[1],[1],[1],[1],[1],[1],[1],[1]],
      [[2],[2],[2],[2],[2],[2],[2],[2],[2],[2]],
      ];

Again, I loop through the array, getting each brick. how would I use interfaces to supply the given brick ?.

The only answer I could think of is the create a brickManager class. a class that identifies each type of brick and retrieves them. But I am trying to be more efficient. I would like to know if there is a better way.

Only thing i could think of is to use dynamic initiations. I didnt know that approach went against oop.

var newVar = new ["brick"+i]();
numerical25
A: 

Yes, like I said, to answer your original question, what you have above will work in AS3 as it did in older versions of AS. So if it's not a big deal, you can go with it.

Overall, I wouldn't say dynamic initialization goes against OOP, per se. However, it just feels a little like a hack. Everytime I start to use something like that, I almost always find a design change that cleanly fixes the problem.

Of course it's hard to spot that change without knowing a LOT about your application. To take a stab at it:

Do you even need different classes for each brick?

Could you just set some property that ultimately changes it's behavior (like this.strength = 2)?

Then, while iterating through the array you do something like

for(var brickType:int in brickArray) {
    addBrickToScreenOrWhatever( new Brick(brickType) );
}

Where the constructor Brick(int) creates a new Brick with a strength equal to that of the parameter.

gmale
+2  A: 

I think there are 2 ways you can do that:

1.Using ApplicationDomain.getDefinition('DynamicTemplate')

something like:

var DynamicClass:Class = this.loaderInfo.applicationDomain.getDefinition('DynamicTemplate') as Class;
addChild(new DynamicClass);

You would need to do this when you file has INITialized.

2.Using getDefinitionByName() :

var DynamicClass:Class = flash.utils.getDefinitionByName('DynamicTemplate') as Class;
addChild(new DynamicClass);

If you need a way to get Class names to create new instances of objects, you could use describeType() or go through the instance's constructor, but I reckon you would know your classes anyway.

var TemplateObj:Class = flash.utils.getDefinitionByName(describeType(yourIntance).@name) as Class;
var newObj = new TemplateObj();
var newObj2 = new yourIntance.constructor();

Hope this help, George

George Profenza
This sounds like a really good solution. and As gmale was saying, I could even encapsulate this into a factory. Or even create a Interface like so var DynamicClass:IClass = flash.utils.getDefinitionByName('DynamicTemplate') as IClass; Thanks for the tip. One more thing. what library do I have to have to use it. Or what class uses this methodss. I just tried the first one and got a method is null error. Im guessing the second one falls under the flash flash.utils library. Thanks!!!
numerical25
flash.utils.getDefinitionByName('PutAnyClassNameHere') returns an Object that you cast to Class then create instances of it. If you got null, that means there is no visible class with the name(string) passed as an argument to getDefinitionByName(). Lots of handy functions in flash.utils.* btw.
George Profenza
as for the second one , I am having issues with that one. I put the code in.
numerical25
Although I have figure it out. I am not sure how I can be broad with the classes I pick, when I have to cast the object into a specific type of class. Yes all my classes are going to be subclasses. But if I cast a sub class into a parent class varible. will i be able to still call the sub class methods. how do I go about doing this. since this is a new issue. I will probably open another post.
numerical25
Learn something new every day - I didn't know about describeType(). That totally just made the "is" keyword obsolete. Thanks, george!
Myk
@Myk Out of curiosity, why would describeType() make "is" obsolete ?
George Profenza
A: 

I think I am going to go with george's solution. Except I am having a little issue with it. I was trying to do the second example. my code is below. When I call the class directly it works, but when i call by definition. doesnt work. any solution

package com.objects {

    import flash.display.Sprite;
    import flash.events.*;
    import flash.display.Stage;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.text.AntiAliasType;
    import flash.utils.*;

    public class Engine extends Sprite {
        private var pad:Paddle;
        private var sRef:Stage;
        private var ball:Ball;
        private var bricks:BrickMap;
        private var brickHolder:Array;
        public var numberOfBricks:Number = 0;
        public var scoreBoard:TextField;
        public var score:Number = 0;
        private var level = 1;
        private var ready:Ready;

        public function Engine(stageRef:Stage):void
        {
            addHud();

            brickHolder = new Array();
            sRef = stageRef;
            pad = new Paddle();
            pad.x = sRef.stageWidth/2;
            pad.y = 550;
            ball = new Ball();
            ready = new Ready();
            ready.x = sRef.stageWidth/2;
            ready.y = sRef.stageHeight/2;

            ready.addEventListener(MouseEvent.CLICK,gameStart);
            addChild(ready);
            bricks = new BrickMap();

            generateMap();          
        }

        private function generateMap():void
        {
            var mapW:Number = bricks.mapArry[0].length;
            var mapH:Number = bricks.mapArry.length;
            for(var y = 0; y < mapH; y++)
            {
                brickHolder[y] = new Array();
                for(var x = 0; x < mapW; x++)
                {
                    var classRef = getDefinitionByName('Brick2') as Class;
                var brick:Brick2 = Brick2(new classRef());
                    brick.name = x+""+y;
                    brick.getBall(ball);
                    brick.getEngine(this);
                    brick.x = x * brick.bWidth + brick.bWidth;
                    brick.y = y * brick.bHeight + 100;
                    numberOfBricks += 1;
                    addChild(brick);

                }
            }
        }//End Generate Map
       }
}

I edited the above to

var classRef = getDefinitionByName('Brick2') as Class;
var brick:Brick2 = Brick2(new classRef());

also change import to

import flash.utils.*;
numerical25
Do import flash.utils.*; to make things easierDo you have a class called Brick2 ?If so you would either do var classRef = getDefinitionByName('Brick2') as Classvar brick:Brick2 = Brick2(new classRef());I don't understand where the brick2(lowercase b) comes from? Do you have a class called brick2 ?The whole advantage in using getDefinintionByName() is you can access classes dynamically, say you have 20 different Brick classes, you would have something like:for(var i:int = 0 ; i < 20 ; i++) {var Brick:Class = getDefinitionByName('Brick'+i) as Class;var brick:MovieClip = new Brick();}
George Profenza
The class I am trying to access is Brick2. I have Brick and Brick2. But in this case, I was trying to get to Brick2. there is no brick. that was a typo. but now i got a new error. I updated the code above. i get this error.... ReferenceError: Error #1065: Variable Brick2 is not defined.
numerical25
I notice i have to cast it into a specific class. So I will probably have to set up an interface to get their methods to work correctly. Am right ??
numerical25
I think i fixed it.Instead of passing 'Brick2' into the parameter, I put the full path. com.objects.Brick2 !
numerical25