Skip to main content

FactoryModule

Tsvrc.Editor.FactoryModule generates a Create{Name}(Transform parent) method per registered prefab, for on-demand, non-networked runtime instantiation. TsConfig.FactoryEntries/TsBuiltinConfig.FactoryEntries register the prefabs; builtin entries are resolved first so a name collision between a builtin and a project's own factory always suffixes the project's, never the builtin's. See Spawn a local, non-networked object on demand for a task-oriented walkthrough.

Usage

Register a prefab on the Configure window's Factory tab, then spawn instances by calling the generated method from any behaviour:

var vfx = _ts.CreateHitSpark(transform);

Reach for this only for local, non-networked objects (see below): a projectile or one-off UI popup, not anything other players need to see.

Naming always includes the full group chain

Unlike Global, where IncludeInName makes group-based namespacing an opt-in per group (and Construct, which has no generated name to namespace at all), a Factory entry's generated method name is always prefixed by its full group ancestor chain. A prefab named "Bolt" inside a group "Enemies" nested inside "Boss" becomes CreateEnemiesBossBolt(parent), unconditionally. There's no toggle to opt out, since the whole point of Factory grouping is organizing what would otherwise be a large flat method namespace.

The same prefab registered under two differently-named groups (for example, both an "Enemies" group and a "Traps" group spawning the same projectile prefab) is a legitimate, common case, not a mistake. Unlike Pool's duplicate-reference detection, FactoryModule deliberately does not flag a prefab appearing more than once across different registrations.

Generated method shape

public {Type} Create{Name}(Transform parent)
{
var go = (GameObject)Instantiate(_factory{Name}, parent);
if (go == null) return null;
go.SetActive(true);
// for a TsvrcBehaviour prefab:
var instance = go.GetComponent<{Type}>();
if (instance != null) instance.TsConstruct(this);
return instance;
// for a plain GameObject prefab, returns go directly instead
// for any other component type, returns go.GetComponent<{Type}>() directly, uncostructed
}

The instantiation source (_factory{Name}) is itself a pre-instantiated, inactive instance under a "Factories" scene child, not the original prefab asset. Wire() creates one inactive instance per registered factory once, at wire time, and Create{Name} clones that instance at call time via Instantiate, rather than instantiating the original prefab asset directly on every call.

No networking, by design

The Configure window's own tab description states this plainly: an object created through a Factory method never receives a VRChat network ID and can't send or receive network events. Use PoolModule instead for anything that needs to participate in networked gameplay. Factory exists specifically for cheap, purely local, on-demand instantiation (VFX, local UI popups) where networking would be unnecessary overhead.