-
Notifications
You must be signed in to change notification settings - Fork 0
GameObject
- Hirarchy
- Predefined GameObjects
- Create custom GameObjects
- Customize GameObjects without inheritance
- Change update order
- Nest objects
- Cloning objects
A GameObject is the base class for any object that gets added to the Scene.
GameObjects can contain multiple Components and child GameObjects.
A GameObject will have a Transform
Component by default when it gets instantiated. The Transform Component is needed to be able to move, rotate and scale a GameObject.
- BackgroundGrid
- CameraController
- CameraWindow
- DefaultEditor
- LineChart
- RuntimeInfo
Create a class that derives from QSFML::Objects::GameObject
.
You can use QObjects to receive or send signals.
⚠️ Object declaration macro
Add the macroOBJECT_DECL(MinimalObject)
with your new Object name to the header, like you do forQ_OBJECT
. In the cpp file, addOBJECT_IMPL(MinimalObject)
.
The macro implements theclone()
function. It is needed when you want to clone Objects.
More infos about cloning objects can be found here
This Example creates a GameObject that only prints a message to the console.
MinimalObject.cpp | MinimalObject.h |
#include "MinimalObject.h"
OBJECT_IMPL(MinimalObject);
MinimalObject::MinimalObject()
: GameObject("MinimalObject")
{
}
MinimalObject::MinimalObject(const MinimalObject& other)
: GameObject(other)
{
}
MinimalObject::~MinimalObject()
{
// Do not delete components which are added to this object.
// They will be deleted automatically.
}
void MinimalObject::update()
{
logInfo("Update from the MinimalObject");
}
|
#pragma once
#include "QSFML_EditorWidget.h"
#include <QObject>
class MinimalObject : public QSFML::Objects::GameObject
{
public:
OBJECT_DECL(MinimalObject);
MinimalObject();
MinimalObject(const MinimalObject& other);
~MinimalObject() override;
protected:
void update() override;
private:
};
|
The next example creates a object that draws a green square that always sticks to the mouse position.
CustomObject.cpp | CustomObject.h |
#include "CustomObject.h"
OBJECT_IMPL(CustomObject);
CustomObject::CustomObject()
: QObject()
, GameObject("CustomObjectName")
{
// Create a square shape
m_shape = new QSFML::Components::Shape();
m_shape->setPoints({
{-50, -50},
{50, -50},
{50, 50},
{-50, 50}});
m_shape->setFillColor(sf::Color::Green);
m_shape->setFill(true);
// Create a mouse follower component
m_mouseFollower = new QSFML::Components::MouseFollower();
connect(m_mouseFollower, &QSFML::Components::MouseFollower::mousePosChanged,
this, &CustomObject::mousePosChanged);
// Add the components to this object.
addComponent(m_shape);
addComponent(m_mouseFollower);
}
CustomObject::CustomObject(const CustomObject& other)
: QObject()
, GameObject("CustomObjectName")
{
// Create a square shape
m_shape = new QSFML::Components::Shape(*other.m_shape);
// Create a mouse follower component
m_mouseFollower = new QSFML::Components::MouseFollower();
connect(m_mouseFollower, &QSFML::Components::MouseFollower::mousePosChanged,
this, &CustomObject::mousePosChanged);
// Add the components to this object.
addComponent(m_shape);
addComponent(m_mouseFollower);
}
CustomObject::~CustomObject()
{
// Do not delete components which are added to this object.
// They will be deleted automatically.
}
void CustomObject::mousePosChanged(const sf::Vector2f& worldPos,
const sf::Vector2i& pixelPos)
{
// Set the position of this object to the mouse position
this->setPosition(worldPos);
} |
#pragma once
#include "QSFML_EditorWidget.h"
#include <QObject>
class CustomObject : public QObject, public QSFML::Objects::GameObject
{
Q_OBJECT
OBJECT_DECL(CustomObject);
public:
CustomObject();
CustomObject(const CustomObject &other);
~CustomObject() override;
private slots:
void mousePosChanged(const sf::Vector2f& worldPos,
const sf::Vector2i& pixelPos);
private:
QSFML::Components::Shape *m_shape;
QSFML::Components::MouseFollower *m_mouseFollower;
};
|
Instead of creating many many derived classes for different GameObjects in your scene,
the factory pattern can be used.
The next example creates the same object with a green square that gets moved by the mouse,
just like the last example.
QSFML::Objects::GameObject* ExampleScene::customizedObjectFactory(const std::string& name)
{
// Instantiate a GameObject with the given name
QSFML::Objects::GameObject* object = new QSFML::Objects::GameObject(name);
// Create Components
// Create a square shape
QSFML::Components::Shape* shape = new QSFML::Components::Shape();
shape->setPoints({
{-50, -50},
{50, -50},
{50, 50},
{-50, 50}
});
shape->setFillColor(sf::Color::Green);
shape->setFill(true);
// Create a mouse follower component
QSFML::Components::MouseFollower* mouseFollower = new QSFML::Components::MouseFollower();
// Create a lamda function to handle the mouse move event
QObject::connect(mouseFollower, &QSFML::Components::MouseFollower::mousePosChanged,
[object](const sf::Vector2f& worldPos, const sf::Vector2i& pixelPos)
{
// Set the position of this object to the mouse position
object->setPosition(worldPos);
});
// Add the components to the object
object->addComponent(shape);
object->addComponent(mouseFollower);
return object;
}
What if you want to use the GameObject::update
function?
No problem, you can create as many custom update functions for a GameObject as you like.
QSFML::Objects::GameObject* ExampleScene::customizedObjectFactory(const std::string& name)
{
// Instantiate a GameObject with the given name
QSFML::Objects::GameObject* object = new QSFML::Objects::GameObject(name);
...
object->addUpdateFunction([](GameObject& obj)
{
// param obj is a reference to the object (replacement for the this pointer)
obj.rotate(1);
});
return object;
}
If you want to process events, you can add a custom event handler function to a GameObject.
QSFML::Objects::GameObject* ExampleScene::customizedObjectFactory(const std::string& name)
{
// Instantiate a GameObject with the given name
QSFML::Objects::GameObject* object = new QSFML::Objects::GameObject(name);
...
object->addEventFunction([](GameObject& obj, const std::unordered_map<CameraWindow*, std::vector<sf::Event>>& events)
{
// Custom event handler
obj.logInfo("Events: " + std::to_string(events.size()));
});
return object;
}
If you want to draw stuff without creating a painter component, just add a draw function.
QSFML::Objects::GameObject* ExampleScene::customizedObjectFactory(const std::string& name)
{
// Instantiate a GameObject with the given name
QSFML::Objects::GameObject* object = new QSFML::Objects::GameObject(name);
...
object->addDrawFunction([](const GameObject& obj, sf::RenderTarget& target, sf::RenderStates states)
{
// Custom draw function
sf::CircleShape circle(25);
circle.setFillColor(sf::Color::Red);
circle.setOrigin(25, 25);
circle.setPosition(25, 0);
target.draw(circle, states);
});
return object;
}
You can specify how the GameObject processes an update loop. You can define the sequence order for the event, update and draw loop.
object->setEventOrder(...)
object->setUpdateOrder(...)
object->setDrawOrder(...)
The following example shows how you can change the draw order inside the GameObject
QSFML::Objects::GameObject* ExampleScene::customizedObjectFactory(const std::string& name)
{
// Instantiate a GameObject with the given name
QSFML::Objects::GameObject* object = new QSFML::Objects::GameObject(name);
...
object->setDrawOrder({
QSFML::Objects::GameObject::DrawSequenceElement::customDrawFunctions,
QSFML::Objects::GameObject::DrawSequenceElement::components,
QSFML::Objects::GameObject::DrawSequenceElement::childs
});
return object;
}
As you can see, the custom draw function that draws the red sircle, is drawn before the component which draws the square.
This method also allows you to disable some parts of the drawing process by simply not including the sequence element that you do not want to process.
GameObjects are organized in a tree structure. You can add and remove GameObject childs. Transformations are always relative to its parent object. The same applies to Components.
Sometimes it can be handy to clone objects with they're components and childs.
To use the clone feature your Objects must overwrite the clone function. This is done by the macros OBJECT_DECL(MinimalObject)
for class declaration and OBJECT_IMPL(MinimalObject)
for its implementation.
If you don't want to use the macro, just hardcode the same logic:
CustomObject* CustomObject::clone() const
{
return new CustomObject(*this);
}
The clone function needs the copy constructor to be implemented for your object.
⚠️ Qt Signals/Slots Cloning a object does not clone the signal/slots connections from the Qt Framework. If you have connected components with the object or something like this, you have to connect them after cloning.
void someFunc()
{
GameObject *baseObject = new GameObject();
baseObject->addComponent(new Component("SomeComponent"));
...
// Apply the changes of the object, otherwise the added components/objects are not cloned
baseObject->updateObjectChanges();
// Create a cloned version
GameObject *cloned = baseObject->clone();
}