Both scenes in this sample (NoCode_MenuScene.unity and NoCode_LevelScene.unity) come
pre-wired — every button, event, and cross-scene handoff is already connected. Everything
below this point documents what’s already there and how to adapt it. All connections use
Unity’s Inspector (drag & drop, UnityEvent wiring) — no C# needed anywhere in this sample.
This sample ships with no custom art. Both scenes use Unity’s default UI Button/Text styling, flat colors, and unstyled panels — there are no custom sprites, fonts, icons, or layout polish. “Pre-wired” means the wiring and logic are done, not that the screens are finished. Before shipping, restyle the Canvas, buttons, panels, and board backdrop with your own art — see Bundled assets and Before you ship below for what’s safe to restyle without breaking the wiring.
Two things are still required before pressing Play, even though nothing else needs wiring:
- Add both scenes to Build Settings (File ▸ Build Settings ▸ Scenes In Build) — the
menu-to-level and level-to-menu transitions callSceneManager.LoadSceneby name, which
throws if the scene isn’t registered there, even in the Editor. - Assign (or create) an
AquariumGameConfig— without one,NoCode_MenuScenespawns no
size buttons at all (see below), and any other config-dependent field falls back to whatever
the project has at the canonical Resources path, which is nothing until you run
Tools ▸ Aquarium ▸ Project Setup ▸ Create Project Configuration at least once. Every
empty Game Config/Registry field in this sample’s Inspectors has an Assign button
that does this for you in one click — finds an existing project asset of that type, or
creates one via Project Setup, and drops it into the field.
What’s already wired
NoCode_LevelScene — has no board-size buttons of its own. The board size always comes
from whatever the menu scene sent over (a specific level, or a category via the size
buttons there) — that’s what AquariumSceneHandoff is for. Press Play directly in this
scene (without going through the menu first) and it falls back to PuzzleController‘s
Default Board Width/Height (6×6). What’s wired:
- A board (
AquariumPuzzleBoardView) wired to aPuzzleController, which reads the pendingSceneHandoffselection onStartautomatically. - Check and Reset buttons, a status text, and an optional
ProgressTrackerthat
marks the current level complete on solve. - Next Level →
AquariumPuzzleController.LoadNextLevel— advances within the level’s
collection if one is active, otherwise generates another random puzzle at the same size. - Back to Menu → a small
AquariumSceneLoadercomponent (Scene Name =NoCode_MenuScene).UnityEvents in the Inspector can’t call static methods likeSceneManager.LoadScene
directly, so this component exists purely to give Button.onClick something to call. - The board sits inside a
PlayAreapanel that leaves room for the buttons around it.AquariumBoardAutoFit(wired toOnPuzzleLoaded) scales the board down to fitPlayArea
only when a puzzle is big enough to need it — a 6×6 or 10×10 board renders at its normal
cell size; a 15×15 board shrinks just enough to stay clear of the buttons.
NoCode_MenuScene — press Play to see one size button per category in your assigned
AquariumGameConfig (three, for the default 6×6/10×10/15×15 categories), plus an (empty)
authored-level panel:
LevelMenuControllerreads its Game Config field onStartand auto-spawns oneAquariumRandomButtoninto theRandomButtonsPanelfor every category it finds there — add
or remove a category in the config and the buttons follow automatically, nothing to re-wire.
If no config is assigned or found (see Two things are still required above), no buttons
spawn at all and a warning is logged instead.- Levels (assign your collections) panel is empty until you assign your own
AquariumLevelCollectionassets — see below. This is the one part of the sample that
can’t ship pre-filled, since it’s exactly the content you’re meant to author yourself. - Picking a size or a level writes into the
SceneHandoffasset and immediately loadsNoCode_LevelScenevia the sameAquariumSceneLoadercomponent used for “Back to Menu” —
both scenes are already connected end to end, press Play and click through.
Everything past this point is either (a) how to connect your own levels into the parts that ship empty, or (b) a from-scratch reference if you want to rebuild the layout differently than the bundled one.
Connecting and changing your levels
The authored-level list ships empty on purpose, since your levels are the one thing the sample can’t guess. The recommended way to fill it in is the registry — one project-wide list that every scene can read automatically, so you only maintain your levels in one place:
- Author some levels: Assets ▸ Create ▸ Aquarium ▸ Puzzle Asset, or use
Tools > Aquarium > Puzzle Workbenchto draw/generate them. - Group them: Assets ▸ Create ▸ Aquarium ▸ Level Collection, drag your levels into it
(or use theAquariumLevelCollectionModepresets —ByTheme/ByDifficulty/BySize
— and the “Add all matching levels” button in the collection’s Inspector). - Run Tools ▸ Aquarium ▸ Project Setup ▸ Sync Project Level Collection Registry (this also
creates the project’sAquariumLevelCollectionRegistryasset the first time, if one doesn’t
exist yet). This scans the project for everyAquariumLevelCollectionand lists them in the
registry — run it again any time you add, remove, or rename a collection. - Leave
LevelMenuController‘s Collections field empty. Press Play — the level list fills
in from the registry immediately, nothing else to wire. (If the field isn’t already empty,
clear it — see Collections vs. the registry below for why a non-empty one wins instead.)
If you’d rather link a specific registry asset instead of relying on it being auto-discovered
from the project’s canonical Resources path, drag it onto LevelMenuController‘s own
Registry field. This is optional — most projects never need it, since the auto-discovered
project registry already does the same job.
Collections vs. the registry
LevelMenuController‘s Collections field is for a curated, hand-picked set that bypasses
the registry entirely: a different menu per game mode, a “featured levels” screen, or hiding a
work-in-progress collection even though it’s already in the project registry. It’s exclusive,
not additive — the moment Collections has one or more entries, the registry (and the
Registry field) is ignored completely, and only what’s in Collections shows up. Leave
Collections empty to show everything from the registry instead, as described above.
To change which levels show up later (swap a collection, add more levels, remove one):
edit the collection asset itself (drag levels in/out, or use the “Add all matching levels”
button again after adding new level assets) — LevelMenuController reads it fresh every
time the scene starts, nothing to re-wire. If you’re using Collections directly, also add
or remove the collection asset itself from that array as needed.
To change the board sizes offered (the 6×6/10×10/15×15 buttons): this sample has no bundled
config of its own — click Assign next to the empty Game Config field (on
LevelMenuController or PuzzleController), or run
Tools > Aquarium > Project Setup > Create Project Configuration directly, then edit its
categories there. The buttons in this sample reference categories by id (size_6x6,
size_10x10, size_15x15), so renaming a category’s id means updating the matching
AquariumRandomButton‘s Category Id field too (or switch that button over to
AquariumCategoryDropdownPopulator, which reads
whatever categories exist automatically — see Filters (optional) below).
Scene transitions already work in both directions via AquariumSceneLoader (a tiny helper
that just calls SceneManager.LoadScene — UnityEvents in the Inspector can’t target static
methods directly, so this component exists to give a Button something to call):
- Menu → Level: wired onto
LevelMenuController‘sOnLevelSelected/OnRandomCategoryRequested,
alongside theSceneHandoffcalls. - Level → Menu: the Back to Menu button in the level scene.
Both scenes must be added to File ▸ Build Settings ▸ Scenes In Build for
SceneManager.LoadScene to find them by name — Unity throws otherwise. If you rename either
scene file, update the matching AquariumSceneLoader‘s Scene Name field to match.
Building it yourself from scratch (reference)
Everything below documents how the bundled scenes were put together, in case you want a different layout, a ScrollView instead of the plain stacked panel, or Dropdown-based filters instead of the three fixed size buttons. None of it is required just to use the sample.
NoCode_MenuScene — Level Select Menu
Goal
A menu with two sections:
- Random puzzle buttons — one per board-size category defined in your config asset.
- Authored level buttons — scrollable list from your collections, filterable by difficulty and size.
Clicking anything fires an event you route to your gameplay scene or controller.
Step 1 — Canvas & EventSystem
GameObject → UI → Canvas (Screen Space – Overlay recommended).
Unity creates an EventSystem automatically.
Step 2 — Random Puzzle Buttons
This is the recommended approach: place buttons manually, pick the category in the Inspector.
For each board size you want to offer:
GameObject → UI → Button— style it as you like.- Add Component →
Aquarium Puzzle No Code → Aquarium Random Button. - In the component Inspector:
- Config — assign your
AquariumGameConfigasset (or leave empty to use the project config from Resources). - Category — a dropdown appears with all categories from the config (e.g.
6×6,10×10,15×15). Pick the one for this button. - Category Name Text — optionally assign a child
Textto auto-fill the label.
- Config — assign your
- Wire the Button’s onClick →
AquariumRandomButton.Click. - Wire OnClicked →
AquariumPuzzleController.GenerateRandomByCategoryId
(works across scenes via a persistent manager, or directly if both are in the same scene).
Tip: duplicate the button for each category — change only the Category dropdown.
Step 3 — Authored Level Buttons
3a. Create a Level Button Prefab
GameObject → UI → Button— style it as you like.- Add Component →
Aquarium Puzzle No Code → Aquarium Level Button. - Optionally add child
Textobjects and assign them to Level Name Text / Level Size Text / Level Difficulty Text — they are filled automatically. - Wire the Button’s onClick →
AquariumLevelButton.Click. - Save as a Prefab.
3b. Create a ScrollView
GameObject → UI → Scroll View.- Add a Vertical Layout Group on the
Contentchild (recommended for auto-stacking).
3c. Create the LevelMenuController
- Create an empty
GameObject, name itLevelMenuController. - Add Component →
Aquarium Puzzle No Code → Aquarium Level Menu Controller. - Collections — assign your
AquariumLevelCollectionasset(s). - Level Button Container — assign the ScrollView’s
Content. - Level Button Prefab — assign the prefab from step 3a.
- Wire On Level Selected →
AquariumPuzzleController.LoadLevel.
Step 4 — Filters (optional)
Filter by difficulty:
GameObject → UI → Dropdown, options:All / Easy / Medium / Hard.Dropdown.onValueChanged→AquariumLevelMenuController.FilterByDifficulty.
Filter by size (recommended way):
GameObject → UI → Dropdown.- Add Component →
Aquarium Puzzle No Code → Aquarium Category Dropdown Populator. - Assign the same
Dropdown, tick Include All Option, optionally assign Config
(leave empty to use the project config from Resources). - Wire On Category Selected →
AquariumLevelMenuController.FilterBySizeByCategoryId. - The populator builds the dropdown’s options from the config itself, so there’s nothing to
keep in sync by hand, and “All” always means the empty-string id it reports.
Filter by size (manual alternative):
GameObject → UI → Dropdown, options:All+ one entry per category in the config
(e.g.All / 6×6 / 10×10 / 15×15). Order must match the category order in the config asset —
if you add or reorder categories later, update the Dropdown’s options too, or switch to the
populator above.Dropdown.onValueChanged→AquariumLevelMenuController.FilterBySizeByIndex.
Clear all filters button:
Button.onClick→AquariumLevelMenuController.ClearFilters.
NoCode_LevelScene — Gameplay
Goal
A board view with buttons for Generate, Check, Reset, and optional Next/Previous navigation.
Step 1 — Canvas & EventSystem
Same as the menu scene.
Step 2 — Add AquariumPuzzleBoardView
Copy the BoardView GameObject (with its cell and clue prefabs) from the Demo sample’s
UGUIDemoScene.unity — it is fully reusable. Restyle it without touching any scripts.
Step 3 — Create the PuzzleController
- Create an empty
GameObject, name itPuzzleController. - Add Component →
Aquarium Puzzle No Code → Aquarium Puzzle Controller.
Inspector fields to assign:
| Field | What to assign |
|---|---|
| Board View | Your AquariumPuzzleBoardView |
| Random Config | Your AquariumGameConfig asset (optional — falls back to project config) |
| Status Text | A Text component (optional) |
| Default Board Width/Height | Starting size when no category is selected |
Step 4 — Wire buttons
| Button label | Method to wire |
|---|---|
| Generate | AquariumPuzzleController.GenerateRandom |
| Check | AquariumPuzzleController.CheckMistakes |
| Reset | AquariumPuzzleController.ResetPuzzle |
| Next | AquariumPuzzleController.LoadNextLevel |
| Previous | AquariumPuzzleController.LoadPreviousLevel |
Step 5 — Wire board size selection
Option A — Random buttons in the same scene (recommended)
- Place
AquariumRandomButtoncomponents on buttons (see Menu Scene Step 2). - Wire OnClicked →
AquariumPuzzleController.GenerateRandomByCategoryId. - This sets the size from the config and immediately generates a puzzle in one step.
Option B — Dropdown (recommended way)
GameObject → UI → Dropdown.- Add Component →
Aquarium Puzzle No Code → Aquarium Category Dropdown Populator. - Assign the same
Dropdownand, optionally, Config. Leave Include All Option unticked —
a board-size selector always needs a concrete category. - Wire On Category Selected →
AquariumPuzzleController.SetBoardSizeByCategoryId. - A separate Generate button →
AquariumPuzzleController.GenerateRandom. - (Or skip the separate button and wire straight to
GenerateRandomByCategoryIdinstead ofSetBoardSizeByCategoryId— see Option A above; it sets the size and generates in one call.)
Option B′ — Dropdown (manual alternative)
GameObject → UI → Dropdown.- Add one option per category in the config asset (order must match — if the config changes
later, update the Dropdown’s options too, or switch to the populator above). Dropdown.onValueChanged→AquariumPuzzleController.SetBoardSizeByIndex.- A separate Generate button →
AquariumPuzzleController.GenerateRandom.
Option C — Inspector default
- Set Default Board Width / Height directly in the controller Inspector.
- Press Generate — done. No runtime size switching needed.
Step 6 — Wire events (optional)
In the Events section of AquariumPuzzleController:
| Event | Example use |
|---|---|
OnPuzzleSolved | Play a win sound, show a “Congratulations” panel |
OnMistakesFound (int) | Flash the board, play an error sound |
OnNoMistakes | Show a checkmark animation |
OnPuzzleLoaded | Hide a loading spinner |
OnStatusChanged (string) | Drive a TMP or additional Text component |
Passing a selection from the menu scene to the level scene
If NoCode_MenuScene and NoCode_LevelScene are two separate scenes, a plain click event can’t reach
across the scene load by itself — you need somewhere to park “what was picked” until the level scene’s
Start runs. AquariumSceneHandoff is a small ScriptableObject asset built for exactly this, and needs
no C#:
- Assets ▸ Create ▸ Aquarium ▸ No-Code ▸ Scene Handoff — make one asset, e.g.
SceneHandoff. - Menu scene: wire it into the same events you already wired to
AquariumPuzzleController, in
addition to them:AquariumLevelMenuController.OnLevelSelected→SceneHandoff.SetLevelAquariumLevelMenuController.OnRandomCategoryRequested→SceneHandoff.SetCategory- (if you spawn
AquariumRandomButtons directly without the menu controller, wire theirOnClicked→SceneHandoff.SetCategoryinstead)
- Level scene: drag the same asset onto the Scene Handoff field on
AquariumPuzzleController. - Trigger your own scene load however you normally do it (a
Buttonwired toSceneManager.LoadScene,
or any scene-loading asset/router you already use) — the handoff only remembers the selection, it
does not load scenes itself.
AquariumPuzzleController checks the handoff on Start and, if it holds a pending level or category,
loads that instead of its own Default Board Width/Height, then clears the handoff. The asset stays
loaded across the scene switch as long as both controllers reference it, exactly like any other asset
reference — no singleton, DontDestroyOnLoad, or static field required. (The bundled scenes already
have this wired via Config/SceneHandoff.asset — this section is for rebuilding it from scratch.)
Tracking level completion (optional)
AquariumLevelProgressTracker remembers which authored levels a player has finished, using
PlayerPrefs. It’s a completion flag only, not a save system — it doesn’t store board state,
hints/checks used, currency, or anything else.
Gameplay scene:
- Add an empty GameObject, name it
ProgressTracker. - Add Component →
Aquarium Puzzle No Code → Aquarium Level Progress Tracker. - Puzzle Controller — assign your
AquariumPuzzleController. - Wire
AquariumPuzzleController.OnPuzzleSolved→AquariumLevelProgressTracker.MarkCurrentLevelComplete.
Random puzzles have no level id and are silently skipped — nothing to mark complete.
Menu scene — showing a completed badge on level buttons:
- On the level button prefab (Step 3a), add a child object for the badge (a checkmark
Image
works well) and assign it toAquariumLevelButton‘s Completed Badge field. It’s hidden
unless the level is complete. - Add an
AquariumLevelProgressTrackerto the menu scene too (same steps as above, just without
theOnPuzzleSolvedwiring — the menu only reads completion, it doesn’t write it). - On
LevelMenuController(Step 3c), assign that tracker to its Progress Tracker field.
The gameplay scene’s tracker and the menu scene’s tracker can be two separate component instances
in two separate scenes — they still share the same completion data, since it’s stored in
PlayerPrefs, not on the component. Just keep the Player Prefs Key Prefix field identical on
both (it defaults to Aquarium.Completed. and there’s no reason to change it unless this project’s
PlayerPrefs namespace already has a collision).
To feed AquariumThematicCollectionsOrder‘s progression queries
(TryGetNextUncompletedByDifficulty/BySize), pass the tracker’s IsCompletedCallback method as
the isCompleted argument. Those two methods aren’t events, so calling them takes a line of C# —
everything above this paragraph needs none.
Currency / reward hookup (optional)
Each board-size category in your AquariumGameConfig can define a RewardCurrencyId/WinReward
(and an optional LaunchCostCurrencyId/LaunchCost), set in the config asset’s Inspector. This
works for authored levels (with the level’s own CustomCompletionReward as an override) and for
random puzzles alike, matched by board size.
No-code path — AquariumCurrencyRewarder:
- Add an empty GameObject in the gameplay scene, name it
CurrencyRewarder. - Add Component →
Aquarium Puzzle No Code → Aquarium Currency Rewarder. - Puzzle Controller — assign your
AquariumPuzzleController. - Wire
AquariumPuzzleController.OnPuzzleSolved→AquariumCurrencyRewarder.AwardCurrentLevelReward. - Tick Award Currency On Solve to turn payouts on. Leave it ticked; untick it later to pause
payouts without touching the wiring — useful while you’re still tuning the economy.
That’s it — no C# required. The balance is stored per currency id via PlayerPrefs (same minimal
approach as AquariumLevelProgressTracker: one integer per currency, no transaction history, no
spending API — launch cost is defined on the config but not deducted by this component). Read a
balance for a HUD Text with GetBalance(currencyId), or subscribe to the CurrencyAwarded event
from your own script to update it live.
Code path, if you’d rather resolve the reward yourself (e.g. to hand it to an existing wallet
system instead of the built-in PlayerPrefs one): call
AquariumGameConfig.TryResolveWinReward(level, out currencyId, out amount) for an authored level,
or TryGetCategoryForSize(width, height, out category) for a random puzzle (no level asset to pass),
from a script wired to OnPuzzleSolved the same way.
Bundled assets
This sample ships these prefabs and one data asset you can reuse or restyle directly, under
Samples~/NoCodeIntegration/Prefabs/ and .../Config/:
AquariumLevelButton.prefab— the level list button used byLevelMenuController.AquariumRandomButton.prefab— the size-category button used in both scenes.Cell.prefab,ClueText.prefab,Border.prefab— assigned toAquariumPuzzleBoardView‘s
Cell Prefab, Clue Prefab, and Line Overlay Prefab fields inNoCode_LevelScene: the
board’s per-cell visual, the row/column clue text, and the line image drawn for region borders
and (if enabled) cell grid lines. This sample ships its own copies so it works without the
Demo sample also being imported.Config/SceneHandoff.asset— the shared handoff asset both scenes already reference.
The same no-custom-art warning from the top of this document applies to all of these — none of
them carry final visuals. Restyle them (colors, fonts, adding a background image) and the
scenes’ own UI before shipping. The components and their wiring stay intact as long as you don’t
remove the Text/Image/Button references each script’s Inspector fields point at.
Before you ship
This sample is a wiring reference, not finished UI. At minimum, before release:
- Replace the default Unity Button/Text styling on every button and panel in both scenes with
your own art. - Add a background/theme to
BoardPaneland the menu Canvas (currently a flat placeholder
backdrop). - Add real category art/labels instead of the plain
6×6/10×10/15×15button text. - Decide what happens when a random puzzle is solved — it has no level id, so it never gets a
completion badge (that’s level-id based), but it can still pay out currency by size category
if you’ve wired Currency / reward hookup above. - Fill the empty Levels (assign your collections) panel with your own authored content (see
Connecting and changing your levels).
Component Reference
| Component | Scene | Purpose |
|---|---|---|
AquariumRandomButton | Menu or Level | One button per board-size category. Pick category via Inspector dropdown. |
AquariumLevelButton | Menu | One button per authored level. Auto-fills name/size/difficulty labels and a completed badge. |
AquariumLevelMenuController | Menu | Spawns level buttons from collections, manages difficulty/size filters. |
AquariumPuzzleController | Level | Gameplay: generate, load, check, reset. Reads sizes from config. |
AquariumSceneHandoff | Menu + Level (shared asset) | Carries a level/category selection from the menu scene to the level scene across a scene load. Optional — only needed when they’re separate scenes. |
AquariumCategoryDropdownPopulator | Menu or Level | Fills a Dropdown from the config and reports the selection by category id instead of a raw index. Recommended over SetBoardSizeByIndex/FilterBySizeByIndex. |
AquariumLevelProgressTracker | Menu and/or Level | Marks authored levels complete in PlayerPrefs and reports completion back. Optional. |
AquariumCurrencyRewarder | Level | Pays out the solved puzzle’s configured currency reward (authored or random) into a PlayerPrefs balance. Optional. |
AquariumSceneLoader | Menu and Level | Calls SceneManager.LoadScene(sceneName) — exists so a Button.onClick (or another UnityEvent) has something to call, since events can’t target static methods directly. |
AquariumBoardAutoFit | Level | Scales the board down to fit an available-space rectangle, only when it would otherwise be too big for it. Wired to OnPuzzleLoaded. |
Tips
AquariumRandomButtoncategory is selected via dropdown in the Inspector — no string IDs to type.GenerateRandomByCategoryIdsets the size and generates in a single call — one wiring in the Inspector covers both actions.- For any other Dropdown that should pick a category, use
AquariumCategoryDropdownPopulatorand wire
its On Category Selected to a...ByCategoryIdmethod (SetBoardSizeByCategoryId,GenerateRandomByCategoryId,FilterBySizeByCategoryId). It builds the options from the config itself,
so there’s no “Dropdown order must match config order” bookkeeping and no risk of an off-by-one. - The older
SetBoardSizeByIndex/FilterBySizeByIndexmethods still work and are still wired above as
manual alternatives, but they take a rawintand disagree with each other on what index0means
(first category vs. “All”) — prefer the populator +...ByCategoryIdpairing for new setups.