Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

6. February 2017

 

In our previous tutorial we materials in our ongoing Babylon Tutorial Series but today we are going to take things a step higher up and let someone else worry about doing the work for us.  Today we will look at exporting 3D models from Blender for use in our game.  A number of exporters exist (3ds Max, FBX, Cheetah, Unity), but today we are specifically going to talk about using Blender.

First we need to download and install the plugin.  Fortunately they make a zip version available for download.  Head on over to https://github.com/BabylonJS/Babylon.js/blob/master/Exporters/Blender/Blender2Babylon-5.2.zip and click Download:

image

 

Save the zip file somewhere you will remember.  Now fire up Blender.  Select File->User Preferences…  then select the Add-Ons tab

image

 

Now choose Install From File… and select the newly downloaded zip.  Then in the filter area type “Bab” and Import-Export: Babylon.js should be available.  Simply check the checkbox to the right hand side to enable it.  We can now export our scene as a .babylon file for use in our game.  Simply select File->Export->Babylon.js:

image

 

There are no settings for the exporter, so simply pick your game asset directory and click Export Babylon.js Scene.  A .Babylon file and all of your textures will be created in your selected save location:

image

 

Now let’s look at the code required to load and display this model in our game:

    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);
        engine.enableOfflineSupport = false; // Dont require a manifest file
        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();


            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(0),
                    BABYLON.Tools.ToRadians(0),
                    10.0,BABYLON.Vector3.Zero(),scene);
            camera.attachControl(canvas,true);
            var light = new BABYLON.PointLight("PointLight",new BABYLON.Vector3(
            0,0,0),scene);
            light.parent = camera;
            light.intensity = 1.5;

            BABYLON.SceneLoader.ImportMesh("","","ShippingContainer.babylon",
            scene,function(newMeshes) {
                newMeshes.forEach(function(mesh){
                    mesh.rotation = new BABYLON.Vector3(BABYLON.Tools.ToRadians(
                    45),0,0);
                }                );
            });

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            scene.render();
        });

    });

 

The magic is done using the Y() function call.  You can load a specific asset from within the .babylon file by specifying it’s name, but in this case we just want the whole thing.  Import mesh has a callback when the mesh is loaded ( it’s loaded async, so you need to use this callback ) and it’s passed an array of Mesh objects.  We simply loop through this array ( in this example it’s only one item long, so we could have just as easily done newMeshes[0] and accomplished the same thing ).  Sometimes you will need to reposition your objects when loaded due to different coordinate systems, this example shows rotating each mesh by 45 degrees along the X axis.

 

This example uses the ShippingContainer blend file available as part of the Patreon dropbox assets, but you can use any applicable Blend file to create the example.  If you are a Patreon support (thanks by the way!) you can find this model in Art\Blender\ShippingContainer and the code is all available (including the exported Babylon file) in Tutorial Series\Babylon\5 - PartFive – Models.

 

When we run this example, we should see:

GIF

 

The Video

Programming, Art , , , ,

18. January 2017

 

In our previous tutorial we covered lighting in our ongoing Babylon Tutorial Series but the objects in our game are still remarkably drab.  A big part of this is the lack of materials applied to them.  In this tutorial we are looking at using the StandardMaterial which handles all the grunt work for you.  You can think of StandardMaterial as a container for several different kinds of textures (diffuse, opacity, etc. ) that can be applied to an object.  It also has some built in attributes such as diffuse (color), emissive (self lighting) and more.  Let’s start straight away with an example that we covered in a previous tutorial.  Applying a simply wireframe to our cube:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var material = new BABYLON.StandardMaterial("material1",scene);
            material.wireframe = true;
            box.material = material;

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            scene.render();
        });

    });
</script>
</body>
</html>

 

When you run it:

image

 

