AS3: Return MovieClip from Function - actionscript-3

Sorry if this question has already been answered but I can't seem to find any relevant examples online. I basically have a class which loads a set of MovieClip objects and provides accessor functions to return them.
public function getMovieClip( mc:MovieClip ):Boolean
{
if( allFilesLoaded )
{
mc = fileLoader.content;
return true;
}
else
{
return false;
}
}
Obviously this doesn't work, but it gives an idea of what I'm trying to do. I want to return a MovieClip to the calling code only if the object has been loaded.

You can’t modify the parameter like that. Instead, return the MovieClip like this:
public function getMovieClip():MovieClip
{
if ( allFilesLoaded )
{
return fileLoader.content;
}
else
{
return null;
}
}
And then you can just use it like this, even with reading the MovieClip:
var mc:MovieClip; // defined somewhere
// later...
if ( ( mc = x.getMovieClip() ) )
{
// all files were loaded and mc is not null.
}
else
{
// files were not loaded and mc is null.
}

Try this:
// change the allFilesLoaded value to view the other result
var allFilesLoaded:Boolean = true;
trace(getMovieClip(null) );
function getMovieClip( mc:* ):*
{
if( allFilesLoaded )
{
mc = new MovieClip();
return mc;
}
else
{
return false;
}
}
The asterisk denotes that the type of data that is returned is unknown, and we could both be Boolean as a MovieClip.
If allFilesLoaded then returns the mc (MovieClip) otherwise it returns false (Boolean).
Hope that helps.

Related

Can't call method after casting

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.

Is it possible to make use of a value returned from a method called by a delegate?

While experimenting with a basic coding stuff, I wondered, if a value returned by a method called by a delegate, could be used or captured. In other words, where will the return value will go ?
For example :
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener( "myClick" , myClick);
}
function myClick(e:Event):String //returning a string
{
return "What happens to this return value ???"
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener( MouseEvent.CLICK , onClick);
}
function onClick(e:Event):String //returning a string
{
dispatchEvent(new Event("myClick"));
}
}
As I know it's not possible to do, but, there are at least some ways to implement the logic similar to what you've told about.
For example, you may call a method of a dispatcher, from a listener method:
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener("myClick" , myClick);
}
function myClick(e:Event):void
{
mc.specialMethod("some string");
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener(MouseEvent.CLICK , onClick);
}
function onClick(e:Event):void
{
dispatchEvent(new Event("myClick"));
}
public function specialMethod(param:String):void
{
// Do something to the param
}
}
Also, you may think about dispatching an event from the Main class, and listen to it in the MyMc class, or pass a callback, which returns a string, from Main to the MyMc.
It's according to you and your needs to return something from the listener function because normally it must return nothing :
... This function must accept an Event object as its only parameter and must return nothing, ...
but you can of course get the returned value(s), take a look on this little example :
var light_on:Boolean = false;
btn_light_on.addEventListener(MouseEvent.CLICK, btn_on_onPress);
btn_light_off.addEventListener(MouseEvent.CLICK, btn_off_onPress);
function btn_on_onPress(e:MouseEvent): Boolean {
light_on = true;
if(e.target === btn_light_off){
light_on = false;
}
return light_on;
}
function btn_off_onPress(e:MouseEvent): void {
trace('The light is', btn_on_onPress(e) ? 'on' : 'off');
}
Hope that can help.

Actionscript 3 error code 1119 when dealing with this.parent?

