- Genre: Puzzle
- Expert blueprint for puzzle games emphasizing clarity, experimentation, and "Aha!" moments.
- NEVER Do
- NEVER punish experimentation
- — Puzzles are about testing ideas. Always provide undo/reset. No punishment for trying.
- NEVER require pixel-perfect input
- — Logic puzzles shouldn't need precision aiming. Use grid snapping or forgiving hitboxes.
- NEVER allow undetected unsolvable states
- — Detect softlocks automatically or provide prominent "Reset Level" button.
- NEVER hide the rules
- — Visual feedback must be instant and clear. A wire lighting up when connected teaches the rule.
- NEVER skip non-verbal tutorials
- — Level 1 = introduce mechanic in isolation. Level 2 = trivial use. Level 3 = combine with existing mechanics.
- Available Scripts
- MANDATORY
-
- Read the appropriate script before implementing the corresponding pattern.
- command_undo_redo.gd
- Command pattern for undo/redo. Stores reversible actions in dual stacks, clears redo on new action. Includes MoveCommand example.
- Core Loop
- Observation
-
- Player assesses the level layout and mechanics.
- Experimentation
-
- Player interacts with elements (push, pull, toggle).
- Feedback
-
- Game reacts (door opens, laser blocked).
- Epiphany
-
- Player understands the logic ("Aha!" moment).
- Execution
- Player executes the solution to advance. Skill Chain Phase Skills Purpose 1. Interaction godot-input-handling , raycasting Clicking, dragging, grid movement 2. Logic command-pattern , state-management Undo/Redo, tracking level state 3. Feedback godot-tweening , juice Visual confirmation of valid moves 4. Progression godot-save-load-systems , level-design Unlocking levels, tracking stars/score 5. Polish ui-minimalism Non-intrusive HUD Architecture Overview 1. Command Pattern (Undo System) Essential for puzzle games. Never punish testing.
command.gd
class_name Command extends RefCounted func execute ( ) -> void : pass func undo ( ) -> void : pass
level_manager.gd
var history : Array [ Command ] = [ ] var history_index : int = - 1 func commit_command ( cmd : Command ) -> void :
Clear redo history if diverging
if history_index < history . size ( ) - 1 : history = history . slice ( 0 , history_index + 1 ) cmd . execute ( ) history . append ( cmd ) history_index += 1 func undo ( ) -> void : if history_index
= 0 : history [ history_index ] . undo ( ) history_index -= 1 2. Grid System (TileMap vs Custom) For grid-based puzzles (Sokoban), a custom data structure is often better than just reading physics.
grid_manager.gd
var grid_size : Vector2i = Vector2i ( 16 , 16 ) var objects : Dictionary = { }
Vector2i -> Node
func move_object ( obj : Node , direction : Vector2i ) -> bool : var start_pos = grid_pos ( obj . position ) var target_pos = start_pos + direction if is_wall ( target_pos ) : return false if objects . has ( target_pos ) :
Handle pushing logic here
return false
Execute move
- objects
- .
- erase
- (
- start_pos
- )
- objects
- [
- target_pos
- ]
- =
- obj
- tween_movement
- (
- obj
- ,
- target_pos
- )
- return
- true
- Key Mechanics Implementation
- Win Condition Checking
- Check victory state after every move.
- func
- check_win_condition
- (
- )
- ->
- void
- :
- for
- target
- in
- targets
- :
- if
- not
- is_satisfied
- (
- target
- )
- :
- return
- level_complete
- .
- emit
- (
- )
- save_progress
- (
- )
- Non-Verbal Tutorials
- Teach mechanics through level design, not text.
- Isolation
-
- Level 1 introduces
- only
- the new mechanic in a safe room.
- Reinforcement
-
- Level 2 requires using it to solve a trivial problem.
- Combination
-
- Level 3 combines it with previous mechanics.
- Common Pitfalls
- Strictness
-
- Requiring pixel-perfect input for logic puzzles.
- Fix
-
- Use grid snapping or forgiving hitboxes.
- Dead Ends
-
- Allowing the player to get into an unsolvable state without realizing it.
- Fix
-
- Auto-detect failure or provide a prominent "Reset" button.
- Obscurity
-
- Hiding the rules.
- Fix
-
- Visual feedback must be instant and clear (e.g., a wire lights up when connected).
- Godot-Specific Tips
- Tweens
-
- Use
- create_tween()
- for all grid movements. It feels much better than instant snapping.
- Custom Resources
-
- Store level data (layout, starting positions) in
- .tres
- files for easy editing in the Inspector.
- Signals
- Use signals like state_changed to update UI/Visuals decoupled from the logic. Reference Master Skill: godot-master