object-oriented (?) actionscript probleem

Pagina: 1
Acties:

  • xshredx
  • Registratie: Maart 2001
  • Laatst online: 21-08 15:17
Ik heb een probleem met flash waar ik maar niet uitkom. Ik zit er nu al geruime tijd op te kijken en te proberen en ik snap het niet meer.
Ik heb een platformgame gemaakt en wil daar nu een object georienteerde versie van maken.
Ik baseer me in eerste instantie op het voorbeeld van een object georienteerde toepassing uit het boek "Actionscript for Flash MX: the definitive guide" van Colin Moock. Ik wou om te beginnen dat voorbeeld een beetje overnemen om een treffelijk begin te hebben.
Maar voor het ogenblik zit ik vast, en ik heb al vanalles geprobeerd om toch maar zo dicht mogelijk bij de code uit het voorbeeld te blijven, maar ik blijf struikelen.

Mijn code tot nu toe (minimum om te kunnen testen)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ******************************************
// LOAD CLASSES
// ******************************************
// classes exist in the namespace 'filip'
#include "filip.Game.as"
#include "filip.GameGUI.as"


// ******************************************
// START GAME
// ******************************************
main();

// function: main()
// this function starts the game
function main() {
    // Create a new game. The game class manages the game flow
    var platformGame = new filip.Game();
    
    // Create a GameGUI instance to display the game and recieve user input.
    var platformGameGUI = new filip.GameGUI(this, platformGame);
    
    // Tell the Game instance which GameGUI instance will be used to render 
    // the game to screen.
    platformGame.setGameGUI(platformGameGUI);
    
    platformGame.startGame();
}


De game klasse:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Define the namespace
if (_global.filip == undefined) {
    _global.filip = new Object();
}

// ****************************************************************
// Game class
// _________
// This class contains settings for the game, updates the GUI class, and manages
// the game flow
// ****************************************************************

/*
* game constructor
*
*/
_global.filip.Game = function () {
    this.gameGUI = null;    // an instance of gameGUI   
    this.level = 1;
    this.levelName = "";
    this.numLevels = 5;
    this.numLives = 3;
    
};

/*
* Method: Game.setGameGUI()
* Desc: Stores a reference to the GameGUI instance.
*
* @param    gameGUI     A reference to a gameGUI instance
*/
filip.Game.prototype.setGameGUI = function (gameGUI) {
    trace("setGameGUI");
    this.gameGUI = gameGUI;
};

/*
* Method: Game.startGame()
* Desc: Starts the game.
*/
filip.Game.prototype.startGame = function () {
    trace("start game");
    var gameObj = this;
    // Tell the GameGUI to display the welcome screen.
    // Wait 500 milliseconds before displaying the splash screen. This is because of a bug
    // that causes Stage.height and Stage.width to be inaccurate during the first frame
    // of a movie (cfr. Actionscript for Flash MX: the definitive guide)
    var intID = setInterval(function () {
        gameObj.gameGUI.displaySplashScreen();
        clearInterval(intID);
        }, 500);
};

/*
* Method: Game.loadLevel()
* Desc: Loads a level.
*
* @param    levelNumber     Which level to load?
*/
filip.Game.prototype.loadLevel = function (levelNumber) {
    this.levelName = "level" + levelNumber + ".xml";
    // let the GUI know a new level needs to be loaded
    this.gameGUI.onLoadLevel();
};

/*
* Method: Game.getLevelName()
* Desc: Getter method for the levelname. Setter method is not needed (loadLevel method)
*/
filip.game.prototype.getLevelName = function() {
    return this.levelName;
};


en de code voor de gameGUI klasse:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Define the namespace
if (_global.filip == undefined) {
    _global.filip = new Object();
}

// ****************************************************************
// GameGUI class
// _________
// This class displays the game (in the game class)
// ****************************************************************

