Accessing Variables in a sub ccb-file cocos2d-x - cocos2d-x

How to access the member variables defined in sub-ccb, from a custom layer class for the main "ccb" file (cocos2d-x) ?
Here is the base scenario for this question,
I have a CCB file named "ladybirds_page.ccb" and associate custom loader class as follows (root is a CCLayer)
ladybirds_pageLayerLoader.h
ladybirds_pageLayer.h
ladybirds_pageLayer.cpp
and i have another CCB file named "ladybird.ccb" and associate custom loader class as follows (root is a CCNode)
ladybirdLoader.h
ladybird.h
ladybird.cpp
"ladybirds_page.ccb" file contains several instance of "ladybird.ccb". And they are associated with relevant member variables (mLadyBird_1,mLadyBird_2,..)
and "ladybird.ccb" it self contains several CCSprites associated with member variable (mDotSprite_1, mDotSprite_2,...)
i want to get the access for these sub-ccb(ladyBird)'s member variable for each instance of it. Like
mLadyBird_1->mDotSprite_1
mLadyBird_1->mDotSprite_2
Here is the my coding approach for this so far,
Create a method names setNumber(int num) in ladyBird.cpp as follows
void ladybird::setNumber(int num)
{
.......
.........
mDotSprite_1->setVisible(false);
mDotSprite_1->setVisible(false);
......
}
mDotSprite_1 and mDotSprite_1 were assigned using CCB_MEMBERVARIABLEASSIGNER_GLUE macro
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "mDotSprite_1", CCSprite *, this->mDotSprite_1);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "mDotSprite_1", CCSprite *, this->mDotSprite_1);
loading ladybirds_page.ccb file with its custom loader class
void intro_pageLayer::loadNextPage()
{
/* Create an autorelease CCNodeLoaderLibrary. */
CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::newDefaultCCNodeLoaderLibrary();
ccNodeLoaderLibrary->registerCCNodeLoader("ladybird", ladybirdLoader::loader());
ccNodeLoaderLibrary->registerCCNodeLoader("ladybirds_pageLayer", ladybirds_pageLayerLoader::loader());
/* Create an autorelease CCBReader. */
cocos2d::extension::CCBReader * ccbReader = new cocos2d::extension::CCBReader(ccNodeLoaderLibrary);
/* Read a ccbi file. */
CCNode * node = ccbReader->readNodeGraphFromFile("ccb/ladybirds_page.ccbi", this);
ccbReader->release();
CCScene * scene = CCScene::create();
if(node != NULL) {
scene->addChild(node);
}
/* Push the new scene with a fancy transition. */
CCDirector::sharedDirector()->pushScene(CCTransitionPageTurn::create(0.5f, scene, false));
}
assign member variable for instance of ladyBirds in ladybirds_pageLayer.cpp
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "mLadyBird_1", ladybird *, this->mLadyBird_1);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "mLadyBird_2", ladybird *, this->mLadyBird_2);
and finally, try to call a method setNumber() on mLadyBird_1 and mLadyBird_2 as follows
mLadyBird_1->setNumber(1);
mLadyBird_2->setNumber(2);
i got EXC_BAD_ACCESS on
mDotSprite_1->setVisible(false);
Obviously, my scenario was not implemented in my code. So can anybody please enlighten me about the correct way of doing this ?
Thank You.

I have the same problem loading CCSprite through
CCB_MEMBERVARIABLEASSIGNER_GLUE
macro.
I solved it using
CCB_MEMBERVARIABLEASSIGNER_GLUE_WEAK
macro.
Hope that helps.
EDIT
A better solution is use however
CCB_MEMBERVARIABLEASSIGNER_GLUE
and initialize to NULL the variable
variableName = NULL;
and gently release into destructor
CC_SAFE_RELEASE(variableName);

Related

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.

movie clip class parameters ane null

