Translations of this page:

This is an old revision of the document!
—-

3. Premier tour dans les entrailles de WME

Nous avons découvert les outils et nous avons appris les bases du script, il maintenant temps de voir comment ces connaissances s'applique à la conception d'un jeu. Avant de commencer ce tour nous allons d'abord faire un bref survol des types de fichiers que vous allez utiliser dans WME (et je recommande fortement d'utiliser cette convention de nomination):

*.script – sont les fichiers qui contiennent le programme proprement dit.
*.inc – sont les fichiers Include qui peuvent être intégré dans les fichiers de script. Leur but est de vous faire économiser des centaines de lignes de code, qui serait autrement répéter dans plusieurs fichiers. Le parfait exemple de ceci est le fichier data\scripts\base.inc. Vous intégrerez ce fichier dans presque tous les scripts.
*.scene – est le fichier qui contient la définition de la scène tel que défini dans l'éditeur de scène. Bien que vous puissiez définir ce fichier à la main, c'est beaucoup plus confortable d'utiliser sceneEdit pour faire le sale boulot.
*.sprite – produit de l'éditeur de sprite
*.actor – est le fichier qui définit l'acteur et ses animations.
*.entity – est le fichier qui stocke la définition des éléments sprite.
*.font – est le fichier de définition de police. C'est une interface entre la police effective et celle de WME. Actuellement, il existe deux types de polices pris en charge - de type bitmap et true-type.
*.window – est un fichier de définition des éléments de fenêtre GUI.
*.image – s'agit d'un fichier spécial pour stocker une image (avec la possibilité de tiling, etc.) Ceci est utile pour les éléments de l'interface graphique.
*.button – est un fichier contenant la définitions des boutons.

De plus WME a quelques fichiers spéciaux prédéfinis que vous pouvez également changer, mais ceux-ci sont par défaut:

default.game et startup.settings sont des fichiers créés par le Gestionnaire de projet et stockés dans le Package (principal) du jeu.
string.tab situé au même endroit contient la table de localisation pour les textes. (TRADUCTION A REVOIR)
items.items contient la définition des objets de l'inventaire utilisé dans le jeu (data\items)
responses.def est le fichier qui définit la manière dont la fenêtre de dialogue devrait ressembler.
inventory.def est le fichier qui définit à quoi ressemble la boite de l'inventaire (data\interface)

Mais assez parlé de cela et nous allons commencer avec la chose la plus simple - les ajustements de scène. Si nous explorons le contenu de l'une des scènes créées dans l'explorateur, nous allons voir qu'il y a toujours un fichier de scène et un dossier scr qui contient le fichier scene_init.script

WME lit le fichier de scène de la scène courante, prépare tous les nœuds tel que défini dans le fichier de scène, joint tous les scripts qui sont présents dans le fichier de scène et exécute le fichier appelé scene_init.script. C'est le fichier que nous allons examiner maintenant et je vais y ajouter mes commentaires:

#include "scripts\base.inc" 
 
// voici les trucs qui initialise la scène 
actor.SkipTo(733, 562); 		// On positionne l'acteur a une certaine position 
actor.Direction = DI_DOWN;	// On définit la direction vers laquelle il fait face 
actor.Active = true;			// On fait qu'il soit visible pour cette scène 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////// 
// état de la scène 
///////////////////////////////////////////////////////////////////////////////////////////////////////////// 
global Statewarehouse; 	// On créé une variable globale qui a le même nom que la scène précédé du mot State 
 
// Valeurs par defauts 
if(Statewarehouse==null)  	//Nous lançons le jeu pour la première fois donc la valeur n'a pas été encore initialisée 
{				 
  Statewarehouse.Visited = false;  //Nous n'avons jamais été dans la scène encore 
} 
//////////////////////////////////////////////////////////////////////////////// 
// scène de configuration en fonction des variables d'état 
//////////////////////////////////////////////////////////////////////////////// 
if(!Statewarehouse.Visited) //Si nous n'avons jamais été dans la scène 
{ 
  Statewarehouse.Visited = true; //Définir que nous sommes venu ici. 
 
  // ceci est notre première visite dans cette scène ... 
}

