Skip to main content

How TsVRC fits together

TsVRC splits into two halves: one lives in the Unity editor while you build, the other runs at runtime for players. This page explains how they relate, and how the pieces from the first-behaviour tutorial fit together. Read it once: every module's reference page assumes you already have this picture instead of re-explaining it.

Two halves: codegen-time and run-time

The codegen half lives entirely in the Unity editor, under Editor/CodeGen. It reads what you've configured through Tsvrc > Configure: which behaviours you've registered as constructs, which pools exist, which globals are exposed. Then it writes plain C# files into your project. That configuration always comes from exactly one linked scene per project, never whatever scene happens to be open (see TsLinkedScene). Nothing in this half ships in the built world; it only ever runs while you're editing.

The runtime half lives under Runtime/ and is ordinary UdonSharp. Some of its types, like TsvrcBehaviour and Instance, are base classes your world's scripts extend, always through a generated shadow rather than directly (the next section covers why). Others, like TsvrcMemory, are services your scripts call into through _ts without ever subclassing them. This is the part that runs for players.

The generated files are the bridge between the two: they're real, checked-in-looking C# that extends the runtime half and gets compiled by Unity like any other script. You never hand-write them, and re-running codegen regenerates them from your configuration each time.

Why a generation step exists at all

Most frameworks like this solve two problems at runtime: finding whichever object of a given type is currently registered, and giving you a typed reference to your project's own subclass instead of a generic base type. The usual tools for that are runtime reflection or a dependency injection container.

TsVRC solves both while you're still in the editor instead. The codegen pass reads your project's types and configuration once, at edit time, and writes plain code with direct references already baked in. Nothing gets looked up while the world is actually running.

Whether UdonSharp specifically requires this approach is a separate question this page doesn't try to answer. What's certain: TsVRC generates every extension point instead of resolving it at runtime. That's why your own scripts extend a generated shadow class (TsBehaviour, TsInstance, and so on) instead of the framework class directly: the shadow is what carries your project's own concrete types.

What the root object bootstraps

TsRoot is the framework's own root type: an abstract class exposing Instance, Memory, and Log. It's not something you extend yourself. ScaffoldModule, one of the codegen modules, generates a concrete subclass for your project and creates the scene object that carries it. Every TsvrcBehaviour in your world holds a reference to that one object (_ts), assigned once when the behaviour is constructed, and reaches the rest of the framework through it: _ts.Memory for shared state, _ts.Log for logging, _ts.Instance for instance-level concerns like master detection.

That's also why construction is a distinct step from Awake/Start: a TsvrcBehaviour doesn't have a working _ts reference until something explicitly calls TsConstruct on it, which is what registering a behaviour as a construct (or a pool member, or a factory product) in Tsvrc > Configure arranges for the generated code to do. A behaviour that's sitting in the scene but never registered anywhere never gets constructed, and any of its TsStart logic never runs.

Where to go from here

Module reference pages assume you've read this page. If you're looking for how a specific runtime type behaves in detail, or how a specific codegen module decides what to generate, start from that module's own reference page rather than this one.