Skip to content

Coming from Unity

Romain Milbert edited this page Nov 19, 2024 · 7 revisions

This page is made to ease the use of RaZ, in case you already know how to write scripts with Unity.

Unity uses something close to an ECS. It can't really be called an ECS, since it doesn't have any accessible system. But aside from this (and obviously the editor), it does not have much difference with how RaZ can be used.

GameObject

The concept of Unity's game objects is actually entities. In an ECS, an entity is an aggregate of components, just like a GameObject is. Keep in mind though that unlike Unity, RaZ's entities do not automatically possess a Transform component when created.

// Raz::Application app;
// Raz::World& world = app.addWorld(...);

// Here's your GameObject!
Raz::Entity& entity = world.addEntityWithComponent<Raz::Transform>();

Components

There's not much to be said here, since Unity uses the exact same principle: any component can be added to an entity/game object, which define what the entity is and does.

Creating a Unity script is really like creating your own component with specific actions and attributes. In RaZ, this is the same: you can create any component you want, as long as they inherit from Raz::Component.

class CustomComponent : public Raz::Component {
public:
    int getValue() { return m_value++; }

private:
    int m_value = 0;
};

Update method

Unlike Unity, the update method is not implemented in RaZ's components: those are meant to be only data, while the entire logic is made in systems. Like components, you can create any system you want as long as they inherit from Raz::System.

At the time of writing, each system has a single update() method, called once every frame, with a structure as parameter holding time-related information:

  • deltaTime, representing the amount of time (in seconds) elapsed since the last frame (equivalent to Unity's Time.deltaTime);
  • globalTime, representing the amount of time (in seconds) elapsed since the application started (equivalent to Unity's Time.time);

It also contains additional values that should be used when requiring small discrete steps to keep the simulation stable in the long run, e.g. for physics (somewhat corresponding to Unity's FixedUpdate() function):

  • substepCount, representing the number of steps the system should process to have a stable simulation with the corresponding time below;
  • substepTime, representing the (fixed) time each substep should use (equivalent to Unity's Time.fixedDeltaTime).

The update function returns a boolean. This indicates if the system is still active or not: it should return false when the system has finished working, and will never be executed again in the same application run. If no remaining system is active in any world, the application stops by itself.

class CustomSystem : public Raz::System {
public:
    CustomSystem() {
        // A system must be told which components to accept. All entities containing (at least)
        //  one of these components can be processed in update()
        // If all the accepted components are removed from an entity, it will be
        //  automatically unlinked from this system
        registerComponents<CustomComponent>();
    }

    // Reimplement this update() function to update the system and operate on its entities
    bool update(const Raz::FrameTimeInfo& timeInfo) override {
        for (Raz::Entity* entity : m_entities) {
            // If you need a delta time here, use `timeInfo.deltaTime`

            const int val = entity->getComponent<CustomComponent>().getValue();
      
            // If val equals 10, stop the system
            if (val == 10)
                return false;
      
            std::cout << val;
        }

        // Should you need fixed-time updates, you can loop over the substep count:
        for (int substep = 0; substep < timeInfo.substepCount; ++substep) {
            for (Raz::Entity* entity : m_entities) {
                // If you need a delta time here, use `timeInfo.substepTime`
                ...
            }
        }

        return true;
    }

protected:
    // Optionally, entity link/unlink can be overridden as well, if something must be made at that moment

    void linkEntity(const Raz::EntityPtr& entity) override {
        std::cout << "Entity linked" << std::endl;

        // ALWAYS remember to call the actual System one, otherwise your entity won't be linked!
        System::linkEntity(entity);
    }

    void unlinkEntity(const Raz::EntityPtr& entity) override {
        std::cout << "Entity unlinked" << std::endl;

        // Again, always call the System one
        System::unlinkEntity(entity);
    }
};
// Now that you have your own system, let's add it to the world so that it can actually be executed
world.addSystem<CustomSystem>();

// Let's also add our custom component to our entity
entity.addComponent<CustomComponent>();

If we run our application, we get the following output:

Entity linked
0123456789

Enjoy!

If you're coming from an Unity background, I hope this has been helpful to you! As always, never hesitate to tell me whether you feel something is unclear or have suggestions about something that should be explained.

Clone this wiki locally