Changing world's in flashpunk - actionscript-3

I'm trying to make my first actual game in AS3, for this I am using flashpunk due to it's simplicity.
I have got to the point where I have a moving character and a textbox when I go infront of a door.
When I am infront of a door, two variables are changed inside the player class:
public var onDoor:Boolean = false;
public var doorType:String = ""
You can probably guess what onDoor does, doorType is the name the class. It's set like this:
public static var BedroomDoor:Door = new Door(350, 331, "ApartmentBedroom")
ApartmenBedroom being the name of the world the door leads to.
Inside the main class where flashpunk is initialised, I have this function:
public static function ChangeLevel(world:String)
{
var newWorld = getDefinitionByName(world) as Class
FP.world = new newWorld
}
But when I go to the door and press X infront of the door which calls the function, I always get this error:
[Fault] exception, information=ReferenceError: Error #1065: Variable ApartmentBedroom is not defined.
Can anyone help fix this?

In order to use getDefinitionByName() you have to include your class in the code first. Anything will do, for example simple var a:ApartmentBedroom; or more complex one like var allLevelsClasses:Array = [ApartmentBedroom, ApartmentLivingRoom];
So your Main.as code should look like this:
public static function ChangeLevel(world:String)
{
var allLevelsClasses:Array = [ApartmentBedroom, ApartmentLivingRoom]; // put everything here
var newWorld = getDefinitionByName(world) as Class
FP.world = new newWorld
}
Also you could avoid full classname confusion when creating Door. Instead of passing string with its name into Door constructor just pass world's class:
private var worldClassName:String;
public function Door(x:Number, y:Number, worldClass:Class):void
{
/// init what's necessary
this.worldClassName = getQualifiedClassName(worldClass);
}
And later just:
public static var BedroomDoor:Door = new Door(350, 331, ApartmentBedroom)

Related

access parent movieclip class variable from child/inside movieclip AS3

Maze Runner MovieClip has a "AS Linkage" class MazeRunner
MazeRunner class
public class MazeRunner extends MovieClip
{
public var _startCave :String;
public function MazeRunner():void
{
}
}
and movieclip (Maze Runner>mc_terrain) wants to access _startCave from "MazeRunner" class to be used in "mc_terrain" timeline.
can it be done?
i tried using var mr:MazeRunner = new MazeRunner(); - but it's an error cause i think you can't access your own class/movieclip?
if Maze_Runner is a DisplayObjectContainer and mc_terrain is a DisplayObject attached to Mazerunner via addChild:
var mr:MazeRunner = new MazeRunner(); // you have to have an instance of MazeRunner to run it, anyway
var mt:Terrain = new Terrain();
mr.addChilld(mt);
then you can use
(mc_terrain.parent as MazeRunner)._startCave
to access it.
If they are not attached this way, then you need to reference the MazeRunner inside a mc_terrain (you need a new property for that):
var mr:MazeRunner = newMazeRunner;
mr.mc_terrain.maze_runner = mr;
// ...access:
trace(mc_terrain.maze_runner._startCave);
Lastly, if you always have only one active instance of MazeRunner, you can change its _startCave property to static:
public static var _startCave :String;
This will allow you to modify and read it from any place using static reference:
MazeRunner._startCave = "1";
trace(MazeRunner._startCave);
But it is generally not recommended as it may lead to issues if MazeRunner happens to have several instances which need different _startCave's.

How do you access a dynamic text field from a package / class?

I tried the following line of code inside a class, but it results in an errormessage.
textbox1.text = this.attribute1;
Error description: 1120: Access of undefined property textbox1.
package yourpackage
{
public class MyClass
{
public var dynamicText:TextField;
public function MyClass(){
dynamicText = new TextField();
}
}
}
Now you can create a new instance from this class:
package yourpackage
{
public class MyOtherClass
{
public var myInstance:MyClass;
private var attribute1:int = 0;
public function MyOtherClass(){
myInstance = new MyClass(); //create new instance
myInstance.dynamicText.text = String(attribute1); //set the text of it's
//dynamicText textfield's text to the attribute you want
}
}
}
The error you get actually says that textbox1 is not a defined variable in the current context, or the access modifier is private, thus you can't access it only from within the class. You can create static variables which you can access from anywhere like this:
MyClass.dynamicText.text = "something";
The above code assumes that you declared the dynamicText as a static variable.
public static var dynamicText:TextField = new TextField();
Thanks for your help! However I managed to access and change the text of my textfield by the following two lines of code inside my clickHandler function.
var r:MovieClip = MovieClip(root);
TextField(r["textbox1"]).text = this.navn;
Even though I didn't fully understand your code, I guess it involved creating a text field for each of the objects inside my class?
I will by the way, as you recommended, continue my reading on classes. :)
Thanks again.

stage.loaderInfo.parameters works but LoaderInfo(this.root.loaderInfo).parameters doesn't

