A Drag-and-Drop Color-Fit Puzzle — Unity Template
Version 1.0 | Unity 6+ | URP
This asset is designed as a template and starting point for your own game development. It provides the core systems, architecture, and tools needed to build a drag-and-drop color-fit puzzle. You are expected to customize, extend, and modify this template to create your unique game. This is not a complete, ready-to-publish game — it's a foundation for developers to build upon.
Fit Blast is a drag-and-drop color-fit puzzle where players pull tetromino-shaped colored blocks from a queue at the bottom of the screen and snap them onto pedestals on the board. Each pedestal clears when two conditions are met at the same time: every cell is filled, and every block on it is the same rendered color. Clear every pedestal to win the level. The game ships with 500 pre-made unique levels on a progressive difficulty curve, a visual level editor, an automatic level generator, three boosters, and a developer debug panel.
Touch/click a block in the bottom queue, drag it onto any pedestal. Valid placements snap into position; invalid ones bounce back. Pedestals clear when fully filled with a single color.
Full Unity Editor window for authoring levels — configure pedestal layouts, pre-placed blocks, starting-queue pieces, per-level booster counts, and grid sizes. Read/write JSON on disk.
Batch-generate dozens of playable levels along a configurable difficulty curve. Pre-placement, decoy blocks, and color counts are all tunable from a single editor window.
Three in-game boosters — Hammer (smash a pedestal), Shuffle (randomize every piece on screen), and Hint (flash the best queued move) — with level-gated unlocks, popup tutorials, and usage tracking.
Ships with 500 pre-made unique levels on a progressive difficulty curve, ready to play out of the box. Generate more at any time with the auto generator.
No ad SDKs, no analytics, no cloud save. Level progression persists through a single PlayerPrefs key. Drop it into any project without dragging in third-party monetization dependencies.
Edit > Project Settings > Graphics)Assets/Fit Blast/Scenes/GameNew.unityOnce in Play mode, pick up any piece from the bottom queue (the start grid) and drag it onto a pedestal. Valid placements snap into position and the piece animates into its destination cells; invalid placements return to the queue. When a pedestal becomes fully filled with blocks that share a single rendered color, particles burst, the pedestal slides off, and any pedestals above it drop down into the freed slot. Clear the last pedestal to win the level.
Each level features a set of pedestals (2×3 or 3×3 grids) on the board and a start grid at the bottom of the screen that holds the queue of playable pieces. The goal is to clear every pedestal by filling it with blocks that all share a single color.
gridStartBlockYOffset baked in at level load, and the top row is clamped to the authored ceiling (plus the optional DoNotCrossTop marker if present).All).BlockType); it must fit entirely inside the target pedestal with no collision.Optional Transform on the GameLevel component. If a scene GameObject named DoNotCrossTop exists (typically parented under the queue's Depestal(Clone) backdrop), any start-grid drop whose shape cells would land above the marker's world Y is rejected before the snap distance is even checked. Auto-discovered by name at Start() if the Inspector slot is empty. Leave the marker out to fall back to the shape-aware cell-grid ceiling alone.
The game supports thirteen predefined colors defined in the ColorType enum. Each is mapped to a render color by ColorData:
| Color Key | Color | Reference Hex |
|---|---|---|
Red |
Red | #FF4757 |
Green |
Green | #7BED9F |
Blue |
Blue | #48DBFB |
Yellow |
Yellow | #ECCC68 |
Cyan |
Cyan | #00D2D3 |
Magenta |
Magenta | #EF5777 |
Orange |
Orange | #FFA502 |
Purple |
Purple | #BC8CFF |
Brown |
Brown | #A0522D |
Gray |
Gray | #A4B0BE |
Black |
Black | #2D3436 |
White |
White | #F5F6FA |
Pink |
Pink | #FF6B81 |
The palette is defined by ColorData.asset; the hex values above are the reference look used by the shipped art. Colors that share a rendered RGB (e.g. two custom hues that both resolve to white) are treated as the same color by the match check — match logic compares rendered RGB, not enum value.
Pieces are authored as tetromino-like shapes in BlockData.asset. The shipping set includes 14 distinct shapes (Block1 through Block14), ranging from single cells to three-cell L / I / T variants. Each shape has an origin cell and a list of offset cells relative to the origin; placement validity and the sibling-Z render order are derived from this offset list, so you can add or edit shapes without touching gameplay code.
LevelManager.AdvanceLevel(), and the next level loads automatically.FitBlastGameManager.TriggerLose() — call it from your own heuristic (e.g. an explicit "give up" button) or wire in a move-limit, timer, or stuck-state detector. The lose panel has a Retry button that reloads the current level.All animations are hand-rolled coroutines — no DOTween or LeanTween. Pieces ease into position on drop via MoveBlockTo; pedestals slide off-screen via MoveImageTo; the queue settles upward via LevitateStartGrid. Particle bursts fire via the single shared ParticleSystem reference on the GameLevel. The Hammer booster pulses every pedestal's tint on a sine curve while armed, and the Hint booster instantiates a translucent ghost of the suggested piece at the target cells for ~2.2s.
Gameplay scripts live under Assets/Fit Blast/Scripts/ in the FitBlask.Scripts namespace. Reusable engine scripts live in the same folder under the HypercasualGameEngine namespace.
| Script | Description |
|---|---|
FitBlastGameManager |
The orchestrator. Tracks game state (Playing / Won / Lost), owns arrow-key level navigation (which is why it's always active, not on the dev panel), bridges LevelManager to GameLevel, and routes win/lose through LoseWinPanelManager. Decorated with [DefaultExecutionOrder(-100)] so it wakes before any other gameplay script. |
LevelManager |
Static JSON level loader. Reads every Level_N.json from Assets/Fit Blast/Resources/Levels/ (editor) or via Resources.Load<TextAsset>("Levels/Level_" + i) (runtime), tracks progression via the FitBlast_CurrentLevel PlayerPrefs key, and self-heals cached references that Unity's scene-load Resources.UnloadUnusedAssets sweep destroys. |
LevelData |
ScriptableObject (+ serializable POCO) defining one level: grid size, pedestal layout, block positions, start-grid queue, and the three per-level booster counts (hammerBoosterCount, shuffleBoosterCount, hintBoosterCount). JSON hydrates into this via JsonUtility.FromJsonOverwrite. |
GameLevel |
The gameplay heart. Builds pedestals and the start grid from LevelData on Start, handles DragBlockTo / DragBlockTempTo, runs CheckCompleteGrid after every placement, executes the CompleteGrid clear sequence, and hosts the three booster routines (HammerSmash, ShuffleAllContainers, ShowHint). |
InputManager |
Touch + mouse pickup & drag using the New Input System. Raycasts the canvas on press, detaches the picked block to a floating rect, and hands off to GameLevel.DragBlockTo on release. Also intercepts taps while Hammer mode is armed to call GameLevel.HammerSmash. |
Block |
Single-block runtime component. Holds blockType, colorType, gridPosition, the sprite Image, a CanTap gate, and a computed draw-order priority. Placed on every instance of Block.prefab. |
BlockData / BlockDataEditor |
ScriptableObject mapping each BlockType enum value to a sprite and a list of cell offsets. The custom inspector draws a visual cell-grid editor. Edit cells to author new shapes — placement validity follows automatically. |
ColorData |
ScriptableObject mapping each ColorType enum value to a Color. Unmapped enums default to white at runtime, so match logic compares rendered RGB rather than enum equality. |
PedestalRef |
Lightweight component (inner class of GameLevel.cs) stamped on every pedestal Image at spawn time. Carries the owning grid key so InputManager can resolve a Hammer tap that lands on bare pedestal (between blocks). |
HypercasualGameEngine)| Script | Description |
|---|---|
BoosterManager |
Reusable 3-booster manager. Locates Booster-Button-1/2/3 by name in the HUD, gates by level, consumes on use, shows a first-use tutorial popup keyed BoosterPopupShown_<Name> in PlayerPrefs, and exposes IsHammerMode + ConsumeHammerCharge for the armed-tap flow. |
SoundManager |
Singleton audio facade. One AudioSource + PlayOneShot per call site. Exposes PlayLevelWin, PlayLevelFail, PlayBoosterUse, PlayShuffle, PlayBoosterHint, PlayBoosterHammer, PlayButtonClick, PlayBlockTap, PlayBlockPlace, PlayBlockMatch, PlayBlocked. |
LoseWinPanelManager |
Win / lose panel controller. Auto-discovers child panels by name, rotates the radial-shine decor, and advances the level via LevelManager.AdvanceLevel() on next-level load. Wires the retry button to a scene-reload. |
DeveloperModeButton |
Corner toggle that shows/hides the Developer Settings panel. Pre-placed in the DeveloperModeButton.prefab. |
DeveloperSettingsController |
Dev-panel controls: next / previous / reload level, trigger win / lose, unlock all boosters, refill boosters, reset tutorial popups, visual theme cycling. Shift+U / Shift+W / Shift+L / Shift+R hotkeys when the panel is open. (Arrow-key level navigation lives on FitBlastGameManager instead so it works while the panel is closed.) |
CameraSwitcher |
Multi-camera helper for preview builds. Unused in the default scene but ships ready to drop into launcher / home screen flows. |
| Script | Description |
|---|---|
LevelEditorWindow |
Unity Editor window (Tools > Fit Blast > Level Editor) for visually authoring levels. Left panel: list of levels with add / duplicate / reorder / delete / save-all. Right panel: grid size, pedestal placement, per-pedestal block positions, start-grid queue, and per-level booster counts. |
LevelGeneratorWindow |
Unity Editor window (Tools > Fit Blast > Auto Level Generator) for batch-generating levels. Knobs for pedestal count, color count, start-grid dimensions, pre-placement fraction, decoy count & probability, and booster counts. Appends new levels; never overwrites existing ones. |
FitBlastWelcomePopup |
Welcome popup that appears on project open (via [InitializeOnLoadMethod]). Two CTAs: leave an Asset Store review and cross-promote the HyperCasual Game Engine. Session-locked; permanently dismissable via the "Do not show this popup again" checkbox. |
In the Unity menu bar, go to Tools > Fit Blast > Level Editor. This opens a dedicated Editor window — no need to enter Play mode.
ColorData color and occupy their BlockData shape cells. What you see in the editor is exactly what loads at runtime.BlockData.blockMappings; click one to pick the active paint shape. The standard dropdown is also there.ColorData.colorMappings; click to pick the paint ColorType. Unmapped enum values aren't shown so you can't accidentally paint white-renders.gridSizeX × gridSizeY grid. Empty slots show a + add pedestal placeholder that clicks to drop in a new 3×3 pedestal at that grid position.2×3 / 3×3 size toggles (auto-prune out-of-bounds blocks) and a red × to remove the whole pedestal.Assets/Fit Blast/Resources/Levels/Level_N.json.(x, y) on the board.2×3 / 3×3 buttons in its header. Existing blocks that would fall out of bounds are pruned automatically.× in its header.All levels are stored as individual JSON files in Assets/Fit Blast/Resources/Levels/ with the naming convention Level_1.json, Level_2.json, etc. They are loaded in the editor directly from the hard-coded absolute path (bypassing Unity's Resources system to avoid cross-contamination with other Resources/Levels/ folders), and in built players via strict sequential Resources.Load<TextAsset>("Levels/Level_" + i).
The Automatic Level Generator is an Editor tool that batch-generates playable color-fit levels procedurally. It uses an exact-cover tiling algorithm for pedestal solutions, pre-places a difficulty-driven fraction of the solution for visual scaffolding, optionally seeds off-color decoy blocks, and emits one JSON file per level.
In the Unity menu bar, go to Tools > Fit Blast > Auto Level Generator.
| Setting | Description |
|---|---|
| Levels to Generate | How many new level files to create. New levels are appended after existing ones (never overwrites). |
| Random Seed | Seed for the deterministic RNG. Use the same seed to reproduce a batch. |
| Min / Max Pedestals | Range for pedestal count per level. Interpolated along the difficulty curve (easier levels hit the min, harder levels hit the max). Defaults 1—12 (2 columns × 6 rows at the top end). |
| Min / Max Colors | Range for distinct colors per level. Default 2—12. |
| Allow 3×3 Pedestals | If disabled, only 2×3 pedestals are spawned. 3×3 pedestals are noticeably harder because a match needs nine same-colored cells. |
| Unique Color Per Pedestal (if possible) | When true, the generator assigns a distinct color to every pedestal in a level when the color count allows it. Prevents "two pedestals compete for the same color" grief. |
| Start Grid Width / Height | Dimensions of the bottom queue grid (in cells). Default 7×3. If the solution pieces don't fit the queue, overflow is placed on the owning pedestal instead. |
| Pre-Place Min / Max Fraction | Fraction of each pedestal's solution that starts already placed on the pedestal. Interpolated inversely with difficulty: easy levels hit the max (~55% pre-placed), hard levels hit the min (~10%). |
| Curve Exponent | Power applied to the linear progress t = k / (target - 1) before sampling pedestal count, color count, pre-placement, and 3×3 eligibility. 1.0 is linear; values below 1 ramp difficulty faster in early levels. Default 0.4 — by level 50 of a 500-level batch difficulty is already ~40%, by level 250 it's ~75%. |
| Enable Decoys | If enabled, pedestals may be seeded with small off-color blocks that the player must move or smash with Hammer. |
| Max Decoys / Pedestal & Chance a Pedestal Gets Decoys | Per-pedestal decoy budget and the coin-flip that governs whether a given pedestal is even eligible. |
| Booster Counts | Per-level starting counts for Hammer, Shuffle, and Hint. Applied identically to every generated level. |
Level_N.json fileThe generator never overwrites existing level files. It always starts numbering from the next available index. Use the "Delete All Levels on Disk" button if you want to start fresh.
Every generated level is solvable by construction. The invariants the generator maintains:
The game includes three booster abilities that players can activate during gameplay. Each booster has a configurable number of uses per level (set in the level JSON) and a first-use tutorial popup keyed in PlayerPrefs.
| Booster | Effect | Activation |
|---|---|---|
| Hammer | Arms a targeting mode: the next tap on any pedestal destroys that pedestal and every block on it — same end-state as a natural match. Every pedestal pulses amber while armed so the targets are obvious. The charge is consumed only when you actually smash a pedestal; clicking the Hammer button again (or tapping empty space) cancels without spending. | Tap the Hammer button (Booster-Button-1) to arm. Tap a pedestal to smash, or tap the button again to cancel. |
| Shuffle | Attempts random pairwise swaps across every container at once — both pedestals and the start-grid queue. Only commits swaps where each piece physically fits in the other's grid without collision. A piece can move between containers, so a start-grid piece may land on a pedestal and vice versa. A shuffle can directly trigger matches if a swap happens to complete a pedestal. | Tap the Shuffle button (Booster-Button-2). Consumes one use immediately. |
| Hint | Scores every (piece, pedestal, origin) triple and flashes the best move. Piece is every block currently in play — start-grid queue and any pedestal, so cross-pedestal moves (e.g. consolidating a yellow L onto another yellow pedestal to complete it) are considered, not just queue → pedestal drops. Priority: (1) placement that completes a pedestal, (2) pedestal already mono in the piece's color (extends progress), (3) empty pedestal (neutral starter), (4) pedestal already mixed colors, (5) pollution of a mono pedestal. Flashes a translucent ghost of the suggested piece at the target cells while pulsing the source piece for ~2.2 seconds. | Tap the Hint button (Booster-Button-3). Consumes one use immediately. |
Booster buttons are found in the GameHUD_Panel/BottomHolder in the scene UI. BoosterManager locates them by the literal names Booster-Button-1, Booster-Button-2, Booster-Button-3 — do not rename them. Each button has:
Bg) that shows/hides based on availabilityPowerUpCountText) showing remaining uses, or the unlock level if still lockedThe first time a player taps each booster, a one-time tutorial popup appears explaining it. BoosterManager.HookUpExistingBoosters searches the active scene for Booster-Popup-1/2/3 (including inactive objects, since they're serialized as m_IsActive: 0 by default) — in the shipping scene these are Popup_Powerup_Picker prefab instances parented under Games / #5 / WorldCanvas. If the scene has no match for a name, a simple centered card with a dark scrim + TMP text is built at runtime as a fallback (scrim-click dismisses).
Popups auto-hide after 2.5 s so the player never gets stuck, and runtime cards can be tapped away sooner via the scrim Button. The "seen" flag is keyed in PlayerPrefs as BoosterPopupShown_Hammer, BoosterPopupShown_Shuffle, BoosterPopupShown_Hint. Use the dev panel's Reset Popup Prefs button to re-test the popups.
During Play mode, open the Developer Settings panel (gear-icon button in the top-right) to unlock every booster, refill usage counts, trigger win/lose, jump levels, and reset popups. Hotkeys while the panel is open: Shift+U (unlock + refill), Shift+W (win), Shift+L (lose), Shift+R (reload level). Level navigation with Left Arrow / Right Arrow works regardless of whether the dev panel is open because it lives on FitBlastGameManager.
To add a new block color:
ColorData.cs and add a new entry to the ColorType enum:
ColorData.asset in Assets/Fit Blast/Datas/ and append a Color entry for Teal. Unmapped enums default to white at runtime, so forgetting this step silently breaks the match check.Tools > Fit Blast > Level Editor > Save All to Disk (or let the Auto Generator re-generate) so the JSON files reference the new color by its integer enum value.The new color is automatically available in the Level Editor, Auto Generator, and at runtime.
To add a new piece shape, open BlockData.asset in the Inspector. Add a mapping entry: pick the next BlockType enum value (or extend the enum in LevelData.cs first), drag a sprite for the shape, and fill in the list of cell offsets relative to the origin. The custom BlockDataEditor draws a visual cell-grid editor so you can paint offsets click-by-click. Placement validity, pedestal-match detection, and the gravity-up settle all follow from this offset list — no gameplay code changes required.
Art/Sprites/Blocks/ and re-reference them on each BlockData mapping entry.Art/Sprites/ and wire them into GameLevel.pedestalSprites.ParticleSystem on GameLevel drives every clear burst. Edit its material or curves in the Inspector.GameHUD_Panel.prefab and Popup_Powerup_Picker.prefab are standalone UI prefabs; edit their children freely as long as you preserve the load-bearing names Booster-Button-1/2/3, Booster-Popup-1/2/3, PowerUpCountText, Bg.SoundManager.cs exposes one serialized AudioClip field per event (see the sfxBoosterHammer / sfxBlockMatch / sfxLevelWin / … list) plus four random-pick arrays (successClips, failClips, popClips, switchClips). Drop in your own AudioClips by filename from Assets/Fit Blast/Audio/; the shipping clips are already wired up on the SoundManager component in GameNew.unity.
LevelData — e.g. hammerBoosterCount → rotateBoosterCount — if you are adding a new booster that needs per-level tuning. Rerun the Auto Generator after renaming to refresh the JSON files.On<Name>Clicked click handlers in BoosterManager.cs and the matching Apply<Name> entry points on GameLevel.HookUpExistingBoosters slot findings at Booster-Button-1/2/3 exactly as before — the HUD prefab's GameObject names are load-bearing.Each level is a JSON file with the following structure:
The gridItems array lists every pedestal on the board; each has a width, height, position on the level's gridSizeX/gridSizeY layout, and a list of pre-placed blocks. The gridStart object is the bottom queue — a single grid holding the player's initial pieces. blockType is an integer index into the BlockType enum (0-based), and colorType is an integer index into ColorType. positionInGrid is the origin cell of the piece inside that grid.
| Issue | Solution |
|---|---|
| No levels load / empty scene | Ensure JSON level files exist in Assets/Fit Blast/Resources/Levels/ with the naming convention Level_1.json, Level_2.json, etc. Run the Auto Level Generator if the folder is empty. |
| Level 1 plays, then the next reload silently loads nothing | Runtime-created ScriptableObjects get swept by Resources.UnloadUnusedAssets() on every scene load. LevelManager.AddLevelFromJson already sets hideFlags = HideAndDontSave for this reason — do not remove that line when editing LevelManager. |
| Pink/magenta materials on objects | The project requires URP. Go to Edit > Project Settings > Graphics and ensure a URP Render Pipeline Asset is assigned. Check that the Settings/ folder contains valid URP assets. |
| Pieces can't be picked up | Go to Project Settings > Player > Other Settings > Active Input Handling and set it to Both. Also verify EventSystem is present in the scene and the Canvas has a GraphicRaycaster component. |
| Booster buttons don't respond | Ensure the GameHUD_Panel/BottomHolder hierarchy contains children named Booster-Button-1, Booster-Button-2, and Booster-Button-3 — BoosterManager.HookUpExistingBoosters looks them up by literal name. Each needs a Button component and a child PowerUpCountText TextMeshProUGUI. |
| Pedestal keeps a wrong-color block after a mistake | Use the Hammer booster to destroy the whole pedestal, or jump to a fresh level via the developer panel. Fit Blast intentionally has no single-block remove to keep the mechanic focused on shape placement; Hammer replaces that role. |
| Hint keeps picking empty pedestals when a committed-color pedestal exists | Confirm the committed-color pedestal's blocks actually render the same RGB as the piece you'd expect it to pick. The match logic compares rendered blockImage.color, so two enum values that resolve to the same RGB in ColorData are treated as the same color. |
| Level Editor shows empty level list | Ensure the Assets/Fit Blast/Resources/Levels/ folder exists and contains valid JSON files. Run the Auto Level Generator if the folder is empty, or click "Reload from Disk" inside the Level Editor. |
Assets/Fit Blast/Scenes/GameNew.unity should be enabled in EditorBuildSettings.InputManager reads Pointer.current (the Input System's cross-device abstraction) before falling back to an active-touch scan and then Mouse.current. This is necessary because WebGL's runtime creates a virtual Touchscreen device even on desktop browsers; a naive Touchscreen.current.touches.Count > 0 check always wins and silently blocks mouse input. The current flow works identically on Standalone, mobile, WebGL-desktop, and WebGL-mobile.Assets/Fit Blast/Scripts/TabletAspectScaler.cs (attached to the All GameObject). On Awake it reads Screen.width / Screen.height, computes short / long aspect, and if it's at or above tabletAspectThreshold (default 0.65) overrides the transform's uniform local scale to tabletScale (default 0.3452272). Phone aspects (~0.45–0.56) fall below the threshold so the authored phone scale is left intact. Both values are Inspector-tunable.
By design — to keep the package tight and focused:
systems/14-assembly-definitions.md)Resources/Levels/ folder regularly| Version | Changes |
|---|---|
| 1.0 | Initial release. 500 pre-made unique levels, Hammer / Shuffle / Hint boosters, Level Editor, Auto Level Generator, Welcome Popup, Developer panel, SoundManager + 11 SFX slots, ad-free build. |
If you get stuck or have any issues, feel free to reach out to us at ragendom@gmail.com. We are happy to help!