Simple enough.  We create a StandardMaterial, passing in it’s identity and the scene to create it in.  We set the materials wireframe property to true, then apply the material to our object’s material property.  Note each object can only have a single material, although a compound material exists if you need to mix multiple materials together.  Now let’s look at a slightly more colourful example, this time using more of the built in properties of StandardMaterial.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);

            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            5,5,0),scene);
            light.diffuse = new BABYLON.Color3(1,1,1);



            var material = new BABYLON.StandardMaterial("material1",scene);
            material.diffuseColor = BABYLON.Color3.Blue();
            material.emissiveColor = BABYLON.Color3.Red();

            material.specularColor = BABYLON.Color3.Red();
            material.specularPower = 3;
            material.alpha = 1.0;
            box.material = material;

            var plane = BABYLON.Mesh.CreatePlane("plane", 10.0, scene, false, 
            BABYLON.Mesh.DOUBLESIDE);
            plane.material = new BABYLON.StandardMaterial("material2",scene);
            plane.material.diffuseColor = new BABYLON.Color3.White();
            plane.material.backFaceCulling = false;
            plane.position = new BABYLON.Vector3(0,0,-5);

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var material = scene.getMeshByName("Box").material;
//            material.alpha -= 0.01;
//            if(material.alpha < 0) material.alpha = 1.0;
            scene.render();
        });

    });
</script>
</body>
</html>

Running this example results in:

image

 

Here you can see we’ve set the diffuse, emissive and specular values of the cube.  I also created a plane so you can see the emissive value of our cube has no effect on it.  The diffuse property can be thought of as the colour in the traditional sense.  Emissive on the other hand is a value for an internal light of the material, there aren’t actually that many emissive parallels in the real world, but some mosses and a few creatures have an emissive property to them.  Specular color determines how external light sources interact with the surface.  If you look at the commented code in the main loop you will also see commented code affecting the alpha channel of the material.  Alpha can be thought of transparency, with a value of 1 being fully opaque, while 0 is fully transparent.

What the majority of people think of when they work with materials is textures.  Textures are simply images that are applied the surface of an object like virtual wallpaper.  There are different types of textures as well, some effect the color of a surface, others affect the transparency or normals.  Here is an example:

<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            0,10,0),scene);
            light.parent = camera;
            light.diffuse = new BABYLON.Color3(1,1,1);


            var material = new BABYLON.StandardMaterial("material1",scene);

            material.diffuseTexture = new BABYLON.Texture("gfs.png",scene);
            material.bumpTexture = new BABYLON.Texture("gfs_normal.png",scene);
            material.roughness = 0.5;
            box.material = material;



            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){

            scene.render();
        });

    });
</script>

 

And when you run it:

image

 

This example uses two different textures, a diffuse texture:

gfs

 

And a normal map:

gfs_normal

 

For more details on Normal Maps, check this video on Normal Map 101.  Somewhat confusingly, BabylonJS refers to normal maps as bump textures.  This only scratches the surface of the material and texture options available, you also have options like ambient, opacity, reflection, light and specular textures, but you will find they almost all work exactly the same way.

Programming , , ,

12. January 2017

 

Today we are going to take a quick look at the Tilengine 2D game engine.  Tilengine in their own words is:

Tilengine is a free, cross-platform 2D graphics engine for creating classic/retro games with tilemaps, sprites and palettes. Its unique scanline-based rendering algorithm makes raster effects a core feature, a technique used by many games running on real 2D graphics chips.Untitled 3

