Unity client
When a Unity game needs to consume authoritative Go state, the Unity client handles the protocol connection and turns generated entities into GameObjects. golem-bake emits C# classes for your schemas, the package opens WebSocket or WebTransport connections, and the prefab bridge spawns the matching Unity objects.
Recommended project layout
Section titled “Recommended project layout”Keep the Golem project root as the folder that contains golem.yaml, then place the Unity project below it:
MyGame/ golem.yaml cmd/server/ schemas/ Unity/ Assets/ Packages/In Unity, add the Golem package and WebTransport package to Packages/manifest.json:
{ "dependencies": { "io.demiurgos.golemengine": "file:C:/Demiurgos/golem-engine/golem-unity", "io.demiurgos.webtransport": "file:C:/Demiurgos/webtransport-csharp/artifacts/upm/io.demiurgos.webtransport" }}For registry installs, use the published package versions instead of file: paths. The Golem Unity package depends on the WebTransport package, but local sample projects usually list both packages explicitly so Unity can resolve both local folders.
Choose an authoring workflow
Section titled “Choose an authoring workflow”You can keep entity, custom-type, and world schemas as handwritten YAML, as shown throughout the configuration guides. If prefabs and ScriptableObjects are your team’s primary content source, Golem Scribe exports those schemas, catalog data, and prefab collision footprints from Unity.
Both workflows feed the same golem-bake pipeline and generated clients. Scribe-owned YAML is generated output; handwritten files remain supported alongside it.
Generate C# for Unity
Section titled “Generate C# for Unity”Add the C# client integration to golem.yaml and point it at folders inside your Unity project:
simulation: dimensions: 3 # optional; include when your Unity game needs PosZ
integrations: go-server: out: internal/synced/ package: synced csharp-client: out: Unity/Assets/GolemGenerated/ package: MyGame.GolemGenerated unity: out: Unity/Assets/GolemViews/ package: MyGame.GolemGeneratedcsharp-client emits the protocol client: ClientFactory, EntityManager, SyncedPlayer-style entity classes, command builders, and protobuf codec wrappers for your schema. The optional unity integration emits a simple MonoBehaviour bridge per entity type.
In Unity, open Golem > Settings… and set Project Root to the folder that contains golem.yaml. golem.yaml stays the source of truth for generated output folders; the Unity settings page only displays the resolved csharp-client.out path.
Use Golem > Generate Code to run golem-bake from that project root. The default Bake Command Mode is Go Command, which runs:
go run github.com/demiurgos-hub/golem-engine/cmd/golem-bakeUse Path Executable when golem-bake is installed on PATH, or Custom Command when your team pins a project-local binary such as Tools/golem-bake.exe.
Configure the server
Section titled “Configure the server”For WebTransport, use the default Golem transport and datagram state-update lane:
srv, rt := synced.NewServer(golem.ServerConfig{ Addr: ":4433", DevSelfSignedCert: true, // local development only})
rt.Commands.BindAllHandlers(func(sess *golem.Session, err error) { log.Println("command error:", err)})For WebSocket, explicitly select the stream state-update lane:
srv := golem.NewServer(golem.ServerConfig{ Addr: ":8080", Transport: golem.TransportWebSocket, StateUpdateLane: golem.StateUpdateLaneStream,})Command routing is the same as the JavaScript and Go clients. Bind all generated handlers so WebSocket stream commands and both WebTransport command lanes reach the same router:
rt.Commands.BindAllHandlers(func(sess *golem.Session, err error) { log.Println("command error:", err)})Connect from Unity
Section titled “Connect from Unity”Fetch the server’s realtime config, append the session values required by OnUpgrade, and pass the resulting transport-aware options to the generated client:
using System.Collections.Generic;using System.Threading;using GolemEngine.Unity;using MyGame.GolemGenerated;
var realtime = await GolemRealtimeBootstrap.FetchRealtimeConfigAsync( "https://game.example.com/api/realtime-config", cancellationToken);
var options = GolemRealtimeBootstrap.ConnectOptionsFromRealtimeConfig( realtime, new[] { new KeyValuePair<string, string>("token", sessionToken), new KeyValuePair<string, string>("char_id", selectedCharacterId.ToString()), });
var client = ClientFactory.CreateClient();client.Connect(options);The config selects WebSocket or WebTransport and carries the server’s eventual-state ACK interval. Query values are appended without rewriting existing URL bytes, duplicate values, or fragments. The fetch rejects oversized and malformed responses.
client.Connect("wss://game.example.com/ws") remains available when the game already owns an explicit WebSocket URL.
Development certificates
Section titled “Development certificates”Browser-style certificate hashes cannot be applied by the native Unity WebTransport dependency. A non-empty serverCertificateHashes list therefore fails before connecting instead of being ignored. Publicly trusted production certificates need no special setup.
For a local generated certificate, omit browser hashes from the realtime config and use an options-aware factory that explicitly allows the development certificate:
using GolemEngine.Unity;using MyGame.GolemGenerated;using WebTransport;
var client = ClientFactory.CreateClient(options =>{ if (options.Transport == GolemConnectOptions.TransportWebTransport) { GolemRealtimeBootstrap.EnsureNativeWebTransportSupportsCertificateHashes( options.ServerCertificateHashes); return new GolemWebTransportTransport( new WebTransportClientOptions { AllowUntrustedCertificates = true, }, GolemConnectOptions.EffectiveEventualAckIntervalMs( options.EventualAckIntervalMs)); } return new GolemWebSocketTransport();});Use AllowUntrustedCertificates only for local development. On the server, set RealtimeConfigOptions.IncludeServerCertificateHashes to a pointer to false for this native-client route; browser clients that rely on certificate hashes should use a separate config response or a trusted certificate.
The old parameterless custom transport factory remains usable with Connect(string), but it cannot honor transport-aware options and is deprecated. Custom options-aware factories are responsible for applying or explicitly rejecting every option they receive.
Send commands
Section titled “Send commands”For each command schema, generated C# includes a builder on CommandBuilders. Use Send when commands do not depend on one another, and SendOrdered when their processing order matters:
client.Send(CommandBuilders.BuildMoveCommand(playerEntityId, dx, dy));client.SendOrdered(CommandBuilders.BuildUseItemCommand(playerEntityId, inventorySlot));Generated ClientFactory helpers build and send in one call when you prefer typed arguments:
ClientFactory.SendMove(client, playerEntityId, dx, dy);ClientFactory.SendUseItemOrdered(client, playerEntityId, inventorySlot);On WebTransport, Send uses reliable unordered datagrams with a 1176-byte encoded command limit. SendOrdered uses reliable ordered datagrams with a 1174-byte limit. Oversized commands throw and do not fall back to the stream.
On WebSocket, both methods share the same ordered reliable-stream queue. Commands are batched into ClientPacket payloads under 32000 bytes and retain call order.
Spawn prefabs
Section titled “Spawn prefabs”Create a GolemPrefabRegistry asset and add one entry per entity type, such as Player or Monster. Add GolemEntitySpawner to your scene and bind it to the generated entity manager after creating the client:
spawner.Bind(client.Entities);When an entity spawns, the spawner looks up a prefab by generated entity type name, instantiates it, and binds any GolemEntityView<T> or GolemTransformBinding components on the prefab. GolemTransformBinding copies the generated PosX and PosY properties into the GameObject transform and can smooth movement between updates. In 3D projects it also copies PosZ; 2D projects keep the prefab’s existing transform.position.z.
When a prefab component uses [GolemEntity], Scribe exports its entity schema and upserts the same entity-name mapping in the registry. See Author in Unity with Golem Scribe.
For a 3D project, move server-side entities with SetPosition3D(x, y, z) so the generated C# client receives all three coordinates.
Events and world data
Section titled “Events and world data”If your schemas include events, ClientFactory.CreateClient() wires an EventManager. Global and session events can be handled through generated OnXxx methods on the event manager, while entity events are reflected onto methods on the bound synced entity when present.
If your schemas include world data, the generated WorldManager stores the latest data and invokes typed OnXxxUpdate callbacks when the server sends initial or refreshed world data.
Editor menu
Section titled “Editor menu”The top-level Golem menu helps with local Unity project setup:
- Generate Code runs the configured bake command from the folder containing
golem.yaml. - Scribe > Export All reconciles Unity-authored schemas, catalogs, collision footprints, and the managed-artifact manifest.
- Scribe > Validate checks those generated artifacts without changing files.
- Run Server opens the configured server command in an external terminal at the project root. Unity does not own, stop, or restart that process.
- Validate Setup checks the normal Unity setup and runs the side-effect-free Scribe validation.
- Configure Current Scene creates or selects the prefab registry, entity spawner, and client object.
- Create menu items create individual scene/setup assets.
- Settings… opens the project-scoped Golem settings.
The settings page also controls Scribe auto-export, schema-change auto-bake, and the project-relative footprint output path. The Asset Folder setting is only for Unity-side assets created by the scene setup tools. Generated C# and schema paths come from golem.yaml.
Troubleshoot WebTransport connects
Section titled “Troubleshoot WebTransport connects”Unity WebTransport failures often happen before Golem’s reliable stream protocol starts, so check the transport setup first:
- Use the WebTransport endpoint, not the WebSocket endpoint. A typical local URL is
https://localhost:4433/wt;:8080is commonly used by WebSocket examples. - The server must run WebTransport over HTTP/3/QUIC with TLS. For local development, set
DevSelfSignedCert: trueand use a loopback URL. - Browser-style
ws://andwss://URLs are for WebSocket only. WebTransport useshttps://. - Production origins must be allowed by server config when the page/API port differs from the WebTransport port.
The Unity logs include the transport, redacted URL, phase, and error category. If a failure says phase=connect, check the URL, TLS certificate, UDP/QUIC reachability, and origin policy before looking at generated entity or command code.
Transport limits
Section titled “Transport limits”The reliable stream keeps each logical packet under 32000 bytes. WebTransport uses a 1200-byte datagram cap before protocol overhead, leaving 1176 bytes for Send commands and 1174 bytes for SendOrdered commands. Generated C# managers apply compact datagram state automatically when using WebTransport; WebSocket and custom transports without datagrams should use StateUpdateLaneStream.
See also Channels and transports, State updates, and Client commands.