I am passing Flashvars in my game.html file.
MyClass is the Document class name for my game.swf
public class MyClass extends MovieClip {
public function MyClass() {
loaderInfo.addEventListener(Event.COMPLETE, _Init);
}
public function _Init(e:Event) {
var parameters:Object = LoaderInfo(this.root.loaderInfo).parameters;
// There is nothing in parameters object.
}
}
My game.fla contains various layers on the main timeline. I have another game which has only one layer and that game loads the parameters correctly. I am finding this very strange.
Note:
trace root.name in the above code -> instance8 (there are 8 layers in the time line)
trace root.name in the other game (in which flashvars is working) -> root1 is the traced output
Am I missing something very basic here?
Solved this but couldn't understand the reason
So, I changed the line
var parameters:Object = LoaderInfo(this.root.loaderInfo).parameters;
to
var parameters:Object = stage.loaderInfo.parameters;
Why did the first approach didn't work?

how do I make non-document-class classes 'aware' of stage components in Flash AS3?

I am working on a text adventure game which will have at least a few components (a text area for narrative and text input for user input) on the stage at all times. Therefore, I have created those components statically through Flash's WYSIWYG design environment. I gave them instance names "myTA" and "myTI" respectively. I was able to get my main class (the document class for the stage) to interact with them (dynamically adding text one character at a time like a typewriter at runtime), but other classes in the same package don't seem able to recognize the stage components. Below is the relevant code:
Case A, in which everything happens within the Main class:
package {
public class Main extends MovieClip {
public var myTA:TextArea;
var displayedChar:String = new String();
var textToWrite:String = new String();
var i:int = 0; var intervalId:uint;
var done:int = 0;
public function Main {
setUpTA();
}
public function setUpTA(){
myTA.text = "" + playAtInterval("Hello Player!");
}
public function writeCharsSlowly(){
textToWrite = arguments[0];
displayedChar=textToWrite.substring(i,i+1);
myTA.appendText(displayedChar);
i++;
if (i == textToWrite.length) {
done = 1;
clearInterval(intervalId);
}
}
public function playAtInterval(theText:String) {
i = 0;
intervalId = setInterval(writeCharsSlowly, 100, theText);
}
}
}
Case B, where Main calls on a second class 'TypeWriter' to handle the typewriter-printing:
Main:
package {
public class Main extends MovieClip {
public var myTA:TextArea;
public var myTI:TextInput;
var str:String = new String();
public function Main{
testTypeWriter();
}
public function testTypeWriter(){
typeW.playAtInterval("Hello Player");
typeW.addEventListener(MouseEvent.CLICK,testTypeWriter2);
typeW.addEventListener(KeyboardEvent.KEY_DOWN,inputEngine2)
addChild(typeW);
}
public function testTypeWriter2(event:MouseEvent){
if (myTI.text == "a") {
typeW.playAtInterval("yo");
} else {
typeW.playAtInterval("Greetings, I am a test...");
}
addChild(typeW);
}
public function inputEngine2(event:KeyboardEvent){
str = String.fromCharCode(event.charCode);
myTI.appendText(str);
}
TypeWriter:
package {
public class TypeWriter extends MovieClip {
public var myTI:TextInput;
public var myTA:TextArea;
var i:int = 0;
var done:int = 0;
var intervalId:uint;
var displayedChar:String = new String();
var textToWrite:String = new String();
public function TypeWriter(){
///nothing here
}
public function writeCharsSlowly(){
textToWrite = arguments[0];
displayedChar = textToWrite.substring(i,i+1);
myTA.appendText(displayedChar);
i++;
if (i == textToWrite.length) {
done = 1;
clearInterval(intervalId);
}
}
public function playAtInterval(theText:String) {
i = 0;
intervalId = setInterval(writeCharsSlowly, 100, theText);
}
}
}
Case A works, but in case B Flash is giving me the error "Error #1009: Cannot access a property or method of a null object reference" and notes the first line in TypeWriter where I try to operate on myTA as the problem.
how can I make other classes besides the document class 'aware' of existing stage components?
Thanks,
CCJ
I would recommend the Service Locator Pattern for this. The most naive approach would be to create a resource class which contains public static variables. Then in your document class you assign the stage instances to the corresponding static variable in the resource class. Then you can simply access these stage components anywhere.
var someTextArea = Resource.TA; //probably should rename to something more meaningful
For something a little more ingenious you should read the article I linked to.
I think this is better than the dependency injection as constructor injection could lead to huge parameter list as you might add more items to the stage, and I am not so fond on setter injection as it is easy to forget to set them.
EDIT:
Just to make it a bit more clear I thought I would add some code :)
Resource class
package
{
//TODO imports
public class Resource
{
public static var TA:TextArea;
public static var TI:TextInput;
}
}
Document class
//....setup function
Resource.TA = myTA; //myTA is the name of the instance on stage
Resource.TI = myTI;
Foo class
Resource.TA.x = 100;
//or
_myClassMemberVariable = Resource.TA;
_myClassMemberVariable.x = 100;
I think some dependency injection will solve this problem. When you instantiate your Typewriter class, pass references to myTA and myTI. ex:
public function Main{
testTypeWriter(this.myTA, this.myTI);
}
Then your Typewriter constructor should look like this:
public function TypeWriter(ta:TextArea, ti:TextArea){
this.myTA = ta;
this.myTI = ti;
}
This also has the benefit of making your application less tightly coupled, so for example you can reuse your Typewriter class with a different text area and text input.
Edit
Some extra info that may help you in the future: you can access stage elements through the root object. But this only works with objects that have been added to the display list. Let's say that Typewriter represents an object in the display list, you could then access myTA like this:
MovieClip(root).myTA
(Change MovieClip to Sprite if that's what your document class extends).
However, since it seems that Typewriter does not get added to the display list, I recommend using my first suggestion of dependancy injection.
Also check out this page, it dicusses using CasaLib to access the stage from any object. I personally haven't tried it, so that's why it's at the end here ;-)
Are myTA and myTI actually on the stage at author time or are the added dynamically?
In the first case, just add an instance name to each. Then add a variable to each class
var main_mc : Main = root as Main;
You can then access the instances via main_mc.myTA and main_mc.myTI (assuming those are the instances names you chose) and everything should be type-safe.
In the dynamic case, just make sure you save off references in the main class to each as you add them.
Another option is to have classes for myTA and myTI send an event in their constructor to announce their existence. The main class can then listen for these and register the references.
To be honest, though, you are mixing up your display with your logic. Read up on MVC and PureMVC in particular. With a good design, everything can be handled by messages, and the instances don't need direct references to each other. IIRC, Moock's AS3 book has a chapter on MVC (but it could be his AS2 book).
The fact that they will be on stage at all times shouldn't stop you from creating specific classes for them.
Depending on your game structure, you could either create a MovieClip with your TextFields and link them to the Typewriter class, or simply create a class for the TextFields and use Events to modify the text content.
You're using external classes so there are no reason to keep any kind of logic inside Flash CS.
Just to be clear. When you are writing stage you really mean the document class for your flash document. Stage is a class that every instance that has been added to the displaylist Have access to.
I would pass the textfields into the classes that needs to update them.
var tw : Typewriter = new Typewriter();
tw.inputField = myTI;
tw.textArea = myTA;
Or
var tw : Typewriter = new Typewriter(myTI, myTA);

AS3 driving me nuts

Ok here is what I am currently trying to do. I have a class called vdata.as which takes 2 paramaters both are strings sent from the main stage. Parameter one is the location for an XML file that I need to open and read. The second parameter is the name of the video I am currently looking for.
Now I can get the data from the XML file and display it with out any issue if its called from my class but when I try to access any of it from the stage I get undefined.
import flash.net.*;
import flash.display.*;
import flash.events.*;
public class videoData
{
private var mName:String;
private var mLink:String;
private var mCategory:String;
public static var elementArray:Array;
// Constructor
public function videoData(xmlPath:String,xmlVidSrc:String,pMC:MovieClip)
{
pXmlPath = xmlPath;
pXmlVidSrc = xmlVidSrc;
xmlloader = new URLLoader();
elementArray = new Array();
}
public function getXML()
{
XMLData();
}
private function XMLData()
{
xmlloader.load(new URLRequest(pXmlPath));
xmlloader.addEventListener(Event.COMPLETE,parseXMLData);
}
private function parseXMLData():void
{
var x:XML = new XML(xmlloader.data);
Init(x);
}
private function Init(m:XML):*
{
var i:Number;
for(i=0; i<m.videos.videoname.length(); i++)
{
if(m.videos.videoname[i].#name == pXmlVidSrc)
{
videoData.elementArray.push(m.videos.videoname[i].#name);
videoData.elementArray.push(m.videos.videoname[i].#category);
videoData.elementArray.push(m.videos.videoname[i].link.#url);
}
}
}
}
When I call it from the main stage the code is as follows.
var xData:videoData = new videoData(xmlPath,vidSrc,this);
xData.getXML();
then when I try to access any elements of videoData.elementArray they come up undefined...
Im just smacking my head on my desk trying to figure this out any help would be great.
Why is elementArray a static var, you only need to make it public to use it outside the function.
I'm quite confusing but you may want to try a debugging tool like "De MonsterDebugger", I would start by tracing xmlloader.data in the parseXMLData function.
"addEventListener" doesn't "fire"...the event does. You'll need to add a boolean to state for the stage that elementArray has been populated and set that after the init function.
Is elementArray something that needs to be true across all instances of videoData? If not, it shouldn't be static. You can use MovieClip(this.root).xData to access that instance of the video class from one of your other classes.
If the event has completed and the array is still empty - then it wasn't populated by your parser. You can also do checks to see if the elementArray.length > 0.
EDIT in response to comment:
as a public member or preferably a read-only property make a boolean variable:
var parseComplete:Boolean;
Set it to false in your constructor.
Then, after your call to "Init" in your Event.COMPLETE callback set:
parseComplete=true;
Then make sure parseComplete == true before you ever access elementArray. If you're waiting for the parser to complete you might want to set a timeout or some sort of try/catch mechanism just in case there are any unforeseen errors that would cause some sort of:
while( !xData.parseComplete ) { }
To loop indefinitely. It all depends on the usage. Personally I'd probably add a callback from the event listener to the stage to trigger whatever is supposed to happen next.