RRCG
C# to CV2 Graph Compiler
Install / Use
/learn @notrabs/RRCGREADME
RRCG - Rec Room Circuit Generator
What if you never had to move a wire by hand? RRCG brings text-based scripting support to Rec Room's visual scripting language CV2.

| :warning: WARNING | | --- | | Consider upvoting this feature request: https://recroom.featureupvote.com/suggestions/482338/circuits-api <br /> This package only contains the compiler frontend to validate the graph generation. Wihout a proper Circuits API the conversion into actual graphs is to unstable to release. |
<!-- toc -->Install
RRCG comes as a Unity package to be installed in your Rec Room Studio project.
Using the Package Manager install a package from this Git URL:
https://github.com/notrabs/RRCG.git
Occasionally an update might include breaking changes, most likely if chips get changed or the compiler internals are changed. This can invalidate RRCG's generated files. To safely update, please follow these steps:
- Make sure Unity is open before you update
- Update using the Package Manager (or git)
- If you get errors in generated files => Use "Clean All" in the window menu
- If you get errors in source files => Resolve them manually (e.g. adjust to chip changes)
- Hit "Recompile All" to make sure your files are compiled with the latest compiler
Clone the repository into the "Packages" folder of your Studio project.
e.g. as a submodule: git submodule add https://github.com/notrabs/RRCG.git Packages/RRCG
Using the Compiler
- Create a prefab from the
RRCGwindow menu. Place it in a location with enough space. The chip area will grow as indicated by the arrows. - Open the Inspector for the
RRCGprefab - Select a
CircuitDescriptor(or use the example) - Click
Build Circuit(placeholder for now. Until we have a Circuits API you can only create the debug DOT Graph)
RRCG compiles every file in your project with a .rrcg.cs extension. Any CircuitDescriptor class that was successfully compiled by RRCG will be available to select in the RRCG inspector. See the next chapter for how to write valid code.
You can get started with this example file that is configured by default when you spawn the prefab.
</details> <details> <summary> Standalone Projects </summary>RRCG offers an advanced standalone compiler to speed up your iteration cycles. It bypasses Unity's dreadingly slow Assembly Reload, without sacrificing any major benefits that come from being integrated into Unity. The main difference in development is where the script files are located. Instead of in the Assets folder, your circuit scripts are placed inside an RRCG folder in the project root. The syntax and your access to Unity data stays the same.
Technically this is achieved by dynamically compiling and loading each iteration of your code. This works for RRCG (and not regular Unity Editor code), because RRCG code contains no side-effects beyond the compilation process.
The main limitation for now is that there is no mechanism for dependencies between standalone projects yet, but you can still reference any assembly compiled within your regular Assets. So the compromise for now is that shared RRCG code needs to stay within the slow Unity compiler.
To get started using standalone projects:
- Open the Inspector for your
RRCGprefab - Select the "Standalone Project" option
- Create a new project (or select an existing one)
- Wait for the initial compilation
- Select a Descriptor from the project (the project generates with a standard one)
- Open the RRCG_Project.sln in the
RRCGfolder of your Project root
After that, you can build circuits like with the integrated workflow, just without waiting. Subsequent builds within a session should also see additional speed ups thanks to caching mechanisms.
</details> <details> <summary> Watch Mode </summary>With watch mode on all .rrcg.cs files in your project are compiled automatically when Unity imports them. This also happens every time you make a change to a script file. There's no downside to leaving it watch mode on, but the option to disable it is there if you want to disable automatic compilation during development.
In case you want to manually recompile a file you can use Unity's reimport functionality or use the "Recompile all" feature from the RRCG window menu. This should only be needed after you downloaded a new compiler version or during compiler development.
DOT is a standard graph format that can be visualized online. You can copy a DOT graph for a compiled circuit by pressing the button in the inspector.
</details>Writing Code
The goal of this language is to be an intuitive, direct mapping of C# to Circuits. With the C# execution flow being mapped to exec lines and data flow being mapped to data lines. C# Language features should do what you expect.
The Circuit Descriptor
The Circuit Descriptor is your entry point. Your chips start building from the CircuitGraph() method, but beyond that you can organize your code however you like. Place it inside a .rrcg.cs file anywhere in your project.
using RRCGSource;
public class ExampleRoom : CircuitDescriptor
{
public override void CircuitGraph()
{
// Your circuits go here
}
}
<details>
<summary> Circuit Libraries </summary>
If you want to create reusable logic without an entry point, extend the CircuitLibrary class instead.
This will also hide the class in the Circuit Selection menu. Place it inside a .rrcg.cs file anywhere in your project.
using RRCGSource;
public class ExampleLibrary : CircuitLibrary
{
// Your circuits go here.
// You can use it as a normal class or with static methods.
}
</details>
<details>
<summary> Additional Entry Points </summary>
A room is usually made up of multiple graphs.
You can create separate graphs within a function by using exec chips with no exec inputs, or the StartNewGraph() method.
But for code organization it is often nicer to have them as separate methods.
Use the [CircuitGraph] attribute to mark functions in your CircuitDescriptor as additional entry points.
Note that they must be parameterless functions to work.
public class ExampleRoom : CircuitDescriptor
{
public override void CircuitGraph()
{
// Your 1st graph goes here
EventReceiver(); // implicitly creates a new graph, because receivers have no exec input.
// Your 2nd graph goes here
StartNewGraph(); // explicitly creates a new graph.
// Your 3rd graph goes here
}
[CircuitGraph]
void Foo()
{
// Your 4th graph goes here
}
}
Sometimes you need an exec chip, without connecting it to your exisiting execution flow.
You could place it in a separate graph and reference to it with variables, but that quickly gets very verbose.
Instead use the InlineGraph() helper to create new graphs without destryoing your current context.
public class ExampleRoom : CircuitDescriptor
{
EventDefinition<string> onInput = new EventDefinition<string>();
public override void CircuitGraph()
{
ChipLib.Log("Start");
// Placing a receiver would normally create a new graph. This only extracts its data.
var input = InlineGraph(() => onInput.Receiver());
// This log chip will be connected to the previous log chip, but get the data from the event receiver.
ChipLib.Log(input);
}
}
</details>
Placing Chips
Chips are available as static functions in the Chips class. For convenience you can access them through the extended CircuitDescriptor class.
public void ExampleCircuit()
{
Chips.RandomInt(1,10);
// or
RandomInt(1,10);
}
In addition to placing chips explicitly, you can also use the shorthands provided in the classes of RR types.
public void ExampleCircuit()
{
SetPosition(GetLocalPlayer(), new Vector3(1,2,3));
// or
GetLocalPlayer().Position = new Vector3(1,2,3);
}
Data flow
Ports are data. Data is Ports. Don't worry what the type system might say. Write code as you usually would. RRCG will create and connect chips when necessary to execute operations.
public void ExampleCircuit()
{
int rand1 = RandomInt(0, 10);
var rand2 = RandomInt(0, rand1);
if (rand1 + rand2 > 10) LogString("Today's your lucky day");
else LogString("Try again next time");
}
If a chip has multiple outputs, a tuple will be returned
public void ExampleCircuit()
{
// The underscore is handy to discard unwanted values
var (parsed, _) = ParseFloat("1.0");
// You can also access the named fields of the tuple to quickly get single values
var value = ParseFloat("1.0").Parsed;
}
Exec flow
Functions are invisible. By default the execution flow follows the first pin. If a Chip has no exec input, it automatically starts a new graph.
public void ExampleCircuit()
{
// starts a new graph
RoomEvent.Hz30();
// connects the random chip inside the function directly to the event receiver
var rand1 = MyFunction();
LogString(ToString(rand1));
// starts a new graph
RoomEvent.Hz30();
LogString("1");
}
public void MyFunction()
{
retu
Related Skills
node-connect
351.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
110.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
351.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
351.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
