Skip to content

How a component works

Every part in CRUMB, from a resistor to a microcontroller board, is a component you could have built yourself. The shipped library uses exactly the tools described here, and nothing in CRUMB knows what a “motor” or a “sensor” is. A component is a 3D model, some markers that say what each piece of it is, and two short pieces of code.

A component is a scene with four groups of markers under its root. Everything it does comes from which markers exist, where they are and what they are called.

Group Holds What it means
Rendering Meshes and pivot nodes. A mesh can carry a part body (a rotor, a wheel, a lever) and joints. What you see, and the moving parts.
Connections Pins, terminals, wires and connection groups. The electrical topology: what can be wired to what.
Scripting One circuit logic and one component logic, each with its properties. The behaviour.
Physics The main body, anchors, interactivity zones, and the built-in extras (microcontroller, audio in and out). How it sits in the world and how you interact with it.

Pins are male connections and terminals are female ones. A connection group joins several connections into one electrical point, the way the rows of a breadboard do.

Circuit logic Component logic
Yours to use by name Everything on BaseCircuitLogic Everything on BaseComponentLogic
Deals with Volts, amps, conductances, sources Movement, light, forces, sensing, clicks
Runs On the simulation thread, every tick Every rendered frame, and every physics step
Can use Only what its whitelist allows Only what its whitelist allows

In each half you write fields, the hooks you fill in and your own helper methods, with no class, using or namespace lines. Your markers and properties appear in that code as members:

  • Connections.<Pin> in circuit logic: the index of each pin, named after the marker (Connections.A, Connections.Red).
  • Properties.<Name>: each property you added, with its type and saved value.
  • Rendering.<Node>, Connections.<Wire> and Interactivity.<Zone> in component logic: one member for each node you marked in your model. A mesh with a part body has Rendering.<Node>.Rigidbody.

What you write shows the code you type for a simple part, and the rules it follows.

1. Everything is built from general tools. CRUMB gives you primitives such as “stamp a conductance”, “apply a torque” and “cast a ray”. It never gives you “be a motor”. A motor, a thruster, a servo and a reaction wheel are all built from the same torque and force calls. If you find something you can’t express, that’s a missing general tool: suggest it as a feature and it gets added for everyone. The one exception is the microcontroller chips themselves: they’re tightly integrated with the electrical side of the simulation, so only the CRUMB developers can add a new one. You can still build any board around a supported chip.

2. Circuit logic is purely electrical. Circuit logic never sees a body, a force or a position. Anything physical happens in component logic, and the two halves only pass plain numbers through UserData. A motor’s winding reads the rotor speed as a number and publishes its current. The component logic turns that current into torque. The winding never learns a rotor exists. See Forces and coupling.

Your code is checked against a strict whitelist of types and namespaces before it compiles, every time a part loads: from the editor, a save file or the Workshop. Code that names anything off the whitelist is refused rather than run.

Each half has its own list. Circuit logic gets the Simulation namespace and basic C# types, and component logic adds the types for your model. The lists are on Circuit logic and Component logic, and exactly as CRUMB enforces them on The whitelist in the API reference.

A method can also be tied to one hook: Fail can only be called from CheckFailure. The reference marks any method like it with an only in tag.

A part’s fields are its power-on state. Reset State works like switching every device off, saving and reopening the file: every part gets fresh circuit logic and component logic, so anything held only in a field is gone.

To keep something across a Reset or a save:

  • Properties keep the values the user set in the inspector.
  • SaveState(name, value) in component logic persists a value the part changes at runtime, such as a switch position. Without it, the switch snaps back on reopen.
  • Persistent images (AllocatePersistentImage) hold durable bytes, such as an EEPROM’s contents.

Never keep state in a static field. It is shared by every copy of the part and outlives them all.