Tilengine is open source (sorry, the core isn't open ), available on Github however I never could locate what license it’s released under.

EDIT—Since posted, there has been a bit of conversation about the licensing since this was posted, read here.

  It’s a C library, but contains bindings for Python, C# and Java.  I’m actually going to use the C# bindings for the example in this review as it’s the least documented of the available bindings.  There is a single page class reference available here and a small manual available here.  The engine is geared towards creating retro sprite style games and handles graphics, animations, palettes, input and window management, but has no sound or physics engine built in.  It is also designed to be used as a backend solution to an existing front end renderer.  There are several C based examples available here, and this represents the primary way you will get up to speed.  The graphics system is designed to emulate classic sprite systems like Sega’s SuperScaler arcade board but with Super Nintendo’s Mode 7 style graphics effects available.  Tilengine is layered over SDL and is cross platform, capable of running on most desktop operating systems, as well as Raspberry Pi devices.

Tilengine is composed like so:

image

 

Tilengine has direct support for tiled map files created using the Tiled map editor.  If you want to learn more about Tiled, I have done a complete tutorial series available here.

 

As a pretty straight forward game engine, let’s jump right in with the example created using the C# bindings:

using Tilengine;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var engine = Tilengine.Engine.Init(320,240,1,16,16);
            var window = Tilengine.Window.Create("",Tilengine.WindowFlags.Vsync);
            
            // This is the clear color drawn each frame.  Think of it as the sky color
            engine.BackgroundColor = new Color(0,128,238);

            // Load tsx and tmx file.  These are created in the Tiled level editor
            // tsx is a collection of tiles, tmx is a map painted using those tiles
            var tileset = Tileset.FromFile("SOTB_bg.tsx");
            var tilemap = Tilemap.FromFile("SOTB_bg.tmx","Layer 1");
            
            // create a new layer using our just loaded tiles.  Games can have multiple layers
            var layer = new Layer();
            layer.Setup(tileset,tilemap);
            layer.SetPosition(0,0);

            
            // Now we are loading an animated sprite riped from the 90s classic Shadow of the Beast
            // Spriteset is simply the image collection composing our game Spriteset
            // SequencePack is simple text format describing the available animations, their frames, speed etc
            // While Sequence is a named entry in the SequencePack text file
            Spriteset ss = Spriteset.FromFile("SOTB");
            SequencePack sp = SequencePack.FromFile("SOTB.sqx");
            Sequence walk = sp.Find("walk");

            // Now finally create a sprite using our spritesheet
            Sprite sprite = new Sprite();
            sprite.Setup(ss,TileFlags.None);

            int spriteX = 15;
            sprite.SetPosition(15,215);
            
            // Now play the animation sequence named "walk".  We also pass the final 0 in to tell it how many times the animation
            // should loop.  Zero equals forever
            Animation anim = new Animation();
            anim.SetSpriteAnimation(0,walk,0);
            
            
            int frame = 0;

            // This is your game loop
            while(window.Process()){
                // Draw the current frame of graphics (sprites, layers, etc)
                window.DrawFrame(frame++);

                // Now check if left or right arrow/gamepad are pressed, in which case move in that direction
                // IF moving left, flip the sprite over on the X axis
                if(window.GetInput(Input.Right)){
                    spriteX ++;
                    sprite.Flags = TileFlags.None; 
                }
                if(window.GetInput(Input.Left)){
                    spriteX --;
                    sprite.Flags = TileFlags.FlipX; 

                }
                sprite.SetPosition(spriteX, 185);
                if(spriteX > engine.Width) spriteX = 0;
            }
            

            //Cleanup
            tilemap.Delete();
            tileset.Delete();
            window.Delete();
            engine.Deinit();
        }
    }
}

 

The comments pretty much describe everything that is going on there.  For more details, be sure to check the video version of this tutorial available here [coming soon].  This example loads a sprite and animation from the game Shadow of the Beast, an Amiga platformer classic.  The SequencePack file format is extremely simple XML file, here is the example used:

<?xml version="1.0" encoding="UTF-8"?>

<sequences>
  <sequence name="walk" delay="6" loop="0">
    1,2,3,4,5,6
  </sequence>
</sequences>

 

The tsx and tmx files are generated using the Tiled level editor, another open source and free tool.  As you can see, it’s extremely simple to get up and going.  Run this code you will see:

SOTB

 

This is of course a primitive example, but does show the many parts of a game.  A game loop, sprite loading, animations, level loading, etc.  The major features of the engine, that I’m not covering here, are the various sprite effects it emulates.  You can see these effects demonstrated here or in the samples.

 

The Video

Programming , , ,

2. January 2017

 

