Translations of this page:

This is an old revision of the document!
—-

Dentro del cerebro y el cuerpo de tu juego

Si estás leyendo esto te preguntarás acerca de los mecanismos que existen bajo WME, o eres Mnemonic comprobando las meteduras de pata que escribo :-).
Hoy he decidido guiarte a través de los scripts que incluyen los ejemplos de WME y mostrarte cómo funcionan. Pero empecemos, porque hay bastantes cosas que cubrir.

Vete a la carpeta projects/wme_demo/data del kit de desarrollo WME y empezaremos por ahí.

Verás tres archivos, que son default.game, startup.settings and string.tab.

default.game - es generado por el Project Manager y contiene la configuración básica del juego. Puedes ajustar estos valores en la ventana izquierda del Project Manager, donde puedes editarlos a mano. Este es el método recomendado a los principiantes.

startup.settings - es generado por el Project Manager y contiene la configuración de la ventana de inicio y ajustes básicos, como la ruta del registro de Windows, etc. Las reglas de edición son las mismas que default.game.

string.tab - es muy importante para la localización del juego a otros idiomas. En resumen, es un archivo que contiene una lista de ID - textos. En el juego puedes utilizar estas cadenas de texto con la siguiente instrucción:

  actor.Talk("/TXT0001/Hola");

Normalmente, Molly dice Hola, pero si en el archivo string.tab tienes una línea como esta:
TXT0001[TAB]Bon giorno.
será escrita en su lugar. Ten en cuenta que, en algunos casos, la cadena de texto necesita ser traducida a la fuerza.

Game.Msg("/TXT0001/Hola"); // Esto muestra /TXT0001/Hola 
// Para esto, usaremos la función Game.ExpandString 
Game.Msg(Game.ExpandString("/TXT0001/Hola.")); 
// que mostrará el texto correcto, y además es la 
// forma correcta de manipular cadenas

Así que, cuando pienses en manipular cadenas de texto que serán traducidas a otros idiomas, lo mejor es usar esta función al principio.

Bien. Ahora nos vamos a ir a la carpeta scripts. Aquí encontrarás tres archivos include. Estos son base.inc, const.inc y keys.inc.

base.inc está incluído por defecto en todos los scripts que creas, y debe contener las variables globales del juego. He escrito sobre esto en el tutorial Variables y objetos. base.inc también incluye el archivo const.inc, que tiene las constantes del juego y es mejor mantenerlas separadas de las variables. Ten en cuenta que también son variables, pero son reiniciadas en cada inclusión de script. Así que no caigas en la trampa de crear una variable global (que no va a ser constante) en base.inc. De lo contrario, haría una bonita constante con ella. ;-) keys.inc es para la definición de algunos códigos de teclado especiales. Afortunadamente, Mnemonic ya encontró estos números y nos ha ahorrado algunos problemas.

Una vez resuelto el tema de los archivos include, vamos a algo más serio. Cuando nuestro juego arranca, WME inicializa el objeto Game principal y ejecuta el script que tiene vinculado (puedes cambiar el nombre de este archivo en el Project Manager, en la ventana izquierda, en Game Settings → Scripts). Aquí es donde comenzamos, así que abre el archivo y vamos a echarle un vistazo línea a línea.

#include "scripts\base.inc" // cosas básicas 
#include "scripts\keys.inc" 
 
Keyboard = Game.Keyboard; 
Scene = Game.Scene;

También incluímos el archivo keys.inc, porque vamos a comparar algunos códigos de teclado y los nombres simbólicos son más fáciles de recordar que los números.

A continuación, inicializamos dos variables globales (debes conocerlas de base.inc, donde están definidas), y lo hacemos así porque WME no soporta anidar funciones, así que no puedes escribir Game.Scene.GetEntity(); por ejemplo.

// carga el menú del botón derecho del ratón 
global WinMenu = Game.LoadWindow("interface\menu\menu.window"); 
WinMenu.Visible = false; 
 
// carga el título de la ventana 
var win = Game.LoadWindow("interface\system\caption.window"); 
global WinCaption = win.GetWidget("caption"); 
 
// carga las pistas de la demo 
global WinHints = Game.LoadWindow("interface\demo\demo_hints.window"); 
WinHints.Visible = true; 
 
// carga los créditos 
global WinCredits = Game.LoadWindow("interface\credits\credits.window"); 
WinCredits.Visible = true; 
 
global MenuObject = null;

Además, ya ha sido comentado por Mnemonic, el comportamiento de la ventana se define en los archivos de la ventana, no en game.script, y están vinculados a las ventanas a través de archivos separados. Por último, hemos preparado un objeto vacío con el que trabajaremos más tarde.

// carga nuestro actor principal 
actor = Game.LoadActor("actors\molly\molly.actor"); 
Game.MainObject = actor;

Ahora vemos que MainObject es nuestro actor principal, pero podemos cargar tantos actores como deseemos y cambiar entre ellos con Game.MainObject, así conseguimos el cambio de actor principal. Ten en cuenta que para los modelos en tiempo real necesitas usar Game.LoadActor3D(); en su lugar.