/*
* gameGUI constructor
*
* @param    gameClip        The clip used for game GUI display
* @param gameObject The game instance we're displaying
*/
_global.filip.GameGUI = function (gameClip, gameObject) {
    // instance properties
    this.gameClip = gameClip;       // The clip used for game GUI display
    this.gameObj = gameObject;      // The Game instance
    this.depthCounter = 1000;       // To make sure everything gets a unique depth
    
    // initialization: set movie scale mode
    Stage.scaleMode = "noScale";
    trace("gameGui init");
};

/*
* Method: GameGUI.displaySplashScreen()
* Desc: Displays the game title screen. The game starts here.
*/
filip.GameGUI.prototype.displaySplashScreen = function () {
    trace("display splash screen");
    // store a reference to the display clip and the game object
    var gameClip = this.gameClip;
    var gameObj = this.gameObj;
    
    // create the description
    gameClip.createTextField("desc_txt",++this.depthCounter,0,0,0,0);
    trace("text field init");
    gameClip.desc_txt.autoSize = true;
    gameClip.desc_txt.text = "Platform game: temp description";
    gameClip.desc_txt.selectable = false;
    gameClip.desc_txt.setTextFormat(new TextFormat("_sans", 16, 0xFF0000));
    gameClip.desc_txt._x = Stage.width/2 - gameClip.desc_txt._width/2;
    gameClip.desc_txt._y = gameClip.desc_txt._y + gameClip.desc_txt._height;
    
    // create the begin button
    var begin_mc = this.createNavButton(" Start Game! ", "0x999999");
    begin_mc._x = Stage.width/2 - begin_mc._width/2;
    begin_mc._y = gameClip.desc_txt._y + gameClip.desc_txt._height + 30;
    begin_mc.onPress = function () {
        gameObj.loadLevel(1);
    };
};


/*
 * Method: GameGUI.loadLevelFromXML()
 * Desc:   build the level from the info in the xml file.
 */
filip.GameGUI.prototype.loadLevelFromXML = function () {
        // =======================================================
        // vanaf hier plots geeft een trace(this.gameClip); me "undefined"
        // =======================================================
    trace("load level from xml");
    // store a reference to the display clip and the game object
    var gameClip = this.gameClip;
    var gameObj = this.gameObj;
    
    // create the test textfield
    gameClip.createTextField("test_txt",++this.depthCounter,0,0,0,0);
    trace("test_txt field init");
    gameClip.test_txt.autoSize = true;
    gameClip.test_txt.text = "test_txt test_txt: test_txt test_txt";
    gameClip.test_txt.selectable = false;
    gameClip.test_txt.setTextFormat(new TextFormat("_sans", 16, 0xFF0000));
    gameClip.test_txt._x = Stage.width/2 - gameClip.test_txt._width/2;
    gameClip.test_txt._y = gameClip.test_txt._y + gameClip.test_txt._height;
    
    /*
    var test_mc = gameClip.createEmptyMovieClip("test_mc", ++this.depthCounter);
    trace("create empty movieclip");
    gameClip.test_mc.lineStyle(15,0x00FF00);
    gameClip.test_mc.moveTo(0,0);
    gameClip.test_mc.curveTo(50,-100,100,0);
    gameClip.test_mc._x = 0;
    gameClip.test_mc._y = 0;
    */
    
};

/*
 * Method: GameGUI.createNavButton()
 * Desc:   Creates a new navigation button (e.g., "Next", "Back"), and
 *         returns a reference to it.
 * 
 * @param   text   The text on the button.
 * @param   col   The color of the button border and text.
 */
filip.GameGUI.prototype.createNavButton = function (text, col) {
  // Set button color to black if none is supplied.
  col = col ? col : 0x000000;
  this.depthCounter++;
  var navBut_mc = this.gameClip.createEmptyMovieClip("navBut_mc" + this.depthCounter, this.depthCounter);
  navBut_mc.createTextField("but_txt", ++this.depthCounter, 0, 0, 0, 0);
  navBut_mc.but_txt.autoSize = true;
  navBut_mc.but_txt.text = text;
  navBut_mc.but_txt.selectable = false;
  navBut_mc.but_txt.border = true;
  navBut_mc.but_txt.borderColor = col;
  navBut_mc.but_txt.setTextFormat(new TextFormat("_sans", 14, col));
  
  return navBut_mc;
};

