Creating Game Logic

  • strict warning: Only variables should be passed by reference in /home/gamingfreedom/gluon-site/modules/book/book.module on line 560.
  • strict warning: Non-static method view::load() should not be called statically in /home/gamingfreedom/gluon-site/sites/all/modules/views/views.module on line 879.
  • strict warning: Declaration of views_handler_filter::options_validate() should be compatible with views_handler::options_validate($form, &$form_state) in /home/gamingfreedom/gluon-site/sites/all/modules/views/handlers/views_handler_filter.inc on line 0.
  • strict warning: Declaration of views_handler_filter::options_submit() should be compatible with views_handler::options_submit($form, &$form_state) in /home/gamingfreedom/gluon-site/sites/all/modules/views/handlers/views_handler_filter.inc on line 0.
  • strict warning: Declaration of views_plugin_row::options_validate() should be compatible with views_plugin::options_validate(&$form, &$form_state) in /home/gamingfreedom/gluon-site/sites/all/modules/views/plugins/views_plugin_row.inc on line 0.
  • strict warning: Declaration of views_plugin_row::options_submit() should be compatible with views_plugin::options_submit(&$form, &$form_state) in /home/gamingfreedom/gluon-site/sites/all/modules/views/plugins/views_plugin_row.inc on line 0.

When you hit the limit of the capabilities of the many Components provided by GluonEngine, it is time to create your own game logic. This is where the Scripting Component comes in. Using this component, you can use ECMAScript (also known as JavaScript) to react to changes in the game world, and make changes to the world in return. The scripts are written in version 1.5 of the language, using the QtScript engine. If you are familiar with JavaScript from web developing you are in good shape, but its important to note that web browser specific extensions like DOM and some functions are not available.

Resources

The following is an introduction to how these scripts are written, and how they access the various parts of the game. If you are looking for the full scripting API, please look at the following links which are helpfully collected by the Amarok team, who use the same scripting engine:

JavaScript 1.5 Guide
A thorough overview of the language.
API documentation for the standard functions and objects
This documents some functions only available in JavaScript 1.6, which QtScript does not support; they are marked as such.
Quick reference
A Trolltech provided list of objects and functions.

There are also many other JavaScript guides and tutorials on the Internet. Though often web-browser oriented, many are still relevant.

Introduction to Scripting in Gluon

When writing logic for Gluon Engine based games, it is important to understand the structure of the Gluon engine, and that is described in the next paragraphs. After that is a quick by-example description of how to interact with the various parts of the engine through the scripts.

Available Properties

Any script you write will have a number of predefined properties available to them. They provide a more straight forward access to the complete GameObject hierarchy and other core functionality of the game engine. Each of the properties contains a link to the documentation for the object they represent.

MessageHandler
A simple way of passing messages between multiple objects at the same time in a subscription based manner. The two core functions on these objects are subscribe() and publish(). The messages are simple strings, which allows you to make them as verbose or terse as you wish.
Game
The class which controls the game itself. This is how you change scenes, clone objects and other such things. It is also has certain bits of convenience functionality, such as providing you with random numbers.
this.GameProject
The GameProject instance representing the game you are developing. This is mostly a data container, and contains the game's name, description and the list of Scenes the game contains.
this.Scene
The Scene instance that the GameObject in question is connected to. As with GameProject, this is mostly a data container, and the most vital function in it is the access to the root object in the scene, through the function sceneContents().
this.GameObject
All Components are attached to a GameObject, and this is no different for scripts. This is the GameObject this script is attached to, and it provides all the functionality required for positional information and logic. This is the object you need to move around. The object is also used to find objects in the tree, both Components and other GameObjects (something which you can also do with this.Scene.sceneContents() ).
this.Component
The Scripting Component this script is attached to. This particular Component as those above contains little logic in itself - you create that by writing your code.

Object Lifecycle

The lifecycle of the objects in Gluon games means that a specific set of functions are called in a certain order. As this order is specified very deliberately, you can count on it when building your logic. The six functions below are predefined in your Scripting assets when you add them to your GameProject, and the following describes what their purpose is, and when they are called:

  • this.initialize() - Use this for initialization purposes, for things which must happen before the gameloop begins running. This can be used for example to cache references to other objects which you do not wish to expose as properties.
  • this.start() - This function is run immediately before the first call of the update function. This function can be used to set up data which does not require as much time as those in initialize().
  • These functions are called in a loop, until the Scene is changed or the game stopped:
    • this.update(time) - This function is called 25 times every second, and each time it is called, the time parameter is given the number of milliseconds passed since the last update. You should use this value, rather than hardcoding the time between updates, to ensure that your code is future proof. All logic in your game not directly linked to drawing should go in this function.
    • this.draw(timelapse) - This function is called as often as possible, and the timelapse parameter contains the number of milliseconds since the last update call. Use this to interpolate the positions for positions of objects in the game over time, allowing for smoother animations.
  • this.stop() - The opposite of the start() function, this is called after the last update() and draw() calls.
  • this.cleanup() - The opposite of the initialize() function, this is called immediately before the Scene() is unloaded.
  • Other convenience functions:

    • this.restore() : This function is called when the game-state load mechanism is triggered. In other words, when the user chooses to return to a save-point. Use this to set properties to the state they were in during the save-point.
    • this.serialize() : This is called when the user wants to save progress, thus triggering the game-state save mechanism. Use this to pass properties that are to be saved.

See http://gluon.gamingfreedom.org/content/using-restore-and-serialize on how to use these.

Functions and Properties on Other Objects

Accessing other objects in your scene is done by dotting your way through the object tree, or by using the find functions on GameObjects. As this is ECMAScript, everything is essentially a property, even functions. As such, the only difference between retrieving a property and calling a function is the () operator. An example, then, of calling a function on the GameObject to which the script is attached, you can do the following:

this.update = function(time)
{
    // Rotate the GameObject by 90 degrees per second around the Z axis
    this.GameObject.rotate( QVector3D(0, 0, 1), 3.6*time );
}

Special attention must be paid to how you call functions you declare in other scripts. So, an example might be a game where you have a Scene made up of the following hierarchy of GameObjects:

Some Scene
Camera
Camera Controller Component named "CameraController"
Player
Scripting component named "Body"
Head
Scripting component named "Brain"

A Scripting Asset pointed to by Body might then contain the following code:

this.moveRight = function()
{
    // Move the player 1 unit to the right
    this.GameObject.translate( QVector3D(1, 0, 0) );
}

Another Scripting Asset, this one pointed to by Brain, might want to call the function which moves the player right, and for example it could contain the following code:

this.draw = function(timelapse)
{
    // Move the body this head is attached to right
    this.GameObject.parentGameObject().Body.scriptObject().moveRight();
}