In our previous tutorial in the BabylonJS Tutorial Series we covered positioning a camera in our world.  There were still a few fundamental components missing, the top of which is lighting which we are going to cover today.  Lights are used to, predictably enough, illuminate your scene.  They interact with the color and materials on your various entities that compose your scene.  There are multiple different light types available in BabylonJS including the Point Light, Directional Light, Spot Light and Hemispherical light.  A point light is a single light source that radiates in all directions, like a naked lightbulb for example.  A directional light in a radiates just in the direction it is pointed and it goes on forever with no fall off basically illuminating everything in its path regardless to distance.  A spot light is similar to a directional light but it does fall off over a given distance and is cone shaped.  A flashlight is a classic example of a spot light, as of course is a spot (or search) light!.  A hemispherical light is generally used to represent an ever present ambient light source, the sun being perhaps the most common example.  You can also emit light from textures using their emission property, but we will cover that at a later point.  In this tutorial we are going to implement a point and a spot light.

 

There is an HD video version of this tutorial available here.

 

Let’s start with a point light.  It’s a simple light that radiates from a single point (thus the name) in all directions.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();

            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);
            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            0,10,0),scene);
            light.diffuse = new BABYLON.Color3(1,0,0);


            scene.actionManager = new BABYLON.ActionManager(scene);
            scene.actionManager.registerAction(
                    new BABYLON.ExecuteCodeAction({ trigger:
                            BABYLON.ActionManager.OnKeyUpTrigger, parameter: " " 
                            },
                            function () {
                                light.setEnabled(!light.isEnabled());
                            }
                    ));

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var light = scene.getLightByName("pointLight");
            light.diffuse.g += 0.01;
            light.diffuse.b += 0.01;
            scene.render();
        });

    });
</script>
</body>
</html>

 

There are a couple things illustrated in this example.  Creating a point light is done by calling new BABYLON.PointLight(), passing in the ID of the light, the position of the light in the world and finally the scene in which the light exists.  You can set the color of the light by setting it’s diffuse property, in this case we set it to full red only.  You will notice this example also shows a new concept in BabylonJS, the ActionManager.  This is a way of wiring code to specific events.  In this case we add some code that will be fired when the space key is pressed.  That function simply turns off and on the light source by calling setEnabled() passing a true or false value.  In the render loop we also slowly increase the lights green and blue components, so you can see the effect of diffuse lighting on the scene.  When you run this code you should see:

GIF

 

Lights are implemented as part of the GLSL shader process and the active lights in the scene are passed to each StandardMaterial in the scene.  By default the standard material is limited to a maximum of four active lights.  This value can be overridden using the maxSimultaneousLights property of the StandardMaterial, although this may have some impact on performance, especially on mobile targets.

 

Next lets look at implementing a spot light.  As with all things BabylonJS, the process is quite similar:

<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();

            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);
            camera.attachControl(canvas,true);

            var light = new BABYLON.SpotLight("spotLight",new BABYLON.Vector3(0,
            10,0),new BABYLON.Vector3(0,-1,0),
                    BABYLON.Tools.ToRadians(45), // degrees the light fans out
                    0.1, // falloff/decay of the light over distance
                    scene);

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var light = scene.getLightByName("spotLight");
            light.position.y -= 0.01;
            scene.render();
        });

    });
</script>

 

In this example we create the spot light with a call to new BABYLON.SpotLight, passing in the id, position, direction vector, the degrees or arc of the light cone, the rate the light falls off over distance and finally the scene to create the light in.  In this example instead of changing the color of the light each frame, we instead move it slightly.  Run this code and you should see:

GIF2

 

As the light is pulled back the fall off cone is quite prominently displayed.  Of course the lack of textures makes this example more than a bit stark, so that is what we will cover in the next tutorial.

 

The Video

Programming , , , ,

15. December 2016

 