/*
 * Method: GameGUI.clearScreen()
 * Desc:   Clear the screen: remove all movie clips and text fields from the game clip.
 */
filip.GameGUI.prototype.clearScreen = function () {
    for (var p in this.gameClip) {
        if (typeof this.gameClip[p] == "movieclip") {
            this.gameClip[p].removeMovieClip();
        } else if (this.gameClip[p] instanceof TextField) {
            this.gameClip[p].removeTextField();
        }
    }
};


// *********************************************************************
// Event handlers
// *********************************************************************

/*
 * Method: GameGUI.onLoadLevel()
 * Desc:   Invoked from the game class.
 */
filip.GameGUI.prototype.onLoadLevel = function () {
    this.clearScreen();
    var levelName = this.gameObj.getLevelName();
    trace(levelName);
    
    levelXML = new XML();
    levelXML.onLoad = this.loadLevelFromXML;
    levelXML.ignoreWhite = true;
    levelXML.load(levelName);
    // =======================================================
        // tot hier geeft een trace(this.gameClip); me "_level0"
        // =======================================================
};


De gebruikte .fla (waarin enkel dat eerste stukje geposte code zit) en de .as files kun je ook nog eens hier vinden.

Het probleem zit in de GameGUI.prototype.loadLevelFromXML functie: ik wil een level opbouwen aan de hand van de beschrijving in de xml file, maar ik slaag er gewoon niet om movieclips te laden in mijn main movie. Ik ben zelfs zover gegaan om in deze loadLevelFromXML functie nu eigenlijk een kopie te zetten van de code in de displaySplashScreen functie, maar er gebeurt niets...
Ik heb ook geprobeerd om een lege movieclip aan te maken om in te tekenen (staat in commentaar), maar ook dat lukt niet... eveneens lukt het niet om iets in de library te steken en die movieclip dan te attachen.

Al de rest van de code werkt... dat zie ik in de swf + door de verschillende trace functies die ik in de code heb staan. Ook kan ik zien dat de loadLevelFromXML functie uitgevoerd wordt, omdat de trace die erin staat wel uitgevoerd wordt... het lukt dus enkel niet om movieclips te laden in de mainmovie, of om bv. een textfield aan te maken, wat dus nochtans tevoren in de displaySplashScreen functie met dezelfde code wel lukt...

Het probleem ligt volgens mij in het feit dat this.gameClip plots undefined is in de loadLevelFromXML functie. Dat zie ik als ik een trace doe... maar het vreemde is dat this.gameClip tot net daarvoor (tot op het einde van de onloadLevel functie) gewoon "_level0" is en blijft... waar ik ook mag tracen naar de waarde van this.gameClip, steeds krijg ik dat, totdat ik in de functie loadLevelFromXML ga, dan wordt this.gameClip plots undefined, terwijl ik toch niets meer doe aan deze variabele???
Ergens dus tussen het laden van de xml file (wat goed gebeurd, ik zie de inhoud namelijk bij debug >> variables), en het begin van het uitvoeren van de xml.onLoad functie gaat het dus mis... maar ik begrijp allerminst waarom, wat en hoe er gebeurd dat die this.gameClip variabele plots undefined wordt.


Ziet er iemand de fout of weet er iemand waarom ik hiertegen aan loop?

  • GrimaceODespair
  • Registratie: December 2002
  • Laatst online: 10:32

GrimaceODespair

eens een tettenman, altijd ...

Je doet
Flash ActionScript:
1
levelXML.onLoad = this.loadLevelFromXML;
waarbij je goed moet onthouden dat de context (of meer specifiek: "this") waarin loadLevelFromXML wordt uitgevoerd, gelijk is aan het XML object waarop je je "load" aanroept. Dat wil dus zeggen dat wanneer eenmaal het XML document geladen is, die loadLevelFromXML functie wel wordt aangeroepen, maar dat "this" op dat moment verwijst naar de XML en niet naar de game.

