NWengine 0.9
Loading...
Searching...
No Matches
Tutorial

What is the purpose of this tutorial?

After reading this tutorial, one should be able to manipulate the engine to make a small game. Our goal is is to draw and animate a scene. The final result will look like this:

Let's break down the main steps to achieve the following result without having a detailed knowledge about the engine. These steps can be enumerated as follows:

  • Open a window.
  • Render sprites.
  • Animate sprites.
  • Post-Process.

All the details explained in this tutorial can be classified within these steps or be a substep to them.

Note
The piece of code provided in example folder of the release contains the demo we are making.

The first window

The use of NWengine requires the user to initialize, update, and clean up its systems. However undertaking this process from scratch within an empty entry point may prove to be a long task. The code responsible for these functionalities is what we refer to as frontend.

The Core provides a default frontend, which enables the user to add callbacks that can be triggered at various stages of the engine's runtime. We will explore this callback mechanism in greater detail shortly.

As for now, we will focus on compiling the code necessary to open a window, after which we will get into a more thorough examination by breaking down its components.

#include "NWengineCore.h"
int main() {
NWengineInit();
NWengineLoop();
NWengineShutdown();
}
Definition Maths.h:21

When you run this, if your project is set up correctly, The provided code should result in the creation of an empty window.

  • NWengineInit() funtion intializes the engine and its underlying systems. This includes opening a window, as well as providing the ability to modify various metrics and parameters by calling the appropriate static functions within the Context class.
  • NWengineLoop()function executes the game loop, which continues to run until the close button is clicked for example. During the game loop, the current Scene is updated.
  • NWengineCleanup() is responsible for the proper cleanup and deallocation of any resources utilized by the system management.

It is importat to note that any manually allocated resources should also be manually cleaned up, as the automatic asset management does not yet handle this responsibility.

Like said before, one operation the game loop is responsible of is scene's update. Let us explore this in further detail.

Adding a scene

The process of updating a Scene involves iterating through all the objects within it and updating each of their respective components.

Let us now proceed to write a function that performs a custom initialization. For the time being, this will involve the addition of a scene.
In the same file main.cpp:

void Init() {
RuntimeManager::switchMode(EngineMode::PLAY_MODE);
Scene& s = Scene::CreateNew("New Scene");
s.Start();
}
int main() {
...
}
Represents a scene in the game.
Definition Scene.h:9
static Scene & CreateNew(const std::string &path)
Creates a new scene and pushes it to a container.
Definition Scene.cpp:279
void Start()
Initializes the scene. This method should be called once before the update.
Definition Scene.cpp:216
void MakeCurrent()
Sets the scene as the current scene. The current scene will be updated and drawn during the engine up...
Definition Scene.cpp:199

The first line in the Init() function should be disregarded, as it belongs to a deprecated feature. In order for a Script to execute, this line should be written.

Next, we create a new scene, and assign to it a unique name. As stated previously, the game loop updates the current scene. While creating a new scene pushes it to a container, it does not automatically make it current. Therefore, the next line sets the newly created scene as the current one.

Each GameComponent possesses both Start() and Update() methods. The Scene class also includes these methods. However, the default frontend does not automatically call Start().Consequently, we need to manually invoke it.

At this point, no changes have been made, as we have simply written the function without calling it yet. Since certain steps in the initialization require specific systems to be initialiazed, we need To ensure that this function is called at the appropriate place. User fontend related functions are typically pushed in the following manner:

...
void main() {
NWenginePushFunction(ON_MAIN_CALL_LOCATION::InitEnd, Init);
...
}

Now that we have completed the previous steps the Init() function should be called immediately following the default initialization process.

At this point, we have successfully created a scene, but we have not yet rendered any content. With a bit of patience, we will soon be able to display something on the screen.

Rendering and Camera