Welcome to the next part in the ongoing Closer Look At game engine series.  The Closer Look series combines a review, overview and getting started tutorial in one and aims to give a developer a good idea ofs2closerlook a given game engine is a good fit for them or not.  Today we are looking at the S2Engine, which is a commercial game engine available for purchase on Steam.  Right off the hop the S2Engine is going to have two major strikes against it for many game developers.  First, it’s Windows only.  Figured I’d get the out of the way right up front as if you are looking to create cross platform games, this is not the engine for you.  Second, it’s commercial and closed source, although the price tag is quite low at around $20USD.

 

As always there is an HD video version of this review available here.

 

Without further ado, let’s jump in to the S2Engine.

 

The Tools

The entire development experience in the S2Engine takes place in the S2EngineHD Editor.  Here is the editor in action running the included sample scene:

s1

 

The S2Engine Editor is absolutely loaded with tools, lets take a guided tour.  First off we have the scene window where you can see a live rendering of your game in action:

sceneoptimized

 

Please keep in mind the fuzziness in the picture above comes from the animated gif process, the real-time viewport looks vastly superior to the image above.  The Scene view is where you place entities in your scene and preview the results.

 

You can switch between the various available tools using the following toolbar:

image

Or via menu:

image

 

The Project Window

image

 

This is where the various assets of your game are organized.  It’s an analog to the underlying file system of your project.  The buttons across the left enable you to filter the various different objects ( Animations, Models, FSMs, etc ).

 

The Class Window

image

 

This is where you can instance various game objects.  Select an object, click Create and then click within the scene to place the entity.  The newly created entity (or a selected entity) can then be configured using the lower Params window, which is context sensitive to the selected object.

 

Terrain Tools

image

 

S2Engine has a full terrain editing system in.  The Terrain tool enables you to interactively edit the scenes landscape (height map).  There are tools for raising/lowering, flattening, stepping and painting terrain as well as drawing roads and vegetation.  The actual editing is done in the scene window:

terrain

 

Model Window

image

 

S2Engine supports 3D animated models of FBX format, simply click the Import button in the Project view:

image

 

There are multiple different panels for configuring models, setting and handling animations, managing joints/bones and even vertex level manipulations.

image

 

There is also an animation panel.

image

This enables you to blend animations together, set and remove animation keys and of course, preview animations.

 

Material Window

imageimage

 

A detailed material system allowing multiple layers.  You can control diffuse and normal maps, UV tilting, lighting properties and more.  You also have control over detailed material attributes like alpha blending, animations, scattering, light emission and more.

 

Special FX

image

 

Fine tuned control over multiple special FX of types weather, post processing and environmental.  Fine tune control over all aspects, including water fx, sky, weather, lens effects, etc.  You also have fine tune control over day/night cycles using a keyframe system:

image

 

Cutscene Tool

image

 

S2Engine has a complete system in place for authoring cut scenes.  Includes a curve editor:

image

 

As well as a detailed timeline:

image

 

Hierarchy

image

This is essentially your scene graph.

 

Font Tool

image

Enables you to create png textures for fonts of varying dimensions with font preview.  Fonts are imported in fnt format created using the BMFont tool.

 

GUI Editor

image

Currently in Beta, there is a UI editor available.  You can create a hierarchy of UI widgets and configure them using the Params class panel.

 

imageimage

Currently supported widgets include button, slider, frame, input box, combobox, label, checkbutton, colorbox, image, listbox, groupbox, tabbox and rangeinputbox.

 

Publishing

When you are complete publishing is a pretty straight forward process.  This is one of the advantages of only supporting a single platform publishing is as simple as choosing a file name, starting scene, main script (entry point) and a few other settings and clicking the Publish button.

image

 

Coding

There are two ways to code in S2Engine.  You can use their own scripting language or the visual FSM (Finite State Machine) visual programming language.  The scripting language has the sc2 extension and has a C like syntax.  You can read the language reference here while the API documentation is available here.  Scripts are simply connected to (and thus control) game objects.  Here is an example script that controls a jeep found in the demo game.

