Skip docs navigation
Public Examples
Tutorial
Unity-authored gameplay and asset workflows exported through Unity WebGL into TPG.

Unity WebGL Game Tutorial

Build Unity Bounce Relay from a Unity 2022 LTS WebGL export when you want Unity-authored gameplay while TPG owns room lifecycle, controller identity, state sync, and publishing.

What You Build

Unity Bounce Relay is a compact host-display plus controller example that mirrors a Unity WebGL export without requiring the Unity editor in CI:

  • the host display boots @tpgames/unity-bridge and exposes window.TPG_UNITY
  • Unity WebGL plug-ins call the bridge through Assets/Plugins/WebGL/TpgBridge.jslib
  • Unity C# scripts call plug-in functions with DllImport("__Internal")
  • TPG shared state owns round-level facts such as active zone and total bounces
  • TPG player state owns each controller zone, power, and bounce count
  • transient bounce effects travel through runtime.broadcast("unity:bounce", ...)
  • the bundle includes the web surface files plus unity-export/ project metadata
  • the registration script uploads, reviews, publishes, and verifies the game through the registry API

The tutorial below includes the complete fixture source. The browser bridge is published as @tpgames/unity-bridge.

Supported Unity Path

Target Unity 2022 LTS or newer WebGL exports. The current TPG package is a browser-side bridge, not a Unity editor package that exports or uploads your project for you.

Use this path when your Unity project can:

  • export WebGL artifacts that can run inside a TPG host-display iframe
  • include a WebGL JavaScript plug-in under Assets/Plugins/WebGL/
  • keep authoritative room facts in TPG shared/player state instead of Unity-only globals
  • keep controller input in a DOM controller surface, or explicitly build a second Unity controller export
  • serve the Unity loader, framework, .data, .wasm, and streaming assets from the same bundle root

The checked-in example keeps the CI payload lightweight by including unity-export/ProjectSettings/ProjectVersion.txt, a reference TpgBridge.jslib, and a reference TpgBridge.cs. A production project should export the real Unity WebGL build artifacts into the same unity-export/ folder.

Create the Project

Start with the canonical TP Games project so the game inherits the tested scripts, CI workflow, validation, and bundled AI development skill:

If the author CLI is already installed, tpgames init unity-bounce-relay replaces the first scaffold command.

This tutorial intentionally replaces the starter surface layout with the checked-in Unity example. Copy the example's root host.html and controller.html in place of host.html and controller.html, then remove vite.config.mjs before creating the vite.config.ts shown below. Do not keep both Vite config files.

Use the checked-in example as the reference layout:

Install the normal game kit, the Unity bridge, and the authoring CLI:

For a real Unity game, export your WebGL build into unity-export/, then adapt host.html to load the generated Unity loader. Keep controller.html as a normal DOM surface unless you specifically need a second Unity WebGL export for controller UI.

Configure the Author Tools

Add the device, bundle, and registry commands to package.json:

Configure vite.config.ts with the wrapper surfaces and workbench:

Create tpgames.json; the copy rule carries the real Unity WebGL export into a registry bundle:

Boot the Bridge

Both host and controller pages boot a normal TPG runtime and install the Unity bridge:

createUnityBridgeGame() exposes a browser runtime API on window.TPG_UNITY. The default GameObject name is TPGBridge, so Unity SendMessage callbacks can target methods such as OnTpgLifecycle, OnTpgSharedState, OnTpgPlayerState, and OnTpgMessage.

After the generated Unity loader creates an instance, attach it to the bridge:

The checked-in host shim uses a minimal SendMessage-compatible object so CI can verify the bridge without launching the Unity editor.

Add the WebGL Plug-In

Create Assets/Plugins/WebGL/TpgBridge.jslib in the Unity project:

The plug-in is the browser-to-Unity seam. It should stay thin: convert Unity calls into TPG runtime calls, and keep game rendering plus scene-local animation inside Unity.

Map Unity Lifecycle Events

Create a TPGBridge GameObject with a C# script that imports the plug-in functions and receives TPG callbacks:

Use ReportReady() when the Unity scene can receive runtime events. Use durable state methods for facts that should survive reconnect, and use broadcast() or sendToParticipant() for effects that are safe to miss.

Keep State in TPG

Unity can own rendering, animation, physics, and scene-local effects. TPG should own synchronized facts that need to reach host, controller, spectator, or future dedicated-runtime surfaces.

The example models shared state separately from Unity scene objects:

Each controller writes its own player state:

The host display listens for unity:bounce messages and updates its local feed, while durable shared/player snapshots remain available after refresh or reconnect.