Maintenant cela peut vous paraitre débile comme script qui ne fait rien d'autre que de vérifier si nous somme aller dans une scène ou pas, mais croyez-moi, il y a plus qu'il n'y parait. Ceci est un script vraiment très astucieux qui compte avec beaucoup de possibilités différentes. il compte pour le jeu qui a été récemment lancé, il compte pour une scène revisitée, il compte du fait que vous pouvez avoir un événement spécial à l'entré de la scène. Rappelez-vous ces moments cinématographiques quand vous avez joué à une aventure, marchant dans une scène seulement pour voir le pont s'effondrer? Vous ne voudriez pas le voir s'effondrer à chaque fois que vous visitez cette scène? C'est sûr une fois suffit.

Alors maintenant, que nous avons découvert ce qui se passe dans ce script, c'est vraiment très simple. Comme il y a des milliers de méthodes différentes, j'ai décidé de choisir la manière douce façon de les présenter si besoin, même si Max étaient très malheureux avec moi la dernière fois, j'ai consulté des approches non-violentes.

Regardons un exemple du début du script.

actor.SkipTo(733, 562);

Nous savons que les objets ont leurs méthodes qui sont identifiable par leurs notation .name();. Alors purement et simplement nous appelons la méthode skipTo () de notre objet acteur. "actor" est une variable globale qui contient un objet acteur et est déclarée dans différent script. Maintenant, quelqu'un se lève et hurle: «Vous êtes un menteur, vous nous avez dit, que chaque variable globale doit être déclarée explicitement dans le script par un mot-clé global." Ha, je répondrais, que vous avez raison.Mais elle est là, mais sagement caché dans le fichier include base.inc. Vous constatez la puissance des includes? Vous n'avez pas à déclarer un acteur, que vous utiliserez pour presque chaque script. Il vous suffit de le mettre dans le fichier include et c'est tout. Le modèle fait le reste.

Donc revenons à notre objet acteur et regardons les méthodes que nous avons vu en tout premier et ajouter deux autres méthodes très utiles:

les méthodes et attributs de l'objet acteur
actor.SkipTo(x,y); - place un acteur à une position de l'écran donnée. Toujours ré-vérifier, que cette position est à l'intérieur d'une région.
actor.Direction = ?; - est un attribut qui définit la direction dans laquelle l'acteur attend. Nous avons 8 constantes (DI_UP, DI_DOWN, DI_LEFT, DI_RIGHT, DI_UPRIGHT, DI_UPLEFT, DI_DOWNRIGHT, DI_DOWNLEFT)
actor.Active = true / false; - est un attribut commun à tous les noeuds et acteurs qui le rend actif ou inactif (Visible ou invisible).
actor.GoTo(x,y); - au lieu de placer directement l'acteur, l'acteur se dirige vers coordonnées spécifiées.
actor.Talk(string); - L'acteur dit une ligne de texte.

Donc je parie que vous êtes impatient d'essayer les nouvelles méthodes en action. Travaillons sur notre scene_init.script alors! Ouvrez le fichier scene_init.script situé dans le dossier data→scenes→warehouse→scr. Notez qu'à partir de maintenant, lorsque je ferai référence à scene_init.script vous le trouverez facilement, sans mon aide. Localisez la ligne actor.Active = true; et ajoutez quelques lignes de plus derrière celle-ci de sorte vous obteniez le résultat suivant:

actor.Active = true; 
actor.GoTo(365,561); 
actor.Direction = DI_DOWN; 
actor.Talk("Quel magnifique entrepôt!");

Enregistrez votre script et lancez le jeu depuis le gestionnaire de projet. Avant que nous continuions, nous allons parler de l'un des plus importants aspects de la programmation du jeu dans WME.WME génère un fichier appelé wme.log qui est placé dans le répertoire racine du projet. C'est un fichier erreurs où tous les problèmes, erreurs de script, etc… sont stockées.Donc, si par exemple vous faite une faute de frappe dans le script, le jeu va tout simplement alerter de l'erreur au compilateur de scripts, mais ce fichier vous indique quel fichier et quelle ligne contient cette erreur. Apprenez donc à l'ouvrir souvent ou votre travail sera très lent.

