Writing your First Simulant Application

Welcome to the first Simulant tutorial!

In this tutorial we'll set out to do the following:

  1. Create a new Simulant project
  2. Learn about the Simulant project structure
  3. Compile and run your Simulant project
  4. Learn how render pipelines work
  5. Load and manipulate a 3D model

This tutorial assumes that you are running the Fedora 29 operating system (either natively, or in a Virtual Machine) and you have followed the "Prerequisites" and "Installation" sections in the Fedora installation instructions.

This tutorial also assumes the following fundamental knowledge:

If you get stuck at any point, head over to the Simulant Discord server.

If you are using another Linux distribution the instructions should be very similar.

Why Fedora?

Fedora is a user-friendly operating system which stays up-to-date with the latest advances in the Linux ecosystem. It has good support for Docker and cross-compilation which Simulant depends on.

Starting your project

We're going to create a project called "monsters". Assuming you have Simulant correctly installed then this is very simple. Open a terminal and run the following commands:

mkdir ~/Projects
cd ~/Projects
simulant start monsters

This will create a Projects folder in your home folder, navigate to it, and then start creating your Simulant project.

The start command will download several things:

It may take a little while. When the process finishes you'll find a new directory called "monsters" has been created.

Now open the file manager and navigate to the Projects folder.

figure 1

As you can see there are a number of folders and files here, let's go through the folders first:

Now, there are a couple of files you should know about:

It's recommended that you edit simulant.json at this point and customise it for your project.

Building and Running your Project

Now, let's get something up and running! From a terminal, run the following commands:

cd ~/Projects/monsters
simulant build

This will compile your project executable. It will actually generate two binaries:

There'll be more on testing later.

Once your project has built, you can run it with the following command:

simulant run

If all goes well you should see a Simulant splash screen, followed by a blank screen. Press escape to quit.

If you have Docker installed, you can also build your project for other platforms, for example:

simulant build dreamcast
simulant build windows

And, if you want to rebuild and then run your project in a single command:

simulant run --rebuild

Scenes

A Simulant application is made up of a number of "Scenes". These are classes which subclass the Scene class template. A Scene represents a single portion of your game, for example you might have a Scene for the game's menu, you might have another for the gameplay, another for the game over screen etc.

Scenes have a number of methods which you can override:

You must at least override load(), the others are optional.

The Scene has a number of properties that you have access to:

Once you've created your Scene class, you need to register it in your Application::init method (in your main.cpp file).

scenes->register_scene<MySceneClass>(name, ...);

The first argument is a name for your Scene, then any additional arguments are passed to your Scene constructor.

When registering, the name "main" is special. If you call a Scene "main" it will be the first Scene called when your game starts. By default the "main" Scene is the simulant splash screen and the default generated Scene is called "ingame".

Render Pipelines

Now that you have your project created and running, let's talk about how Simulant renders to the screen.

You control rendering in Simulant via Pipelines. A Pipeline combines the following:

Creating a pipeline is easy:

auto stage = new_stage();  // Create a stage
auto camera = stage->new_camera();  // Create a camera within the stage
auto pipeline = compositor->render(stage, camera);  // Create your pipeline

It is recommended you activate and deactivate your pipeline by linking it to the scene:

void MyScene::load() {
    // ...

    pipeline_ = compositor->render(stage, camera);
    link_pipeline(pipeline_);
}

This is the equivalent of doing:

void MyScene::activate() {
    pipeline_->activate();
}

void MyScene::deactivate() {
    pipeline_->deactivate();
}

You can create multiple pipelines and this gives you the control to do some cool things:

Loading a 3D Model

Let's now talk about the Stage. You created a Stage earlier, and a Camera within it so that you could build a Pipeline.

A Stage is the root of a heirarchical set of StageNodes which you can manipulate and render. StageNodes (e.g. Actors, Cameras, Lights etc.) can be moved around, rotated, and parented to other StageNodes to build up your scene.

The most common object you will create is an Actor. An Actor is normally (but not always) associated with some kind of Mesh for rendering. In your load() method, it's very straightforward to load a 3D mesh, and attach it to an Actor:

auto mesh_id = stage->assets->new_mesh_from_file("models/mymesh.obj");
auto actor = stage->new_actor_with_mesh(mesh_id);
actor->move_to(0, 0, -10);

That's it!

In Simulant the Camera looks down the negative Z axis by default. What we've done here is loaded a 3D model asset, created an Actor that uses it, then moved that Actor so that it can be seen by the Camera.

You'll notice that we accessed a property of the Stage called "assets". Every Stage has its own AssetManager, when the Stage is destroyed then those assets are destroyed too. Sometimes you want to share assets across Stages though, and you can do that with the shared_assets property of the Window.

One last thing to be aware of is that assets are ref-counted. Once you access an asset, or attach it to an Actor it will be destroyed when all users of that asset are destroyed.

You can turn this behaviour off by passing smlt::GARBAGE_COLLECT_NEVER as a second argument to new_mesh_from_file.

Summary

That's quite a lot for an introduction! You've learned that Simulant is structured around Scenes at a high-level, the rendering system is controlled by Pipelines, and you build up your visible scene by manipulating Cameras and Actors within a Stage.

Now, go ahead an experiment!