Can't call method after casting - actionscript-3

I'm trying to figure out why ActionScript won't let me call methods on an Object after casting it.
I have a set of objects in an Array, all of which extend my ActionBase class. I want to go through all of them and call their step() and done() methods.
However, when I try to do so, the compiler gives me an error:
Error: Call to a possibly undefined method
If I simply create an ActionBase Object directly and call these methods on it, it works fine. So I suspect there's something about the casting that ActionScript doesn't like.
This is the code:
private var actionQueue:Array = new Array();
...
var action:ActionBase;
for (var i:int = 0; i < actionQueue.length; ++i)
{
action = actionQueue[i] as ActionBase;
if (action != null && action is ActionBase)
{
action.step();
if ( action.done() )
{
newQueue.push(action);
}
}
}
actionQueue is being appended to by having it's push() method called with objects which extends ActionBase. I'm also using FlashDevelop as my IDE. Not sure if that makes a difference.
My ActionBase class:
public class ActionBase
{
public function ActionBase()
{
}
public function done():Boolean
{
return true;
}
public function step():void
{
}
}

It may be as easy as changing the method from private to public. (didn't try it myself though and sorry can't write comments due to reputation level.)

Your example code looks fine, what does your ActionBase class and Array (actionQueue) assignment look like?
i.e.
actionQueue[actionQueue.length] = new ActionBase();
Cut/Paste Example:
package {
import flash.display.Sprite;
public class Main extends Sprite {
private var actionQueue:Array = new Array();
public function Main() {
var action:ActionBase;
var foobar1:ActionBase = new ActionBase();
actionQueue[actionQueue.length] = foobar1;
actionQueue[actionQueue.length] = new ActionBase();
actionQueue[actionQueue.length] = Object;
for (var i:int = 0; i < actionQueue.length; ++i)
{
action = actionQueue[i] as ActionBase;
if (action != null && action is ActionBase)
{
action.step();
if (action.done())
{
trace("all done");
}
}
}
}
}
}
class ActionBase {
var _done:Boolean = false;
function ActionBase() {
}
function done():Boolean {
trace(done);
return _done;
}
function step() {
_done = true;
trace(step);
}
}

Is your object a movieclip?
Did you try setting ActionBase as base class for your objects?
Or trying casting like this:
ActionBase(Arr[i]).step()
Or just try without casting.

Related

1013 error. This attibute may only be used on class property definitions. I have 7 of this error in my code

