{"id":50654390,"url":"https://github.com/jjmhalew/ngx-unity","last_synced_at":"2026-06-07T23:01:36.926Z","repository":{"id":209005779,"uuid":"723003363","full_name":"jjmhalew/ngx-unity","owner":"jjmhalew","description":"A simplified way to communicate between Unity and Angular","archived":false,"fork":false,"pushed_at":"2026-03-26T22:44:37.000Z","size":76970,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-27T10:40:57.446Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jjmhalew.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-11-24T12:58:24.000Z","updated_at":"2026-03-26T22:38:15.000Z","dependencies_parsed_at":"2023-12-22T19:25:00.706Z","dependency_job_id":"12e8c83d-9fae-4200-a20a-b364658cbffc","html_url":"https://github.com/jjmhalew/ngx-unity","commit_stats":null,"previous_names":["jjmhalew/unity-angular-bridge","jjmhalew/ngx-unity"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jjmhalew/ngx-unity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjmhalew%2Fngx-unity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjmhalew%2Fngx-unity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjmhalew%2Fngx-unity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjmhalew%2Fngx-unity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jjmhalew","download_url":"https://codeload.github.com/jjmhalew/ngx-unity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjmhalew%2Fngx-unity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34041089,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-07T23:00:55.018Z","updated_at":"2026-06-07T23:01:36.908Z","avatar_url":"https://github.com/jjmhalew.png","language":"C#","funding_links":[],"categories":["Framework Interoperability"],"sub_categories":["External Integration"],"readme":"![Release](https://img.shields.io/github/v/release/jjmhalew/ngx-unity)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/jjmhalew/ngx-unity)\n![Downloads](https://img.shields.io/npm/dw/ngx-unity)\n[![License](https://img.shields.io/:license-GNU-blue.svg?style=flat)](https://opensource.org/licenses/GNU)\n\n\u003cimg width=\"170\" height=\"180\" alt=\"ngx-unity logo\" src=\"https://github.com/user-attachments/assets/2eb16db5-f296-49b0-a0af-a2e43a7d9ac0\" /\u003e\n\n# ngx-unity\n\nA type-safe bridge for bidirectional communication between Unity WebGL/WebGPU and Angular.\n\nThis project has two parts:\n\n| Package | Distribution | Contents |\n|---|---|---|\n| **`ngx-unity`** | Angular library (npm) | Reusable viewport component, `IUnityInstance` type, mock utilities |\n| **Unity C# scripts** | Copy to `Assets/` | Attributes, TypeScript code generators, editor settings |\n\n## Demo\nhttps://ngx-unity.web.app/\n\n## Features\n\n- **Angular → Unity**: Call Unity methods from Angular via auto-generated `UnityClient.ts`\n- **Unity → Angular**: Receive Unity events in Angular via auto-generated signals (no RxJS required)\n- **Callback Support**: Request-response and event registration patterns between C# and JavaScript\n- **TSDoc Generation**: C# XML documentation automatically appears in generated TypeScript\n- **Custom Attributes**: `[AngularExposed]` and `[JSLibExport]` for clean, declarative setup\n- **Configurable Output**: Control where generated files are placed via Editor settings\n- **`\u003cngx-unity-viewport\u003e`**: Ready-to-use component that loads Unity WebGL/WebGPU with automatic mock fallback\n\n## Requirements\n\n- Unity 2021.2+ (for `makeDynCall` callback support)\n- Angular 16+ (for signals support)\n\n## Quick Start\n\n### Unity Setup\n\n1. Copy all `.cs` files from this repository into your Unity project (e.g., `Assets/Scripts/UnityAngularBridge/`).\n\n2. **Configure the output paths** (optional):\n   `Tools \u003e UnityAngularBridge \u003e Settings`\n   Set where `UnityClient.ts` and `unity-jslib-exported.service.ts` are generated.\n   By default, `UnityClient.ts` goes to your Documents folder.\n\n3. **Enable callback support** (if using callbacks):\n   `Tools \u003e UnityAngularBridge \u003e Enable Callback Support`\n   This sets the required Emscripten arg (`-s ALLOW_TABLE_GROWTH`).\n\n4. Recompile or click Play in Unity Editor to regenerate all TypeScript files.\n\n### Angular Setup\n\n1. Copy the generated TypeScript files into your Angular project (e.g., `src/app/generated/`).\n\n2. Register `UnityClient` in your app config:\n   ```typescript\n   import { UnityClient } from './generated/unity-client';\n\n   export const appConfig: ApplicationConfig = {\n     providers: [UnityClient],\n   };\n   ```\n\n3. Use the `\u003cngx-unity-viewport\u003e` component to embed Unity:\n   ```typescript\n   import { NgxUnityViewport, type IUnityInstance } from 'ngx-unity';\n\n   @Component({\n     imports: [NgxUnityViewport],\n     template: `\n       \u003cngx-unity-viewport\n         buildPath=\"unity\"\n         height=\"500px\"\n         (instanceReady)=\"onUnityReady($event)\" /\u003e\n     `,\n   })\n   export class MyComponent {\n     onUnityReady(instance: IUnityInstance): void {\n       // Wire up your bridge service\n     }\n   }\n   ```\n\n4. Inject `UnityJSLibExportedService` wherever you need Unity events:\n   ```typescript\n   import { UnityJSLibExportedService } from './generated/unity-jslib-exported.service';\n\n   const jsLib = inject(UnityJSLibExportedService);\n   // Access signals directly\n   const selectedObject = jsLib.sendSelectedObject; // Signal\u003cstring | null\u003e\n   ```\n\n---\n\n## Calling Unity from Angular\n\n### How to use\n\nMark Unity methods with `[AngularExposed]`:\n\n```csharp\n/// \u003csummary\u003e\n/// Load an object by its ID. Called from Angular.\n/// \u003c/summary\u003e\n[AngularExposed(gameObjectName: \"SceneManager\")]\npublic void LoadObject(string objectId)\n{\n    // Your logic here\n}\n```\n\nThis generates `UnityClient.ts` with typed methods and TSDoc:\n\n```typescript\nexport class UnityClient {\n  /** Load an object by its ID. Called from Angular. */\n  public sceneManager_LoadObject(unityInstance: IUnityInstance, objectId: string): void {\n    unityInstance?.SendMessage(\"SceneManager\", \"LoadObject\", objectId);\n  }\n}\n```\n\n### Parameters\n\n- Only `string` and `number` parameter types are supported (Unity WebGL limitation)\n- Maximum 1 parameter per method\n- The `gameObjectName` identifies which Unity GameObject receives the `SendMessage` call\n- Optional `Documentation` property overrides the TSDoc output\n\n**Note**: Generation happens at compile time / Play mode, not at runtime. A default GameObject name is used unless overridden in `[AngularExposed]`.\n\n### How to update\n\nRecompile or click on 'Play' in Unity editor to trigger `AngularExposedExport.cs`.\nThis will automatically generate `UnityClient.ts` to the configured output path.\n\n---\n\n## Subscribing to Unity Events from Angular\n\n### How to use\n\nDeclare `[DllImport(\"__Internal\")]` methods with the optional `[JSLibExport]` attribute:\n\n```csharp\n/// \u003csummary\u003e\n/// Sends the selected object ID to Angular.\n/// \u003c/summary\u003e\n[DllImport(\"__Internal\")]\n[JSLibExport(Category = \"Selection\")]\nprivate static extern void SendSelectedObject(string objectId);\n\n/// \u003csummary\u003e\n/// Sends a pipe-separated list as a string array.\n/// \u003c/summary\u003e\n[DllImport(\"__Internal\")]\n[JSLibExport(IsStringArray = true, Category = \"Objects\")]\nprivate static extern void SendObjectsList(string objectIds);\n\n/// \u003csummary\u003e\n/// Notifies Angular (no data, event-only).\n/// \u003c/summary\u003e\n[DllImport(\"__Internal\")]\n[JSLibExport(Category = \"Lifecycle\")]\nprivate static extern void SendSceneReady();\n```\n\nCall from Unity:\n\n```csharp\n#if PLATFORM_WEBGL \u0026\u0026 !UNITY_EDITOR\n    SendSelectedObject(objectId);\n    SendObjectsList(string.Join(\"|\", objectIds));\n    SendSceneReady();\n#endif\n```\n\nThis generates an Angular service with **pure signals** (no RxJS):\n\n```typescript\n@Injectable({ providedIn: \"root\" })\nexport class UnityJSLibExportedService {\n  /** Sends the selected object ID to Angular. */\n  readonly sendSelectedObject: Signal\u003cstring | null\u003e;\n\n  /** Sends a pipe-separated list as a string array. */\n  readonly sendObjectsList: Signal\u003cstring[]\u003e;\n\n  /** Notifies Angular (no data, event-only). Increments on each event. */\n  readonly sendSceneReady: Signal\u003cnumber\u003e;\n}\n```\n\n### `[JSLibExport]` Attribute\n\n| Property | Type | Default | Description |\n|---|---|---|---|\n| `IsStringArray` | `bool` | `false` | Split pipe-delimited string into `string[]` |\n| `Category` | `string` | `\"\"` | Organize methods (for documentation) |\n| `Documentation` | `string` | `\"\"` | Override TSDoc (falls back to XML docs) |\n| `IsCallbackRegistration` | `bool` | `false` | Mark as callback registration point |\n\n### How to update\n\nRecompile or click on 'Play' in Unity editor to trigger `JSLibExport.cs`.\nThis will automatically generate:\n1. `BrowserInteractions.jslib` — placed in `Assets/Plugins/`\n2. `unity-jslib-exported.service.ts` — placed at the configured output path\n\n---\n\n## Callback Support\n\nBased on [jmschrack.dev/posts/UnityWebGL](https://jmschrack.dev/posts/UnityWebGL/).\n\n### Request-Response (C# → JS → C#)\n\nUnity sends a request to Angular with a callback. Angular processes and responds:\n\n**Unity (C#):**\n\n```csharp\n[DllImport(\"__Internal\")]\n[JSLibExport(Category = \"Data\")]\nprivate static extern void RequestDataFromWeb(string query, Action\u003cstring\u003e onResult);\n\n[MonoPInvokeCallback(typeof(Action\u003cstring\u003e))]\nprivate static void OnDataReceived(string data)\n{\n    Debug.Log($\"Received: {data}\");\n}\n\n// Usage:\n#if PLATFORM_WEBGL \u0026\u0026 !UNITY_EDITOR\n    RequestDataFromWeb(\"my-query\", OnDataReceived);\n#endif\n```\n\n**Angular (TypeScript):**\n\n```typescript\nconst jsLib = inject(UnityJSLibExportedService);\n\n// Register a handler that responds to Unity's requests\njsLib.registerRequestDataFromWebHandler((query, respond) =\u003e {\n    const result = processQuery(query);\n    respond(result); // Sends result back to C# callback\n});\n```\n\n### Event Registration (JS → C#)\n\nUnity registers a callback that Angular can invoke later:\n\n**Unity (C#):**\n\n```csharp\n[DllImport(\"__Internal\")]\n[JSLibExport(IsCallbackRegistration = true, Category = \"Navigation\")]\nprivate static extern void RegisterOnNavigationChanged(Action\u003cstring\u003e handler);\n\n[MonoPInvokeCallback(typeof(Action\u003cstring\u003e))]\nprivate static void OnNavigationChanged(string route)\n{\n    Debug.Log($\"Navigation: {route}\");\n}\n\n// Register once on start:\n#if PLATFORM_WEBGL \u0026\u0026 !UNITY_EDITOR\n    RegisterOnNavigationChanged(OnNavigationChanged);\n#endif\n```\n\n**Angular (TypeScript):**\n\n```typescript\nconst jsLib = inject(UnityJSLibExportedService);\n\n// Later, notify Unity of a navigation change:\njsLib.notifyOnNavigationChanged(\"/new-route\");\n```\n\n### Important Notes\n\n- Callback methods must be `static` and marked with `[MonoPInvokeCallback]`\n- For registration callbacks, enable Emscripten support via `Tools \u003e UnityAngularBridge \u003e Enable Callback Support`\n- Callbacks support `Action` (void) and `Action\u003cstring\u003e` (string parameter)\n\n---\n\n## Configuration\n\n### Output Paths\n\nOpen `Tools \u003e UnityAngularBridge \u003e Settings` to configure:\n\n| Setting | Default | Description |\n|---|---|---|\n| UnityClient.ts path | MyDocuments | Where the Angular-to-Unity client is generated |\n| Service .ts path | Assets/Plugins | Where the Unity-to-Angular service is generated |\n\nPaths can be absolute or relative to the Unity project folder.\n\n### TSDoc / XML Documentation\n\nGenerated TypeScript includes JSDoc comments from either:\n1. **C# XML documentation comments** (`/// \u003csummary\u003e`) — requires XML docs enabled in Unity\n2. **Attribute `Documentation` property** (fallback/override)\n\n---\n\n## ngx-unity Library\n\nThe `ngx-unity` Angular library (in `example/angular-unity-example/projects/ngx-unity/`) provides reusable building blocks:\n\n### `NgxUnityViewport` Component\n\nA drop-in component that handles Unity WebGL/WebGPU loading with automatic mock fallback:\n\n```html\n\u003cngx-unity-viewport\n  buildPath=\"unity\"\n  height=\"400px\"\n  [mockFactory]=\"myMockFactory\"\n  (instanceReady)=\"onReady($event)\" /\u003e\n```\n\n| Input | Type | Default | Description |\n|---|---|---|---|\n| `buildPath` | `string` | `'unity'` | Path to Unity WebGL/WebGPU build (relative to `public/`) |\n| `height` | `string` | `'400px'` | CSS height of the canvas |\n| `mockFactory` | `() =\u003e IUnityInstance` | built-in mock | Custom mock factory for development |\n\n| Output | Type | Description |\n|---|---|---|\n| `instanceReady` | `IUnityInstance` | Emitted when Unity (or mock) is ready |\n\n### `createMockUnityInstance()`\n\nA testing utility that creates a basic mock `IUnityInstance`:\n\n```typescript\nimport { createMockUnityInstance } from 'ngx-unity';\n\nconst mock = createMockUnityInstance({\n  onSendMessage: (obj, method, data) =\u003e {\n    // Simulate project-specific Unity responses\n  },\n});\n```\n\n### Multi-Instance Support\n\nMultiple `\u003cngx-unity-viewport\u003e` components can coexist on the same page.\nEach viewport creates its own `\u003ccanvas\u003e` with a unique DOM ID, and the Unity\nloader script is loaded only once even when viewports share the same `buildPath`.\n\n```html\n\u003c!-- Two viewports side by side using the same Unity build --\u003e\n\u003cngx-unity-viewport\n  buildPath=\"unity\"\n  height=\"300px\"\n  (instanceReady)=\"onFirstReady($event)\" /\u003e\n\n\u003cngx-unity-viewport\n  buildPath=\"unity\"\n  height=\"300px\"\n  (instanceReady)=\"onSecondReady($event)\" /\u003e\n```\n\nEach viewport emits its own `instanceReady` event with an independent\n`IUnityInstance`, so you can wire each one to a separate bridge service or\nhandle them individually:\n\n```typescript\ninstances: IUnityInstance[] = [];\n\nonFirstReady(instance: IUnityInstance): void {\n  this.instances[0] = instance;\n}\n\nonSecondReady(instance: IUnityInstance): void {\n  this.instances[1] = instance;\n}\n```\n\n\u003e **Note:** The auto-generated `UnityJSLibExportedService` registers global\n\u003e `window` callbacks, so Unity → Angular signals (e.g. `sendSelectedObject`)\n\u003e reflect the most recent event from *any* instance. If you need per-instance\n\u003e isolation for Unity → Angular events, maintain separate state in your\n\u003e bridge service keyed by each `IUnityInstance`.\n\n---\n\n## Project Structure\n\n```\nngx-unity/\n├── *.cs                                  ← Unity C# source files\n├── README.md\n├── example/\n│   ├── angular-unity-example/\n│   │   ├── projects/\n│   │   │   └── ngx-unity/                ← Angular library (publishable to npm)\n│   │   │       └── src/lib/\n│   │   │           ├── components/        ← NgxUnityViewport\n│   │   │           ├── models/            ← IUnityInstance\n│   │   │           └── testing/           ← createMockUnityInstance\n│   │   └── src/                           ← Example app\n│   │       └── app/\n│   │           ├── generated/             ← Unity-generated TS files\n│   │           ├── services/              ← Project-specific bridge\n│   │           └── components/            ← Demo UI\n│   └── unity-project/                     ← Example Unity project\n```\n\n---\n\n## TODO\n\n- [ ] Distribute Unity scripts as a UPM package (git URL)\n- [ ] Auto-generate JSON serialization for complex Angular→Unity parameters\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjjmhalew%2Fngx-unity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjjmhalew%2Fngx-unity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjjmhalew%2Fngx-unity/lists"}