Docs

BundleManager

C++ type: BundleManager (static)

Description

A registry for reusable bundles — prefab-like groups of entities that can be spawned into any Scene at runtime. A bundle is authored in the editor and saved as a .bundle file (see Bundles).

Loading a bundle by API

There is no API that loads a .bundle file directly at runtime. Instead, each .bundle file is converted into a factory function during export, and the exporter emits a registerBundle call that wires that factory to a numeric ID and a name:

// Generated by export — you do not write this by hand
BundleManager::registerBundle(1, "enemies/EnemyShip", create_bundle_enemies_EnemyShip);

The generated bundle_<name>.h also declares build_bundle_<name>(Scene* scene, Entity root, std::vector<Entity>& members), the same factory appending the entities it creates to members; generated scene code uses it to reach instance members, whose ids only exist at runtime. Spawn through createBundle rather than calling either function yourself, so the instance is tracked for destroyBundle.

The bundle name is the .bundle file's path with the extension removed, using forward slashes (for example, bundles/enemies/EnemyShip.bundle registers as enemies/EnemyShip). Registration covers the bundles a scene instantiates plus the ones listed in Project → Bundles; a bundle that exists only to be spawned from a script must be listed there, or it is not built and createBundle does not find it (see Bundles in the build). At runtime you spawn an instance by that name (or ID) with createBundle, and remove it with destroyBundle:

local root = BundleManager.createBundle("enemies/EnemyShip", scene)
-- ... later ...
BundleManager.destroyBundle(scene, root)
Entity root = BundleManager::createBundle("enemies/EnemyShip", &scene);
// ... later ...
BundleManager::destroyBundle(&scene, root);

This makes bundles ideal for entities created on demand — enemies, projectiles, item drops, UI cards, and any pooled hierarchy.

Methods

Type Name Langs
static void registerBundle C++
static Entity createBundle C++ | Lua
static bool destroyBundle C++ | Lua
static uint32_t getBundleId C++ | Lua
static std::string getBundleName C++ | Lua
static std::vector\<std::string> getBundleNames C++ | Lua
static void destroyAllInstances C++
static void clearAll C++ | Lua
static void setScriptCallbacks C++
static bool isStopping C++

Constants

Name Type Langs Description
bundleCount int Lua Number of registered bundles (the property form of getBundleCount()).

Method details

registerBundle

  • static void registerBundle(uint32_t id, const std::string& name, std::function factory, std::function destroyer = nullptr)

Registers a named bundle factory. The factory receives the target Scene* and a root Entity, and must return true on success. On failure (or if the factory throws), createBundle does not record an instance and rolls back entities created during the call. Void-returning callables are still accepted and treated as success.

The optional destroyer is called by destroyBundle instead of the default entity destruction; it receives (Scene*, Entity root) and returns true on success.

This call is generated by the export step for every bundle the project builds, so you normally never write it yourself. It is C++-only and not exposed to Lua.

BundleManager::registerBundle(1, "enemies/EnemyShip",
    [](Scene* scene, Entity root) -> bool {
        // create child entities/components under root...
        return true;
    });

createBundle

  • static Entity createBundle(const std::string& name, Scene* scene)
  • static Entity createBundle(uint32_t id, Scene* scene)
  • static Entity createBundle(const std::string& name, Scene* scene, const std::string& parentName)
  • static Entity createBundle(uint32_t id, Scene* scene, const std::string& parentName)
  • static Entity createBundle(const std::string& name, const EntityHandle& parent)
  • static Entity createBundle(uint32_t id, const EntityHandle& parent)
  • static Entity createBundle(const std::string& name, Scene* scene, Entity parent)
  • static Entity createBundle(uint32_t id, Scene* scene, Entity parent)

Spawns an instance of a registered bundle into a scene and returns the root entity of the new hierarchy, or a null entity if the name/ID is not found or the factory fails.

Every overload creates the instance root, so the same bundle can be spawned any number of times. The third argument only says where the new root is parented:

  • The two-argument overloads leave the instance at the top level of the scene.
  • (name/id, scene, parentName) looks up parentName in that scene and parents the instance under it.
  • (name/id, EntityHandle) parents it under that object, using the scene already stored on the handle (any Object, Button, and so on).
  • (name/id, scene, Entity) parents it under an existing entity id. That id must belong to scene.

