Archived: Mike MGarcia's Games Development BlogA hobbyist Mobile/PC/Android/Console game development blogBlog | About | Contact |
Here are my notes about learn Urho3D engine.
The C++ is complex even for a non C++ programmer, ie Template functions.
I will trace the 01_HelloWorld sample which simply prints hello world
to the screen while the helper class ‘Sample’ does a lot more.
You can view the code of this sample online here.
Start your IDE and load up the 01_HelloWorld sample.
In code::block select the 01_HelloWorld build target.
Click the ‘Step into’ button (Shift+F7) to start the debug.
It will stop at this line 36 of 01_HelloWorld.cpp:
35: // Expands to this example’s entry-point
36: DEFINE_APPLICATION_MAIN(HelloWorld)
Defined in Application.h and inherited by the Sample class (more on this later) it says:
“ Macro for defining a main function which creates a Context and the application, then runs it”
This initializes the engine and tells Urho the class of our main app (HelloWord).
Next, put break points at the start of the constructor and CreateText function and click ‘Next line’ (F7).
It will break at line 39:
38: HelloWorld::HelloWorld(Context* context) :
39: Sample(context)
40:{
41:}
This is the HelloWorld constructor.
It is initializing it’s inherited class ‘Sample’ which is a helper class for all the samples passing the context.
The heart of Urho3D is the Context object, which must always be created as the first in a Urho3D application, and deleted last.
All “important” objects that derive from the Object base class, such as scene nodes, resources like textures and models, and the subsystems themselves require Context pointer in their constructor.
This avoids both the singleton pattern for subsystems, or having to pass around several objects into constructors.The Context provides the following functionality:
*Registering and accessing subsystems
*Creation and reflection facilities per object type: object factories and serializable attributes.
*Sending events between objects
Click the ‘Step into’ button (Shift+F7) and it will take you to the constructor code in the Sample.inl file.
This .inl file is the implementation of the class, but it’s code is all ‘inline’ (coded in the header),
it’s included by Sample.h at the last line.
Reading the lines, you will see it continues to set up the screen and inputs.
Click the ‘Debug/Coninue’ button (CTRL F7) to continue to then next break at 46:
43: void HelloWorld::Start()
44: {
45: // Execute base class startup
46: Sample::Start();
We have finished with the constructor of HelloWorld and now in the Start function.
Start is called by the Application class
which does the “Setup after engine initialization and before running
the main loop. Call ErrorExit() to terminate without running the main
loop. Called by Application”.
The Application class is inherited by the Sample class in Sample.h step in (Shift F7) to Sample::Start().
It initializations touch input based on OS, click next line (F7) until at line 73:
72: // Create logo
73: CreateLogo();
Step into (Shift F7) CreateLogo() to line 120:
117: void Sample::CreateLogo()
118: {
119: // Get logo texture
120: ResourceCache* cache = GetSubsystem();
GetSubsystem is a Templated function and is defined in the Object class and inherited by the Application class:
template
T* Object::GetSubsystem() const
{
return static_cast(GetSubsystem(T::GetTypeStatic()));
}
This is the beginning of the sub system.
“Any Object can be registered to the Context as a subsystem, by using the function RegisterSubsystem().
They can then be accessed by any other Object inside the same context by calling GetSubsystem().
Only one instance of each object type can exist as a subsystem.”
It basically returns a pointer to our singleton, resource manager (ResourceCache), just one of many object factories.
next, line 121 we load a resource (Texture2D) to our resource manager and get it’s pointer.
121: Texture2D* logoTexture = cache->GetResource
(”Textures/LogoLarge.png”);
122: if (!logoTexture)
123: return;
Next on line 126 we get the UI subsystem (UI manager) and at line 127 we get create a child UI node with the
125: // Create logo sprite and add to the UI layout
126: UI* ui = GetSubsystem();
127: logoSprite_ = ui->GetRoot()->CreateChild();
The UI system is node based to first you need to getRoot().
CreateChild is defined in UIElement.h as another template function:
template
T* UIElement::CreateChild(const String& name, unsigned index)
{
return static_cast(CreateChild(T::GetTypeStatic(), name, index));
}
It creates and adds Sprite to the UI element (root node).
logoSprite_ is defined as:
SharedPtr
logoSprite_;
SharedPtr and WeakPtr are template classes which manages memory allocation and tracks the pointers in use.
129: // Set logo sprite texture
130: logoSprite_->SetTexture(logoTexture);
Our UI sprite is assigned the texture.
The rest of this function deals with initializing the sprite and the
rest of Sample::Start() is too complex for a beginner tutorial.
Click ‘Debug/Coninue’ button (CTRL F7) to go to HelloWorld::CreateText()
57: void HelloWorld::CreateText()
58: {
59: ResourceCache* cache = GetSubsystem();
60:
61: // Construct new Text object
62: SharedPtrhelloText(new Text(context_));
63:
64: // Set String to display
65: helloText->SetText(”Hello World from Urho3D!”);
66:
67: // Set font and text color
68: helloText->SetFont(cache->GetResource(”Fonts/Anonymous Pro.ttf”), 30);
69: helloText->SetColor(Color(0.0f, 1.0f, 0.0f));
70:
71: // Align Text center-screen
72: helloText->SetHorizontalAlignment(HA_CENTER);
73: helloText->SetVerticalAlignment(VA_CENTER);
74:
75: // Add Text instance to the UI root element
76: GetSubsystem()->GetRoot()->AddChild(helloText);
77: }
Line 62 defines a new SharePtr called HelloText.
In it’s constructor we create a new Text instance with the context_ parameters.
Line 65, we set the text to “Hello World from Urho3D!”
Line 68 & 69, we set a font, size and a colour.
Line 72 & 73, we align text to center of the screen.
Line 76, lastly we add helloText to the UI’s root node.
Stepping out of HelloWorld::CreateText() function we get to the last function, HelloWorld::SubscribeToEvents().
void HelloWorld::SubscribeToEvents()
{
// Subscribe HandleUpdate() function for processing update events
SubscribeToEvent(E_UPDATE, HANDLER(HelloWorld, HandleUpdate));
}
This function tells Urho3D’s message pump, that when there’s an E_UPDATE event to run the handler function HandleUpdate, in class HelloWorld.
The function HelloWorld::HandleUpdate does nothing because it has no game logic or moves anything.
The rest is more complicated, but that’s the basics, and stepping
through the code isn’t difficult and it will get you familiar with the
framework.