Oh-key. Bougeons et essayons quelques interactions avec la scène. Ouvrez la scène d'entrepôt (warehouse) dans l'éditeur de scène à nouveau et sélectionnez l'entité région de la porte, que nous avons créé plus tôt.

Cliquez sur le bouton scripts. Et puis sur le bouton “New script” (Nouveau script).

Dans la boite de dialogue suivante, sélectionnez modèle "empty.script" sur le côté gauche et appuyez sur OK.

Cette opération va créer un fichier vide appelé door.script et le placer dans le dossier scr.

Elle attache également le fichier à l'entité à partir de maintenant vous êtes capable de récupérer une interaction avec notre porte. Appuyez sur OK à nouveau et enregistrer votre scène. Maintenant, ouvrez le fichier nouvellement créé, qui contient les éléments suivants:

#include "scripts\base.inc" 
 
//////////////////////////////////////////////////////////////////////////////// 
//on "event" 
//{ 
//  ... 
//}

Comme vous pouvez le voir ce n'est pas très utile. Il a intégré notre bon vieil ami base.inc mais à part ça il fait rien du tout. Nous devons le développer un peu.J'ai brièvement expliqué les évènements dans le chapitre script et maintenant il est temps de les utiliser. Changer le script pour lire:

#include "scripts\base.inc" 
on "LeftClick" 
{ 
	actor.Talk("J'ai cliqué sur la porte");	 
}

Sauvegardez le script, lancez le jeu et cliquez sur la porte avec le bouton gauche de la souris. Voila! Nous venons de définir notre première interaction avec le monde du jeu. Nous allons développer plus à ce sujet mais avant je vais vous offrir une liste de la plupart des événements utiles qui sont disponibles dans WME.

on "LeftClick" – Bouton gauche de la souris a été cliqué sur le nœud (entité).
on "RightClick" – Bouton droit de la souris a été cliqué sur le nœud (entité).
on "MiddleClick" – Bouton milieu de la souris a été cliqué sur le nœud (entité).
on "LeftDoubleClick" – Bouton gauche de la souris a été double-cliqué sur le nœud (entité).
on "RightDoubleClick" – Bouton droit de la souris a été double-cliqué sur le nœud (entité).
Il y a bien plus d'événements à utiliser, mais pour le début ça devrait aller avec ces derniers.

Les scripts attachés au nœud ont une autre caractéristique intéressante, Ils sont des partie de l'objet vous pouvez donc accéder à leurs paramètres par l'identifiant this.

Faisons l'expérience un peu avec ce concept. Changer le actor.Talk pour avoir:

actor.Talk(this.Name);

Si vous sauvegardez et lancez le jeu, lorsque vous allez cliquer sur la porte l'acteur dira "Door". Ça vous dit quelque chose? Mais bien sûr, c'est le nom nous avons mis dans l’éditeur de scène.Comme vous pouvez le voir, ce que nous avons établi dans l'éditeur de scène est facilement accessible à partir du script ce qui nous amène à une autre méthode pour l'acteur:

actor.GoToObject(Node); - Va aux coordonnées spécifiées dans l'éditeur de scène dans "Walk to" et se tourne alors vers la direction indiquée au même endroit.

Mais nous savons aussi que l'entité est représentée par le mot-clé this. Ca peut pas être plus simple que ça? Essayez de réécrire votre gestionnaire d'événement pour avoir:

on "LeftClick" 
{ 
	actor.GoToObject(this); 
	actor.Talk(this.Name);	 
}

Normalement si vous avez réglé correctement les coordonnées dans l'éditeur de scène, l'acteur doit aller à la porte et dire le mot "Door".

Dernière expérience avec notre fameuse porte le moment est de régler son paramètre Active sur False et donc de désactiver le hot-spot de la porte après l'avoir cliqué.

on "LeftClick" 
{ 
	actor.GoToObject(this); 
	actor.Talk(this.Name);	 
	this.Active = false; 
}

Nous avons égratigné en surface deux objets jusqu'ici. L'objet acteur et l'objet élément Région, que nous avons référencé par mot-clé this et caché en réglant son attribut Active sur false.