#message TakeControl
#message LeftControl
#message LockWaypoints
#message UnlockWaypoints

#use "engine.wav"

var float acc;
var float steer;
var bool brake;
var bool control;
var float steerAng;
var string steerNode;
var string handsNode;
var float oldsteerAng;
var bool _on;

function void Init()
{
	_on=false;
	control=false;
	steerAng=0.0;
	steerNode="steer";
	handsNode="hands";
	AICreateObject(false,false,205.0,200.0);
	resetNodeFlags("camera","visible");
	resetNodeFlags("hands","visible");
	SetSource(10.0,15000.0);
}

function void PostInit()
{
	SetPerObjectMaterialData(3,0.0);
	resetNodeFlags("light01","visible");
	resetNodeFlags("light02","visible");
	_on=false;
}

function void update()
{
	if(control==true)
	{
		acc=0.0;
		steer=0.0;
		brake=false;
		RotateNode(steerNode,"rgt",-steerAng);
		RotateNode(handsNode,"rgt",-steerAng);
		
		SetSourcePitch((PhysicsGetVehicleRPM()*0.0025)+1.0);
		/*LOG( string(PhysicsGetVehicleRPM()) );*/
		
		if(IsKeyPressed("w"))
		{
			acc=1.0;
		}
		if(IsKeyPressed("s"))
		{
			acc=-0.5;
		}
		oldsteerAng=steerAng;
		if(IsKeyPressed("d"))
		{
			steerAng=steerAng+(frametime);
			steer=-1.0;
		}
		else
		{
			if(IsKeyPressed("a"))
			{
				steerAng=steerAng-(frametime);
				steer=1.0;
			}
			else
			{
				steerAng=0.0;
			}
		}
		if(IsKeyPressed(" "))
		{
			brake=true;
		}
		if(steerAng>=35.0)
		{
			steerAng=35.0;
		}
		if( steerAng<=(-35.0) )
		{
			steerAng=-35.0;
		}
		steerAng=ScalarInterpolate(oldsteerAng,steerAng,(frametime/200.0));	
		RotateNode(steerNode,"rgt",steerAng);
		RotateNode(handsNode,"rgt",steerAng);
		PhysicsVehicleControl(steer,acc,brake);
		
		if(acc!=0.0)
		{
			var vec3 fwdaxis;
			fwdaxis=GetWorldAxis("fwd");
			SendMessageMulti("pushOut",string(fwdaxis),400.0,"null");
		}
	}
	else
	{
		PhysicsVehicleControl(0.0,0.0,true);
		if(ObjectIsInRange("player01",300.0))
		{
			SendMessageSingle("player01","DriveVehicle","");
		}
	}
}

function void message()
{
	if( ReceivedMessage("TakeControl") )
	{
		/* lights control */
		var float tod;
		tod=float(GetLevelParam("TimeOfDay"));
		LOG("tod"+string(tod));
		if( (tod>=18.0) && (tod<=24.0) )
		{
			SetPerObjectMaterialData(3,1.0);
			if(!_on)
			{
				SetNodeFlags("light01","visible");
				SetNodeFlags("light02","visible");
				_on=true;
			}
		}
		if( (tod>0.0) && (tod<6.0) )
		{
			SetPerObjectMaterialData(3,1.0);
			if(!_on)
			{
				SetNodeFlags("light01","visible");
				SetNodeFlags("light02","visible");
				_on=true;
			}
		}
		if( (tod>6.0) && (tod<18.0) )
		{
			SetPerObjectMaterialData(3,0.0);
			ResetNodeFlags("light01","visible");
			ResetNodeFlags("light02","visible");
			_on=false;
		}
	
		/*=======================================0*/
		control=true;
		SetNodeFlags("hands","visible");
		PlaySound("engine.wav",true);
	}
	
	if( ReceivedMessage("LeftControl") )
	{
		control=false;
		ResetNodeFlags("hands","visible");
		StopSound();
		SetPerObjectMaterialData(3,0.0);
		ResetNodeFlags("light01","visible");
		ResetNodeFlags("light02","visible");
		_on=false;
	}
}

 

