Skip to main content

glue-project-loading

Run an FRB1/Glue project's .gluj/.glsj/.glej data directly in FRB2, with no Glue codegen. Triggers: GlueProject, GlueScreen, GlueEntity, EngineInitSettings.GlueProjectFile, GlueTypeMap, .gluj, CustomVariables by name.

インストールへ移動

ソース情報

リポジトリ
vchelaru/FlatRedBall2
ソースの最終更新活動
2026年8月10日 04:26
検出された SKILL.md の言語
英語
スター
14
フォーク
8

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
glue-project-loading
description
Run an FRB1/Glue project's .gluj/.glsj/.glej data directly in FRB2, with no Glue codegen. Triggers: GlueProject, GlueScreen, GlueEntity, EngineInitSettings.GlueProjectFile, GlueTypeMap, .gluj, CustomVariables by name.
# Loading Glue Projects FRB2 reads the JSON files Glue writes and builds Screens and Entities from them at runtime. There is no generated C#: `src/Glue/` is the whole implementation, `GlueProject` is the entry point. Booting one is a single call in `Game1.Initialize`, after `base.Initialize()`: ```csharp FlatRedBallService.Default.Initialize(this, "Content/FrbEditor/MyGame.gluj"); ``` The project names its own start-up screen, Gum project and resolution, so nothing else is passed — the engine wires the screen's `Save`/`Project` itself. **The path must be relative**: it resolves through `TitleContainer`, which throws on a rooted path, so an absolute one loads the project and then fails every asset it references. To load a project but boot your own screen instead, use `Initialize<MyScreen>(game, new EngineInitSettings { GlueProjectFile = … })`. ## Where to start | Task | Go to | |---|---| | Boot a project | `Initialize(game, "Content/FrbEditor/MyGame.gluj")` | | Load without booting | `GlueProject.Load(glujPath, content)` | | Find/create an element | `GlueProject.FindScreen`/`FindEntity`/`CreateScreen`/`CreateEntity` | | Move between screens | `Screen.MoveToScreen(string glueName)` | | Read/write an authored variable | the indexer on `GlueScreen`/`GlueEntity`, or `Get<T>` | | Claim a Glue type for FRB2 | `GlueTypeMap` | ## Every loaded element shares one C# type `GlueScreen` and `GlueEntity` are what a loaded element *is*; only its `Save` data distinguishes it. So `MoveToScreen<T>()` and `Factory<T>` cannot tell two loaded screens apart — the by-name overloads (`MoveToScreen(string)`, `GlueProject.CreateEntity(name, screen)`) exist for exactly this reason. Both directions work: a loaded screen can still `MoveToScreen<AHandWrittenScreen>()`. Names are Glue's own, with the folder: `Screens\Level1`, `Entities\Player`. Either separator is accepted and case is ignored, but the prefix is required — a bare leaf is ambiguous, since a screen and an entity can share one. ## Landmines **Assign `Save` inside the `configure` callback, never before it.** The engine retains that callback and replays it on `RestartScreen`; assigning outside means a restarted screen rebuilds with no data — a silently *empty* screen rather than an error. Only applies when you construct a `GlueScreen` yourself: `Initialize(game, glujPath)` and `MoveToScreen(string)` both do it correctly. **Loading is tolerant by design: it collects diagnostics instead of throwing.** A project that references a type this build cannot construct still loads, minus that object. Check `GlueLoadResult.HasErrors` and `Diagnostics`, and `GlueScreen.BuildDiagnostics` for per-element problems — otherwise a half-built screen looks like a working one. `GlueLoadOptions.Strict` turns the first diagnostic into a throw when you would rather fail loudly. **Adding a row to `GlueTypeMap` requires a matching `DynamicDependency`.** The reflected set is closed and the engine publishes AOT/trimmed; without it the type's properties are trimmed away and every assignment to them silently does nothing — in published builds only, never in `dotnet build`. **A Glue project's Gum UI must be loaded through `GlueProjectFile`, not `GumProjectFile`.** Gum only resolves elements from a project its own `GumService` loaded, and the `.gumx` path is not known until the `.gluj` has been read. See `gum-integration` for what goes wrong otherwise. **Abstract elements cannot be created.** Glue marks an element abstract when it leaves an object for a derived element to supply, so it is incomplete by construction. `CreateScreen`/`CreateEntity` throw; this is normal for a `GameScreen` base that levels derive from. ## Hot reload A `GlueScreen` watches the source tree the `.gluj` sits in and restarts itself when Glue writes there: copy the changed file to the build output, reparse the project, rebuild the same screen from the new data. Nothing to call — it registers in `CustomInitialize` whenever `FlatRedBallService.SourceContentRoots` is non-empty, so it is dev-only by construction. Opt out with `FlatRedBallService.IsGlueHotReloadEnabled` before the first screen starts. **The reload restart replaces the retained `configure` callback.** `Save` and `Project` survive — the replacement reassigns them — but anything else a callback you passed did, a difficulty or a seed or a hand-built object, is gone from every restart after the first Glue edit. Put that setup in a `GlueScreen` subclass's `CustomInitialize` or in `RestoreHotReloadState`. Gum files are left to Gum's own in-place pipeline, and `bin`/`obj` are filtered by `ContentDirectoryWatcher.IgnoredDirectories`. `content-hot-reload` covers the watch/copy machinery underneath. ## Variables: two surfaces, two intents `Objects` is the typed dictionary of built objects — `(Circle)screen.Objects["CooldownCircle"]`. The indexer is the variable bag: `entity["Health"] = 100`, `entity.Get<int>("Health")`. The indexer resolves in a fixed order, and the order is the point: a name matching a real CLR member writes that member (`entity["X"] = 5f` moves the entity), a tunneling variable reaches the contained object it targets, and only an unclaimed name is held by name. `Get<T>` is driven by `T`, not by the variable's declared type — Glue's declared types are frequently not CLR types at all. ## Inheritance is resolved at load, once `GlueProjectLoader` flattens every element into the union of its chain before anything inspects it, so a derived screen already carries its base's objects, variables, states, and referenced files. Code reading a `ScreenSave` never walks `BaseScreen` itself. ## What is not wired Pooling (`PooledByFactory`), `SortAxis` partitioning, and input binding (`JumpInput`, `MovementInput`, `EntitySave.InputDevice`) are parsed and not applied — a loaded platformer entity has its authored physics but nothing telling it to move. `CustomClasses` and `Events` are out of scope entirely. `plan/804-glue-project-loader/` records why for each.
GitHubで見る