Generate the Manifest

Use createUnityManifest() when rendering the bundle manifest:

The helper creates a display-plus-controller manifest aligned with the public TPG manifest contract. The public tpgames bundle command follows tpgames.json, copies the built assets, manifest, and unity-export/ into dist/, then zips the archive for registry upload.

Test on Multiple Devices Without Uploading

After exporting the current Unity WebGL build, the example's @tpgames/sdk-dev-kit Vite plugin exposes its display and controller to the normal TPG room flow. Install cloudflared, then run:

The command starts Vite and a temporary Cloudflare Quick Tunnel, then opens the host room on https://play.tp.games. Scan the room QR with two or more phones. No registry upload, API key, or bundle build is required, but the current Unity WebGL export must already exist locally.

To use a tunnel you already manage, keep its origin port aligned with the CLI:

A Quick Tunnel exposes the local Vite server and source modules publicly. Treat the generated host link as private because it contains the temporary development-manifest descriptor; share only the room QR or controller invite. Keep .env files, credentials, and private assets outside Vite's served root, and stop the command and tunnel when finished. Use --no-open when you only want the private host link printed.

Export, Bundle, and Register

In Unity, use WebGL export settings that are compatible with iframe delivery:

  • keep compression disabled or configure server headers for the chosen compression format
  • avoid threaded WebGL unless the deployed origin is cross-origin isolated
  • keep the generated loader, framework, data, wasm, and streaming assets together
  • test the exported canvas at mobile and desktop widths before packaging

Render the TPG bundle:

The output should include:

For local interactive publishing, start the registry and browser approval shell from a TPG repository checkout in two terminals:

Then sign in and register from the Unity example repo:

The registration script uploads the archive to /creator/uploads, submits the version, creator-publishes it with the selected credential, fetches /games/{gameId}/manifest, and verifies a published Unity asset under /published-assets/{gameId}/{version}/unity-export/Assets/Plugins/WebGL/TpgBridge.jslib.

Registration delivers a built game version; it does not create a developer account. Its default upload mode stops after upload, --mode submit requests review, and --mode publish --yes deliberately completes creator publication. Browser login stores a session only for the exact registry origin. Use TPG_API_KEY only for CI or another unattended environment, keep it in the environment rather than arguments or project configuration, and grant only the scopes required by the selected mode.

Launch Verification

After publishing, open the web shell, select Unity Bounce Relay, join with at least two controllers, and start the game. Verify:

  • the host iframe shows the Unity stage shell and bridge feed
  • window.TPG_UNITY exists inside the host and controller iframes
  • the Unity instance is attached and receives SendMessage callbacks
  • controller zone buttons and power input update player state
  • Bounce broadcasts a unity:bounce runtime message and records an analytics milestone
  • refreshing a controller preserves the participant seat and can continue play
  • returning to the lobby stops the surface without leaving a runaway render loop

For browser QA, test mobile, tablet, laptop, and desktop widths. Unity exports often have fixed canvas sizes, so verify no horizontal overflow, readable controller controls, and visible keyboard focus on every button.

Troubleshooting

  • window.TPG_UNITY is undefined: confirm the host/controller page calls bootIframeGame(createUnityBridgeGame(), ...) before Unity scripts call the WebGL plug-in.
  • Unity callbacks never fire: confirm the bridge uses the same gameObjectName as your Unity TPGBridge GameObject and that method names match the defaults or configured overrides.
  • Plug-in calls fail in the editor: WebGL plug-ins only run in WebGL builds; guard editor-only code and test the exported build in a browser.
  • Controller effects do not reach the host: verify the controller calls broadcast("unity:bounce", payload) and the host listens for message events with the same message type.
  • Published assets 404: inspect dist/manifest.json and confirm every entry path is relative to the bundle root, not an absolute /assets/... path.
  • The Unity canvas overflows on phones: use a responsive wrapper around the exported canvas and keep critical controller actions in the DOM controller surface.
  • CI cannot run the Unity editor: keep editor export as a local author step, then commit or upload only the generated WebGL artifacts needed by TPG packaging.

Current Limitations

  • @tpgames/unity-bridge is a browser-side bridge and manifest helper, not a full Unity editor package.
  • The checked-in example does not include generated .wasm or .data artifacts; it is shaped for CI-verifiable TPG packaging.
  • Unity projects still need a WebGL plug-in plus a GameObject with callback methods.
  • Spectator-specific Unity surfaces are not included.
  • Registry review and publish are API-driven for automation, so product creator/reviewer flows should still be tested separately for production submissions.