Laissez-moi revenir sur l'image que nous avons vu au début et regarder cette même image d'un point de vue des développeurs.

Maintenant, ça ne vous surprendra probablement pas que l'objet principal du jeu soit appelé Game(jeu). Vous pouvez aussi voir que tout dans ce schéma est dérivé de l'objet du jeu. Si nous regardons notre entité door (porte) de ce point de vue pour voir où elle est cachée, nous devrions aller par ce chemin:

Game → Scene → Door.

Nous savons que nous pouvons référencer un Scène comme Scène, mais ne vous laissez pas tromper.1)La scène est, dans la démo, en réalité définie dans le game.script qui est le cerveau maitre du jeu comme cela:

Scene = Game.Scene;

In the base.inc our Scene is defined as global Scene; and because we include base.inc into all our files, we can readily reference Scene object from everywhere.

As we can guess, Game object would serve for the global purposes of the game while Scene object would be useful for per Scene operations. Let me introduce you to the most used functions which represent very well each object.

Game.ChangeScene(filename); is a function which changes the scene. It takes as an argument path to the .scene file
Scene.GetNode(NodeName); is a function which returns the object named NodeName from the scene.

We’ll do something, which would demonstrate the power of WME now. Return to your warehouse scene in scene edit and let’s tweak it a bit. First we should create a new region and call it Exit. Position it to the left bottom corner as on the following image (you should immediately discover by now that it’s the red shape). Also note the position in the scene tree. If you put it above the floor, the mechanism presented wouldn’t work because the floor region would cover the Exit region and this way the Exit region would never get his voice. Last thing which remains to do is to attach a script to this region. Use an empty template and save the scene.

Before we go on, let’s discuss two more events (we already know Mouse related events).

on "ActorEntry" occurs when actor enters the current region. This is the basic logic behind so called trap regions. Trap regions mean that actor triggers an event upon entering certain portion of the screen.

on "ActorLeave" occurs when actor leaves the current region.

Last thing we’d need for this example is taking the game out of player’s control. This is the vital part of successful game design. We want to trigger a scene and in the middle player clicks somewhere else, character didn’t finish the whole sequence and you’re stuck in an unsolvable loop. This kind of nightmare met many designers so let’s avoid it while we still can.

Game.Interactive = true / false; Takes the game out of players control or returns it back. Always remember to return the game back to Interactive mode or your game will be stuck and players will get angry. So always think of the noninteractive mode as of a block.

We’ll try to combine those ideas into our first more complex game logic.

First we’ll adapt the door.script to look like this:

on "LeftClick"   
{ 
	Game.Interactive = false;    // We want our player to make more things at once and we don't want to be interrupted. 
	actor.GoToObject(this); 
	this.Active = false;  // We disable the door so the hotspot is not visible or active anymore. 
	actor.Talk("Oh no. The door is welded shut.");	 
	Game.Interactive = true; // Allow player to play some more. 
}

Then we open the exit.script and add a new event there. The file should look like this:

#include "scripts\base.inc" 
 
on "ActorEntry"  //Actor entered the Exit region 
{ 
	Game.Interactive  = false; 
	var door = Scene.GetNode("Door"); //We get the object corresponding to the scene name door into a variable named door 
 
	if (door.Active) // If door was not clicked, it was not disabled, so this path will be used 
	{ 
		actor.Talk("I don't want to return to my flat before I am sure I can't enter the warehouse!"); 
	}	 
	else //we know that the door is welded shut so we can leave the scene 
	{ 
		actor.Talk("Oh well, there really is no way how could I enter that damn warehouse"); 
		Game.ChangeScene("scenes\room\room.scene"); 
	} 
	Game.Interactive = true; 
}

Okay. Time to explain what’s going on in here. Our thought process as a game designer was that player needs to enter the warehouse but the warehouse is welded shut. We for some reason need our player to discover the fact that the door is welded shut so the game could proceed. So we decided to allow him to change the scene only after he discovered that the warehouse was welded shut.

Code-wise we have the region which would be normally used as an exit region, but we’ve added the condition that until the door region was active (which means that player didn’t click on it because if he did, the door region turns inactive), player can’t leave the screen.

