v0.1.x → v0.2.x
Loom v0.2 is a breaking release with two source changes you make by hand:
- Bridge setup. Your
[Bridge]class inherits a base class, and a single Loom UI component does the wiring and runtime setup you used to write yourself. - Bridge state API.
Observable<T>andObservableList<T>are gone. State is now plain{ get; set; }properties; lists and maps useReactiveList<T>/ReactiveMap<K,V>(or snapshot collections).
Updating the package itself is a single step; everything ships together. Your
Solid UI, your member attributes ([BridgeState], [BridgeAction], [BridgeEvent]), and your event-payload DTOs are unchanged. Most projects
finish in a few minutes; it’s mechanical, mostly find-and-replace.
What changed at a glance
| v0.1.x | v0.2.x | |
|---|---|---|
| Bridge class | class GameBridge | class GameBridge : LoomBridgeBase |
| Construction | new GameBridge() in your bootstrap | Loom constructs it |
| Registration | you call RegisterWithLoom() | automatic |
| Runtime | you call LoomRuntime.Initialize() / Shutdown() | automatic |
| Scene setup | a bootstrap MonoBehaviour + Loom View | one Loom UI component |
| Bridge access | MyBootstrap.Bridge.X | GameBridge.Instance.X |
| Scalar state | Observable<int> X { get; } = new(0) | int X { get; set; } |
| Read / write state | X.Value | X |
| List state | ObservableList<T> | ReactiveList<T> (or snapshot List<T>) |
long / ulong in TS | number | bigint |
Update the Unity package.
Update
com.loomguito0.2.0, then restart the Unity Editor so the new native plugin loads. Everything Loom needs ships in that one package, so there are no separate pieces to version or update by hand.Loom -> Sync UI Dependenciesinstalls the matching@loomgui/bridgeand@loomgui/vite-plugininto your UI app from the package you just updated.Inherit
LoomBridgeBase.Your
[Bridge]class must now inheritLoomBridgeBase. The source generator errors (LOOM005) on a[Bridge]class that doesn’t.[Bridge] -public partial class GameBridge { +public partial class GameBridge : LoomBridgeBase { // members }Move state from
Observable<T>to plain properties.A
[BridgeState]member is now a plain{ get; set; }property. Assigning it pushes to the UI. Drop theObservable<T>wrapper and the.Valueaccessor everywhere (reads and writes use the property directly), and move the initial value into the property initializer.[Bridge] public partial class GameBridge : LoomBridgeBase { - [BridgeState] public Observable<int> Score { get; } = new(0); + [BridgeState] public int Score { get; set; } = 0; - [BridgeAction] public void AddPoint() { Score.Value += 1; } + [BridgeAction] public void AddPoint() { Score += 1; } }If you used
Observable<T>.Subscribe(...)for engine-side reactivity, move that logic to where you set the value (a plain property has noSubscribe).Move lists to
ReactiveList<T>(or a snapshot collection).ObservableList<T>is replaced by two options:ReactiveList<T>/ReactiveMap<K,V>: declare get-only and mutate in place (Add,Remove,Clear, indexer). Each change pushes granularly.- Snapshot
List<T>/T[]/Dictionary<K,V>: a{ get; set; }property you reassign to sync (mutating in place won’t push).
-[BridgeState] public ObservableList<string> Messages { get; } = new(); +[BridgeState] public ReactiveList<string> Messages { get; } = new();Add/Remove/Clearcarry over unchanged. For a wholesale replace, either clear-then-add, or use a snapshotList<T>and assign the new list. See State, collections & events.Delete your bootstrap and add the Loom UI component.
Remove the MonoBehaviour that constructed the bridge and started the runtime. Everything it did is now handled for you:
// DELETE this whole file (e.g. LoomBootstrap.cs). [DefaultExecutionOrder(-1000)] public class LoomBootstrap : MonoBehaviour { public static GameBridge Bridge { get; private set; } private void Awake() { Bridge = new GameBridge(); Bridge.RegisterWithLoom(); LoomRuntime.Initialize(/* … */); } private void OnDestroy() => LoomRuntime.Shutdown(); }In your startup scene, run
Loom -> Setup UI in Current Scene. This adds a single Loom UI GameObject that builds the canvas, input capture, and ticker and initializes the runtime on Play. Delete the old “Loom Bootstrap” GameObject if you had one.See Loom UI for the full component reference.
Update how you reach the bridge.
The generator now emits a static
Instanceaccessor on your bridge class. Replace references to your old bootstrap singleton, and drop.Value:-LoomBootstrap.Bridge.Score.Value = 10; +GameBridge.Instance.Score = 10;Check
long/ulongreads in your UI.64-bit integers now cross to TypeScript as
bigint(they werenumber). If your UI reads along/ulongstate field or event payload, use bigint literals (1n), andString(v)to display, sinceJSON.stringifythrows on a bigint. If you only need a large id as text, use astringfield instead.Recompile and regenerate types.
Let Unity recompile, then run
Loom -> Regenerate Types. The contract now includes the new built-inloom.*state (below) and your converted members, so the generatedbridge.d.tsupdates to match.
New in v0.2: built-in loom.* state
Every bridge now exposes a read-only loom block (active scene name, viewport
size, device pixel ratio, connection status) with no declaration on your part.
The UI reads it as bridge.state.loom.*. It’s additive, so adopting it is
optional. See Built-in state.
What didn’t change
- Your Solid UI, components, and CSS.
- The member attributes themselves (
[BridgeState],[BridgeAction],[BridgeEvent]) and the generated TypeScript names (Score→score). Event<T>and event-payload DTO conventions.- Player builds: the UI is still bundled into
StreamingAssets/Loom.