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.
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.
|Quality Setting||Maximum pixel side dimension||Average compressed file size|
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.
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).
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.
A game object (
GameStateObject in code) represents an object that is of interest of the game play.
|The unique ID for this object (compulsory, if omitted, a random number will be chosen)||12|
|A unique internal name for this object (optional)||locomotive1|
|To which group this object belongs (optional)||stations|
|A visible label for this object (optional)||Steam locomotive|
|The team number this object belongs to (optional)||2|
|Set to the name of the parent object, if you want a child relationship (optional)||train1|
Item- Goods and other collectable parts, also bullets
Level- Terrain, train lines, sky & weather, etc.
Vehicle- Controlable / moveable vehicles, living animals and people, toy figures
Place- A designated area for vehicles, creatures and items
Storage- A place to store items
Train- An ordered composition of locomotives and wagons
Weapon- Fires bullets
Zone- Area of interaction for vehicles and creatures
A locomotive is equiped with a motorblock and is controllable by a player.
A wagon is not motorized and needs to be pushed/pulled by a locomotive.
A train is a logical unit that consists of one or more
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).
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.
While cars loosely follow road waypoints, trains drive strictly on railroad tracks.
These railroad tracks are laid out within a
TrainLineSystem contains all pieces there are to the complete railway within the
In its simplest form, the
TrainLineSystem is a collection of paths (or ways) connected at nodes.
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:
The paths visually approximate smooth curves by concatenating small pieces of slightly rotated straight lines together.
For straight railroad track, composing straight lines is easy: they are already straight!
But for curved railroad track, the approximation of the smooth curve is made by a tesselation setting.
The tesselation means the number of straight line points to form a full circle.
The default precision in tesselation is
4 × 12 = 48.
Hint: Keep the radian angles for your curved tracks in multiples of
2 × PI / tesselation (a full circle has 2 PI radians).
This will make sure that the tracks are rendered correctly as the curves always start and end at tesselation points.
Each segment within the
TrainLineSystem is called a
The segment actually contains the 3D track mesh.
The segments are connected to each other via points (railroad switches / turnouts) or connectors.
Railroad track is layed very similar to the construction of a toy model railroad layout: consecutively by connected each train piece to the next. Railroad track is then layed using track piece chains. The order of the track laying must respect the natural driving direction. The following track pieces are available:
straight), which has a
lengthand optionally a
curve), which has a (clockwise)
radiusand optionally a
A starting track (for example a buffer stop) can be given a starting
Any next connected track will be layed in the specified direction.
slope parameter is expressed as a fraction.
slope = 0.01 then the slope is 1 centimeter rise per 100 centimenter flat distance.
Regardless of 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 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
exit-points, tracks are connected to the turnouts.
For example, the
leftturnout has connecting points
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.
team is specified, the
powerconnector becomes a possible starting location for any player in that team.
player is specified, the
powerconnector becomes the starting position for that player locomotive.
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.
group is specified, the
triggermagnet will trigger any logic tied to that
For example, you can open or close a bridge, a gated level crossing, change a signal, etc.
A crane is equiped with a hook and one or more controllable joints. The controllable joints allow for steering and operation of the crane.
The joints can be restricted so that movement or rotation is limited to a certain distance or angle for example by setting the
In the game, places and areas of interest and interaction are marked by the logical
Place and a
Zone have a
position and a
rotation, but only a
Zone has a
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).
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
Place has no shape because it only marks a location.
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.
Place.type options are:
Place for bicycle
Place to dock for nautical
Place to land or take off for aireal
Place to park for road
Place for rail
Place to store goods, fuel and/or
Place for living
Vehicles to wait (on a train for example).
Zone has either a box or sphere
shape to describe the size of the area.
shape also determines the colission detection to use for GameObject models entering and exiting the area.
The default size of the
Use the GameObject parenting system to attach a
Zone to a (often static) GameObject such as a
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
Marks a embarking and/or disembarking zone for living
Vehicles (mainly: figures) on/off
BoardingZone is therefore often attached to
Structures like a train station or bus stop.
Boarding and unboarding
Vehicles earns score points.
The money bounty may depend on the time between boarding and unboarding.
Marks a fueling zone for
Vehicles (mainly: motorized vehicles).
FuelingZone is therefore often attached to
Structures like a gas station or water tower.
FuelingZone's owner has a
Storage-attribute, the supplied fuel will be taken from that
Vehicles costs money.
Marks a healing zone for living
This type of zone is less often used than a
RepairingZone, because the main units in the game are
Vehicles is free.
Marks a loading and/or unloading zone for
Items (mainly: goods) on/off
LoadingZone is therefore often attached to
Structures like a crane or good stations.
Loading and unloading
Items earns score points.
The money bounty may depend on the time between loading and unloading.
Marks a repairing zone for
For example, a locomotive shed will repair locomotive
But a repair wagon may repair damaged track
Structures like bridges and bumpers.
Structures costs money.
This Level Setup file is loaded by the
GameStateObject, when the Player chooses a Level.
The Level Setup file contains instructions for the
Level-constructor about things like:
Meta-data about the level (like a title, author, image, location, etc.);
The environment of the level (like the playground, weather and background);
The track layout (railways, roads, waterways and airways);
The locomotives and wagons that the player(s) will control;
GameStateObjects within the level (like structures, vehicles, creatures, etc.);
Winning and/or losing conditions.
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:
Draw key experiences you want to have for the Player on a notepad/sketchboard;
Design the track layout in the right gauge/scale (optionally with the aid of track design software);
Add the most important
GameStateObjects to the setup (more eyecandy to further dress-up the Level can be added at a later stage);
Add objectives (winning/losing criteria);
Add some more eyecandy to the level to complete the experience;
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¾).
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.
First set the general properties of the level:
|Level number (higher level number means more complex level to complete)|
|Unique internal level name|
|A friendly title for this level, shown to player|
|A longer description for this level, shown to player|
|Name of the level author/creator|
|Geographical location of the level, for example a place name and a country name (especially used for outdoor track layouts)|
|An optional URL to a webpage containing more information on the level for background reading at another time|
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.)
The background to use (rendered behind the skybox, if a skybox is present).
BABYLON.Color4) background color. For example
BABYLON.Color3.Blue()(which is the default fallback color by the way).
'background.jpg'. If no URL is set, the background
colorwill be visible.
The sky (rendered before
Defaults to no sky (thus show background).
To enable a fog effect.
BABYLON.Color3color to use for as fog. Defaults to the generic
BABYLON.Scene.FOGMODEmode to use for as fog. Defaults to
The surface plane (prevents objects from falling into an infinite abyss).
BABYLON.Color3for the surface (if no
BABYLON.Color3for the surface (if no
'dirt'. If no
texturename is set, falls back to a
Meshto create. Choose from
|Textures for the terrain.|
|Optional. Set it to give the terrain a heightmap.|
|Sets the size of the playground (where the train track is put upon).|
Optional. Configures the lighting for the scene.
You can set the type of lighting with
You can optionally set the time with
|Optional. 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:
railway_points(more info soon)
structure_points(more info soon)
locomotive_points(more info soon)
wagon_points(more info soon)
vehicle_points(more info soon)
item_points(more info soon)
scenery_points(more info soon)
tunnel_points(more info soon)
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.
|Short title clearly describing the objective condition.|
|Whether the objective is required to be successfully met in order to complete the level.|
|A function that determines if the objective has been met (|