Y ahora, vayamos a la línea más importante:

// ejecuta el script "daemon" 
Game.AttachScript("scripts\game_daemon.script");

Como debes saber, si vinculamos un script al objeto Game, será válido para todo el juego así que los scripts que definamos aquí serán usados durante toda la aventura. Podemos vincular tantos como deseemos, pero en esta demo sólo hay uno. Ya le echaremos un vistazo de cerca más adelante.

// objetos iniciales 
Game.TakeItem("money");

De nuevo, está bastante claro. Pero recuerda que debes tener un actor principal vinculado antes de intentar darle dinero ("money").

  Game.ChangeScene("scenes\room\room.scene");

Al final conseguimos algo presentable: cargamos nuestra primera escena que está definida en el Scene Manager. La siguiente parte del game.script es la definición de los controladores del juego.

on "LeftClick" 
{ 
  // ¿qué hemos seleccionado? 
  var ActObj = Game.ActiveObject; 
  if(ActObj!=null) 
  { 
    // seleccionando un objeto del inventario con el ratón 
    if(ActObj.Type=="item" && Game.SelectedItem==null) 
    { 
      Game.SelectedItem = ActObj; 
    } 
    // usando un objeto del inventario sobre otro 
    else if(Game.SelectedItem != null && Game.SelectedItem!=ActObj) 
    { 
      var Item = Game.SelectedItem; 
      if(ActObj.CanHandleEvent(Item.Name)) ActObj.ApplyEvent(Item.Name); 
      else if(Item.CanHandleEvent("default-use")) Item.ApplyEvent("default-use"); 
      else if(ActObj.CanHandleEvent("default-use")) ActObj.ApplyEvent("default-use"); 
      else actor.Talk("I can't use these things together."); 
    } 
    // sólo es un click 
    else ActObj.ApplyEvent("LeftClick"); 
  } 
  // de lo contrario, envía el evento LeftClik a la escena 
  else 
  { 
    Scene.ApplyEvent("LeftClick"); 
  } 
}

El primer controlador es para el evento del botón izquierdo del ratón. Game.ActiveObject devuelve el objeto que hay bajo el cursor del ratón, así que lo asignamos a una variable. Si es null, quiere decir que no hay ningún objeto, así que enviamos el evento a la escena para casos especiales (como el control de regiones en el archivo scene.script). Si, por el contrario, recibimos un objeto, entonces ejecutamos los if's para saber qué hacer con él. Resumiendo: si el objeto está en el inventario, lo asignamos como objeto activo para manipularlo después. Si pinchamos con un objeto del inventario sobre otro objeto de la pantalla, hacemos otras comprobaciones. Primero nos aseguramos de si el objeto sobre el que pinchamos tiene un método con el nombre del objeto definido. Si tuviésemos una manzana en la pantalla, cogemos un cuchillo del inventario y lo usamos sobre la manzana, entonces es de esperar que haya un controlador on "cuchillo" {} .

Else if Item in its script has on "default-use" { } handler defined, it will be executed (It can be used for some default actions, like I don't want to cut that! or It's STUCK!) If this isn't defined for the Item, game looks for on "default-use" { } handler of the object we clicked on. If this doesn't exist, it just issue some standard line like I can't use these things together. in our case.

If we clicked on the object without any inventory item attached, we just send a Left click event to the script attached to the region
(It will then expect on "LeftClick" {} defined for doing anything).

on "RightClick" 
{ 
  // ¿está seleccionado el objeto del menú? deselecciónalo 
  if (Game.SelectedItem != null) 
  { 
    Game.SelectedItem = null; 
    return; 
  } 
 
  var ActObj = Game.ActiveObject; 
 
  // ¿es visible el menú del botón derecho del ratón? ocúltalo 
  if(WinMenu.Visible == true) WinMenu.Visible = false; 
  else if(ActObj!=null) 
  { 
    // si el objeto seleccionado puede manejar alguno de los "verbos", muestra el menú del botón derecho del ratón 
    if(ActObj.CanHandleEvent("Take") || ActObj.CanHandleEvent("Talk")  
    || ActObj.CanHandleEvent("LookAt")) 
    { 
      // almacena el objeto seleccionado en la variable global MenuObject 
      MenuObject = Game.ActiveObject; 
      var Caption = WinMenu.GetWidget("caption"); 
      Caption.Text = MenuObject.Caption; 
 
      // ajusta la posición del menú 
      WinMenu.X = Game.MouseX - WinMenu.Width / 2; 
      if(WinMenu.X < 0) WinMenu.X = 0; 
      if(WinMenu.X+WinMenu.Width>Game.ScreenWidth)  
         WinMenu.X = Game.ScreenWidth-WinMenu.Width; 
 
      WinMenu.Y = Game.MouseY - WinMenu.Height / 2; 
      if(WinMenu.Y<0) WinMenu.Y = 0; 
      if(WinMenu.Y+WinMenu.Height>Game.ScreenHeight)  
          WinMenu.Y = Game.ScreenHeight-WinMenu.Height; 
 
      // y muestra el menú del botón derecho del ratón 
      WinMenu.Visible = true; 
 
      // detiene al actor independientemente de lo que fuera a hacer 
      actor.Reset(); 
    } 
    else ActObj.ApplyEvent("RightClick"); 
  } 
}