The surface on which we draw has a low level abstraction called a Framebuffer *(which is essentially an abstraction of OpenGL's framebuffer and renderbuffer)*. Everything we draw is automatically drawn on a framebuffer. We refer to the window surface as the *"default framebuffer"*; we will not be drawing directly on it. Instead, the NWengine provides powerful tools as abstractions to facilitate frames processing.

A FrameBuffer is rarely used directly by the user. Instead, it is contained within a Camera.
The camera has the ability to draw the objects of a scene onto its FrameBuffer but it must be active to do so.

A camera is a component. We need to create a GameObject and attach a component to it.

Note
In general, there is one to one correspondance between a GameObject and its GameComponent, which is why the term attached is used interchangeably between them.

Before adding the Camera, let us take a detour to write our first Scriptable class, a GameManager.

GameManager

To avoid clutter in the main.cpp file, we will add a GameObject that has a script that behaves as a game manager. For now, we will create two new files: Scripts.h and GameManager.cpp.

A Script is a component that can execute user code. It acts as a wrapper for a new type of component, with the user code residing in a Scriptable member of the Script. Which explains the following code in Script.h:

class GameManager : public Scriptable {
public:
SCRIPT_CONSTR(GameManager)
void Start() override;
void Update() override;
};
The base class for scriptable components.
Definition Script.h:17

All user defined scripts must be declared in the manner shown above. The user is free to add other private and public members as needed. In GameManager.cpp:

void GameManager::Start() {
s->Rename("CameraObj" ,camObj);
Camera* camComponent = camObj.AddComponent<Camera>();
camComponent-> camC->clearColor = fVec3(0.4, 0.5, 0.6);
camComponent->Use();
camComponent->ChangeOrtho(720, 480);
};
The Camera class represents a camera in the game world.
Definition Camera.h:9
Class representing a game object.
Definition GameObject.h:68
GameObject & AddObject()
Adds a new empty GameObject to the scene objects container.
Definition Scene.cpp:83
static Scene * GetCurrent()
Gets the current scene.
Definition Scene.cpp:308
const std::string & Rename(const std::string &newName, GameObject *obj)
Renames a GameObject. This method is mostly used internally and can be used by the user to change the...
Definition Scene.cpp:240
Note
It is essential to note that the name of a GameObject should never be the same as the name of any existing GameComponent. If this rule is not respected, it won't be reported by the Core. It is recommended to prefix GameObjects names to avoid any issue.

The code provided is relatively straightforward, we add a Gameobject to which we attach a camera component, and then we set various parameters for the camera, such as the background color and size. Finally, the Use() method sets the camera as current.

To call the method we have just written, we need to add a GameObject to the scene before the one that has the script which utilizes the *Scriptable** class "GameManager". In main.cpp:

RuntimeManager::switchMode(EngineMode::PLAY_MODE);
Scene& s = Scene::CreateNew("New Scene");
s.Rename("GameManagerObj", &manager);
The base class for script components.
Definition Script.h:66

We are almost done! Up to this point, we have created a scene and set up a camera for it.

Renderer

Despsite these efforts, running the game will still result in a blank window. The reason for this is that although we are writing to the current Camera FrameBuffer, we are not yet rendering to the default framebuffer.

To address this, we simply need to use a Renderer, the frontend initialiazes a default one for us.
We can add a Render() method in main.cpp and push it to the game loop. Like follows:

void Render() {
Renderer& renderer = (*Renderer::currentRenderer);
renderer(true);
}
void main() {
NWenginePushFunction(ON_MAIN_CALL_LOCATION::FrameIntermediate, Render);
...
}
The Renderer class is responsible for rendering GameObjects using a specified shader.
Definition Renderer.h:10

You can refer to the technical documentation of Renderer to learn how to fully take advantage of its capabilities.

If you execute the code now, you should see a colored surface that matches the size of the camera. Congratulations on your progress !

From this point forward, the code should be relatively straightforward.

If you have any fixes or suggestions, please don't hesitate to report them !

Enjoy !