Minigames

From Blue Mars Developer Guidebook

Jump to: navigation, search
There are security restrictions on this article

Contents

Overview

Blue Mars minigames are CryEngine entities, so they are developed by writing Entity Scripts and can be replicated and installed by city developers. Essentially, the entity Lua script for a minigame implements the game logic.

Registration

Like other CryEngine entity classes, minigame classes can be registered in one of the following directories mirroring Game/Entities:

  • Game/Levels/COMPANYCODE/Common/Entities (these will load in any <COMPANYCODE> level), or
  • Game/Levels/COMPANYCODE/COMPANYCODE_LevelName/Entities (these will only load in the <COMPANYCODE/COMPANYCODE_LevelName> level)

Here, COMPANYCODE represents your company code, and COMPANYCODE/COMPANYCODE_LevelName represents your level directory. (The Game/Entities directory can be used for an initial test, but as a City Developer you can choose one of the options under your Level directory.) Remember to re-launch the City Editor after adding the .ent file.

For example, the Avatar Reality ARBowlingSupervisor.ent XML file is in Game/Levels/AR/Common/Entities/ARBowlingSupervisor.ent:

<Entity Name="ARBowlingSupervisor" Script="Levels/AR/Common/Scripts/Entities/Minigames/Bowling/ARBowlingSupervisor.lua" />

Placing a Minigame Entity in a Level

Once a minigame class is registered, it will show up in the City Editor's Rollup Bar Entity Browser just like other entity classes. The minigame entity can then be placed in a level by clicking on the class name in the browser list and dragging to the Perspective window, or by double-clicking on the class name in the browser list then clicking in the Perspective window. Any entity in Game/Entities appears in this browser list; an entity saved under Game/Levels/COMPANYCODE/Common/Entities will appear in this list when any COMPANYCODE level is open, and an entity saved under Game/Levels/COMPANYCODE/COMPANYCODE_LevelName/Entities will appear in this list only when the COMPANYCODE_LevelName level is open.

Image:browsesventity.jpg


Placed in a level, the minigame entity can be seen and selected in the Select Objects browser (Ctrl-T or from the View menu).

Image:bowlsventity.jpg

Script

By convention, minigame Entity Scripts will be located under your Scripts directory in /Scripts/Entities/Minigames. You can create a Scripts directory in two locations: in your Common directory (for use in multiple levels) or your COMPANYCODE_LevelName directory (which will only be loaded for this particular LevelName level). The path would either look like Levels/COMPANYCODE/Common/Scripts/Entities/Minigames, or Levels/COMPANYCODE/COMPANYCODE_LevelName/Scripts/Entities/Minigames). For example, the ARBowlingSupervisor.ent mentioned above indicates the script for the bowling game supervisor is Levels/AR/Common/Scripts/Entities/Minigames/Bowling/ARBowlingSupervisor.lua

For minigames, it's a good idea to define entity script properties for parameters that you want to tweak in the Sandbox Editor during development, and to use entity script states to map out the flow of the game.

