Picture Block Puzzle 2D

A Sliding Picture-Assembly Puzzle — Unity Template

Version 1.2 | Unity 6+ | URP

🌟 This asset was made with the HyperCasual Game Engine!   Check it out — mobile puzzle templates & more. Click here to learn more ➔

Important: Template / Starting Point

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 sliding picture-assembly puzzle game. 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.

Overview

Picture Block Puzzle 2D is a sliding picture-assembly puzzle where players drag colour-coded image pieces across a grid to reconstruct the original pictures before the timer runs out. Each block has a “zero” anchor piece that defines where the assembled image should sit; the remaining pieces must be placed at their solved offsets relative to that anchor. The game ships with a time-limit countdown, four boosters, axis-constrained pieces with a visible arrow overlay, a visual level editor, an automatic level generator, and a 500+ starter pack of generated JSON levels.

Slide-to-Assemble Puzzles

Drag image pieces on a 2D grid; drop them on empty cells to reassemble the finished picture. Multi-cell shapes snap to their true footprint and cannot overlap other pieces. The dragged piece darkens while actively held for clear feedback.

Axis-Constrained Pieces

Per-piece movement locks (any / horizontal-only / vertical-only) add a layer of puzzle strategy on harder levels. Constrained pieces display a double-headed arrow overlay so the player knows the allowed axis at a glance.

Visual Level Editor

A full Unity Editor window that mirrors the runtime visuals: pieces are rendered with the same sliced-sprite mapping the game uses, multi-cell shapes are drawn across every cell they occupy, and movement-axis arrows show on constrained pieces. Tool modes for shape placement, drag-to-move, walls, and erase — with a green / red hover preview that validates collisions before you click. A merge helper folds adjacent single-piece blocks into one multi-piece block.

Auto Level Generator

Batch-generate fresh levels with configurable grid size, block count, scramble moves, time limit, and booster allowances. Uses a curated tiling-pattern library so blocks always span a clean 2×2 / 2×3 / 3×2 footprint. Difficulty front-loads so hard levels arrive sooner; wall density, axis-constraint chance, and scramble count all scale with progression. Append-only — never overwrites hand-tuned levels.

Four Boosters

