Urho3D
Quickstart in C++

This example shows how to create an Urho3D C++ application from the ground up. The actual functionality will be the same as in Quickstart in script; it is strongly recommended that you familiarize yourself with it first.

For simplicity, the application is assumed to be compiled on Windows and therefore defines the WinMain() function; look at the file Urho3D.cpp in the Urho3D subdirectory on how to handle cross-platform startup using a macro defined in Main.h in the Core library.

To start with, create a subdirectory "HelloWorld" into the Urho3D root directory, and add the following line to the root directory's CMakeLists.txt file:

add_subdirectory (HelloWorld)

Then, create the following CMakeLists.txt file into the HelloWorld directory (mostly copied from CMakeLists.txt of Urho3D.exe):

# Define target name
set (TARGET_NAME HelloWorld)
# Define source files
file (GLOB CPP_FILES *.cpp)
file (GLOB H_FILES *.h)
set (SOURCE_FILES ${CPP_FILES} ${H_FILES})
# Include directories
include_directories (
../Engine/Container ../Engine/Core ../Engine/Engine ../Engine/Graphics ../Engine/Input ../Engine/IO
../Engine/Math ../Engine/Resource ../Engine/Scene ../Engine/UI
)
# Define target & libraries to link
add_executable (${TARGET_NAME} WIN32 ${SOURCE_FILES})
target_link_libraries (${TARGET_NAME} Container Core Engine Graphics Input IO Math Resource Scene UI)
finalize_exe ()

Before recreating the build files with CMake, create an empty HelloWorld.cpp into the HelloWorld directory. Now you can re-run CMake. If using Visual Studio, the HelloWorld project should now appear in the Urho3D solution, and you can start writing the actual application into HelloWorld.cpp.

To start with, we need the include files for all the engine classes we are going to use, plus Windows.h for the WinMain function:

#include "Camera.h"
#include "Context.h"
#include "CoreEvents.h"
#include "Engine.h"
#include "Font.h"
#include "Input.h"
#include "Light.h"
#include "Material.h"
#include "Model.h"
#include "Octree.h"
#include "ProcessUtils.h"
#include "Renderer.h"
#include "ResourceCache.h"
#include "Scene.h"
#include "StaticModel.h"
#include "Text.h"
#include "UI.h"
#include <Windows.h>

All Urho3D classes reside inside the namespace Urho3D, so a using directive is convenient:

using namespace Urho3D;

To be able to subscribe to events, we need to subclass Object (if we did not use events, we could do everything procedurally, for example directly in WinMain, but that would be somewhat ugly.) We name the class HelloWorld, with functions that match the script version, plus a constructor. Note the shared pointers to the scene that we will create, and to the ResourceCache, which is perhaps the most often used subsystem, and therefore convenient to store here. Also note the OBJECT(className) macro, which inserts code for object type identification:

class HelloWorld : public Object
{
OBJECT(HelloWorld);
public:
HelloWorld(Context* context);
void Start();
void CreateObjects();
void CreateText();
void SubscribeToEvents();
void HandleUpdate(StringHash eventType, VariantMap& eventData);
SharedPtr<Scene> helloScene_;
SharedPtr<ResourceCache> cache_;
};

Before the actual HelloWorld implementation, we define WinMain.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
{
SharedPtr<Context> context(new Context());
SharedPtr<Engine> engine(new Engine(context));
engine->Initialize("HelloWorld", "HelloWorld.log", ParseArguments(GetCommandLineW()));
SharedPtr<HelloWorld> helloWorld(new HelloWorld(context));
helloWorld->Start();
while (!engine->IsExiting())
engine->RunFrame();
return 0;
}

First, we create the Context object, which holds all subsystems and object factories, and keeps track of event senders and receivers. All Object subclasses need to be supplied a pointer to that context. When using an object factory (such as when creating components) that is automatic, but when creating objects manually, the pointer also needs to be passed manually.