Save all scripts and run the game. Test the game thoroughly so you’re sure that you really understand the coding behind this logic. It’s one of the critical aspects of adventure game designing and if you get this right, you’ll be in no time creating your own adventures.

Important:
Critical thing to know is how path in wme works in general. We’ve seen in Game.ChangeScene("scenes\room\room.scene"); that we didn’t start with the data path. That’s totally correct! Write it thousand times – I’ll never include the root package name in the referenced path. Why? Because this would work only in debug mode, and when you compile your game in the packages and send it around, it won’t work. The same applies to all files (images, sounds etc.). Never reference files outside of packages. They’ll work in debug mode, but they won’t get compiled and you’ll be in it for the worst nightmares. Think of package as of a virtual root directory and all references should be starting from that point. More about that in the chapter about packages.

Let’s move on to the last part of this chapter, which will cover the daemon issue. We’ll start right off with our first daemon. Before we explain how it works, we’ll have to take a look at a few new methods:

Game.AttachScript(filename); - Attaches a new script file to the Game object, making it global for the whole game. This script is attached until it’s detached or ends.
Game.DetachScript(filename); - Detaches script from the Game object.

Scene.AttachScript(filename); - Attaches a new script file to the current scene, making it active until player doesn’t change the current scene, or script is detached or ends.
Scene.DetachScript(filename); - Detaches script from the Scene object.

We can of course attach script to the scene object in the Scene Editor or script to Game object in the Project Manager, but we want our script to be attached in the middle of the gameplay.

Next command we’d learn is totally necessary for the daemon writing. It’s command Sleep.
Sleep(time); - Pauses the script for certain amount of milliseconds.

Sleep serves two purposes. First - it lets the script wait for certain amount of time and second - it hands the program flow to other threads. Here’s the typical daemon pitfall:

var a = 0; 
while (1) // infinite loop, the same as while(true) 
{ 
   a = a + 1; 
   if (a > 100) a = 0; 
}

Never do this. It’ll halt your computer. Script takes over the whole processing power and the rest of the game never get control back. This way also Windows never gets control back which results in hung up computer. So how to fix this?

var a = 0; 
while (1) 
{ 
   a = a + 1; 
   if (a > 100) a = 0; 
   Sleep(1);		 
}

This daemon is correct (remember our previous chapter), because it hands over the control from the script. One millisecond is here only formally. The important fact is that you DO hand over the control.

So enough theory and let’s make some daemon fun with the commands, we’ve just learned. Oh wait. We’d need one more command:

Game.Msg(text); - although we’ve seen it before let’s recap that it displays an onscreen debug message. Very useful command.

What we’re going to do is a funny little timed sequence, which occurs in our warehouse scene. The scenario is as follows. Player examines the door to discover that there’s a time bomb attached to it. If he can’t exit quickly enough, the Scene is reloaded (it’s the hardcore-adventure-fan-way how to tell the player, that he has died).

First we’ll create the new script in the scr folder (from the empty template) and name it bomb.script.

Tip: if you get more skilled, you can add the new files manually by creating a text file with a script extension created in the corresponding folder.
Then we modify the door.script so it’ll read the following:

#include "scripts\base.inc" 
 
on "LeftClick"   
{ 
	Game.Interactive = false;    // We want our player to make more things at once and we don't want to be interrupted. 
	actor.GoToObject(this); 
	this.Active = false;  // We disable the door so the hotspot is not visible or active anymore. 
	actor.Talk("Oh no. There's a timed bomb attached to the door! I have to find an exit before it explodes.");	 
	Scene.AttachScript("scenes\warehouse\scr\bomb.script"); 
	Game.Interactive = true; // Allow player to play some more. 
}

I’ve hilighted the line which attaches our new script and thus starting the countdown.
Now let’s look at the bomb.script contents.

#include "scripts\base.inc" 
 
for (var timer=6;timer>-1;timer = timer -1)  // let's set up a loop which would go down from 6 to 0. 
{ 
	Game.Msg("Countdown: " + timer); //Let's display how much time do we have left. 
	Sleep(1000); // Sleep one second (and also hand the game over to other threads) 
} 
 