Time Freeze (pause the countdown for 10 seconds), Hint (ghost-preview a displaced piece's solved position), Shuffle (randomly reposition every block without overlap), and Grid Expander (extend the playable grid by 1 cell on every side for the rest of the level).

Progression-Gated Boosters

Each booster unlocks at a specific level (1 / 2 / 3 / 4 by default) so new players learn one mechanic at a time. Locked buttons show "lvl N" instead of the charge count. Press U in the editor / a development build to unlock & refill everything for testing.

Time-Pressure Countdown

Every level carries its own time budget. A visible FrozenImage indicator appears while the Time Freeze booster is active so the player sees at a glance that the clock has paused.

JSON Levels + Starter Pack

500+ levels ship as plain JSON under Resources/Levels/PictureBlockPuzzle2D/. Regenerate or extend at any time with the Auto Level Generator; delete every file in one click from the generator's danger-zone button.

Getting Started

Requirements

Installation

  1. Import the Picture Block Puzzle 2D package into your Unity project
  2. Ensure the project is set to use the Universal Render Pipeline (check Edit > Project Settings > Graphics)
  3. Open the game scene at Assets/Picture Block Puzzle 2D/Scenes/GameNew.unity
  4. Press Play to test the first level. To author or batch-create levels, use Tools > Picture Block Puzzle 2D > Level Editor or … > Auto Level Generator

Quick Test

Once in Play mode, drag any movable block on the grid to an empty cell. When every non-zero piece of a block sits at its solved offset from that block's zero anchor, the picture completes automatically and the star particles burst. Clear all pictures before the countdown reaches 0 to win the level. On win the next level loads automatically; on time-out the lose panel appears.

Folder Structure

Picture Block Puzzle 2D/ │ ├── Art/ — Sprite artwork referenced by blocks and HUD │ ├── Audio/ — Sound effects (place, merge, hint, shuffle, win, fail, freeze, etc.) │ ├── Datas/ — Legacy ScriptableObject LevelData assets (runtime uses JSON) │ ├── Documentation/ — This documentation file │ ├── Editor/ │ ├── LevelEditorWindow.cs — Visual level designer (shape placement, hover preview, sliced sprite render, merge helper) │ ├── LevelGeneratorWindow.cs — Automatic batch level generator │ ├── PictureBlockPuzzle2DWelcomePopup.cs — Welcome popup on project open (no menu item; auto-shows once per session) │ ├── DragSnapWithoutPlaymodeEditor.cs — Edit-mode drag-snap helper │ ├── ImportAudioIcon.cs — Editor-time audio icon importer │ ├── InputSystemCheck.cs — Warns if Input Handling is not set to Both │ └── URPStartupCheck.cs — Warns if URP is not the active render pipeline │ ├── Materials/ — Shared materials for blocks, tiles, and FX │ ├── Physics/ — 2D PhysicsMaterial2D assets used by blocks and the border │ ├── Prefabs/ — Block prefabs, HUD panel, popups, tiles, star particles │ └── Global_UI/ — GameHUD_Panel, Popup_Powerup_Picker, etc. │ ├── Resources/ │ ├── BlockIcons/ — Per-piece picture sprites loaded by name │ ├── outline_horizontal.png — Move-axis arrow overlay (loaded by Block.cs) │ ├── outline_vertical.png │ ├── outline_1x1.png │ └── Levels/PictureBlockPuzzle2D/ — JSON levels (Level_1.json, Level_2.json, …) │ ├── Scenes/ │ └── GameNew.unity — Main game scene (single playable scene) │ ├── Scripts/ — All runtime scripts (namespace: PictureBlockPuzzle2D.Scripts) │ ├── GridManager.cs — Grid layout, block spawn, countdown, merge check, shuffle │ ├── InputManager.cs — Touch / mouse drag handling for blocks │ ├── Block.cs — Block entity (shape, head position, color, sprites) │ ├── BlockIconLoader.cs — Loads per-piece sprites from Resources/BlockIcons │ ├── TilePrefab.cs — Grid cell visual / state │ ├── GoalManger.cs — Tracks remaining color goals │ ├── Goal.cs — Single goal entry (color + count) │ ├── LevelData.cs — Runtime level data (time, grid size, blocks, boosters) │ ├── JsonLevelData.cs — JSON ↔ runtime conversion + color palette │ ├── LevelManager.cs — Static JSON loader + level progression │ ├── BoosterManager.cs — Time Freeze, Hint, Shuffle │ ├── PictureBlockPuzzle2DGameManager.cs — Scene orchestrator + dev hotkeys │ ├── GameUI.cs — Countdown label + HUD text updates │ ├── LoseWinPanelManager.cs — Win / Lose panels + Next Level wiring │ ├── AudioButtonController.cs — Mute / unmute toggle button │ ├── CameraSwitcher.cs — Portrait / landscape camera swap │ ├── Ecolor.cs — Color enum (None, Red, Blue, Green, Cyan) │ ├── ShapeData.cs — Shape-type → Block-prefab lookup │ ├── SnapToClosestObjectOfList.cs — Utility for edit-time snapping │ ├── SpriteDummy.cs — Editor-only sprite placeholder │ ├── MyUtils.cs — Time formatting + small helpers │ └── Global/ — Shared HypercasualGameEngine helpers (DeveloperSettings, SoundManager, etc.) │ ├── Settings/ — URP render pipeline assets │ ├── Sprites/ — UI sprite atlases │ └── Textures/ — Booster icons (incl. frozen-button.png), background art

Gameplay

Core Mechanics

Each level displays a grid of cells, a set of multi-cell blocks (each painted with a color and carrying a piece of a larger picture), and a visible countdown. The player drags blocks to empty cells; physics colliders prevent overlap. A block is considered assembled when every non-zero piece of its color sits at zeroHead + (pieceHead − zeroPieceHead) — i.e. the picture is reassembled in its authored orientation. Swapping two same-colored pieces does not count; the exact offsets must match.

Block Colours

Seven predefined block colours, defined in Ecolor.cs and mapped to RGB in MyUtils.GetColor / JsonLevelData.cs → BlockColors:

Colour Key Colour Approx. RGB
yellow Yellow (1.00, 0.84, 0.00)
red Red (1.00, 0.20, 0.20)
magenta Magenta (1.00, 0.20, 0.67)
purple Purple (0.54, 0.17, 0.89)
blue Blue (0.20, 0.40, 1.00)
mint Mint (0.40, 0.92, 0.81)
lime Lime (0.71, 0.91, 0.29)

Win & Lose Conditions

Animations & Feedback

Blocks use 2D rigidbodies with snap-to-grid placement for drag feedback. Completed pictures fade their ghost sprites into the final image and emit a star particle burst. The Hint booster spawns a translucent ghost of a displaced piece at its solved position and pulses it for ~2.2 seconds while also briefly scaling the real piece so the player can see which piece should move where. The Shuffle booster reseats blocks with an explicit no-overlap validation pass.

Scripts Reference

Runtime scripts use the PictureBlockPuzzle2D.Scripts namespace. Editor scripts use the PictureBlockPuzzle2D.Editor and PictureBlockPuzzle2D.EditorTools namespaces. Shared helpers (SoundManager, DeveloperSettingsController, LoseWinPanelManager) live under HypercasualGameEngine.

Core Scripts

Script Description
GridManager Main gameplay controller. Builds the grid from LevelData.gridSize, spawns blocks from blockDatas, runs the level countdown coroutine, toggles the FrozenImage indicator during a Time Freeze, validates shape placements with CanApplyShapeToGrid, stamps occupancy via SetBlockOccupancy, rebuilds occupancy from live positions before each merge check, and exposes ShuffleBlocks() used by the booster.
InputManager Touch / mouse drag handling. Raycasts to pick a block, tracks the drag, and snaps the block to the nearest valid grid cell on release.
Block Block entity. Stores color, piece data, head grid position, and the 2D rigidbody / colliders. Exposes GetAllShape(head) to convert a head cell + piece offsets into an occupancy list.
BlockIconLoader Resolves per-piece picture sprites from Resources/BlockIcons/ by name (e.g. ice-cream-2_0).
GoalManger Tracks remaining color goals for the level and drives the goal UI.
LevelData Runtime level descriptor. Contains gridSize, time, a list of BlockData, obstacles, and per-level booster counts. Created at runtime with HideFlags.HideAndDontSave to survive scene reloads.
JsonLevelData JSON-serialisable mirror of LevelData plus the BlockColors palette helper and the ToRuntimeLevelData / FromRuntimeLevelData conversions used by the editor tools and runtime loader.
LevelManager Static loader. Reads Resources/Levels/PictureBlockPuzzle2D/*.json into a sorted list at startup, tracks the current level index under PlayerPrefs key PictureBlockPuzzle2D_CurrentLevel, and exposes GetCurrentLevel, AdvanceLevel, GoToLevel, ForceReload, plus editor-only disk-write helpers used by the Level Editor.
BoosterManager Manages all four boosters: Time Freeze (calls GridManager.FreezeCountdown(10f) and toggles the FrozenImage), Hint (ghost-previews a displaced piece's solved position), Shuffle (delegates to GridManager.ShuffleBlocks()), and Grid Expander (delegates to GridManager.ExpandGrid()). Binds to the scene buttons Booster-Button-1/2/3/4 by string match, gates each booster by level index (undoUnlockLevel=0, hintUnlockLevel=1, shuffleUnlockLevel=2, gridExpandUnlockLevel=3), and shows first-use tutorial popups keyed in PlayerPrefs. Exposes DebugUnlockAndRefillAll() used by the U dev hotkey (self-healing: re-hooks button refs if Init hasn't run yet).
PictureBlockPuzzle2DGameManager Scene orchestrator. Holds dev hotkeys (arrow keys for level nav, U to unlock & refill boosters), refreshes the HUD level label from LevelManager.CurrentLevelIndex, and wires BoosterManager.Init to the current level on startup.
GameUI Writes the formatted countdown string into the HUD's countdown TextMeshPro via UpdateCountDownText(float).
LoseWinPanelManager Owns the Win / Lose panels. LoadNextLevel() calls LevelManager.AdvanceLevel() before SceneManager.LoadScene so progression actually sticks. Namespaced under HypercasualGameEngine for cross-game reuse.
AudioButtonController HUD mute toggle. Persists the muted state and swaps the button sprite accordingly.

Utility Scripts

Script Description
Ecolor Colour enum (None, Yellow, Red, Magenta, Purple, Blue, Mint, Lime) used by blocks and the occupancy map. Also defines MoveAxis (Any / Horizontal / Vertical) used by axis-constrained pieces.
ShapeData Maps a shape-type integer in the JSON to a concrete Block prefab, used at spawn time by MyUtils.GetBlock.
CameraSwitcher Swaps camera presets when orientation / aspect changes.
SnapToClosestObjectOfList Edit-time helper that snaps a Transform to the nearest object in a given list — used when hand-placing HUD elements.
SpriteDummy Placeholder sprite component for editor-time visualisation.
MyUtils Small helpers: time formatting, grid origin calculation, and GetBlock(shapeType, shapeData) prefab lookup.

Engine Scripts (HypercasualGameEngine namespace)

Script Description
SoundManager Singleton audio system with a single AudioSource, master-volume slider, and one PlayX() method per gameplay event (e.g. PlayBlockPlace, PlayUndo, PlayHint, PlayShuffle, PlayLevelClear, PlayFreezeTime).
DeveloperSettingsController Dev-panel controls: next / previous level, trigger win / lose, unlock all boosters, refill all boosters, reset first-use booster popups, ad-panel toggles.
DeveloperModeButton Shows / hides the DeveloperSettingsPanel. Auto-finds the panel child if no Inspector reference is assigned.

Editor Scripts

Script Description
LevelEditorWindow Unity Editor window (Tools > Picture Block Puzzle 2D > Level Editor) for visually designing levels. Mirrors the runtime block visuals: every cell of every multi-cell piece is drawn, the icon sprite is contain-fit-scaled to the block's bounds and clipped to each piece's cell (matching Block.cs), and movement-axis arrows render on constrained pieces. Tools: Select / Move (click to select a block, drag a piece to move its startPos with collision validation), Place Pattern (drop a 1×2 / 2×1 / L / 1×1 single-piece block with a green / red hover preview), Wall, and Erase. Block list shows a live mini preview (color + sprite slice in solved layout), inline per-piece editor (shapeType / startPos / headPosition / moveAxis / zero toggle), per-block color + sprite picker, and a “↪ merge” button that folds a block's pieces into the currently selected block, recomputing blockSize and each piece's headPosition from the combined bounding box. Top-of-panel fields edit cols, rows, timeLimit, and all four booster counts.
LevelGeneratorWindow Unity Editor window (Tools > Picture Block Puzzle 2D > Auto Level Generator) for batch-generating levels. Settings control grid size, block count, scramble moves, time limit, and booster counts. Uses a curated tiling-pattern library (2×2, 2×3, 3×2) so every block fits cleanly; scales wall density, axis-constraint chance, and scramble depth with difficulty; front-loads difficulty with a pow(t, 0.55) curve so hard levels arrive sooner in the batch. Append-only — never overwrites existing levels. A “Delete ALL” button in the danger zone wipes every level for a clean regenerate.
PictureBlockPuzzle2DWelcomePopup Welcome popup shown once per Unity session on project open. Hosts two CTAs: “Leave a Review on the Asset Store” and “Get HyperCasual Game Engine”. Permanently dismissable via the “Do not show this popup again” checkbox (stored in EditorPrefs). The popup has no menu item — the auto-show on session start is its only entry point.
DragSnapWithoutPlaymodeEditor Edit-mode helper that lets authors drag-snap Block prefabs to grid cells in the scene view without entering Play mode.
ImportAudioIcon, InputSystemCheck, URPStartupCheck Editor-time diagnostics that warn the developer if required settings are missing (icons, Both input handling, URP active).

Level Editor

Opening the Level Editor

In the Unity menu bar, go to Tools > Picture Block Puzzle 2D > Level Editor. This opens a dedicated Editor window — no need to enter Play mode.

Layout

Visual Parity with Runtime

The grid renders pieces exactly the way the running game does:

Tools

Tool Behaviour
Select / Move Click a piece to select its block (used by every other tool's color picker and by the merge helper). Drag a piece to move its startPos — the move is collision-validated against walls / other pieces / the grid edge, and the piece's headPosition is preserved so the picture-slice mapping stays correct.
Place Pattern Pick a shape and a color, then click an empty cell to drop a single-piece block. A hover preview shows the cells that would be occupied, tinted green when the placement is valid and red when blocked. The five available shapes match the runtime's ShapeType enum: 1x2 (horizontal pair), 2x1 (vertical pair), L (top-right) — cells (0,0) (0,1) (1,0), L (bottom-left) — cells (0,1) (1,0) (1,1), and 1x1. Each placement creates a new block (auto-selected) so you can immediately set its sprite.
Wall Click (or drag) to add a wall obstacle. Hover preview turns red over a cell that already has a wall or piece. Walls are the runtime's permanent obstacle cells — no block can ever overlap them.
Erase Click (or drag) to remove whatever piece or wall sits at that cell. Empty blocks (no remaining pieces) are pruned automatically.

Building a Multi-Piece Block

Every Place Pattern click creates a single-piece block. To assemble several pieces into one logical multi-piece block (so they share one picture / sprite):

  1. In Place Pattern mode, drop each piece in its solved (adjacent) layout — e.g. for a 2×2 block built from two 1×2 horizontal pieces, click 1x2 at (3,4) and again at (3,5).
  2. Switch to Select / Move and click one of the placed pieces to select it as the destination block.
  3. In the Blocks list, click the “↪ merge” button on every other block you want to fold in. Each merge:
    • Computes the bounding box of every cell of every piece in both blocks.
    • Sets the destination block's blockSize to that bounding box.
    • Rewrites every piece's headPosition to its slot inside the bounding box.
    • Keeps the destination block's color, sprite, and zero-piece flag (extras are demoted; piece 0 is promoted if no zero remains).
    • Removes the now-empty source block.
  4. Optionally switch back to Select / Move and drag pieces around to scramble them. Drag changes startPos only — headPosition stays put, so the picture mapping survives the scramble.

The merge button is greyed out when nothing is selected or when you'd be merging a block into itself.

Block Properties

Every block row in the Blocks list exposes:

Level Storage

All levels are stored as individual JSON files in Assets/Picture Block Puzzle 2D/Resources/Levels/PictureBlockPuzzle2D/ with the naming convention Level_1.json, Level_2.json, … The runtime loader uses Resources.LoadAll<TextAsset>("Levels/PictureBlockPuzzle2D") and sorts by the trailing number in each filename. Save All Levels wipes the existing files in that folder and writes a fresh set, numbered by their order in the level list — reorder the list before saving if you want a specific numbering.

Automatic Level Generator

The Automatic Level Generator is an Editor tool that batch-generates playable picture-assembly levels. It controls grid size, block count, scramble moves, time budget, booster allowances, axis-constraint chance, and wall density — all scaled along a front-loaded difficulty curve so hard levels arrive earlier in the batch. Generated levels always satisfy the shape-footprint / occupancy invariants used by the runtime; the in-game Shuffle, Hint, Undo, and Grid Expander boosters give players a way out of any stuck state (a forward-BFS solvability check is intentionally not run — it was rejecting harder scrambles and producing levels where pieces landed conveniently close to their solved positions).

Opening the Tool

In the Unity menu bar, go to Tools > Picture Block Puzzle 2D > Auto Level Generator. This opens a settings window where you can configure all parameters before generating.

Settings

Setting Description
Levels to Generate How many new level files to create. New levels are appended after existing ones (never overwrites).
Grid Size (Min & Max) Range for the grid width / height. Early levels lean toward the minimum, harder levels toward the maximum.
Block Count (Min & Max) Range for the number of blocks (distinct pictures) per level. Default 2 → 6.
Scramble Moves (Min & Max) Number of legal reverse-slide moves used to displace pieces from their solved positions. The effective total scales with piece count internally so each piece receives ~N moves on average. Default 8 → 30.
Time Limit (Min & Max) Seconds of countdown budget per level (Max used on the easiest level, Min on the hardest).
Booster Counts (Min & Max) Per-booster min / max uses to write into each generated level's JSON.
Random Seed Set to 0 for an unseeded run (different every time), or any non-zero integer for reproducible output.

How It Works

  1. Configure settings — adjust all parameters in the Editor window
  2. Click “Generate Levels” — the tool detects existing levels and appends new ones
  3. For each level, the generator:
    • Randomises grid size, block count, scramble depth, wall density, and axis-constraint chance within your ranges, all scaled by a front-loaded difficulty curve (t = pow(levelIndex / total, 0.55)) so the player reaches hard levels sooner
    • Places blocks using the curated tiling-pattern library (2×2 with two stacked 1×2s, 2×3 / 3×2 with three 1×3s, L-shape + 1×1 combos, etc.) to guarantee clean footprints close to 1:1 aspect ratios
    • Marks one piece per block as the zero-anchor and records the remaining pieces' solved offsets
    • Scrambles non-zero pieces via random legal slides, biased toward destinations far from each piece's solved head so pieces actually travel rather than wobble around their goal
    • Assigns at most one piece per block a moveAxis constraint (Horizontal or Vertical) when the chance roll succeeds — only on pieces whose scrambled → solved delta is already purely along that axis, to keep the constraint self-consistent
    • Scatters wall obstacles across empty cells at increasing density with difficulty (up to ~28% of grid area at the hardest) with a minimum floor that rises with difficulty to produce that “cramped” hard-level feel
    • Writes the JSON level file to Resources/Levels/PictureBlockPuzzle2D/Level_N.json

Non-Destructive

The generator never overwrites existing level files. It always starts numbering from the next available index (e.g., if Level_30 exists, new levels start from Level_31). Use the “Delete ALL Levels” button if you want to start fresh.

Boosters

The game includes four booster abilities that players can activate during gameplay. Each booster has a configurable number of uses per level (set in the level JSON data) and an optional first-use tutorial popup. Each booster unlocks at a specific level so new players learn one mechanic at a time.

Booster Unlock Effect Activation
Time Freeze Level 1 Pauses the level countdown for 10 seconds. Enables the on-screen FrozenImage indicator for the duration, then hides it when the freeze expires. Multiple presses stack the remaining freeze time. Tap the Time Freeze button (Booster-Button-1). Consumes one use immediately.
Hint Level 2 Picks a displaced non-zero piece at random, spawns a translucent ghost of it at the solved position, pulses the ghost for ~2.2 seconds, and briefly scales the real piece so the player sees which piece should move where. Tap the Hint button (Booster-Button-2). Consumes one use immediately.
Shuffle Level 3 Calls GridManager.ShuffleBlocks() which reseats every block on a random valid head cell using CanApplyShapeToGrid to guarantee no two shapes overlap and every footprint stays inside the grid. If a block cannot be placed, the whole board is rolled back and the charge is not consumed. Tap the Shuffle button (Booster-Button-3). A charge is consumed only when the shuffle actually succeeds.
Grid Expander Level 4 Calls GridManager.ExpandGrid() which extends the playable grid by 1 cell on every side, rebuilds the tile map, shifts every block into the new coordinate system, and repositions the border walls. One expansion per level is typically plenty. Tap the Grid Expander button (Booster-Button-4). Consumed on start of expansion; no-op if an expansion is already running or the level has ended.

Booster Buttons

Booster buttons live in the scene under GameHUD_Panel/BottomHolder. Each button has:

Cheat Mode

Press the U key during gameplay to unlock all boosters and refill their usage counts (wrapped in #if UNITY_EDITOR || DEVELOPMENT_BUILD so it's stripped from release builds). The hotkey reads both the legacy Input.GetKeyDown and the new-Input-System Keyboard.current.uKey so it works regardless of which backend owns focus. It calls BoosterManager.DebugUnlockAndRefillAll(), a self-healing variant that re-hooks scene button references if the scene hasn't yet initialised.

Press Right Arrow / Left Arrow during gameplay to jump to the next / previous level (wraps at the ends). Handled by PictureBlockPuzzle2DGameManager.Update().

The Developer Settings Panel also ships with Next Level / Previous Level / Reload buttons.

Customization Guide

Adding New Colours

To add a new block colour (the game already ships with seven — yellow, red, magenta, purple, blue, mint, lime):

  1. Open Ecolor.cs and add a new entry to the EColor enum:
    public enum EColor { None, Yellow, Red, Magenta, Purple, Blue, Mint, Lime, // Add your custom colour: Teal }
  2. Open MyUtils.cs and add the RGB for the new entry to GetColor. Open JsonLevelData.cs and mirror it in the BlockColors dictionaries (colors, toEnum, toKey, AllKeys)
  3. Open LevelGeneratorWindow.cs and add the new key to the COLOR_KEYS array so the generator picks from it
  4. Regenerate or hand-author a level that uses the new colour key

New colours are automatically available in the Level Editor, Auto Level Generator, and at runtime.

Adjusting Grid Visuals

The grid layout is controlled by GridManager.cs. Key Inspector properties:

Modifying Block Visuals

Block rendering is driven by Block + per-piece sprites under Resources/BlockIcons/. To retheme:

Audio

Audio lives on a single SoundManager singleton. Replace any clip on the SoundManager GameObject in the scene to retheme a specific event. Add a new clip field + matching PlayX() method if you introduce a new gameplay event — the existing pattern is documented inside SoundManager.cs.

Adding New Boosters

New boosters can be added by following the existing pattern in BoosterManager.cs:

  1. Add booster state fields (use count, unlock level, UI references)
  2. Wire up the button in HookUpExistingBoosters() by adding a fourth named child (Booster-Button-4) to the scene and matching it by string
  3. Implement the booster logic in a new click handler method (e.g. OnMyBoosterClicked())
  4. Add a count field to JsonLevelData / LevelData and populate it in BoosterManager.Init()
  5. Update UpdateBoosterUI() to draw the new button's lock / unlock / count display

JSON Level Format

Each level is a JSON file with the following structure:

{ "cols": 5, "rows": 6, "timeLimit": 240.0, "blocks": [ { "colorKey": "red", "blockSize": { "x": 2, "y": 3 }, "spriteName": "ice-cream-2_0", "moveAxis": 0, // legacy block-level axis; per-piece axis is source of truth "pieceDatas": [ { "startPos": { "x": 0, "y": 1 }, "headPosition": { "x": 0, "y": 0 }, "shapeType": 0, "isZeroPos": true, "moveAxis": 0 // 0=Any, 1=Horizontal, 2=Vertical } ] } ], "obstacles": [ { "x": 2, "y": 2 } // static wall cells ], "undoBoosterCount": 3, // Booster-Button-1 (Time Freeze) "hintBoosterCount": 3, // Booster-Button-2 (Hint) "shuffleBoosterCount": 3, // Booster-Button-3 (Shuffle) "gridExpandBoosterCount": 1 // Booster-Button-4 (Grid Expander) }

The blocks array defines multi-piece pictures. Each block has a colorKey (one of yellow / red / magenta / purple / blue / mint / lime), a blockSize, a spriteName (resolved from Resources/BlockIcons/), and a list of pieceDatas. Each piece has:

Note on field name: undoBoosterCount is the legacy serialised name kept for save-file compatibility; at runtime it drives the Time Freeze booster.

Note on moveAxis placement: the per-block moveAxis field is legacy — at runtime the per-piece moveAxis inside pieceDatas is authoritative. The generator only constrains one piece per block (on an axis where that piece's scrambled → solved delta is already purely along that axis) so levels stay self-consistent.

Support

Common Issues

Issue Solution
No levels load / empty grid appears Ensure JSON level files exist in Assets/Picture Block Puzzle 2D/Resources/Levels/PictureBlockPuzzle2D/ with the naming convention Level_1.json, Level_2.json, etc. Use the Auto Level Generator to create levels if the folder is empty.
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.
Time Freeze booster icon doesn't appear The FrozenImage GameObject must exist in the scene (child of WorldCanvas). It uses the sprite Textures/frozen-button.png. If you've deleted it, re-add the object or re-import the package.
Blocks pass through each other Every Block prefab needs a Rigidbody2D and matching Collider2D(s). Also verify the Border walls under GridManager.borderRoot are enabled — they constrain the play area for the drag physics.
Booster buttons don't respond Ensure the GameHUD_Panel/BottomHolder hierarchy contains children named Booster-Button-1, Booster-Button-2, and Booster-Button-3. Each needs a Button component. If you added a Canvas component for sorting order, you must also add a GraphicRaycaster.
First-use booster popups never show The popups Booster-Popup-1/2/3 must exist in the scene with m_IsActive: 0. GameObject.Find skips inactive objects, so BoosterManager uses an inactive-aware scan — if you renamed the popups, restore the literal names or update the constants at the top of BoosterManager.cs. To replay the tutorial, click “Reset Booster Popups” on the Developer Settings panel.
Shuffle booster consumes a charge but nothing changes On a tightly packed board the shuffle occasionally can't find a valid layout for every block and rolls back. In that case the charge is not consumed (the method returns false). If the count still ticks down, ensure no other script is decrementing shuffleBoosterCount behind BoosterManager's back.
Level Editor shows empty grid Click “Reload from Disk” in the Level Editor window, or ensure the Resources/Levels/PictureBlockPuzzle2D/ folder exists and contains valid JSON files.

Tips

Contact Us

If you get stuck or have any issues, feel free to reach out to us at ragendom@gmail.com. We are happy to help!