Documentation

Developers Documentation

You are reading GeekTrains.com developer's technical documentation, written by Quintus Hegie for computer geeks and train enthousiasts. With this documentation you'll be able to develop new levels and create new game objects (locomotives and/or wagons).

Please note: The game specific soure code is protected with legal copyright! You may not use this code to create your own train or similar game, but you can use some components and logic for any other type of game. The code may be changed at any time without any notification, rendering any custom additions maybe no longer compatible with the latest version.

3D Engine

Scene Optimization

In comparison with regular train simulator games with large worlds that are partly visible, my game is about model trains where the whole play world fits into a single room so to say. The player thus generally has a good camera view over the whole level (room). Therefore most meshes are visible and need to be rendered most of the time. This creates a challenge for keeping a constant and high framerate, because only a little fraction of the meshes will be out of view most of the time. That is why I use some specific scene optimization techniques for this train game.

Texture Quality

To main save on memory use (for low end devices) and network bandwidth (slow connections) and server traffic (costs), the textures used in the game come in several qualities. We set the quality mainly by choosing the resolution or pixel dimensions (width and height) of the texture. Depending on the type of texture contents, we choose for a JPEG (photo/realistic) or PNG (drawing/cartoonistic) compression technique and file format. The GPU generaly won't care which format we use, and often has enough RAM, but if the player has a low end device with no GPU then we want to reduce the load on CPU and normal memory.

Texture Qualities
Quality Setting Maximum pixel side dimension Average compressed file size
Low64-128 pixels10-25KB
Medium256-512 pixels50-250KB
High1024-2048 pixels500KB-2MB

Some gameobjects use non-square textures (e.g. a rectangular texture for sphere materials). For these textures the maximum pixel dimension to take is that of the longest side (width or height). So a low quality rectangular texture for use on a sphere mesh may be 128×64 pixels.

For sprite atlas textures, the dimension of a single sprite may not exceed the specified limits. So a low quality sprite atlas which holds 4 square sprites, may be at most (4×128)×(1×128), or (1×128)×(4×128) or (2×128)×(2×128) pixels in size.

Level of Detail (LOD)

A level mainly consists of a terrain or playing ground, that is visible in full or in large part. Next in line of frequently visible meshes are the train tracks. The train tracks may be observed from very close by (when camera is set to train drivers view mode) as well as from a large distance (when camera is set to level map view). A technique to speed up rendering when using various distances to view the same mesh is to apply Level of Detail (LOD).

Level of Detail (LOD) on a Rail
Levels of Detail on a Rail: left the full mesh (rendered when viewed from close-by), right the simplified mesh (rendered when viewed from a distance)

Cameras

BABYLON.Cameras render the BABYLON.Scene of the GameState. Typically, the GameState has only one BABYLON.Scene but has multiple BABYLON.Cameras.

In single player mode, only the BABYLON.Scene.activeCamera is used. But in (local) multi player mode, the BABYLON.Scene.activeCameras are used (one Camera for each split-screen).

Layer Masks

Layer masks limit the rendering of GameStateObject.models for certain BABYLON.Cameras only. The main practical of the layerMask feature is for local multiplayer support (split screen co-operative/counter-operative mode).

The decoding table for layerMasks is as follows:

layerMask decoding table
layer.layerMaskcamera.layerMaskPurpose
0x0FFFFFFF0x0FFFFFFFDefault mask ('always show'), excluding Player-only visibility
0x080000000x0FFFFFFF3D GUI elements (do not interact with the world)
0xF00000000xFFFFFFFFModels visible to Players only
0x100000000x0FFFFFFFVisible to Player 1 only (e.g. that Player's GUI)
0x200000000x2FFFFFFFVisible to Player 2 only (e.g. that Player's GUI)
0x400000000x4FFFFFFFVisible to Player 3 only (e.g. that Player's GUI)
0x800000000x8FFFFFFFVisible to Player 4 only (e.g. that Player's GUI)

Viewports

BABYLON.ViewPorts are used to enable split-screen co-operative/counter-operative local multiplayer support.

BABYLON.Camera.viewport configuration table
Game modeNumber of (local) playersViewport setting
Single player1 PlayerFull screen (no split)
Local multi player2 Players50/50 Vertical or Horizontal screen split
Local multi player3 Players25/25/25 screen split (with a Level map camera in the 4th quadrant)
Local multi player4 Players25/25/25/25 screen split

Screen splits are always in order from top-left to bottom-right.

Game Engine

General logic

Config file

The game consists of many different game object types, which each have their own unique capabilities. To keep the game logic as simple as possible, I chose to use a config or settings file to set up a specific game object type.

This config file contains the properties to set for the different game objects. This way it would be easier for me to tweak the game settings in order to get the best gaming experience. The specific game object's type config will be loaded in the GameObject definition from the settings.js file.

Game Objects

A game object (GameStateObject in code) represents an object that is of interest of the game play.

GameStateObject
PropertyMeaningExample
unique_idThe unique ID for this object (compulsory, if omitted, a random number will be chosen)12
nameA unique internal name for this object (optional)locomotive1
groupTo which group this object belongs (optional)stations
labelA visible label for this object (optional)Steam locomotive
teamThe team number this object belongs to (optional)2
parentSet to the name of the parent object, if you want a child relationship (optional)train1

Hierarchy

Locomotives

A locomotive is equiped with a motorblock and is controllable by a player.

Locomotive Game Object properties
Locomotive Game Object: accessors of attached objects

Wagons

A wagon is not motorized and needs to be pushed/pulled by a locomotive.

Wagon Game Object properties
Wagon Game Object: accessors of attached objects
Wagon accessors
AccessorDescription
mesh.frameThe wagon base frame (always present)
mesh.frame.bufferholdersThe holders for buffers on the frame (often 2 in front, 2 in back)
mesh.buffersThe buffers put into the holders (if any)
mesh.frame.bogeyconnectorsThe connectors for bogies on the frame (often 1 in front and 1 in back)
mesh.bogiesThe bogies attached on the connectors
mesh.couplingsThe couplings that can be hooked by a coupling hook
mesh.couplinghooksCoupling hooks present on the coupling (if any)
mesh.piecesBuild-up construction pieces (used for visual construction and/or destruction of wagon)

Trains

A train is a logical unit that consists of one or more vehicles (array). Because this is a train game, the vehicles in a train are typically rail vehicle game objects like wagons and locomotives. A train has at most 1 master, that is the unit that controls or moves the train (often the 1st locomotive).

Train Game Object properties
Train Game Object: accessors of attached objects and constraints

A train vehicle sequence imposes a new (additional) constraint on its vehicles. Where a wagon and/or locomotive is already rail track bound by its wheels, a train also is bound to the condition that the vehicles are attached to each other. The separate vehicles together form a whole 1 train.

The constraint added is that the distance between each coupling is limited by a min and max hook 'rope'. This is of course a simplification of the physical constraint for real world model trains (as hooks aren't ropes but rigid bodies), but it gives enough effect of a train composition because the vehicles are bound to the rail track anyway. As rail tracks won't make sudden abrupt 90-degree angles, but only slow smooth curves, the 'rope' constraint will give nice enough visual impression of inter-wagon connection.

Way Point System

A Way Point System is composed of a set of waypoints (array of coordinates).

Waypoints are useful for giving Vehicles more precise movement orders.

The waypoints (coordinates) can be expressed in 2 ways:

Train Line System

While cars loosely follow road waypoints, trains drive strictly on railroad tracks. These railroad tracks are laid out within a TrainLineSystem. So the TrainLineSystem contains all pieces there are to the complete railway within the level.

In its simplest form, the TrainLineSystem is a collection of paths (or ways) connected at nodes.

Approximation of visually smooth track curves

The 3D Engines currently draws straight lines. Therefore a curve needs to be approximated by a number of smaller straight lines. See the following figure:

A line, a curve and a path
A (straight) line, a curve and a path approximation curves

The paths visually approximate smooth curves by concatenating small pieces of slightly rotated straight lines together.

Train Line

Each segment within the TrainLineSystem is called a TrainLine. The segment actually contains the 3D track mesh.

The segments are connected to each other via points (railroad switches / turnouts) or connectors.

A TrainLine is composed of track pieces
A TrainLine is composed of track pieces

Laying railroad track

Railroad track is laid very similar to the construction of a toy model railroad layout: consecutively by connected each train piece to the next. Railroad track is then laid using track piece chains. The order of the track laying must respect the natural driving direction.

Track laying logic
Track laying logic

The following track pieces are available:

A starting track (for example a buffer stop) can be given a starting position and rotation. Any next connected track will be layed in the specified direction.

The slope parameter is expressed as a fraction. So when slope = 0.01 then the slope is 1 centimeter rise per 100 centimenter flat distance. Regardless of the slope the length is always measured in flat distance (does not take into account the equal or longer length of the slope distance). This makes connecting track pieces with slopes more easily in a 3D-world, as the distance is always measured on the 2D-surface plane.

Turnouts

Turnouts are logical units and don't have a track size (length) themselves, so you must connect straight or curved tracks yourself. All turnouts must have an unique id each so the track layer knows which tracks to connect where to it in what direction. Based on the entry and exit-points, tracks are connected to the turnouts. For example, the leftturnout has connecting points start, left and straight.

Power connectors

Power connectors are logical units and don't have a track size (length) themselves. You cannot start or end track with a powerconnector; it must be placed within a track segment. If a team is specified, the powerconnector becomes a possible starting location for any player in that team. If a player is specified, the powerconnector becomes the starting position for that player locomotive.

Trigger magnets

Trigger magnets are logical units and don't have a track size (length) themselves. You cannot start or end track with a triggermagnet; it must be placed within a track segment. If a group is specified, the triggermagnet will trigger any logic tied to that group. For example, you can open or close a bridge, a gated level crossing, change a signal, etc.

Crane

A crane is equiped with a hook and one or more controllable joints. The controllable joints allow for steering and operation of the crane.

Crane Structure Game Object properties
Crane Structure Game Object: accessors of attached objects

The joints can be restricted so that movement or rotation is limited to a certain distance or angle for example by setting the min and max properties.

Places & Zones

In the game, places and areas of interest and interaction are marked by the logical Place and Zone GameObjects. Both a Place and a Zone have a position and a rotation, but only a Zone has a scaling attribute.

The usage of places and areas enables interaction in the level for moveable GameObjects. For example for a Vehicle (including locomotives, wagons and figures), but also for Item (such as goods). Both a Place and a Zone trigger an action on moveable GameObjects entering and/or leaving the marked location or area.

A difference between a Place and a Zone is that an occupant bounding box need not fit in a Place, but must fit as a whole in a Zone.

Places

A Place has no shape because it only marks a location. The rotation may indicate at which direction the occupant must occupy the place.

We defined a special set of Places that mark some standard location automatically. The special Place.type options are:

Zones

A Zone has either a box or sphere shape to describe the size of the area. The shape also determines the colission detection to use for GameObject models entering and exiting the area.

The default size of the Zone is Babylon.Vector3.One().

Use the GameObject parenting system to attach a Zone to a (often static) GameObject such as a Structure.

We defined a special set of Zones that accomplish some standard task automatically. These types of Zones also show visible markers, so their presense and usage is more clear to the Player. The special Zones are:

Level Setup

Players get to play Levels.

If you want to add a new Level to the list of playable levels, you will need to prepare a Level Setup Javascript file. This Level Setup file is loaded by the Level GameStateObject, when the Player chooses a Level.

The Level Setup file contains instructions for the Level-constructor about things like:

Steps in creating the Level Setup file

Design fun and challenging Levels can be an art in itself. Though these are the steps you need to take in general to design a Level Setup file:

  1. Draw key experiences you want to have for the Player on a notepad/sketch­board;

  2. Design the track layout in the right gauge/scale (optionally with the aid of track design software);

  3. Convert the designed track layout to the GeekTrains-format (Javascript);

  4. Add the most important GameStateObjects to the setup (more eye­candy to further dress-up the Level can be added at a later stage);

  5. Add objectives (winning/losing criteria);

  6. Playtest the level to see if the Level is indeed fun and challenging to play and really communicates the experience you want the Player to have;

  7. Add some more eye­candy to the level to complete the experience;

  8. Offer the Level to the lead developer for official publication;

The GeekTrains game uses the G-scale track gauge format of approximately 45mm (1¾ inch) by default.

Also larger 'big toy' / 'home garden' / 'fun parc' railroad scales are eligable to be played. For example 3½, 5, 7¼, 7½, 10¼, 12¼ and 15 inch gauge.

The code will scale all level scenery down such that the track becomes G-scale gauge again. So when placing scenery in a larger-than-G-scale-gauge Level, make sure you adjust the scaling-property of the GameStateObject to match the relative adjustment factor between your desired scale and the G-scale (e.g. 2 for converting 3½ to 1¾).

3rd Party Railway layout tools

You can design your railroad track layout using 3rd party tools from vendors like:

Using some simple logic, the designed track plan can be converted to the GeekTrains system. E.g. by approximating the length of straight track parts and the radius of curved track parts.

Elements of the Level Setup file (JavaScript)

level variable

First set the general properties of the level:

Property: level
PropertyMeaningExample
iLevel number (higher level number means more complex level to complete)1
nameUnique internal level name'geektrainsdemo1'
titleA friendly title for this level, shown to player'GeekTrains Demo Level #1'
descriptionA longer description for this level, shown to player'A demo level of GeekTrains consisting of a basic circle railway track layout.'
authorName of the level author/creator'Conduqtor Q'
locationGeographical location of the level, for example a place name and a country name (especially used for outdoor track layouts)'Rotterdam, The Netherlands'
urlAn optional URL to a webpage containing more information on the level for background reading at another time'https://geektrains.com/geektrainsdemo1/about'

world_config variable

Second, construct the empty world (that will later contain the model train tracks). It can have the following optional properties: (When you omit some properties, they will fall back to a default setting.)

background

The background to use (rendered behind the skybox, if a skybox is present).

color
The BABYLON.Color3 (or BABYLON.Color4) background color. For example BABYLON.Color3.Blue() (which is the default fallback color by the way).
url
URL to the background file, for example 'background.jpg'. If no URL is set, the background color will be visible.
sky

The sky (rendered before background). Defaults to no sky (thus show background).

fog

To enable a fog effect.

color
A BABYLON.Color3 color to use for as fog. Defaults to the generic clearColor.
mode
A BABYLON.Scene.FOGMODE mode to use for as fog. Defaults to BABYLON.Scene.FOGMODE_NONE.
snow
Adds a snowy cloud to the level. Optionally can make a hail storm.
rain
Adds a rain cloud to the level. Optionally can make a thunder storm.
url
URL to the skybox JPEG file, without the file extension and dot. Will load the 6 face textures by adding a postfix per face to the URL (_nx, _ny, _nz, _px, _py, _pz). For example 'sunny'.
surface

The surface plane (prevents objects from falling into an infinite abyss).

diffuseColor (same as: mainColor for grids)
Sets the diffuse BABYLON.Color3 for the surface (if no texture is specified).
emissiveColor (same as: lineColor for grids)
Sets the emissive BABYLON.Color3 for the surface (if no texture is specified).
texture
Name of the texture within the texture library to use for the surface, for example 'dirt'. If no texture name is set, falls back to a diffuseColor material.
type
The type of surface, typically named after the Mesh to create. Choose from 'box', 'cylinder', 'disc', 'plane', 'tiledground'.
Property: world_config
PropertyMeaningExample
terrainTextures for the terrain. 
heightmapOptional. Set it to give the terrain a heightmap. 
groundSets the size of the playground (where the train track is put upon). 
sun

Optional. Configures the lighting for the scene.

You can set the type of lighting with type. Choose from: HemisphericLight (great for debugging), PointLight (great for indoor scenes), DirectionalLight (great for outdoor scenes) and SpotLight (great for mini-scenes). The scene will always be lit with at least a HemisphericLight.

You can optionally set the time with time (default: clock). That set the scene's lighting to a fixed time in day (midnight, night, dusk, morning, day, noon, afternoon, evening, dawn, night), a specific hour (integer between 0 and 24), or simulate a day/night schedule with setting clock (24 hours pass in 24 minutes, starts at midnight).

 
waterOptional. Adds a full-scale water to the level. 

Third, place the railway tracks:

track_line_layout (more info soon)

Fourth, add all other objects to complete the scene:

objectives variable

Finally, decide upon 1 and up to 5 mission objectives in order to complete the level. The objectives are stored as an Array, where each Item represents an Objective.

The Level is over when all objectives have been met, a critical objective has been failed, or when another non-objective event occurs like running out of health, fuel or money.

Property: objectives[]
PropertyMeaningExample
iLevel number.0
objectiveShort title clearly describing the objective condition.'Arrive exactly at 12:00 at the Train Station'
compulsoryWhether the objective is required to be successfully met in order to complete the level.true
conditionA function that determines if the objective has been met (+1), failed (-1) or is still in progress/undecided (0).function () { return (<put your winning/losing condition here>); }

Hardware

Wide range of USB devices supported.

Human Interface Device (HID)

Standard support through the browser for these standard/common HID hardware:

Mouse

Any standard mouse with a button. Preferred: a mouse with 3 buttons and a scroll wheel.

Keyboard

Any keyboard. Preferred: a keyboard with alphanumeric keys and control keys (Space, Shift, Enter, etc.).

Gamepad Controller

Any gamepad with or without vibration. Preferred: a gamepad with many buttons, a DPAD and analogue sticks.

  1. 4 button with DPAD

    NES style. A+B+Select+Start. Absolute minimum.

  2. 8 button with DPAD and shoulders

    SNES style. A+B+X+Y+Select+Start+LeftShoulder+RightShoulder. Minimum.

  3. 10 button with DPAD and analogue

    N64 style. A+B+Fire+Start+LeftShoulder+RightShoulder. Useable.

  4. 12 button with DPAD and two analogues

    Playstation / XBOX style. A+B+X+Y+LeftFire+RightFire+Select+Start+LeftShoulder+RightShoulder+AnalogueStick1+AnalogueStick2. Recommended.

WebHID

Experimental support in the browser for specialistic/customer HID hardware through WebHID (draft).

Train Cab Controller

Resources

Literature

I can recommend the following literature for developers:

Bookcover Title
Julian Chenard - Learning Babylon.js Learning Babylon.js
Julian Chenard
Julien Moreau-Mathis - Babylon.js Essentials Babylon.js Essentials
Julien Moreau-Mathis
Aditya Ravi Shankar - Pro HTML5 Games: Learn to Build your Own Game Using HTML5 and Javascript (2nd Edition) Pro HTML5 Games: Learn to Build your Own Game Using HTML5 and Javascript (2nd Edition)
Aditya Ravi Shankar
Eric Lengyel - Mathematics for 3D Game Programming and Computer Graphics (3rd Edition) Mathematics for 3D Game Programming and Computer Graphics (3rd Edition)
Eric Lengyel
Jesse Schell - The Art of Game Design: A Book of Lenses (2nd Edition) The Art of Game Design: A Book of Lenses (2nd Edition)
Jesse Schell
Robert Nystrom - Game Programming Patterns Game Programming Patterns
Robert Nystrom
Ian Millington - AI for Games (3rd Edition) AI for Games (3rd Edition)
Ian Millington