Wat je kan doen om dit te verhelpen, is voordat je "load" aanroept een variabele te zetten op de xml die je daarna weer gebruikt om de this pointer goed te zetten:

Flash ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
path.to.myFunc = function()
{
    // Zet handmatig de this pointer naar de goeie plek
    this = container;

    // Doe nu gewoon alsof je vanuit container werkt
    [...]
}

levelXML.container = this;
levelXML.onLoad = path.to.myFunc;
levelXML.load("myDoc.xml")


Merk ook op dat het eigenlijk niet zo netjes is om de "this" pointer te verleggen, omdat dat voor verwarrende code kan zorgen. Daarom is het eigenlijk beter om rechtstreeks die "container" variabele te gebruiken. Maar zoals ik em nu heb geschreven, hoef je aan de rest van de code in myFunc (of loadLevelFromXML voor jou) niets meer te veranderen.

Wij onderbreken deze thread voor reclame:
http://kalders.be


Verwijderd

de this scope veranderen kan, maar is in de optiek van nette code echt te smerig, zeg maar illegaal. :)

Wat je beter kan doen is dit:
Flash ActionScript:
1
2
3
4
5
levelXML.onLoad = function(){
         this.owner.xml = 'blaat'
}

levelXML.owner = this;

hier zet je dus eigenlijk een _parent voor een non mc object.

[ Voor 8% gewijzigd door Verwijderd op 17-04-2003 09:55 ]


Verwijderd

Nog ff klein tippie voor de topic starter:
Flash ActionScript:
1
2
3
4
5
6
7
8
_global.filip.Game = function () { 
    this.gameGUI = null;    // an instance of gameGUI     
    this.level = 1; 
    this.levelName = ""; 
    this.numLevels = 5; 
    this.numLives = 3; 
     
}; 
Hier hoeft dus niet _global voor... :)

[ Voor 5% gewijzigd door Verwijderd op 17-04-2003 10:00 ]


  • GrimaceODespair
  • Registratie: December 2002
  • Laatst online: 10:32

GrimaceODespair

eens een tettenman, altijd ...

Verwijderd schreef op 17 april 2003 @ 09:54:
de this scope veranderen kan, maar is in de optiek van nette code echt te smerig, zeg maar illegaal. :)
offtopic:
Ok ok... heb het zelf nooit gebruikt (echt waar :*) ), maar ik dacht eraan dat het kon tijdens het schrijven van mijn post. Misschien getuigt het wel van de evilness waaruit ik opgetrokken ben, dat ik een beginned AS-er deze mogelijkheid van de hand doe })

[ Voor 3% gewijzigd door GrimaceODespair op 17-04-2003 10:22 ]

Wij onderbreken deze thread voor reclame:
http://kalders.be


  • xshredx
  • Registratie: Maart 2001
  • Laatst online: 21-08 15:17
GrimaceODespair: bedankt voor je uitleg!
B-Top: ook erg bedankt... je code werkt inderdaad goed.
Ik heb nog even nagedacht over jullie uitleg, en denk wel dat ik het nu snap.
Wat ook werkt is als ik net voor het xml object een referentie naar het huidige this object opsla in een variabele, en die dan gebruik in de levelXML.onload functie. (is eigenlijk gewoon hetzelfde als wat B-Top doet)
Mijn code is nu:
Flash ActionScript:
1
2
3
4
5
6
7
levelXML = new XML();
levelXML.onLoad = function () {
    this.owner.loadLevelFromXML();
}
levelXML.ignoreWhite = true;
levelXML.load(levelName);
levelXML.owner = this;

of
Flash ActionScript:
1
2
3
4
5
6
7
8
9
//store a reference to the game object for use inside the levelXML.onload callback
var gameObj = this;

levelXML = new XML();
levelXML.onload = function () {
   gameObj.loadLevelFromXML();
}
levelXML.ignoreWhite = true;
levelXML.load(levelName);


Anyway, vriendelijk bedankt jullie beiden! Weer wat bijgeleerd. En B-Top: bedankt voor de extra free tips ;)
Pagina: 1