all of these functions are getting this error. The setuplayer, the makeBalloons and the makeBalloon. I copied and pasted this code from my teacher. So I don't know what it could been stuck on trying to get these ballons to spawn in a game. Before I added the touch layer it would run but no balloons spawned. So I decided to add a touch layer and then I got these errors.
private function setupTouchLayer(evt: Event): void {
touchLayer.graphics.beginFill(0x000000, 0);
touchLayer.graphics.drawRect(0, 0,stage.stageWidth, stage.stageHeight);
touchLayer.graphics.endFill();
}
private function makeBalloons(): void {
balloonSpawnCounter++;
if (balloonSpawnCounter > balloonSpawnDelay) {
balloonSpawnCounter = 0;
balloonSpawnDelay -= difficultyRate;
difficulty += difficultyRate;
makeBalloon();
}
}
private function makeBalloon(): void {
var i: int;
for (i = 0; i < Math.floor(difficulty); i++) {
var newBalloon: Balloon = new MouseBalloon();
newBalloon.x = 1050;
newBalloon.y = Math.random() * 300 + 150;
newBalloon.xVel = (-Math.random() * difficulty) - 5;
newBalloon.sinMeter = Math.random() * 10;
newBalloon.bobValue = Math.random() * difficulty;
newBalloon.addEventListener(Particle.PURGE_EVENT, purgeBalloonHandler);
balloonsLayer.addChild(newBalloon);
balloons.push(newBalloon);
}
}
private function purgeBalloonHandler(evt: Event): void {
var targetBalloon: Particle = Particle(evt.target);
purgeBalloon(targetBalloon);
}
private function purgeBalloon(targetBalloon: Particle): void {
targetBalloon.removeEventListener(Particle.PURGE_EVENT, purgeBalloonHandler);
try {
var i: int;
for (i = 0; i < balloons.length; i++) {
if (balloons[i].name == targetBalloon.name) {
balloons.splice(i, 1);
balloonsLayer.removeChild(targetBalloon);
i = balloons.length;
}
}
} catch (e: Error)
{
trace("Failed to delete arrow!", e);
}
}
private function hitTest(shark: Particle): void {
for each(var balloon: Balloon in balloons) {
if (balloon.status != "Dead" && balloon.hitTestPoint(shark.x, shark.y)) {
balloon.destroy();
}
}
}
private function update(evt: Event): void {
for each(var balloon: Particle in balloons) {
balloon.update();
}
makeBalloons();
}
}
}
}
If you are copying and pasting code from a class file, into a timeline, you have to remove any package or class blocks, and any scope keywords like private,public,protected, final etc.
For example, if you had the following .as file:
package com.mystuff {
public class ExampleClass extends Object {
public function ExampleClass(){
trace("Hello World!");
}
private function doSomething():void {
trace("Doing something...");
}
}
}
To put that on a timeline keyframe, you'd have to change it to:
trace("Hello World");
function doSomething():void {
trace("Doing Something...");
}
The function that has the same name as the class name, you want to be by itself at the top of the code. All other functions, just remove the public and private keywords.
Now, if you'd like to use that code in an actual class file (which is good), you would do the following: (assuming you just have the code and not a file, and are using Animate/FlashPro as your IDE).
In AnimateCC, go to file -> new and choose ActionScript 3.0 Class. Give it the class name that matches what you see in the code.
Paste your code.
Save the file in the same directory as your .fla, assuming the code starts with package {. If the code starts with something like package com.mystuff, the file should saved in a subfolder called mystuff that is inside another folder called com which is in the same directory as your .fla.
To use that class file in the timeline (or another class file), you'd have to instantiate it like so:
import com.mystuff.ExampleClass;
var ec:ExampleClass = new ExampleClass();
ec.dosomething();
If you are using a class file, but are getting that error, it probably means you've accidentally closed your class block, like the following:
package {
public class ExampleClass {
}//if you put an extra one of these closing curly braces in your code, it will end your class, or if you on purpose do it like this example.
private function dosomething():void {
//will error because this function needs to live inside the class block
}
}

Add Event-Listener for the whole Symbol

I´ve put a Label and a ComboBox together in a Symbol, like in the code below.
public class LabelCombo extends MovieClip {
//myLabel is an instance of fl.controls.Label
//myCombo is an instance of fl.controls.ComboBox
public function LabelCombo() {
}
public function setLabelText(s:String):void{
myLabel.text = s;
}
public function getLabelText():String{
return myLabel.text;
}
public function removeAllItems():void{
if(myCombo.dataProvider.length > 0)
myCombo.removeAll();
}
public function setSingleItem(o:Object):void{
removeAllItems();
myCombo.addItem(o);
}
public function setList(itemList:Array):void{
for(var i:int = 0; i < itemList.length; i++){
if(i==0)
setSingleItem(itemList[i]);
else
addSingleItem(itemList[i]);
}
}
public function addSingleItem(o:Object):void{
for(var i:int = 0; i < myCombo.dataProvider.length; i++){
if(o == myCombo.getItemAt(i))
return;
}
myCombo.addItem(o);
}
public function addList(itemList:Array):void{
for(var i:int = 0; i < itemList.length; i++){
addSingleItem(itemList[i]);
}
}
public function getSelectedItem():Object{
return myCombo.selectedItem;
}
public function getItemByLabel(s:String):Object{
return Object(myCombo.getChildByName(s));
}
public function selectItemAt(index:int):void{
myCombo.selectedIndex = index;
}
public function getselectedIndex():int{
return myCombo.selectedIndex;
}
}
Now I want to addEventListener(Event.CHANGE, changeHandler)
on the whole Symbol so I can do something like that
trace(LabelCombo(event.currentTarget).getLabelText())
trace(LabelCombo(event.currentTarget).getSelectedItem().data)
I need information from both single controls.
It`d be nice if someone can show me how to do this.
Thanks in advance
EDIT: I´ve recognized that there are some misunderstandings:
I need this for a communication via XMLSocket. The server has to know two things: 1) what is the name of the ComboBox (in this case I get the name with myLabel) 2) the new selected Index
Hope now everything is much clearer
I really don't understand you question but you could try this
public class LabelCombo extends MovieClip {
//myLabel is an instance of fl.controls.Label
//myCombo is an instance of fl.controls.ComboBox
public function LabelCombo() {
myLabel.addEventlistener(Event.CHANGE,onChange);
myCombo.addEventlistener(Event.CHANGE,onChange);
}
private function onChange(e:Event):void {
dispatchEvent(new Event(Event.CHANGE));
}
}
add change event listeners to the label and combo. Using dispatchEvent you could dispatch custom event .
You could use like this
var labeCombo:LabelCombo = new LabelCombo();
addChild(labeCombo);
labeCombo.addEventlistener(Event.CHANGE,onChangeMv);
function onChangeMv(e:Event){
trace(labeCombo.getLabelText());
trace(labeCombo.getSelectedItem().data);
}
You can use custome event class to send data along with the event dispatcher.
You just set the event listener to myCombo but the listener should be located in the code of LabelCombo, this way you won't need tricks with event.currentTarget and just use this context to work with children, as the listener will only be triggered by changes in this object's myCombo child.
myCombo.addEventListener(Event.CHANGE,onChange,false,0,true);
function onChange(e:Event):void {
trace(getSelectedItem().data);
}
All I meant was instead of this:
trace(LabelCombo(event.currentTarget).getLabelText())
try like this: (if possible)
LabelCombo.setLabelText("Just_A_Test");
var str_Check : String = LabelCombo.getLabelText();
trace ("returned string is : " + str_Check);
It should say returned string is : Just_A_Test...
If LabelCombo is a child of myCombo then the correct reference path is: myCombo.LabelCombo.getLabelText(); etc etc.

Get instances by class name in AS3

I need to get all the instances in my stage according to an especific class name. I'm doing this:
var class_ref:Class = getDefinitionByName('fran.MyOwnClass') as Class;
var element;
for (var i:uint = 0; i < this.parent.numChildren; i++)
{
element = this.parent.getChildAt(i);
if (element is class_ref)
{
trace('Found element of class fran.MyOwnClass');
}
}
But I want a better way (more efficiently, without checking all the MCs). Is it possible?
If you can start tracking instances from the very beginning of you application life, I'd recommend simply add event listener:
// in document class constructor, before doing anything else
stage.addEventListener(Event.ADDED, stage_addedHandler);
stage.addEventListener(Event.REMOVED, stage_removedHandler);
private function stage_addedHandler(event:Event):void
{
var obj:DisplayObject = event.target as DisplayObject;
// do something, e.g. if (obj is MyClass) objCounter++;
}
...
If you can't track from the beginning, you can't avoid loops.. Just make them more optimized:
var n:int = container.numChildren;
while (n-- > 0)
{
...
}
Overriding everywhere addChild() and others — that's simply impossible solution in real projects.
You could keep a list of all the MC's of a certain type by extending the container class and overriding its addChild(), addChildAt(), removeChild() and removeChildAt() functions.
public class MySprite extends Sprite {
public var ownClasses:Vector.<MyOwnClass> = new Vector.<MyOwnClass>();
override public function addChild(child:DisplayObject):DisplayObject {
addOwnClass(child as MyOwnClass);
return super.addChild(child);
}
override public function addChildAt(child:DisplayObject, index:int):DisplayObject {
addOwnClass(child as MyOwnClass);
return super.addChildAt(child, index);
}
private function addOwnClass(child:MyOwnClass):void {
if (child) ownClasses.push(child);
}
override public function removeChild(child:DisplayObject):DisplayObject {
removeOwnClass(child as MyOwnClass);
return super.removeChild(child);
}
override public function removeChildAt(index:int):DisplayObject {
removeOwnClass(getChildAt(index) as MyOwnClass);
return super.removeChildAt(index);
}
private function removeOwnClass(child:MyOwnClass):void {
if (child) {
var i:int = ownClasses.indexOf(child);
if (i != -1) ownClasses.splice(i, 1);
}
}
}
Using this class, every time a child is added, you check whether it's a MyOwnClass and if it is you add it to the ownClasses list. Similar for removing children.
Now you can simply access the list when you need it without looping over the MC's.
public class Main extends MySprite
{
public function Main()
{
addChild(new Sprite());
addChild(new MyOwnClass());
trace(ownClasses);
}
}
This will output [object MyOwnClass]

ActionScript - Global Custom Events?

up until now, the way i've been needing to handle my own custom events is by adding an event listener to the object that was dispatching the custom event. while this method of event handling works just fine, i've come to the point where i would like my custom events to be globally accessible, where the listening object does not need to be the same object that is dispatching the event.
in this example, my main Controller class is instantiating and adding to the display list 2 sprite classes: Square and Triangle. the 4th and final class is a custom event called ColorChangeEvent.
i'm attempting to dispatch a new ColorChangeEvent from the Square class, which uses a timer to dispatch a new random color once every second, while Triangle will listen for the dispatched event and change its fill color to the color that was dispatched by Square.
Controller.as:
package
{
import flash.display.Sprite;
public class Controller extends Sprite
{
public function Controller()
{
var sq:Square = new Square();
sq.x = sq.y = 100;
var tr:Triangle = new Triangle();
tr.x = tr.y = 250;
addChild(sq);
addChild(tr);
}
}
}
Square.as:
package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Square extends Sprite
{
public function Square()
{
graphics.beginFill(0x999999);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
var myTimer:Timer = new Timer(1000);
myTimer.addEventListener(TimerEvent.TIMER, dispatchNewColor);
myTimer.start();
}
private function dispatchNewColor(evt:TimerEvent):void
{
var randomColor:Number = Math.random() * 0xFFFFFF;
trace("Square Class Dispatched: " + randomColor);
dispatchEvent(new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor));
}
}
}
Triangle.as:
package
{
import flash.display.Sprite;
import flash.geom.ColorTransform;
public class Triangle extends Sprite
{
public function Triangle()
{
graphics.beginFill(0x999999);
graphics.moveTo(0, 0);
graphics.lineTo(100, 50);
graphics.lineTo(-50, 150);
graphics.endFill();
addEventListener(ColorChangeEvent.CHANGE, changeColor);
}
private function changeColor(evt:ColorChangeEvent):void
{
var ct:ColorTransform = new ColorTransform;
ct.color = evt.color;
transform.colorTransform = ct;
trace("Triangle Class Received: " + evt.color);
}
}
}
ColorChangeEvent.as:
package
{
import flash.events.Event;
public class ColorChangeEvent extends Event
{
public static const CHANGE:String = "change";
public var color:Number;
public function ColorChangeEvent(type:String, color:Number)
{
super(type);
this.color = color;
}
override public function clone():Event
{
return new ColorChangeEvent(type, color);
}
}
}
needless to say, this isn't working.
of course, i could add the event listener to the Square instance in the Controller class, who's event handler could pass that value to Triangle via a public function to change the color, but this is exactly the kind of limitation i'm trying to avoid.
it's not always easy to access and pass a value to a class from where the custom event is dispatched, which is why i'm looking for an actual global solution to handling custom events.
I have been using this class for some time now. To use it you would do this in square:
data.EventManager.instance.publish("someName", randomColor);
and then in your Triangle:
data.EventManager.instance.subscribe("someName", handleColorChange);
private function handleColorChange(color:Number):void {
// implementation here
}
You can even pass the ColorChangeEvent instead of just the color.
data.EventManager.instance.publish(ColorChangeEvent.CHANGE, new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor);
And then
data.EventManager.instance.subscribe(ColorChangeEvent.CHANGE, handleColorChange);
private function handleColorChange(colorChangeEvent:ColorChangeEvent):void {
// implement here
}
I removed a lot of code that is specific to my projects, so I am not 100% it is usable exactly as-is. But, you should be able to modify it to get it working correctly. If not, let me know and I can try to work it out with you.
This class handles additional things that I will not go into, though you are free to explore. Be aware, however, that anything that subscribes for event notification has a strong reference by the EventManager. That means that if you want to destroy something for garbage collection, you need to call EventManager.instance.cancel(ColorChangeEvent.CHANGE, handleColorChange) before the Triangle instances can be collected.
package data {
import flash.utils.*;
public class EventManager extends Object {
private var _subscribers:Dictionary;
private var _calls:Dictionary;
private var _feeds:Dictionary;
private var _requests:Dictionary;
private var _notify:Dictionary;
private var _services:Dictionary;
private static var __instance:EventManager;
public function EventManager() {
if (__instance) {
trace("EventManager is a Singleton class which should only be accessed via getInstance()");
}
_feeds = new Dictionary(true);
_subscribers = new Dictionary(true);
_requests = new Dictionary(true);
_services = new Dictionary(true);
_notify = new Dictionary(true);
}
public function getFeedData($name:String) {
if (_feeds[$name]) {
return _feeds[$name];
}
return undefined;
}
public function unpublish($name:String) {
var _post:* = _feeds[$name];
delete _feeds[$name];
return _post;
}
public function cancel($name:String, $subscriberFunc:Function, ...args): void {
var _cnt:Number;
var _subscriberArray:Array;
if (_subscribers[$name]) {
for (_cnt = 0; _cnt < _subscribers[$name].length; _cnt++) {
if (_subscribers[$name][_cnt] == $subscriberFunc) {
_subscribers[$name].splice(_cnt, 1);
}
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name];
_cnt = _subscriberArray.length;
while (_cnt > 0) {
if (_subscriberArray[_cnt] == $subscriberFunc) {
_subscriberArray.splice(_cnt, 1);
}
_cnt--;
}
}
}
public function subscribe($name:String, $subscriber:Function, ...args): void {
var _funcArray:Array;
var _func:Function;
if (_feeds[$name]) {
$subscriber(_feeds[$name]);
}
if (! _subscribers[$name]) {
_subscribers[$name] = new Array();
}
_subscribers[$name].push($subscriber);
if (_notify[$name]) {
_funcArray = _notify[$name];
for each (_func in _funcArray) {
_func();
}
delete _notify[$name];
}
}
public function request($name:String, $feedFunction:Function): void {
var _requestArray:Array;
var _request:Function;
if (! _feeds[$name]) {
if (! _requests[$name]) {
_requests[$name] = new Array();
}
_requests[$name].push($feedFunction);
} else {
$feedFunction(_feeds[$name]);
}
if (_notify[$name]) {
_requestArray = _notify[$name];
for each (_request in _requestArray) {
_request();
}
delete _notify[$name];
}
}
public function publish($name:String, $data:*, $args:Object = null): void {
var _subscriberArray:Array;
var _func:Function;
var cnt:Number = 0;
_feeds[$name] = $data;
if (_subscribers[$name] != undefined) {
_subscriberArray = _subscribers[$name].slice();
_cnt = 0;
while (_cnt < _subscriberArray.length) {
_func = _subscriberArray[_cnt] as Function;
if ($args) {
_func($data, $args);
}else {
_func($data);
}
_cnt++;
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name].slice();
delete _requests[$name];
_cnt = 0;
while (_cnt < _subscriberArray.length) {
if (_subscriberArray[_cnt] != null) {
_subscriberArray[_cnt]($data);
}
_cnt++;
}
}
}
public function notify($name:String, $subscriber:Function): void {
if (_requests[$name] || _subscribers[$name]) {
$subscriber();
}else {
if (! _notify[$name]) {
_notify[$name] = new Array();
}
_notify[$name].push($subscriber);
}
}
public static function getInstance(): EventManager {
if (! __instance) {
__instance = new EventManager();
}
return __instance;
}
public static function get instance(): EventManager {
return getInstance();
}
}
}
I got this to work by creating a singleton: EventDispatchSingleton that extends EventDispatcher. It's basically an empty singleton that provides the dispatchEvent and add/removeEventListener methods (these are automatically provided by extending EventDispatcher).
Anywhere I want to dispatch an event I import EventDispatchSingleton and then call EventDispatchSingleton.instance.dispatchEvent(<someEvent>);.
Then, wherever I want to listen to that event, I just import EventDispatchSingleton and call EventDispatchSingleton.instance.addEventListener(eventName, callback);
You should look into event bubbling, specificly I think you will find the Capturing phase of the event propagation useful. Take a read of Event propagation from Adobe LiveDocs. It's in the Flex docs, but it is about AS3 Events.
Also Senocular has a good post on Flash Event Bubbling.

In ActionScript 3, tracing all properties of a value object

I have a number of value objects and I want to be able to create a function within it to trace out the properties and the values without specifying them directly. It will allow me to use the same code in all value objects as opposed to referring to variables within the value object. Ideally, I would like to replace the code in blogURLVars with this code.
Here's a sample value object
package items {
public class Blog {
private var _itemID:uint;
private var _blogTitle:String;
private var _blogText:String;
private var _blogCreated:String;
private var _blogCategory:String;
private var _blogFrontpage:Boolean;
public function Blog (itemID:uint,blogTitle:String,blogText:String,blogCategory:String,blogCreated:String,blogFrontpage:Boolean=false) {
_itemID = itemID;
_blogTitle = blogTitle;
_blogText = blogText;
_blogCreated = blogCreated;
_blogCategory = blogCategory;
_blogFrontpage = blogFrontpage;
}
public function get itemID():uint {
return _itemID;
}
public function get blogTitle():String {
return _blogTitle;
}
public function get blogText():String {
return _blogText;
}
public function get blogCategory():String {
return _blogCategory;
}
public function get blogCreated():String {
return _blogCreated;
}
public function get blogFrontpage():Boolean {
return _blogFrontpage;
}
public function toString():void {
}
public function blogURLVars():String {
var s:String;
s = "itemID="+ _itemID;
s += "blogTitle="+ _blogTitle;
s += "blogText="+ _blogText;
s += "blogCategory="+ _blogCategory;
s += "blogCreated="+ _blogCreated;
s += "blogFrontpage="+ _blogFrontpage;
return s;
}
}
}
DescrybeType could be of help here. I'm basing this answer in this other answer (you might want to check it out): Fastest way to get an Objects values in as3
Basically, the describeType function will let you inspect the public interface of your object. That means public variables, getter/setters and methods (plus some other info not relevant to your problem). So you get a list of all the getters and with that, return the names of said properties plus their actual values for a given object. Not that this is not exactly the same as your sample code, since this will not use the private variables, but rather will call the getters.
In code, this could be something like this (based on code in the linked question).
package {
import flash.net.URLVariables;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
public class PropertiesHelper {
public function PropertiesHelper() {
}
private static var typePropertiesCache:Object = {};
public static function getPropertyNames(instance:Object):Array {
var className:String = getQualifiedClassName(instance);
if(typePropertiesCache[className]) {
return typePropertiesCache[className];
}
var typeDef:XML = describeType(instance);
trace(typeDef);
var props:Array = [];
for each(var prop:XML in typeDef.accessor.(#access == "readwrite" || #access == "readonly")) {
props.push(prop.#name);
}
return typePropertiesCache[className] = props;
}
public static function getNameValuePairs(instance:Object):URLVariables {
var props:Array = getPropertyNames(instance);
var vars:URLVariables = new URLVariables();
for each(var prop:String in props) {
vars[prop] = instance[prop];
}
return vars;
}
}
}
Use:
var blog:Blog = new Blog(1,"title&","text","cat","created");
var urlVars:URLVariables = PropertiesHelper.getNameValuePairs(blog);
trace(urlVars);
I'm using a URLVariables object since it seems that's what you want (though not actually what you blogURLVars method does), but you could change that in the getNameValuePairs method to suit your needs if necessary. An advantage of using a URLVariables object is that it handles the url-encoding for you automatically, so reserved characters such as &, =, etc, should not be a problem.