Game.Interactive = false; //Death cutsene 
actor.Talk("I've just died. Let's try again"); 
Game.ChangeScene("scenes\warehouse\warehouse.scene"); 
var door = Scene.GetNode("Door"); 
door.Active = true; // If we die, we need to return the door to its active state or we'll never discover the bomb again. 
Game.Interactive = true;

And the last change we’ll make is to the exit.script. If we found an exit in time, actor will be able to escape, but what if he arrives there in the nick of time and while talking his line, the time runs away and he dies? So let’s prevent this from happening by detaching the bomb script and letting him say something more reasonable.

So the contents of the exit.script would look like this:

#include "scripts\base.inc" 
 
on "ActorEntry" 
{ 
	var door = Scene.GetNode("Door"); 
	Game.Interactive = false; 
	if (door.Active) 
	{ 
		actor.Talk("I don't want to return to my flat before I am sure I can't enter the warehouse!"); 
	}	 
	else 
	{ 
		Scene.DetachScript("scenes\warehouse\scr\bomb.script"); 
		actor.Talk("Phew. That was close"); 
		Game.ChangeScene("scenes\room\room.scene"); 
	} 
	Game.Interactive = true; 
}

Save everything and test our little scheme to see some more of the effects.

On closing of this chapter I’ll introduce a little change to our scheme. This change is not necessary in our example, but it will be necessary in different case. For now we’re testing, if the entity region Door is Active for another decision. But what if we need to apply the similar logic to more than one scene? If we stand in different scene, we logically can’t use entity this way because it’s not anymore part of the current scene.

So what we can do is using a global variable. Let’s make a change to our scripts then!
Last code listing for this script triplet is then (changes in bold):

bomb.script

#include "scripts\base.inc" 
 
for (var timer=6;timer>-1;timer = timer -1)  // let's set up a loop which would go down from 6 to 0. 
{ 
	Game.Msg("Countdown: " + timer); //Let's display how much time do we have left. 
	Sleep(1000); // Sleep one second (and also hand the game over to other threads) 
} 
 
Game.Interactive = false; //Death cutsene 
actor.Talk("I've just died. Let's try again"); 
var door = Scene.GetNode("Door"); 
door.Active = true; // If we die, we need to return the door to its active state or we'll never discover the bomb again. 
global doorClicked = false; 
Game.ChangeScene("scenes\warehouse\warehouse.scene"); 
Game.Interactive = true;

door.script

#include "scripts\base.inc" 
 
on "LeftClick"   
{ 
	Game.Interactive = false;    // We want our player to make more things at once and we don't want to be interrupted. 
	global doorClicked = true; 
	actor.GoToObject(this); 
	this.Active = false;  // We disable the door so the hotspot is not visible or active anymore. 
	actor.Talk("Oh no. There's a timed bomb attached to the door! I have to find an exit before it explodes.");	 
	Scene.AttachScript("scenes\warehouse\scr\bomb.script"); 
	Game.Interactive = true; // Allow player to play some more. 
}

exit.script

#include "scripts\base.inc" 
 
on "ActorEntry" 
{ 
	var door = Scene.GetNode("Door"); 
	Game.Interactive = false; 
	global doorClicked; 
	if (!doorClicked) 
	{ 
		actor.Talk("I don't want to return to my flat before I am sure I can't enter the warehouse!"); 
	}	 
	else 
	{ 
		Scene.DetachScript("scenes\warehouse\scr\bomb.script"); 
		actor.Talk("Phew. That was close"); 
		Game.ChangeScene("scenes\room\room.scene"); 
	} 
	Game.Interactive = true; 
}

We’ve seen in this chapter some neat tricks with the way how we can handle the game logic. We’ve also learned some of the important concepts for the tying scene to the code and we are able to use a couple of very basic commands and object methods. But for now we all the time used a lot of prebuilt demo code. Next chapter is all about starting the project from scratch. We’ll build upon this blank project until we create a little feature packed game. So stay tuned, we’re getting into it.

1) Rappelez-vous ce problème de notation imbriqué?
 
fr/wmebook/ch3.1318949929.txt.gz · Last modified: 2011/10/18 16:58 by Anto0085
Recent changes RSS feed Creative Commons License Driven by DokuWiki