With the context at hand, we create the Engine and initialize it. The arguments for the Initialize() function are the initial window title, the log file name, and command line parameters, which are parsed using the ParseArguments() helper function.

After this, we instantiate the HelloWorld object, call its Start() function, and run the main loop until Engine tells that we should exit. The shared pointers will take care of deleting the objects in the correct order; the Context will be the last to be destroyed.

Now we can start implementing HelloWorld.

OBJECTTYPESTATIC(HelloWorld);
HelloWorld::HelloWorld(Context* context) :
Object(context),
cache_(GetSubsystem<ResourceCache>())
{
}

Note the OBJECTTYPESTATIC(className) macro, which creates the static type name and type name hash for object type identification. For each OBJECT macro, a matching OBJECTTYPESTATIC must appear in a .cpp file.

During construction, we only store the ResourceCache subsystem pointer for later access.

In the Start() function the Scene will be created:

void HelloWorld::Start()
{
helloScene_ = new Scene(context_);
CreateObjects();
CreateText();
SubscribeToEvents();
}

Like in the script example, CreateObjects() does the actual scene object creation and defines the viewport.

void HelloWorld::CreateObjects()
{
helloScene_->CreateComponent<Octree>();
Node* objectNode = helloScene_->CreateChild();
Node* lightNode = helloScene_->CreateChild();
Node* cameraNode = helloScene_->CreateChild();
StaticModel* object = objectNode->CreateComponent<StaticModel>();
object->SetModel(cache_->GetResource<Model>("Models/Mushroom.mdl"));
object->SetMaterial(cache_->GetResource<Material>("Materials/Mushroom.xml"));
Light* light = lightNode->CreateComponent<Light>();
light->SetLightType(LIGHT_DIRECTIONAL);
lightNode->SetDirection(Vector3(-1.0f, -1.0f, -1.0f));
Camera* camera = cameraNode->CreateComponent<Camera>();
cameraNode->SetPosition(Vector3(0.0f, 0.3f, -3.0f));
GetSubsystem<Renderer>()->SetViewport(0, new Viewport(helloScene_, camera));
}

Unlike in script, where properties were used to set the component values and scene node transforms, here we must use setter functions instead. Also, whereas strings were used in script to identify the components to create, here it is most convenient to use the template form of CreateComponent():

The text overlay creation is next. Again, setters are used throughout:

void HelloWorld::CreateText()
{
SharedPtr<Text> helloText(new Text(context_));
helloText->SetFont(cache_->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 30);
helloText->SetText("Hello World from Urho3D");
helloText->SetColor(Color(0.0f, 1.0f, 0.0f));
helloText->SetHorizontalAlignment(HA_CENTER);
helloText->SetVerticalAlignment(VA_CENTER);
GetSubsystem<UI>()->GetRoot()->AddChild(helloText);
}

Finally we get to event subscribing and handling.

void HelloWorld::SubscribeToEvents()
{
SubscribeToEvent(E_UPDATE, HANDLER(HelloWorld, HandleUpdate));
}

The helper macro HANDLER is used to create pointers to the event handler member functions: it takes the class name followed by the function name. Note also that unlike script, where events and event parameters are identified with strings, in C++ precalculated hash constants are used instead. The frame update event is defined in CoreEvents.h.

In C++ the event handler function must always have the signature "void HandleEvent(StringHash eventType, VariantMap& eventData)". Note that when accessing event parameters, the event's name is used as a namespace to prevent name clashes:

void HelloWorld::HandleUpdate(StringHash eventType, VariantMap& eventData)
{
float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
if (GetSubsystem<Input>()->GetKeyDown(KEY_ESC))
GetSubsystem<Engine>()->Exit();
}

Now you should be ready to compile HelloWorld.cpp. The resulting executable will be placed in the Bin directory. It should be substantially smaller than Urho3D.exe due to leaving out the scripting functionality.