It’s a straight forward enough language, but I generally prefer that engines use an off the shelf scripting engine instead of rolling their own.  This gives the community access to a much larger source of expertise, sample code and generally is much more time tested and stable.  As you can see from the script above, much of the logic and communication is implemented via message passing.

The majority of in game programming however is done using FSM (Finite State Machines ) via the FSM graph.

s2

If you’ve ever worked in Blueprints in Unreal Engine or Flowgraph in CryEngine you should have a pretty good idea how FSM programming works.  You’re code responds to various events and creates program flows using a series of connecting cables to other states.  Each node can have multiple actions, configured like so:

s3

There are dozens of states available, and new ones can easily be created.

image

Variables are easily created as well.

image

In addition to local variables, parameters and globals can also be defined.

image

 

 

The Documentation, Community and Content

The S2Engine has a decent amount of documentation, reference materials, getting started videos and beginner projects.  There are however a few issues, the first of which is English.  The developers primary language is not English and it shows on occasion in the documentation.  The actual UI is very well translated but some of the documentation  is certainly a tad “Engrish”.  Worse, some of the linked starting videos aren’t in English at all.  I have no issue with non-English videos, but I would recommend not linking them directly from an English localized application.

In terms of actual available documentation, there is a Wiki available here, a very solid reference manual available here, and a series of video tutorials available here.  S2Engine also comes with the beginner scene you’ve seen throughout this review.

The community for S2Engine isn’t huge but there is an active forum.  There is also a Trello bug tracking board available on Trello as well as a few other community options.  One impressive thing about the engine is the engine developer is very responsive to user requests and feedback.

One interesting aspect of the S2Engine is the existence of Content DLC.  These are themed content packs you can download and use in your game.  Currently the only content pack is the Medieval content pack shown in the video below. There is another content DLC pack in the works.

 

 

Conclusion

I pointed out the two biggest negatives to this game engine in the very first paragraph.  It’s Windows only, both for tooling and target platforms.  It’s closed source and commercial.  For many those are going to be big enough deal breakers that nothing else really matters.  For the rest though, is this a worthwhile engine?  For a small team effort, there is a massive amount of functionality included in this engine, it’s capable of some staggeringly pretty results and the workflow, once understood, is pretty accessible to people with limited programming experience.

My biggest recommendation to the developer behind this engine is to make a proper demo available.  What will get people to use this engine over the other options is that they prefer the workflow, the tools, the built in assets, etc.  The lack of a current demo is going to vastly limit the potential audience.  Even with a low price tag, few people will spend money to evaluate an engine and having a previous weaker version of your engine available as the trial is certainly a mistake.  When you go to the download section of the website, you are greeted by this text:

NOTE: The version to be downloaded here (1.4.5) is a previous, very old, FREE BETA version. It is useful just for letting you to view How S2ENGINE HD is organized and How it works. It doesn’t represent the final quality of the 1.4.6 Steam version.

This is quite simply a mistake.  A demo is about selling people on your engine, so having them experience a “very old” version is a bad idea.  Always put your best foot forward when showing an engine.  I would recommend creating a version of the current engine that is full featured but either time locked, save limited or publish limited.  You will have a great many more developers willing to give your engine a try. 

I have found the performance to be a bit inconsistent.  I was running consistently at 70+ FPS, then struggled to hit 15FPS for a while with a ton of UI glitches.  Upgrading to the newest nVidia drivers didn’t help.  Then oddly, switching Optimus to use the integrated GPU, then back to dedicated seemed to fix the problems.  Hopefully these problems are localized to me and not widespread.  I wish the developers used a standard UI toolkit like Qt, as their custom implementation can be a bit buggy or not perform as you’d expect.  I also unfortunately experienced a half a dozen crashes while evaluating the engine, including one while making the video version of this review.

 

The Video

Programming , ,

Month List

Popular Comments