This is so well commented that again I am just stating the obvious. First we deselect potentional Inventory Item when we issue a right click. Then we check if our Right Click menu is not visible. If it is, it gets hidden. If we didn't click on any assigned regions on screen, we just forward the right click to the scene (as we did with the left click before). The biggest code portion is that if we actually can do anything with the region (it supports some of the handlers like Take, Talk or LookAt) we show the menu. There is some obvious positioning of the menu on the screen and lastly it stops the actor.

on "Keypress" 
{ 
  // pulsando Esc o F1 
  if(Keyboard.KeyCode==VK_ESCAPE || Keyboard.KeyCode==VK_F1) 
  { 
    // carga y muestra la ventana del menú principal 
    WinCaption.Visible = false; 
    var WinMainMenu = Game.LoadWindow("interface\system\mainmenu.window"); 
    WinMainMenu.Center(); 
    WinMainMenu.GoSystemExclusive(); 
    Game.UnloadObject(WinMainMenu); 
  } 
}

Another handler handles all keystrokes (see our handy key symbolic names?). We just (upon escape or F1) load the Main Menu (load, save, exit etc), center it on the screen and switch to exclusive mode which means, that everything else is stopped until this window ends. When we close the menu window, it gets unloaded again.

Last handler (phewwww) of the game.script is just handling the quit dialog and game quitting.

on "QuitGame" 
{ 
  // con Alt+F4 (se cierra la ventana) 
  // carga y muestra la ventana de confirmación de salida 
  WinCaption.Visible = false; 
  var WinQuit = Game.LoadWindow("interface\system\quit.window"); 
  WinQuit.Center(); 
  WinQuit.GoSystemExclusive(); 
 
  // y si el usuario elige Sí 
  if(WinQuit.xResult) 
  { 
    // sale del juego 
    Game.QuitGame(); 
  } 
  // ed lo contrario, borra de la memoria y cierra la ventana 
  else Game.UnloadObject(WinQuit); 
}

As you can see, it works similary to Main Menu window and when we clicked on yes, it sets xResult to true so Game.QuitGame(); is called. Not exactly a rocket science.
—-
Ok… Still don't have enough? In the second part of this tutorial we will have a look at game_daemon.script file.

You can see that after some declaration it starts with infinite loop. It means that it will just go and on until the game is ended or you Detach the script. Funny part about infinite loops is, that you should end them with some little Sleep(); or it will take the whole control over the game end results in freezing. Not nice experience…

  // guarda el objeto activo para recuperarlo más tarde 
  var ActObj = Game.ActiveObject; 
 
  // maneja el rótulo flotante estándar 
  if(Game.Interactive && ActObj!=null) 
  { 
    if (Game.SelectedItem==null) 
    { 
      WinCaption.X = Game.MouseX; 
      WinCaption.Y = Game.MouseY + 20; 
      WinCaption.TextAlign = TAL_LEFT; 
      WinCaption.Text = ActObj.Caption; 
 
      // mantiene el rótulo en pantalla 
      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; 
     }

First portion of the daemon handles with the Captions when you hover with a mouse over some defined regions with no inventory item attached. It positions it, assign a caption according to caption defined in Scene Edit, scales it and prepares it.

    // maneja los rótulos cuando quieres usar un objeto con otro 
    else 
    { 
      var Item = Game.SelectedItem; 
 
      WinCaption.X = 0; 
      WinCaption.Y = 580; 
      WinCaption.Width = Game.ScreenWidth; 
      WinCaption.TextAlign = TAL_CENTER; 
      WinCaption.Text = "Use " + Item.Caption + " with " + ActObj.Caption; 
    } 
    WinCaption.Visible = true; 
    WinCaption.Focus();

If you on the other hand have some item assigned, the caption is prepared in a manner of Use knife with apple. Again it is pretty obvious from the code itself. Common denominator is that Caption is displayed and gets focus. If we are over empty space with no inventory item in hand we hide the caption with WinCaption.Visible = false;. So much for captions.

Last portion of the daemon is to handle the inventory behavior.

  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;

If we position mouse into upper part of the screen, we show the inventory (provided the game is in interactive mode, main menu is not visible, and we are not in dialogue mode). When we get out of the inventory box or we are in the dialogue mode or game is not interactive, we hide it. It's a bit crude and you should look at faq on some inventory handling tips.

Lastly the aformentioned Sleep is issued and we are set.

That concludes it for this tutorial and I hope you'll find it at least a bit useful.

 
es/kbase/inside_the_brain_and_body_of_your_game.1268904928.txt.gz · Last modified: 2010/03/18 10:35 by saboteur
Recent changes RSS feed Creative Commons License Driven by DokuWiki