====== 4. Commencer un projet ======
Juste avant nous avons fait des essais sur une démo pré-compilées, Mais maintenant il est tant de revenir à la racine et de créer un nouveau projet complet comme on a envie. Démarrez le gestionnaire de projet et presser Ctrl+N (sinon cliquez sur l’icône nouveau projet (new project)ou choisissez Nouveau projet (New Project) dans le menu Fichiers (File) ). Nommez votre projet comme vous le souhaitez et laissez WME le créer.
Vous pouvez voir que WME a créé un nouveau projet en y mettant quelques éléments prédéfinis ainsi le démarrage de notre nouveau jeu sera plus simple car nous pouvons dès maintenant lancer ce projet minimaliste et ça marche. Ce que nous allons quand même faire, c'est de porter notre attention sur le point avec lequel le jeu commence réellement. Ce point c'est un simple fichier appelé **game.script** //(Qui se trouve dans data->scripts)//.
Vous pouvez facilement vérifier dans votre gestionnaire de projet, que le jeu a un script attaché à lui et ça ne sera pas une surprise pour vous, que ce script c'est ... game.script. Tout simplement dit, la toute première chose que fait WME après avoir initialisées toutes les fenêtres du système c'est d’exécuter ce fichier. Je parle de ça assez largement, parce que je veut que vous compreniez bien la souplesse de WME. A ce niveau rien n'est décidé, à quoi va ressembler le jeu, comment le jeu se joue, etc...
Essayons de découvrir ce que le script du jeu fait par petit blocs:
#include "scripts\base.inc"
#include "scripts\keys.inc"
D'abord nous intégrons le fichier base.inc, qui contient 3 variables globales - actor, Scene et Keyboard. Comme ça on a pas besoin, dans chaque script, à déclarer global Scene; avant d'appeler par exemple Scene.GetNode();.
Base.inc à son tour, introduit un autre fichier appelé const.inc qui contient les constantes les directions de l'acteur et les alignement de texte. Au cours de la création de notre jeu nous ajouterons des variables globales et des constantes dans ces fichiers pour garder notre jeu bien rangé. Nous pouvons également séparer nos Includes dans plus de fichiers si on est obligé. Le deuxième fichier contient la définition des codes claviers, nous n'avons donc pas à deviner des chiffres mais tapez simplement leur représentation textuelle comme VK_ESCAPE au lieu de 27 si on veut tester la touche Esc.
Keyboard = Game.Keyboard;
Scene = Game.Scene;
Comme vous devriez déjà le savoir maintenant, tous les objets de base sont membres de l'objet Game. Mais il y a deux raisons pour lesquelles nous avons besoin de variables pour stocker ces objets imbriqués. Premièrement WME ne permet pas les imbrications donc vous ne pouvez pas utiliser par exemple:
var e = Game.Scene.GetNode("Door");
et deuxièmement ça semble, de toute façon, plus clair si vous écrivez //Scene.GetNode("Door");//.
Donc, retour à ces lignes - Nous avons créé une variable globale Keyboard qui fait référence à l'objet //Game.Keyboard// et qui sera utilisé pour interrogé les saisies clavier et une variable globale qui fait référence à l'objet Scene.
Comme petite annotation dans la marge - Cet objet est toujours le même, même si vous décidez de changer des scènes ainsi vous n'avez pas à la réassigner à chaque fois que vous avez changé la scène.
global WinMenu = Game.LoadWindow("interface\menu\menu.window");
WinMenu.Visible = false;
Par défaut vous avez un truc prédéfini, que si vous faites un clic droit sur un hot-spot, un petit menu apparait dans lequel vous pouvez choisir entre trois actions. **Maintenant, faite moi plaisir et dégommer moi ces 2 lignes.** Nous allons essayer de le dépouiller.
Vous devez vous demander, qu'est que window (fenêtre)? Window c'est un groupe d'éléments graphique (boutons, images, champs d’édition pour saisir un texte) ce qui est bien pratique pour concevoir un GUI (Interface Graphique pour l'Utilisateur). Le gros avantage c'est que c'est indépendant de la scène, vous pouvez donc l'avoir à l'écran pour toutes les scènes sans avoir à replacé, expressément, les éléments à chaque fois.
Nous verrons la fabrication des fenêtres plus tard, focalisons nous pour le moment sur ce que veulent dire ses deux lignes. La première ligne charge un fichier .window (qui contient une définition de la fenêtre graphique) et la seconde la rend invisible. Cette fenêtre est attachée à l'objet Game donc le changement de scène ne l'affecte pas. Notez que les fenêtres sont des exceptions. Les entités utilise //Active//, les fenêtres utilise //Visible//.
var win = Game.LoadWindow("interface\system\caption.window");
global WinCaption = win.GetWidget("caption");
Et encore un chargement de fenêtre! Celle-ci est destinée au légendes (infos-bulles) chaque fois que vous passez votre souris sur le hot-spot. De plus, nous introduisons une toute nouvelle méthode pour faire référence à une partie spécifique de la fenêtre. Celui-ci est le champ réel qui contient le texte. Maintenant pour me faire plaisir - GetWidget est l'ancienne manière d'obtenir le contrôle. Maintenant nous utilisons plus facilement GetControl , donc réécrivez moi la seconde ligne en :
global WinCaption = win.GetControl("caption"); //Vous pouvez également constater la similitude Scene.GetNode(name); Window.GetControl(name);
vient ensuite:
global MenuObject = null;
Nous stockons un **null** dans MenuObject qui sera utilisé plus tard pour voir ce qui est cliqué. Null est une valeur speciale, qui indique qui n'y a absolument rien. Et vous pouvez, aussi bien, effacer cette ligne.
actor = Game.LoadActor("actors\molly\molly.actor");
Game.MainObject = actor;
Maintenant ces lignes sont importantes. La variable "actor" est une variable globale dans base.inc qui sera remplis, maintenant, avec la définition de notre fichier .actor. Encore plus tard nous verrons comment les fichiers d'acteur sont construit. Pour le moment vivons avec le fait que nous avons Molly et qu'elle peut faire tout ce qu'on veut.
Aussi en note dans la marge, nous pouvons une deuxième fonction **Scene.LoadActor(actor);** qui charge l'acteur mais l'attache à la scène courante et l'acteur est détruit quand vous quittez la scène.
La dernière ligne, nous plaçons notre acteur fraichement chargé dans l'attribut **MainObject**, ce qui fait la seul chose - lorsque l'acteur se déplace et que la scène est censée défiler, le défilement s'oriente vers l'acteur. Au cas ou il y aurait plusieurs acteurs échangeables (Comme par exemple dans Day of the Tentacle) Cet attribut permettra d'avoir un défilement correct quelque soit le personnage sélectionné. Vous pouvez régler Null dans cet attribut.
Game.AttachScript("scripts\game_loop.script"); //anciennement connu comme game_daemon.script
Nous allons fixer le processus clés, qui s'occupe de l'affichage des légendes et de l'apparition de l'inventaire quand vous glissé votre souris en haut. Ce sera notre second fichier que nous allons examiner dans une minute. Là encore vous pouvez voir, que ce fichier est globalement attaché au jeu entier.
Et voici quelque chose autrefois extrêmement important - désormais paramétrable à partir du gestionnaire de projet:
Game.ChangeScene("scenes\Room\Room.scene");
C'est le point où vous définissez à quel scène commence votre jeu. Habituellement ça sera un genre d’écran menu sauf si vous démarrez par une intro. ou du genre. Maintenant vient la partie principale de nettoyage. Comme nous sommes en plein épuration de notre jeu, nous allons maintenant virer toutes les interactions avec l'inventaire seulement pour les ajouter plus tard lorsque nous serons plus qualifiés.
Donc modifiez simplement les événements dans “LeftClick” pour avoir:
on "LeftClick"
{
var ActObj = Game.ActiveObject;
if(ActObj!=null)
{
ActObj.ApplyEvent("LeftClick");
}
else
{
Scene.ApplyEvent("LeftClick");
}
}
Quand nous cliquons avec le bouton droit dans la scène, on regarde en premier si le clique était sur un Hot-spot. WME sait s'il y a un hot-spot sous le pointeur de la souris et remplit l'attribut **ActiveObject**en conséquence.
Nous avons, maintenant, réduit notre logique à deux états. Si nous avons cliqué sur un objet, nous allons appelé sont événement “LeftClick”. Ça nous ramène à nos essais dans le dernier chapitre, où nous avons attribué l'événement “LeftClick” de la porte. C'est la raison pour laquelle cet événement s'est appelé en premier. **ApplyEvent** c'est une méthode qui est utilisée pour lancer des événements manuellement.
Si on clique n’importe où (Pas de hot-spot sous le pointeur), l'événement de la scène "LeftClick" est déclenché. Là, j'ai cru vous entendre crier : " Attends un peu, c'est quoi l’événement de la scène “LeftClick”?!".
C'est une autre astuce de WME qui sort du modèle de scène (Scene template). Si vous créez une nouvelle scène, ou vous ouvrez votre salle de la scène dans l’éditeur de scène et que vous allez dans //l'onglet Properties// et que vous cliquez sur le bouton **"Scripts..."**, vous allez voir qu'il y a deux scripts attachés. Votre scene_init.script préféré et scripts/scene.script. et comme c'est le modèle que chaque scène utilise, découvrons ce sombre mystère que contient ce fichier?
#include "scripts\base.inc"
on "LeftClick"
{
actor.GoTo(Scene.MouseX, Scene.MouseY);
}
HA! C'est ça. Si cet évenement est déclenché, notre acteur va à la position actuelle de la souris dans la scene. Donc pour resumer avec des choses simples: Si on clique sur un hot-spot c'est le "left click" qui est declenché alors notre acteur marche vers la position courante de la souris. Et c'est juste le clique gauche pour le moment.
Déplaçons nous vers l’événement suivant “RightClick” et effacez le bloc entier. Nous n'en avons pas besoin tout suite et ne ferais que vous faire tourner la tête.
Les deux derniers événement (“Keypress” et “QuitGame”) fonctionnent avec les menus d'affichage et la manipulation des combinaison de touches Alt + F4. Nous n'avons pas à nous embêter avec elle pour l'instant, mais rassurez-vous, nous allons les voir rapidement aussi.
Récapitulons les nouvelles méthodes et les attributs que nous avons appris jusqu'à présent dans ce chapitre:
**var window = Game.LoadWindow(window filename);** - Charge une définition de fenêtre, qu'il attache à l'objet Game et lui retourne éventuellement un référence (dans ce cas à la variable window)
**window.Visible = true / false;** - Fait que la fenêtre chargée est visible/invisible à l'écran.
**window.GetContro(controlname);** - Retourne une référence à un contrôle se trouvant dans la fenêtre.
**Game.LoadActor(actor filename);** - Charge un acteur d'un fichier et l'attache à l'objet Game.
**Scene.LoadActor(actor filename);** - Charge un acteur d'un fichier et l'attache à un objet scène se détruisant après avoir quitter la scène.
**Game.MainObject = actor;** Défini vers quel acteur le défilement de la scène est synchronisé.
**Game.ActiveObject;** - Stocke la reference de l'objet au dessus duquel votre souris se trouve.
**Game.ApplyEvent(eventName);** - c'est une méthode qui déclenche les événements associés à l'objet Game.
**Scene.ApplyEvent(eventName);** - C'est une méthode qui déclenche les événements associés à un objet scène.
**Node.ApplyEvent(eventName);** - C'est une méthode qui déclenche les événements associés au nœud (entité) stocké dans l'objet en question.
Pour mieux comprendre la dernière commande, nous pourrions facilement nous précipiter dans notre dernier chapitre à l’événement “LeftClick” de notre porte qui était comme ceci:
var door = Scene.GetNode("Door");
door.ApplyEvent("LeftClick");
J'espère que vous voyez ce que je veux dire maintenant ... Examinons maintenant si vous avez le même fichier **game.script** que j'ai fait avant que nous commencions:
#include "scripts\base.inc"
#include "scripts\keys.inc"
// store some of the game's attributes in global variables for convenience
Keyboard = Game.Keyboard;
Scene = Game.Scene;
// load the "caption" window
var win = Game.LoadWindow("interface\system\caption.window");
global WinCaption = win.GetControl("caption");
// load our main actor
actor = Game.LoadActor("actors\molly\molly.actor");
Game.MainObject = actor;
// run the "daemon" script
Game.AttachScript("scripts\game_daemon.script");
// which scene to load?
Game.ChangeScene("scenes\Room\Room.scene");
on "LeftClick"
{
var ActObj = Game.ActiveObject;
if(ActObj!=null)
{
ActObj.ApplyEvent("LeftClick");
}
else
{
Scene.ApplyEvent("LeftClick");
}
}
on "Keypress"
{
// on Esc or F1 key
if(Keyboard.KeyCode==VK_ESCAPE || Keyboard.KeyCode==VK_F1)
{
// load and display the main menu window
WinCaption.Visible = false;
var WinMainMenu = Game.LoadWindow("interface\system\mainmenu.window");
WinMainMenu.Center();
WinMainMenu.GoSystemExclusive();
Game.UnloadObject(WinMainMenu);
}
}
on "QuitGame"
{
// on Alt+F4 (window close)
// load and display the quit confirmation window
WinCaption.Visible = false;
var WinQuit = Game.LoadWindow("interface\system\quit.window");
WinQuit.Center();
WinQuit.GoSystemExclusive();
// and if the user selected Yes
if(WinQuit.xResult)
{
// quit the game
Game.QuitGame();
}
// otherwise just unload the quit window from memory
else Game.UnloadObject(WinQuit);
}
Maintenant testons le jeu et regardons s'il fonctionne encore vu que nous avons fait quelques changement. Et comme nous voulons continuer à epuré notre jeu au strict minimum, nous devons voir dans **game_loop.script** (Ancienement nommé game_daemon.script)
#include "scripts\base.inc"
global WinCaption;
global WinMenu;
Nous pouvons tranquillement effacer la ligne **global WinMenu;** parce que nous avons déjà abandonné l'idée de menu clic droit.
while(true){
Ça sera toujours vrai. avec ça nous envisageons une boucle sans fin. Pour clore cette boucle, on aurait pu faire appel //Game.DetachScript("scripts\game_loop.script");//
var ActObj = Game.ActiveObject;
Nous avons vu exactement la même ligne dans le game script
if(Game.Interactive && ActObj!=null)
{
D'abord on test si le jeu est interactif et que notre souris est au dessus d'un objet actif. Mais nous allons rendre cette condition un peu plus simple, donc nous allons mettre de côté toute logique d'inventaire d'éléments et ça va ressembler à ceci:
if(Game.Interactive && ActObj!=null)
{
WinCaption.X = Game.MouseX;
WinCaption.Y = Game.MouseY + 20;
WinCaption.TextAlign = TAL_LEFT;
WinCaption.Text = ActObj.Caption;
WinCaption.SizeToFit();
if(WinCaption.X + WinCaption.Width > Game.ScreenWidth) WinCaption.X = Game.ScreenWidth - WinCaption.Width;
if(WinCaption.Y + WinCaption.Height > Game.ScreenHeight) WinCaption.Y = Game.ScreenHeight - WinCaption.Height;
WinCaption.Visible = true;
WinCaption.Focus();
}
else WinCaption.Visible = false;
Du coup que ce passe t'il si on a notre souris au dessus l'objet?
WinCaption.X = Game.MouseX;
WinCaption.Y = Game.MouseY + 20;
WinCaption contient les références au champs de texte à l'intérieur de la fenêtre des légendes chargés dans le script du jeu. Nous avons fixé sa position aux mêmes coordonnées que la position en X de la souris et la position en Y de la souris + 20 donc la légende sera 20 pixels en-dessous.
WinCaption.TextAlign = TAL_LEFT;
WinCaption.Text = ActObj.Caption;
Ensuite, nous avons défini l'alignement du texte à gauche et mis le texte de la légende dans le nœud légende (node caption) comme défini dans l'éditeur de scène.
WinCaption.SizeToFit();
Nous redimensionnons la légende pour que sa taille soit la même que la taille du texte affiché.
if(WinCaption.X + WinCaption.Width > Game.ScreenWidth) WinCaption.X = Game.ScreenWidth - WinCaption.Width;
Si la largeur de légendes s'étend toutefois hors de l'écran, nous avons réglé notre position X de la légende pour finir exactement à la frontière de l'écran (basé sur la résolution).
if(WinCaption.Y + WinCaption.Height > Game.ScreenHeight) WinCaption.Y = Game.ScreenHeight - WinCaption.Height;
Nous faisons pareil avec la hauteur et la bas de l’écran.
WinCaption.Visible = true;
WinCaption.Focus();
Nous affichons notre légende en la rendant visible et la mettre en évidence, ce qui est dans ce cas pas nécessaire, parce que nous n’interagissons pas avec elle. Donc on peut simplement effacé cette ligne aussi.
}
else WinCaption.Visible = false;
Le reste signifie soit que le jeu est passé en mode non-interactif ou soit nous avons déplacé notre souris hors de tout hot-spots actifs. Dans les deux cas nous avons tout simplement cacher la légende.
if(Game.Interactive && Game.MouseY < 45 && !Game.ResponsesVisible && !WinMenu.Visible) Game.InventoryVisible = true;
else if(Game.MouseY > 100 || Game.ResponsesVisible || !Game.Interactive) Game.InventoryVisible = false;
Ces deux ligne concerne l'apparition/disparition de l'inventaire et on peut donc les effacer.
Sleep(20);
Souvenez vous ce que j'ai dis au sujet des boucles sans fin et les blocages d'ordinateur? j'en ai assez dit.
Alors regardons notre **game_daemon.script** révisé.
#include "scripts\base.inc"
global WinCaption;
// infinite loop
while(true){
var ActObj = Game.ActiveObject;
if(Game.Interactive && ActObj!=null)
{
WinCaption.X = Game.MouseX;
WinCaption.Y = Game.MouseY + 20;
WinCaption.TextAlign = TAL_LEFT;
WinCaption.Text = ActObj.Caption;
WinCaption.SizeToFit();
if(WinCaption.X + WinCaption.Width > Game.ScreenWidth) WinCaption.X = Game.ScreenWidth - WinCaption.Width;
if(WinCaption.Y + WinCaption.Height > Game.ScreenHeight) WinCaption.Y = Game.ScreenHeight - WinCaption.Height;
WinCaption.Visible = true;
}
else WinCaption.Visible = false;
Sleep(20);
}
Comme vous pouvez le voir, c'est l’épure de notre projet de départ. Pas d'inventaire, pas de menus, un simple jeu Point and Click. En outre, vous comprenez maintenant, qu'est qui supervise derrière le jeu et comme nous bâtirons sur elle, vous vous sentirez plus à l'aise en sachant que vous avez une base solide comme la roche. Prochaine étape: **les acteurs!**