ARBowlingSupervisor = 
{
  Properties = {
    sMasterEntityClass = "ARBowlingGame_Master",
    sPlayerEntityClass = "ARBowlingGame_MP",
    sSinglePlayerEntityClass = "ARBowlingGame_SP",
    sGameClass = "",
    sGameMenuMovie = "",
    bAutoStart = 0,

    myLaneInfo = {
      pinAreaId = 101,
      gutterAreaId = 102,
      pitAreaId = 103,
      bAutoStartInEditor = 0,
      finalFrameNum = 10,
    },
  },
ARBowlingGame_SP =
{

  States = 
  {
    "Start",
    "PrepareToThrow",
    "PerformThrow",
    "Backswing",
    "ThrowBall",
    "ThrowComplete",
    "ResetAfterThrow",
    "Finish",
  },
  ...

By convention, we have a function named GameStart that is the entry point for each minigame and will typically go to the initial/start game state:

function ARBowlingGame_SP:GameStart()	
  ...
  self:GotoState("Start");
end


Once you have your basic minigame script set up, you can use the Blue Mars Lua API to implement your game, for example:

  • Implement a HUD
  • Implement custom Cameras
  • Spawn new entities with the System SpawnEntity function
  • Create particle effects with Entity functions
  • Control animation with Entity functions
  • Add multiplayer support, covered below and described in more detail on the Multiplayer_Minigame_API page

See the Security Sandbox Environment page for details on restricted functions.

Triggering a Script

There are multiple ways to trigger a script: upon entering an area, upon clicking on an item, upon entering a city level, etc.

Using an ARAvatarTrigger

The script will start when the avatar enters an area.

  • Place an ARAvatarTrigger in your level
  • Use the trigger's ScriptCommand property.
    • Test this out with the ScriptCommand ARDebugMessage("Entered Trigger!"). In Preview Mode in the Editor, walk into the ARAvatarTrigger and see the message displayed. Then replace that command with a function in your script:
    • Call the GameStart() function in your script by entering the following ScriptCommand. Assuming your script is registered and placed in the level (as explained above), your GameStart() function will be triggered.
EntityNamed("YourScript1"):GameStart()
  • Alternatively, rather than using the ScriptCommand, the avatar trigger Enter event can also be used in the FlowGraph as described for the item below.


Using an ARItem

The script will start when the user clicks on a selectable item.

  • For this setup, the script needs to define an event to be used in the FlowGraph, this example defines the Start event (re-launch the Editor after this is done)
function ARConfirm:Event_Start()
  self:Start();
end

ARConfirm.FlowEvents = {
  Inputs = {
    Start = {ARConfirm.Event_Start, "bool"},
  },
}
  • Place both your script entity and an ARItem in your level.
  • In the FlowGraph, connect the ARItem's Select event to the script's Start event in the Flow Graph (shown below).

Image:startevent.png


Upon entering a level

A script can be triggered in Blue Mars automatically upon loading a level, by including the MMO:RegisterStartCallback hook in the OnInit callback. The start hook will allow the level to finish loading before starting the script. Otherwise, starting your script before the level is loaded can lead to errors, if your script tries to access other entities before they are loaded.

function YourScript:OnInit()
  MMO:RegisterStartCallback (function()
                               self:GameStart();
                             end)
end

The MMO functions depend on a network connection in the Blue Mars client, so testing in the City Editor is treated separately.

In the City Editor, you can either call GameStart() directly from the OnInit callback, or to safely start after the level has finished loaded, check the HUD/IsAvailable function in an OnUpdate callback and proceed when it returns true:

function YourScript:OnInit()
  if (System.IsEditor()) then
    self:GotoState("Init"); --to start immeditately, use: self:GameStart();
  else
     MMO:RegisterStartCallback (function()
                               self:GameStart();
                             end)
  end
end
YourScript.Init =
{
  OnBeginState = function(self)
    self:Activate(1);
  end,
  OnUpdate = function(self, time)
    if (HUD.IsAvailable()) then
      self:GameStart();
      self:Activate(0);
    end   
  end,
}

Sharing Scripts

To share a script between developers, the .lua script and .ent files are needed (make sure the .ent file points to the appropriate location of the .lua file, as described in the registration section). It helps to include some instructions regarding level setup (if the script has external dependencies, e.g., another entity, an object by a particular name, entity links, a FlowGraph setup, etc.) and include those scripts and/or related objects (e.g., saved in a group file in the City Editor) if necessary.

Multiplayer Overview

A Simple Example

As described in the Multiplayer_Minigame_API, when clients connect to a room to participate in a game, one client (specifically, the first one that joins) is selected to host the game master.

So conceptually there are two distinct entity types in a multiplayer session, one for the game master and one for all the others. For example, consider a really simple multiplayer game like "What Number Am I Thinking Of?"

The game master would have a state machine that

  1. thinks of a number
  2. asks each player to guess a number
  3. when each player has guessed then pick a winner
  4. return to the think-of-a-number state to choose a new round

The state machine for a player would be more or less

  1. wait for my turn to guess a number
  2. guess a number
  3. wait for everyone to guess
  4. am I a winner?
  5. play again

Image:guessanumber.jpg

The player script might use UpdateStatus to send it's guess. The master script would call SendEvent to tell a player it's their turn to guess.

Initialization

Now that we have the high-level design covered, we need to implement the mechanics for starting the game. Let's use our golf game as an example, which is more complex than the simple number guessing example, even in the initialization. When a client connects to the golf game room, it may start up a single player game or multiplayer, and in the case of multiplayer, it may run both the game master and a player, or just a player.

So it's best to separate the game initialization entity from the game entities. The golf "supervisor" entity script uses the Multiplayer_Minigame_API which invokes the Multiplayer InitializeAndJoin function and implements its callbacks. The results determine whether it spawns a single-player game, a multiplayer entity, or a multiplayer entity and a game master entity.

Image:golfinit.jpg

Message Passing

Since the supervisor entity is the one that joined the game room, all messages delivered through that room will be sent to its callbacks. So we need to forward messages as appropriate to the multiplayer and master entities (of course, there's nothing to forward to the single-player entity).

Conversely, when the player and master scripts invoke the Multiplayer_Minigame_API, they call them using the supervisor entity as the source, not "self".

This diagram shows the message on a machine that has both a player and game master.

Image:Golfmessage_plyr_master.jpg


Of course, in a multiplayer game session, only one machine will be running a game master, so most participants will just have this:

Image:golfmessage_plyr_no_master.jpg




Spectator Overview

The Dynamic Object framework is used to communicate across clients outside of the multiplayer minigame framework. It is intended for low-frequency updates, defaulting to one update per second.

The concept of an avatar obtaining and releasing "ownership" of an object in order to control it is illustrated in the simple Elevator script. The Bowling Spectators example describes how to use this framework to communicate game events to spectator clients.



References

See the Basic Script, Bowling, Golf and Multiplayer_Golf examples. More examples can be found in the Script Center.

Problems with this wiki page? Contact us either by: Support Email or Support Ticket System

Blue Mars Guidebook Privacy Policy
Blue Mars Guidebook Community Guidelines

Personal tools