I am fairly new to AS and am playing with a minesweeper game I found. For each cell on the game board, I have a movie clip (cell_mc) embedded with a dynamic text box (number_txt) for the numbers, a solid color square movie clip (block_mc), and a little flag to mark the bombed boxes (flag_mc). I also have a separate dynamic text box to count how many mines are left on the board (minecounter_txt).
My problem is that when I try to run the game, every where I have a "this.parent.flag_mc" or a "this.parent" with any of the mentioned elements, Flash returns an error code - "1119: Access of possibly undefined property flag_mc (or other element) through a reference with static type cell." And with minecounter_mc it returns "1119: Access of possibly undefined property flag_mc (or other element) through a reference with static type flash.display:DisplayObjectContainer"
I assumed that since it said it couldn't reference the elements with a static type, that it was unable to recognize them as dynamic. I searched for a solution, and read that it helps to declare the dynamic element before the parent, i.e. "MovieClip(this.parent.flag_mc), but it didn't help.
Can anyone help? Thanks.
package {
import flash.display.MovieClip;
import flash.display.DisplayObjectContainer;
import flash.events.MouseEvent;
public class cell extends MovieClip {
public var state:Boolean;
public var revealed:Boolean;
public var xcor:uint;
public var ycor:uint;
public var marked:Boolean;
public var cellValue:int;
public function cell(corx:uint, cory:uint) {
// constructor code
this.state = false;
this.revealed = false;
this.marked = false;
this.cellValue = 0;
this.xcor = corx;
this.ycor = cory;
this.flag_mc.visible = false;
this.addEventListener(MouseEvent.CLICK, cellClicked);
}//end of constructor
private function cellClicked(event:MouseEvent):void{
if(event.shiftKey){
if(this.marked){
this.flag_mc.visible = false;
this.marked = false;
this.parent.minecounter_txt.text = String(int(this.parent.parent.minecounter_txt.text) + 1);
} else{
this.flag_mc.visible = true;
this.marked = true;
this.parent.minecounter_txt.text = String(int(this.parent.parent.minecounter_txt.text) - 1);
}
} else{
if(!state){
openCell();
} else{
if(!this.marked){
this.parent.play_btn.visible = true;
}
}
}
}
private function openCell(){
if(!this.marked && !this.revealed){
this.block_mc.visible = false;
this.revealed = true;
this.removeEventListener(MouseEvent.CLICK, cellClicked);
}
}
}//end of class
}//end of package
this.parent will be null unless the movieclip is added to the parent's display list.
It would be better to pass the reference of the parent & store it in a class property.
var parentObj:Object = null;
// Constructor
public function cell(parentObj:Object, corx:uint, cory:uint) {
this.parentObj = parentObj;
//...
And call it as :
var cellObj = new cell(this, ...);
If you know which Class would be the parent, then you could also set the typeof parentObj to it.

How to have the variable in a class updated when its value is set in another class?

I'm rather new with AS3, so forgive me if my question appears stupid.
Let me clarify what I want specifically: The Player goes on a mission, in which he would earn points when he catches some objects on his way, for example object A gives one point, object B gives two points, object C gives bonus points, etc... IF the Player hits some obstacle on his way, he fails the mission and he has to replay the game until he reaches the destination (end of level).
So what I want is that how I can store the points that the Player earned before he hits the obstacle and the earned points would be accumulated every time the Player has to replay the game until he reaches the end of the game level so that the total points the Player earned would be the sum of all the points, including those that he earned before he actually reaches the end of the game level.
My Class Player is the subclass of the class Box that belongs to the framework WCK. Is there a way for me to implement Dependency Injection properly to pass the variables to the other class in order to store them there?
Thank you in advance for your help!
ADDED: Here is what I implemented based on Amy's example with some adjustment to suit the setup of my game. It still doesn't work as the value of variable _totalEarnedPoints did not update the variable _earnedPoints in Class PlayerClient via EventDispatcher when I tried to retrieve it back in Class Player for accumulating all the points. I'm not sure what I did wrong or missed anything else? Can anyone please point out and tell me how to retrieve back the variable _totalEarnedPoints from Class PlayerClient?
CLASS Player:
package
{
public class Player extends Box
{
private var contacts:ContactList;
private var myLoader:SWFLoader;
private var mcTemp:MovieClip;
private var bonusPoints:int = 0;
private var onePoint:int = 0;
private var twoPoints:int = 0;
private var objectACount:int = 0;
private var objectBCount:int = 0;
private var objectCCount:int = 0;
private var replayCount:int = 0;
protected var _earnedPoints:int;
private var myPlayerClient:PlayerClient = new PlayerClient();
}
public function Player()
{
super();
}
public override function create():void
{
super.create();
listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, handleContact);
contacts = new ContactList();
contacts.listenTo(this);
}
public function handleContact(e:ContactEvent):void
{
//Detect collison with other static objects
var myObjectA:objectA = e.other.m_userData as objectA;
var myObjectB:objectB = e.other.m_userData as objectB;
var myObjectC:objectC = e.other.m_userData as objectC;
var myObstacle:obstacle = e.other.m_userData as obstacle;
if(myObjectC)
{
objectCCount++;
myObjectC.remove();
if (objectCCount > 0)
{
bonusPoints = bonusPoints + 5;
}
}
else if(myObjectA)
{
objectACount++;
myObjectA.remove();
if (objectACount > 0)
{
onePoint = onePoint + 1;
}
}
else if(myObjectB)
{
objectBCount++;
myObjectB.remove();
if (objectBCount > 0)
{
twoPoints = twoPoints + 2;
}
}
else if(myObstacle)
{
var myEarnedPoints:int = myPlayerClient.totalEarnedPoints;
_earnedPoints = bonusPoints + onePoint + twoPoints + myEarnedPoints;
dispatchEvent(new Event("EarnedPointChanged"));
myLoader = new SWFLoader("myMovie.swf",{container:swfHolder,alpha:0,onComplete:completeHandler});
//adds the loader content to the display list before raw content is loaded.
addChild(myLoader.content);
stop();
myLoader.load();
function completeHandler(e:LoaderEvent):void
{
replayCount++;
if (replayCount <= 1)
{
TweenMax.to(e.target.content, 1, {alpha:1});
mcTemp = myLoader.rawContent;
mcTemp.gotoAndPlay(1);
}
else if (replayCount >= 1)
{
mcTemp = myLoader.rawContent.stop();
myLoader.unload();
mcTemp = null;
}
}
myObstacle.remove();
}
}
public function get earnedPoints():int
{
return _earnedPoints;
}
}
CLASS PlayerClient
package
{
public dynamic class PlayerClient extends MovieClip
{
private var _totalEarnedPoints:int;
protected var _player:Player;
public function get player():Player
{
return _player;
}
public function set player(value:Player):void
{
if (value != _player)
{
if (_player)
{
//[prevent memory leaks][2]
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player)
{
//listen for changes
_player.addEventListener("EarnedPointChanged", updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void
{
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
}
protected function updatePlayerScore(e:Event):void
{
_totalEarnedPoints = _player.earnedPoints;
}
public function get totalEarnedPoints():int
{
return _totalEarnedPoints;
}
}
}
First, what you're doing is going to cause you a world of hurt unless your project is a banner or something that you know 100% that you will work on for 2 days and never look at again. See http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
What you need to realize is that your Points.pointsEarned looks like it will only ever change if you create a new instance of Player, and if you have several players, each new Player will have a higher number of points to start out with than the one before. That's probably not what you want. You don't really say what it is that you want, so let me throw out one thing that you could want and tell you how to code it. If that's not right, we'll go from there.
Edit:
OK, so now we know that you want to be able to just increment the earned points, so this Class now becomes much simpler (check the revision history of the post to see what parts were removed):
package{
public class Player extends EventDispatcher {
protected var _earnedPoints:int;
public function Player() {
super();//not passing a parameter, so target of events will be this
}
public function get earnedPoints():int {
return _earnedPoints;
}
public function set earnedPoints(value:int):void {
if (value != _earnedPoints) {
_earnedPoints = value;
//other Classes can listen for this to see if earnedPoints has changed:
dispatchEvent(new Event('earnedPointsChanged');
}
}
}
}
You didn't ask about this part, but I think it's a necessary part for you to understand. I'm going to explain it just in case it's not obvious. Your "objects" need to have a Class of their own:
package {
class TargetObject extends MovieClip {
public var points:int;
}
}
You can set those values when each TargetObject is created, or you can create several subclasses, where the value is hard-coded.
So now, whenever there's a collision, you can do something like this:
_player.earnedPoints += target.points;
When you set that variable, your PlayerClient will update, even when it is set from another Class.
In summary, you need to generate an event that other instances can listen for to update themselves:
package {
public class PlayerClient extends MovieClip {
public var playerScoreText:TextField;//assume this is on stage
protected var _player:Player;
//assume [dependency injection][1]
public function get player():Player {
return _player;
}
public function set player(value:Player):void {
if (value != _player) {
if (_player) {
//[prevent memory leaks][2]
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player) {
//listen for changes
_player.addEventListener('earnedPointsChanged', updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void {
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
}
/* This will trigger no matter what changes the value of
earnedPoints or bonusPoints.
*/
protected function updatePlayerScore(e:Event):void {
if (playerScoreText) /*in case it wasn't put on stage */ {
playerScoreText.text = _player.earnedPoints
}
}
}
}
I got my question resolved by myself: I used SharedObject method to store the points and then retrieve the point value back into my class Player for accumulation. EventDispatcher did pass the points to the other class, but all the points value get back to initial state once Player hits the obstacle and the game replays, which makes it difficult to retrieve the points I passed to the other class back to the class Player for accumulation. SharedObject method did help me resolve this problem for my case study.

what to addEventListener to when listening for custom events?

I am sending an event when an sql query returns no matches so that I can continue with the addition to the database.. It seems like actionscript is requiring that I attach the listener to something, but I don't really have any variables that seem like logical candidates at the point where I am in my code.
I just want to listen for the isNewRecord event to be called so that I can then run the insert query; right now it's saying call to possibly undefined method for addEventListern and for dispatchEvent
public function addBG(BG:Number, datetime:String, batch:Boolean = false):void{
checkRecord('Gb', datetime, matchRecord);
addEventListener("isNewRecord", recordExists);
function recordExists()
{/*code to execute query*/}
public function matchRecord(result:SQLResult):void {
var match:String = result.data[0];
if (match == null) {
var allClear:Event = new Event("isNewRecord");
dispatchEvent(allClear);
}
}
Your code is buggy. You have a function within a function.
Also, is your code extending EventDispatcher class (or any class that extends it, like Sprite, MovieClip, etc.?) Make sure it is.
Try this:
public function addBG(BG:Number, datetime:String, batch:Boolean = false):void
{
// note, you're adding this event listener EVERY TIME you call the
// addBG function, so make sure you remove it OR add it somewhere in the
// init or complete functions
addEventListener("isNewRecord", recordExists);
checkRecord('Gb', datetime, matchRecord);
}
public function recordExists():void
{/*code to execute query*/}
public function matchRecord(result:SQLResult):void {
var match:String = result.data[0];
if (match == null) {
var allClear:Event = new Event("isNewRecord");
dispatchEvent(allClear);
}
}
You don't need to use events. Your processing of the SQLResult seems synchronous, there is no latency due to any interaction with the user, the server or anything that may take some time.
When Flash executes your code it does the folowing:
checkRecord('Gb', datetime, matchRecord);
//then
var match:String = result.data[0];
if (match == null) {
var allClear:Event = new Event("isNewRecord");
dispatchEvent(allClear);
}
//and finally
addEventListener("isNewRecord", recordExists);
The event is dispatched before the listener is added.
Here is what you should do:
public function addBG(BG:Number, datetime:String, batch:Boolean = false):void
{
if (checkRecord('Gb', datetime, matchRecord))
{
recordExists();
}
}
public function recordExists():void
{/*code to execute query*/}
public function matchRecord(result:SQLResult):Boolean{
var match:String = result.data[0];
if (match == null) {
return true;
}
return false;
}
Cheers