Is there a way to pass an object reference to a component directly from the property/component parameter window? Using the [Inspectible] tag only allows me to input strings and not actual object references.
For example, I have a custom component called "knob" which should hold a reference to a door on the stage which it opens. I know this can be easily done in code with "knob.door = someDoor;" but since there are many objects in the scene I would prefer if I could do it visually trough the properties window.
I don't think you can do this. Your best bet is to pass in a string identifier (perhaps a whole dot-separated path if your clips are deeply nested), and then implement code inside your custom component to find that item by name.
I've got a custom component which lays itself out relative to horizontal and vertical predecessor components, so I do this:
protected var horizontalPredecessor:String = "";
[Inspectable(name = "HorizontalPredecessor", type = String, defaultValue="")]
public function set HorizontalPredecessor($value:String):void
{
horizontalPredecessor = $value;
drawNow();
}
override protected function draw():void
{
if (parent)
{
if (horizontalPredecessor != "")
{
var hp:DisplayObject = parent.getChildByName(horizontalPredecessor);
if (hp)
{
x = hp.y + hp.height + horizontalSpacing;
}
}
}
}
... which is made easy because all these components share the same parent.
Alternatively, if there's only one door, you could make it a singleton, and give it a static reference, like this:
public class Door
{
private static var _singleton:Door;
public static function get Singleton():Door
{
if(!_door) _door = new Door();
return _door;
}
}
Then your handle can just refer to Door.Singleton and you don't have to worry about passing anything in. Alternatively, you could have a static array of Doors in the Door class, and give your handle an index number to link it to a specific Door.
Related
I am working in actionscript3, and since I'm self-taught, I think I've developed some bad habits, including coding on the timeline and using multiple scenes.
I am hoping to rectify this now that I'm working on a larger project.
Based on what I've read, linking multiple .fla files together is a better practice, each with their own document class. Is that correct?
If so, how do I load one .fla with its document class and then link that into the subsequent .fla file (instead of using scenes)? Or am I misinterpreting what was recommended?
Thanks!
There's no point to split your application in several loadable modules unless you have any of the following preconditions:
you have smart resource management to load and unload content
if you put everything into one file it gets just too big and hard to work with in design time or it takes far too long to compile
Regular AS3 alternative to working with scenes is creating/destroying content instances and using the main document class as their manager. You design content in the library and create behavior AS3 classes for them. Lets say, you have two content classes A and B. At the start the manager should show one of them and wait for the signal to show next one:
private var APage:A;
private var BPage:B;
gotoA();
function gotoA():void
{
if (BPage)
{
BPage.destroy();
removeChild(BPage);
BPage.removeEventListener(Event.CLOSE, gotoA);
}
APage = new A;
APage.addEventListener(Event.CLOSE, gotoB);
addChild(APage);
}
function gotoB():void
{
if (APage)
{
APage.destroy();
removeChild(APage);
APage.removeEventListener(Event.CLOSE, gotoB);
}
BPage = new B;
BPage.addEventListener(Event.CLOSE, gotoA);
addChild(BPage);
}
So, both A and B should have respective methods .destroy() that release used resources, unsubscribes methods from events, remove display objects, and so on, and they both should fire Event.CLOSE when they're done.
If you have many pages like that, you need to go for more algorithmic approach. For example, to create class BasicPage which will interact with manager and have the methods needed in all pages already declared:
package
{
import flash.display.Sprite;
class BasicPage extends Sprite
{
// A reference to the page manager instance.
public var Manager:PageManager;
public function destroy():void
{
while (numChildren > 0) removeChildAt(0);
Manager = null;
}
// Subclasses will have an access to this method to tell manager to show another page.
protected function showOtherPage(pageClass:Class):void
{
Manager.showPage(pageClass);
}
// A method that is called by manager when everything is ready.
// If page should take any actions on start it is a good idea to override this method.
public function startEngine():void
{
}
}
}
Then, example page A:
package
{
import flash.events.MouseEvent;
public class A extends BasicPage
{
// Lets say, class A in library have a designed button named Click.
public var Click:SimpleButton;
// We have things to undo here.
override public function destroy():void
{
Click.removeEventListener(MouseEvent.CLICK, onClick);
Click = null;
// Pass the destruction to superclass so it wraps its existence either.
super.destroy();
}
override public function startEngine():void
{
Click.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void
{
// Lets use inherited method to show other page.
showOtherPage(B);
}
}
}
So, PageManager will be like:
package
{
public class PageManager extends Sprite
{
private var Page:BasicPage;
// constructor
function PageManager()
{
super();
showPage(A);
}
function showPage(pageClass:Class):void
{
if (Page)
{
Page.destroy();
removeChild(Page);
Page = null;
}
Page = new pageClass;
Page.Manager = this;
addChild(Page);
Page.startEngine();
}
}
}
This all could look scary at first, but it really isn't. PageManager will always have a current page, once there's a need to show another page, the current will be destroyed on a regular basis. Each page class will tend to its own content, which makes coding simpler, for you don't need to see the whole picture. If you need any persistent data, keep it in the PageManager so each page will have access to the data with no need for the pages to communicate with each other.
I'm trying to iterate through the properties of a custom class, however, the methods provided by Adobe appear to not work. I'm not receiving compile or run-time errors.
Class
package {
public dynamic class enum {
public var foo:Number = 123;
public function enum() {
this.setPropertyIsEnumerable("foo", true);
if (this.propertyIsEnumerable("foo") == false) {
trace("foo:" + foo + " is not enumerable.")
}
}
}
}
// outputs "foo:bar is not enumerable."
Implementaiton
var test:enum = new enum();
for (var property:String in test) {
trace(property);
}
// outputs nothing
I try to keep my code fast and flexible, so it's really frustrating when you must change the class to Dynamic just to be able to use for ... in on the properties. Jackson Dunstan's testing confirms that this can be 400x slower than static class properties, but those have to be explicitly referenced (impractical for property agnostic methods), or use reflection of the class (computationally expensive) to be accessible.
The only way I've found to sidestep the whole issue is to use dynamically declared variables... which is pointless since at that point using setPropertyIsEnumerable(prop, true) is superfluous; all dynamically created properties already are enumerable. Additionally, dynamic variables cannot be strongly datatyped, and performance goes out the window.
For example...
Class
package {
public dynamic class enum {
public var foo:String = "apple";
public function enum(){
this.dynamicVar = "orange";
this.dynamicProp = "banana";
this.setPropertyIsEnumerable("foo", true);
this.setPropertyIsEnumerable("dynamicProp", false);
}
}
}
Implementation
var test:enum = new enum();
for (var key:String in test) {
trace(key + ": " + test[key]); // dynamicVar: 1
}
// outputs "dynamicVar: orange"
Now that the class is dynamic, we see that only one of our 3 test properties are being iterated. There should be 2.
It almost feels like Adobe wants us to adopt bad programming habits. Words fail me...
Non-dynamic classes do not provide enumerable properties or methods.
As stated in the description of the link you provided.
Sets the availability of a dynamic property for loop operations.
I think you might want to refactor your code on this approach.
I have never had to loop over a classes properties like you are doing here.
If you want to track items dynamically you should use an associative array and track them that way not at the class level like you are doing.
And if you want strong data typing then use a vector.
Hi guys,
I have two objects on stage so I presume they are in the Display list as well (Progress_mc, Ship_mc). I have Calculator class which doesn't represent any visual shape or anything but as3 code so it isn't in the display list.
What is the best way to work with the properties of Progress_mc?
Example: Calculator_as has to receive Progress_mc.width any time width has been changed and after some calculation Calculator has to send some calculated results to Ship_mc.x.
I was thinking if I have to addChild(Calculator) on stage so I can have access to those MCs in Calculator.as but this class isn't a visual object so I am not sure this is the right way.
Or I have to do this (code below) in Calculator class and then try to access the properties but I this way wont work either because the properties wont be of the instances on stage:
private var prg:Progress_mc = new Progress_mc;
private var ship:Ship_mc = new Ship_mc;
Or I have to add them as children of Calculator and add Calculator on stage?
The other problem is that I can't just use setter and getter as static functions in Calculator because "width" property is a read-only and cannot be used in static function (error:?)
What is the best way to access those properties and manipulate them?
Thank you so much good people!
I'm assuming Calculator instance is sort of globally accessible. In that case, I think you have
public function setProgressMcWidth(width:Number):void {...}
in Calculator class. This function needs to be called whenever progressMc's width is updated. Later when calculator needs to pass some width to shipMc, it can dispatch an event such as
package {
public class CalculatorEvent extends Event {
private var _width:Number = width;
public function CalculatorEvent(type:String, width:Number)
{
super(type);
_width = width;
}
override public function clone():Event {
var ret:CalculatorEvent = new CalculatorEvent(type, _width);
return ret;
}
public function getWidth():Number {return _width;}
}
}
and have dispatch code in Calculator like:
dispatch(new CalculatorEvent("shipWidthCalculated", calculatedShipWidth));
Ship mc, in turn, would listen to calculator's event like:
calculator.addEventListener("shipWidthCalculated", handleShipWidthCalculated);
private function handleShipWidthCalculated(event:CalculatorEvent):void {
trace('calculator calculated my width to be: ' + event.getWidth);
}
But if the calculator instance isn't in the display list, it won't receive any events.
I have my main stage, and I have two objects (blocks), these two objects both extend from the "Block" class. The "Block" class is NOT extended from the Main Class.
I would LIKE to call a function, in either the "Block" class or in it's subclasses, from the Main Stage Class. The functions will do slightly different things depending which object you are calling the function (Added different things, and different number of things to an array). What is the best way to implement this?
I am sorry I have no code to show right now, I am just attempting to sit down and do it now but feel quite lost.
Not quite sure I follow so I'm going to assume you mean this.
You have a class called Block
You create two of these Block's and store them, possibly in an array from your base class.
//stage base class
var blockArray:Array = new Array()
private function createBlocks():void{
var blockOne:Block = new Block(1); //passing in an int to block, could be anything but this
// will be used to do slightly different things
var blockTwo:Block = new Block(2);
blockArray.push(blockOne...blockTwo)
}
Now in your block class
//block class
class Block{
var somethingDifferent:int; //this is where we will store the int you pass in when the blocks are made
public function Block(aInt:int){
somethingDifferent = aInt //grabbing the int
}
public function doSomething():void{
trace(somethingDifferent); //will trace out the number passed
}
}
Now back in your main class
//stage base class
private function doSomethingToBlocks():void{
//lets call doSomething on each block
Block(blockArray[0]).doSomething() //this will trace 1 because we passed that into the block in our array slot 0
Block(blockArray[1]).doSomething() //this will trace 2
}
Hopefully this is what you're after
The general idea is to define the function in the parent class and then to override the function in the subclass to do different things. Then, you can call the function on the various subclasses and it will do different things depending on the block.
A brief example:
Block class:
public function getBlockType():String
{
return "I am a plain block";
}
First block subclass
public override function getBlockType():String
{
return "I am a cool block";
}
Second block subclass:
public override function getBlockType():String
{
return "I am an even cooler block";
}
Stage:
//add the first block
var coolBlock:CoolBlock = new CoolBlock();
addChild(coolBlock);
//add the second block
var coolerBlock:EvenCoolerBlock = new EvenCoolerBlock();
addChild(coolerBlock);
//call the functions
trace(coolBlock.getBlockType());//outputs "I am a cool block"
trace(coolerBlock.getBlockType());//outputs "I am an even cooler block"
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));
}