I have this class structure below
public class Main extends Sprite
{
private var myObject:MyObject;
private var mySecondObject:MySecondObject;
private var myThirdObject:MyThirdObject;
public function Main()
{
myObject = new MyObject();// also extends sprite
addChild(myObject );
mySecondsObject = new MySecondObject(this);
addChild(mySecondsObject);
myThirdObject = new MyThirdObject(this);
addChild(myThirdObject);
}
public function getMyObject():MyObject
{
return myObject;
}
}
public class MySecondObject extends Sprite
{
private var _main:Main;
private var _myObject:MyObject;
public function MySecondObject(main:Main)
{
_main = main;
_myObject = _main.getMyObject();
// do some stuff with _myObject, add on enter frame listener etc...
}
public function start(): { /* do some stuff */}
public function stop(): { /* do some stuff */}
}
public class MyThirdObject extends Sprite
{
private var _main:Main;
private var _myObject:MyObject;
public function MyThirdObject(main:Main)
{
_main = main;
_myObject = _main.getMyObject();
// do some stuff with _myObject, add on enter frame listener etc...
}
public function start(): { /* do some stuff */}
public function stop(): { /* do some stuff */}
}
The problem that appears is the sprite of MyObject dissapears when I do this with MySecondObject and MyThirdObject
MySecondObject.stop();
MyThirdObject.start();
MyObject still exists in memory but I cannot see it on the stage. WTF ??? :)
To make it clear I dont remove anything from the stage with removeChild().
The problem I try to solve is I want to have accessors to update state of MyObject from MySecondObject and MyThirdObject
Problem solved. I changed to add all children on stage like this.stage.addChild(child) instead of this.addChild(child) so now it works and it show all objects on the stage, after start/stop
Related
I have a function, sceneLoader, that will instantiate an object of the Class Scene1 from a string using getDefinitionByName. I then want to access a function (sceneHandler) in Scene1, from another function.
public class Main extends MovieClip{
private var sceneNumber:int = 1;
private var SceneRef:Class;
private var sceneString:String;
public function Main(){
sceneLoader();
responseHandler();
}
private function sceneLoader():void{
sceneString = "Scene" + int(sceneNumber);
SceneRef = getDefinitionByName(sceneString) as Class;
var scene = new SceneRef();
addChild(scene);
}
private function responseHandler():void{
scene.sceneHandler(); //Obviously this will not work
}
}
Class Scene1
public class Scene1 extends MovieClip{
public function sceneHandler():void{
//Do something
}
}
The problem is that scene is out of scope. I cannot instantiate Scene1 outside the function as I need to first call getDefinitionByName. I also want to keep the function for loading scenes later on.
How can I call sceneHandler from responseHandler?
EDIT: I've tried
public class Main extends MovieClip{
private var sceneNumber:int = 1;
private var SceneRef:Class;
private var sceneString:String;
public function Main(){
sceneLoader();
responseHandler();
}
private function sceneLoader():void{
addChild(getScene());//Does not work
}
private function responseHandler():void{
getScene().sceneHandler(); //Works now
}
private function getScene():Object{
sceneString = "Scene" + int(sceneNumber);
SceneRef = getDefinitionByName(sceneString) as Class;
var scene = new SceneRef();
//addChild(getScene());//This does work but doesn't suit my need
return scene;
}
}
Now I can't add the object to the stage.
I get the Error:
Implicit coercion of a value with static type Object to a possibly unrelated type flash.display:DisplayObject.
I figured it out. I just forgot to cast as a DisplayObject.
public class Main extends MovieClip{
private var sceneNumber:int = 1;
private var SceneRef:Class;
private var sceneString:String;
public function Main(){
sceneLoader();
responseHandler();
}
private function sceneLoader():void{
addChild(getScene() as DisplayObject);
}
private function responseHandler():void{
getScene().sceneHandler();
}
private function getScene():Object{
sceneString = "Scene" + int(sceneNumber);
SceneRef = getDefinitionByName(sceneString) as Class;
var scene = new SceneRef();
return scene;
}
}
Obviously:
import flash.display.DisplayObject;
How can I add a children (instance) on stage every time I press the button without replacing the existing children?
I have four Classes: Symbol1, Symbol3, Symbol4, all.
When I Press Symbol3 which is a button I want to create an instance of Symbol1 on the stage through class all.as. With Symbol4 I want to delete one of the created instance in order of creation on stage.
Example: I have pressed Symbol3 three times and I have created three instances of Symbol1 on stage. Now if I press Symbol4 I will delete the first created instance. If I press Symbol4 one more time I will delete the second created instance.
public class Symbol3 extends SimpleButton
{
private var creator:all;
private var child:Symbol1 = new Symbol1 ;
private var child2:Symbol1 = new Symbol1 ;
private var child3:Symbol222 = new Symbol222 ;
public function Symbol3()
{
addEventListener(MouseEvent.CLICK, onCLICK);
}
private function onCLICK(s:MouseEvent)
{
creator = new all(child);
stage.addChild(creator);
}
}
.
public class all extends MovieClip
{
private var _thief1:MovieClip;
public function all(par1:MovieClip)
{
_thief1 = par1;
addEventListener(Event.ADDED_TO_STAGE, onADDED_TO_STAGE);
}
private function onADDED_TO_STAGE(e:Event)
{
removeEventListener(Event.ADDED_TO_STAGE, onADDED_TO_STAGE);
this.addChild(_thief1);
_thief1.x = Math.random() * 200;
_thief1.y = Math.random() * 200;
}
}
.
public class Symbol4 extends SimpleButton
{
public function Symbol4()
{
addEventListener(MouseEvent.CLICK, onCLICK);
}
private function onCLICK(s:MouseEvent)
{
stage.removeChild(?);
}
}
This I have so far.
Thanks
You should put all your addable/removable sprite in one same container, let's call it container.
Then the add button will look like this:
private function onCLICK(s:MouseEvent)
{
container.addChild(new all(new Symbol1()));
}
And the remove button:
private function onCLICK(s:MouseEvent)
{
container.removeChildAt(0);
}
When removing the child on layer 0, the other children will go one layer down and the next child to remove will come on 0.
Thanks for the help Kodiak!
I made it finally. I am not sure if this is the right approach but at least it works.
I have three Classes:
AddChild2.as - linkage to Button
Creator.as
Ship2.as - linkage to MovieClip
The tricky moment was that the stage had to be transferred as a parameter to avoid error:1009. The other think is the empty constructor function of the Creator that makes the code more flexible and independent. Now Creator can produce any passed movieClip. Again I believe that there is another better way to do this, so any improvement is welcome.
public class AddChild2 extends SimpleButton
{
private var creatorche:Creator = new Creator;
private var s:Ship2;
public function AddChild2()
{
// constructor code
addEventListener(MouseEvent.CLICK, onCLICK)
}
private function onCLICK(e:MouseEvent)
{
s = new Ship2;
creatorche.onCreator(s, stage);
}
}
.
public class Creator extends MovieClip
{
private var ship:MovieClip;
public function Creator()
{
// constructor code
}
public function onCreator(par1:MovieClip, par2:Stage)
{
ship = par1;
par2.addChild(ship);
ship.x = Math.random() * 200;
ship.y = Math.random() * 200;
}
}
.
public class Ship2 extends MovieClip
{
public function Ship2()
{
// constructor code
}
}
For some reason, I can't modify the value of a field in Haxe. Of course, this doesn't seem to be affecting all of my fields, just this one. Here's (what I'm pretty sure is) the applicable code. First, in the parent class:
class TopMenu extends Sprite
{
public function new()
{
super();
init();
}
private function init()
{
var tempField:BitmappedTextField = new BitmappedTextField( "File", 100, false );
trace( tempField.textWidth );
}
}
Then, in the child class:
class BitmappedTextField extends Sprite
{
private var _fieldText:String;
private var _fieldWidth:Int;
private var _addToStage:Bool;
public var textWidth:Int;
public function new( thisText:String, thisWidth:Int = 100, adTStg:Bool = true )
{
super();
_fieldText = thisText;
_fieldWidth = thisWidth;
_addToStage = adTStg;
textWidth = 55;
init();
}
public function init()
{
textWidth = 777;
}
}
I would expect the trace statement to return 777, but instead it will always return 55. In fact, no matter what I do, I can't seem to modify a field outside of the constructor class and then retrieve that value via the parent class. There's something horribly simple I must be missing, but I just can't figure it out. Maybe it has to do with the way Haxe uses getters and setters? Any help is appreciated, thank you.
I cant reproduce your problem however and you are missing a ; and a super call.
Try this code.
package;
import nme.display.Sprite;
import nme.display.MovieClip;
class HelloWorld extends MovieClip
{
public function new( )
{
super();
var tempField:BitmappedTextField = new BitmappedTextField();
trace( tempField.textWidth );
}
}
class BitmappedTextField extends Sprite
{
public var textWidth:Int;
public function new( )
{
super();
textWidth = 55;
init( );
}
public function init( )
{
textWidth = 777;
}
}
I just started programming OOP and I'm running into a scope problem.
In the following project, I have a masterClass called App. The App-class has Screens:Screen-class and a Navigation-class as it's children. From the navigation class I want to control which screens will be displayed. I don't know how to do this...
Please check the code to fully understand my intentions
Your help is really appreciated, I'd love to really learn programming and not just a dirty solution :) but all suggestions are welcome!
// Main Class //
public class App extends Sprite
{
private var screens:Array;
private var screen1:Screen;
private var screen2:Screen;
private var screen3:Screen;
private var screen4:Screen;
public var currentScreen:String;
//
private var navigation:Navigation;
public function App()
{
init();
}
private function init():void {
buildScreens();
buildNavigation();
}
private function buildScreens():void {
screen1 = new Screen();
screen1.name = 'startScreen';
currentScreen = screen1.name;
addChild(screen1);
screen2 = new Screen();
screen2.name = 'irrelevantA';
screen3 = new Screen();
screen3.name = 'irrelevantB';
screen4 = new Screen();
screen4.name = 'irrelevantC';
screens = new Array(screen1, screen2, screen3, screen4);
}
private function buildNavigation():void {
navigation = new Navigation(screens);
}
}
// Screen Class //
public class Screen extends Sprite
{
public function Screen()
{
// creates a new screen
}
}
// Navigation Class //
public class Navigation extends Sprite
{
private var buttons:Array;
public function Navigation(screens:Array)
{
addButtons(screens);
}
private function addButtons(screens:Array):void {
buttons = new Array();
for each(var screen:Screen in screens) {
var button:Button = new Button();
button.link = screen.name;
button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
buttons.push(button);
}
}
private function mouseDown(e:MouseEvent):void {
// THIS IS WHAT MY QUESTION IS ABOUT: How should I talk to the parent class in an OOP correct way?
// and how can I add and remove a screen in the App class from here?
// Here some of my tries
// I don't think using parent to get there is a good way because next time it might be; parent.parent.parent
trace(e.target.parent.parent.currentScreen);
this.parent.currentScreen;
stage.App.currentScreen;
App.currentScreen;
//---------------------------------
}
}
// Button Class //
public class Button extends Sprite
{
public var link:String;
public function Button()
{
// creates a new button
}
}
If you directly access parent classes from child objects, you create strong coupling - which is exactly what you don't want in a well-built system. It is best not to access the application object directly, but to use event listeners and custom events to promote changes from e.g. the navigation.
Here's an example. First, create a custom event:
public class MyCustomEvent extends Event {
public static const MENU_ITEM_SELECTED : String = "MENU_ITEM_SELECTED";
public var selectedItem:String;
}
Then, let the navigation dispatch it, when a button is clicked:
public class Navigation extends Sprite () {
// ...
private function onButtonClicked(ev:Event) : void {
ev.stopPropagation();
var custEvent:MyCustomEvent = new MyCustomEvent(MyCustomEvent.MENU_ITEM_SELECTED);
custEvent.selectedItem = ev.target.name;
this.dispatchEvent (custEvent);
}
// ...
}
Finally, let the application handle the custom event and bring up a different screen:
public class App {
// ...
public function createNavigation () : void {
navigation = new Navigation ();
navigation.addEventListener (MyCustomEvent.MENU_ITEM_SELECTED, onMenuItemSelected);
// ... more stuff happening
}
// ...
private function onMenuItemSelected (ev:MyCustomEvent) : void {
switchToScreen (ev.selectedItem);
}
private function switchToScreen (name:String) : void {
// choose screen by name, etc.
}
}
For all of this, neither the screen, nor the navigation have to know anything about any other objects involved, so you can easily replace each one without breaking the rest of the system.
You basically want to communicate downward (parent to child) by passing in references as arguments (as you're doing with the screens array) and upwards (child to parent) by calling public functions.
So, in your case something like this:
App class:
private function buildNavigation():void {
navigation = new Navigation(this, screens);
}
//etc
public function changeScreen(newScreen:int):void{
//Your logic for adding/removing screens goes here
}
Navigation class:
private var app:App
public function Navigation(app:App, screens:Array)
{
this.app = app
addButtons(screens);
}
private function addButtons(screens:Array):void {
buttons = new Array();
for each(var screen:Screen in screens) {
var button:Button = new Button();
button.link = screen.name;
button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
buttons.push(button);
}
}
private function mouseDown(e:MouseEvent):void {
app.changeScreens(2);
}
}
Obviously, change the implementation to suit your needs (for example, now that you have a reference to the App class, consider if you need to pass in a separate reference to the screens array or not) - this is just an example of how you can communicate.
Suppose in document class
public class Test extends MovieClip
{
public function Test()
{
var object1:ClassA = new ClassA();
//addChild(object1);
object1.accessRoot();
}
}
public class A extends MovieClip
{
public function accessRoot()
{
var mc : MovieClip = root as MovieClip;
mc.box.visible = false;
}
}
Now box is placed at stage. but when Class A is added to Test Class, it works and when object of Class A is not added in Test constructor, root becomes in-accessible. Is there any way that objects not on display-list can access root or display-list objects??
I would not recommend having your classes fiddle around with root or stage, it's way better to dispatch events and have the proper encapsulation.
Hacky way:
public class A extends MovieClip
{
private var _root:MovieClip;
public function A(root:MovieClip)
{
_root = root;
}
public function accessRoot()
{
_root.box.visible = false;
}
}
Proper way:
public class A extends MovieClip
{
public static const ACCESS_ROOT:String = "access_root";
public function accessRoot()
{
dispatchEvent(new Event(ACCESS_ROOT));
}
}
// in your document class
var myA:A = new A();
myA.addEventListener(A.ACCESS_ROOT, handleAccessRoot);
public function handleAccessRoot(e:Event):void{
box.visible = false;
}
I normally create a sort of base class that holds a reference to the document class - or "main" class. Anything that I create from here that should need reference to anything defined in Main would extend Element. Example:
The Main class (or document class):
public class Main extends MovieClip
{
/**
* Constructor
*/
public function Main()
{
var obj:MyElement = new MyElement();
obj.main = this;
// stage will be outputted
}
}
Element - which stores reference to the main class.
It also contains an init() function which I generally use in place of a constructor by overriding it.
public class Element extends MovieClip
{
private var _main:Test;
public function set main(m:Main):void
{
_main = m;
init();
}
/**
* Called when _main is defined
*/
protected function init():void
{
// override me
}
public function get main():Main{ return _main; }
}
And here's how you would use Element as a base class for your classes:
public class ClassA extends Element
{
/**
* Override init rather than using a constructor
*/
override protected function init():void
{
trace(main.stage);
}
}
The only thing really to note is that you of course have to set the _main property whenever you create an object. (as shown on line 9 of Main).