I have a movie clip with an external class attached.
here is the MC code (I've shorten it only for the relevant part...)
package {
//all the imports here...
public class mc_masterChapter extends MovieClip {
public function mc_masterChapter() {
trace (picFile,strChapTitle);
}
//Properties
public var picFile:String;
public var strChapTitle:String;
}
}
In the main class file I'm adding this object to stage using addChild:
var masterChapter:mc_masterChapter = new mc_masterChapter;
masterChapter.picFile = "pic_Chap1.jpg";
masterChapter.strChapTitle = "ABCD:
addChildAt(masterChapter,1);
now, the trace in the MC class code gives nulls to both parametes but if i put a trace inside the MC timeline (instead of the attached class code), it gives the right value!
how can I access the values from the MC class itself without getting nuls?
Thank you.
It works! Let me explain:
var masterChapter:mc_masterChapter = new mc_masterChapter; // Calls class constuctor
// so calls trace() too!
// You will get null null
masterChapter.picFile = "pic_Chap1.jpg"; // Assign the variables
masterChapter.strChapTitle = "ABCD"; // so they can be read
trace(masterChapter.picFile, masterChapter.strChapTitle); // Should trace pic_Chap1.jpg ABCD
If you add the following method to your class:
public function test():void {
trace(picFile, strChapTitle);
}
Then call masterChapter.test() it will successfully trace those two properties. So yes, the class can read its properties.
Make the var you use in your main class public static vars.
OK!
I solved the mystery.
I put two traces. one in the main MC class saying "hey, I'm inside the MC - the picFile="
and one in the put Function saying "I'm putting this file into picFile:"
well this is what I've got:
hey, I'm inside the MC - the picFile=null
I'm putting this file into picFile:image.jpg
got it!?! at the moment I asked him to give birth to an instance of the MC (even before putting it on stage - just defining the object (with this line:)
var masterChapter:mc_masterChapter = new mc_masterChapter;
it allready run the class, so of course that in this stage the parameters were not defined allready and were null.
the definition code came right after that line (in the main.as)
masterChapter.pic="pic_Chap1.jpg";
so what I did, was to move all the code from the main class of the MC object to a public function inside the same package called init(). Then I called this function manually from the parent main class.
By that I can decide when to call it (after I declare all the parameters of course).
That's it.
god is hiding in the small details : )
tnx for all the helpers.
Possibly a better solution would be to use a getter/setter pair, so you can know at the exact moment the properties are set:
protected var _picFile:String:
public function get picFile():String {
return _picFile;
}
public function set picFile(value:String):void {
if (value != _picFile) {
_picFile=value;
trace('picFile set to', _picFile);
}
}

How can I give flash stage instances unique properties in Flash Professional to pass to AS3 script?

I've started building a rough game engine framework in Flash Professional and I'm curious how I can create objects in the Flash library that I'm able to drag onto the stage and assign properties that are accessible from AS3.
Example:
I want to create a switch object (e.g. a light switch), so that when the player interactes with it, it triggers something specific in code such as a light in the room turns on.
I understand that Flash has built in UI components that you can define properties within the Flash Professional environment (see image below), and I'm wondering if there's a way to create my own custom style components so that I can essentially have my level file open in flash (.fla) and then drag a switch component from my library, and type in some information such as what light it is controlling, and any other information I want.
(above is an example of the type of parameter control I'm looking for)
I've read a bit about extending the flash UIComponent class but I feel that that's not the right approach because it's overkill for what I want. All I want is to pass some basic parameters from a library stage instance into AS3. I do not want to pass data via the instance name because this seems very messy if I want to have more complex interaction.
Thanks!
I would create a "switch" movie clip and export it to actionscrip, same with a "light" movie clip. The in the main class .as file I would inset them into the stage, using addChild (clips) and then add a click listener to the "switch" movie clip to control the "light".
This can be easily done.
Component(s) are wrong approach in my opinion.
Firstly you would want to setup Actionscript linkage / label your Library item.
In Library Panel.
- Right Click on "yourMC" >> click "Properties".
- In Properties dialog Tick "Export for Action Script"
- Then Name your Class eg "yourMC_Class"
now MC is ready to be referenced in your code.
next you would want to Dynamically add your "yourMC" from library to stage.
which can be done like such.
// first reference library item
var yourMC_ref:yourMC_Class = new yourMC_Class();
// Then load dynamic mc item into var
var your_MC_OBJ = yourMC_ref;
// then add your MC to stage.
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
in a nutshell that's how I add library items to stage.
Obviously thats the basic function / code.
In a project I would have all code in an external class, in which case you would just set vars as public vars
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
and the last 3 lines of code into a public function
public function ADD_First_MC()
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
Now 'your_MC_OBJ' can be used in more complex ways.
eg. to create a light switch there are many options depending on how you need to approch functionality.
eg. Apply a different MC library item to "your_MC_OBJ"
play specific frame within MCs.
However If it was me I would just use mouse function to switch light on or off using addChild removeChild.
eg.
public var LightON = 0;
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
then create a public function that handles on / off events
public function LightON_OFF()
{
if(LightON == 1)
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
if(LightON == 0)
{
this.removeChild(your_MC_OBJ);
}
}
Hope this helps.
So, for what you want, while it may not be the best way to do what you want, I understand it's your experience you are constructing.
Use components, yes...in the following way (the most simple one):
Create a Movie Clip
Right-click it in library
Click on "Component Definitions"
Add a property, set a name, a variable name (var test, for this matter) and a default value
Click OK
Open your movie clip
Open code for the first frame and declare the variable without an initial value (var test:String;)
Trace it's value ( trace( test ); )
Go back to the stage root
Drag and drop the item from library to stage
Test it (Cmd/Ctrl + Enter) (maybe it will print null, dunno why, it ignores the default value sometimes)
Select your component on stage
Open the properties panel (Windows > Properties)
Go to Component Parameters on this panel and change the property value
You should see the value traced on console
And, I think, like this you can use properties from components for what you want, like using a String and getting the controlled mc by its name.
Good luck
I think what people are trying to say is that you can have the whole thing is data driven, and so you can combine the IDE with the data to come up with your final game.
But consider this ... it might be what you want.
If you have, for instance, a BaseSwitch Class:
public Class BaseSwitch extends MovieClip {
private var _lightName:String;
private var _light:Light;
public function get lightName():String {
return lightName;
}
public function set lightName(value:String):void {
if (value != _lightName) {
_lightnName = value;
//Note I don't advocate having children reach into their parents like this,
//but you sound like you don't want the parent involved in the process, so
//this is one way you could do it.
if (parent.hasOwnProperty(lightName) && parent[lightName] is Light) {
_light = parent[lightName];
} else {
trace('Could not find light', _lightName);
}
}
}
//other code to listen for gestures and operate the light
}
Now, when you want a switch to operate a specific light name, create a library instance and set its base class to BaseSwitch. When you close the dialog where you set the base Class, you'll notice that it gives you a dialogue that it couldn't find the Class in the Class path and one will be generated. You're going to replace it with a Class that sets the lightName. Create a new AS3 Class in the root directory with the same name as your library instance. It should look something like this:
public class SpecificSwitch {
public function SpecificSwitch() {
super();
lightName = 'theSwitch';
}
}
Other possible choices involve having the parent Class match up instances of switch with instances of light based on name, so if it finds a light1 and a light1Switch, it either gives a reference to the light to the switch or it simply sets up a mapping in its own event listening system.

Is there a way to clear out embedded Bitmap assets in AS3/AIR

first time posting on here.
I'm creating an AIR 3.0 app.
For a lot of my graphical assets I'm using the Flex embed metadata to embed bitmap objects as Classes and then instantiating them.
The problem is that it seems these never get garbage collected. I haven't found much info online but I've seen a couple of posts that seem to confirm this.
Anytime one of my classes gets instantiated that has these embedded assets, they always create new instances of the Bitmaps and BitmapDatas rather than reusing what's already in memory. This is a huge problem for memory. And I can't find any way of de-referenciong them or getting them to leave memory.
So the only solution I can think is to just load the graphics from disk rather than using the embed tag. But I'd rather not do this seeing as how when the app is packaged and installed, all of those graphcial assets will be on the end users computer rather than contained within the SWF.
Anyoen run into this? Have a solution? Or an alternate solution than the one I can think of?
Thanks!
Kyle
Well, I guess this is expected behaviour, because the new operator should always create new objects. But those new objects should get garbage collected, just the asset class will not, since it is a class.
You could build a cache that acts like a singleton factory. You request your image by specifying an id, the cache then either creates that image if it doesn't exist already, or just return the single instance if it does. It's been a while since I last coded ActionScript, so maybe you should take this as pseudo-code ;)
public class Cache {
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
[Embed(source="example.gif")]
public static var ExampleGif:Class;
/**
* The single instance of the cache.
*/
private static var instance:Cache;
/**
* Gets the Cache instance.
*
* #return
* The Cache
*/
public static function getInstance():Cache {
if (Cache.instance == null) {
Cache.instance = new Cache();
}
return Cache.instance;
}
/**
* The cached assets are in here.
*/
private var dictionary:Dictionary
public function Chache() {
if (Cache.instance != null) {
throw new Error("Can not instanciate more than once.");
}
this.dictionary = new Dictionary();
}
/**
* Gets the single instantiated asset its name.
*
* #param assetName
* The name of the variable that was used to store the embeded class
*/
public function getAsset(assetName:String):Object {
if (this.dictionary[assetName] == null) {
var AssetClass = getDefinitionByName(assetName) as Class;
this.dictionary[assetName] = new AssetClass();
}
return this.dicionary[assetName];
}
}
You could then use it like this:
public class Example {
public static function main() {
Bitmap exampleGif1 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
Bitmap exampleGif2 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
trace("both should be the same instance: " + (exampleGif1 == exampleGif2));
}
}
I didn't test this, so let me know if it works.
I think what you're looking for is dispose() http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html?#dispose()
If you decide to go with a caching system, here is a link with some code that is tested http://thanksmister.com/2009/01/29/flex-imagecache-a-cheap-way-to-cache-images/ . The link it has to another technique, using SuperImage, is broken, but I managed to find this http://demo.quietlyscheming.com/superImage/app.html .

ActionScript Basic Question

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...