Each successful call tracks the instance internally so destroyBundle can later remove it, leaving the parent alone. Note the argument order: the bundle name/ID comes first, then the scene.

The instance's scripts, Lua and C++, start before createBundle returns, the same way a scene's scripts do. Only scripts that have not started yet run, so the rest of the scene is left alone; spawning into a scene whose scripts were already cleaned up starts all of them again. If a script destroys its own instance from init() or its constructor, createBundle returns a null entity.

Entity IDs are scene-local

Each scene allocates its own entity IDs. When parenting, prefer createBundle(name, scene, "parentName") so the name is resolved in that scene, or pass an object that already carries its scene. Passing a raw Entity from another scene (for example a main-scene id into a UI scene) targets the wrong entity.

-- New root
local root = BundleManager.createBundle("enemy_grunt", scene)

-- By ID
local r2 = BundleManager.createBundle(1, scene)

-- Parented under a named entity in that scene
BundleManager.createBundle("enemy_grunt", scene, "spawn")

-- Parented under an object that already carries its scene
BundleManager.createBundle("enemy_grunt", someObject)

-- Parented under an existing entity id (must belong to scene)
BundleManager.createBundle("enemy_grunt", scene, existingEntity)
Entity root = BundleManager::createBundle("enemy_grunt", &scene);
Entity r2   = BundleManager::createBundle(1, &scene);
BundleManager::createBundle("enemy_grunt", &scene, "spawn");
BundleManager::createBundle("enemy_grunt", someObject);
BundleManager::createBundle("enemy_grunt", &scene, existingEntity);

destroyBundle

  • static bool destroyBundle(Scene* scene, Entity rootEntity)

Destroys a bundle instance by its root entity. Its scripts stop first, then every entity the spawn created is removed together with anything parented under the instance since (children first, then the root). A bundle spawned under the instance goes through its own destroyBundle before that. The entity the instance was parented to is not touched. If the bundle was registered with a custom destroyer, that is called instead of the default destruction. Returns false if rootEntity is not a tracked bundle root. Note the argument order: scene first, then the root entity.

BundleManager.destroyBundle(scene, root)
BundleManager::destroyBundle(&scene, root);

A C++ script destroying its own bundle

The script is deleted during the call, so return right after it without touching a member. A Lua script can finish the callback, but its entity is already gone.

void Bullet::onUpdate() {
    if (hit) {
        BundleManager::destroyBundle(getScene(), getEntity()); // the script is on the root
        return;
    }
}

getBundleId / getBundleName

  • static uint32_t getBundleId(const std::string& name)
  • static std::string getBundleName(uint32_t id)

Look up a bundle's numeric ID by name, or its name by ID. Returns 0 / "" if not found.


getBundleNames

  • static std::vector\<std::string> getBundleNames()

Returns all registered bundle names, in registration order. Use the bundleCount property (Lua) or getBundleCount() (C++) for the count.


destroyAllInstances

  • static void destroyAllInstances(Scene* scene)

Destroys every tracked bundle instance belonging to scene, calling the appropriate destroyer for each. An instance spawned under another goes with it. C++-only; useful when tearing a scene down manually.


clearAll

  • static void clearAll()

Removes all registered bundles and all tracked instances. Used by the editor when resetting state.


setScriptCallbacks

  • static void setScriptCallbacks(std::function start, std::function stop)

Sets how spawned instances start and stop their scripts: start runs every script of the scene that has not started yet, and stop the scripts of one entity. By default only Lua scripts run. The generated main.cpp passes the project's initScripts and cleanupEntityScripts, and the editor sets its own for Play, so you only call this when running the engine without the generated code. C++-only.


isStopping

  • static bool isStopping(Scene* scene, Entity entity)

Returns true while destroyBundle stops the scripts of that entity. A start callback skips such entities, so a script that spawns a bundle from its destructor does not restart what is being destroyed. C++-only.