Skip to content

GameObject

Alex Krieg edited this page Oct 15, 2024 · 20 revisions

GameObject

TitleImage

Hirarchy

a

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.

Predefined GameObjects

  • BackgroundGrid
  • CameraController
  • CameraWindow
  • DefaultEditor
  • LineChart
  • RuntimeInfo

Create custom GameObjects

Create a class that derives from QSFML::Objects::GameObject.
You can use QObjects to receive or send signals.

⚠️ Object declaration macro
Add the macro OBJECT_DECL(MinimalObject) with your new Object name to the header, like you do for Q_OBJECT. In the cpp file, add OBJECT_IMPL(MinimalObject).
The macro implements the clone() function. It is needed when you want to clone Objects.
More infos about cloning objects can be found here

MinimalObject

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:

};





Movable Square

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;
};



































Customize GameObjects without inheritance

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;
}

Custom update function

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;
}

Custom event function

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;
}

Custom draw function

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;
}

Change update order

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.


Nest objects

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.

Cloning objects

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();
}