Debugging with Dev Tools
The Doodle Engine provides a framework-agnostic browser console API (window.doodle) for debugging and testing your game during development. Dev tools are part of @doodle-engine/core and work with any renderer (React, Vue, Svelte, vanilla JS).
Dev tools are automatically stripped from production builds via tree-shaking.
Enabling Dev Tools
Section titled “Enabling Dev Tools”Dev tools are part of @doodle-engine/core and work with any framework. Import and enable them in development mode:
The React renderer automatically enables dev tools in development mode:
// Already integrated in GameProvider - no extra setup needed!import { GameProvider } from '@doodle-engine/react';If you’re building a custom React renderer:
import { useEffect } from 'react';import { Engine, enableDevTools } from '@doodle-engine/core';
function MyRenderer({ engine }) { const [snapshot, setSnapshot] = useState(engine.getSnapshot());
useEffect(() => { if (import.meta.env.DEV) { enableDevTools(engine, () => setSnapshot(engine.getSnapshot()));
return () => { delete window.doodle; }; } }, [engine]);
// ... rest of renderer}<script setup>import { onMounted, onUnmounted, ref } from 'vue';import { enableDevTools } from '@doodle-engine/core';
const props = defineProps(['engine']);const snapshot = ref(props.engine.getSnapshot());
onMounted(() => { if (import.meta.env.DEV) { enableDevTools(props.engine, () => { snapshot.value = props.engine.getSnapshot(); }); }});
onUnmounted(() => { if (import.meta.env.DEV) { delete window.doodle; }});</script>Svelte
Section titled “Svelte”<script> import { onMount, onDestroy } from 'svelte' import { enableDevTools } from '@doodle-engine/core'
export let engine
$: snapshot = engine.getSnapshot()
onMount(() => { if (import.meta.env.DEV) { enableDevTools(engine, () => { snapshot = engine.getSnapshot() }) } })
onDestroy(() => { if (import.meta.env.DEV) { delete window.doodle } })</script>Vanilla JavaScript
Section titled “Vanilla JavaScript”import { enableDevTools } from '@doodle-engine/core';
let engine = new Engine(registry, initialState);let snapshot = engine.getSnapshot();
if (import.meta.env.DEV) { enableDevTools(engine, () => { snapshot = engine.getSnapshot(); render(snapshot); // Your render function });}Using Dev Tools
Section titled “Using Dev Tools”-
Start your dev server:
Terminal window npm run dev -
Open your game in the browser (usually
http://localhost:3000) -
Open the browser console (F12 or right-click → Inspect → Console)
-
Type
doodle.inspect()to see all available commands
Available Commands
Section titled “Available Commands”Flag Manipulation
Section titled “Flag Manipulation”Flags are boolean game state values used in conditions and branching.
// Set a flagdoodle.setFlag('quest_started');doodle.setFlag('met_merchant');
// Clear a flagdoodle.clearFlag('quest_started');Use case: Test dialogue branches that depend on flags without playing through the entire game.
Variable Manipulation
Section titled “Variable Manipulation”Variables store numeric or string values (gold, counters, player name, etc.).
// Set a variabledoodle.setVariable('gold', 500);doodle.setVariable('player_name', 'Alice');
// Get a variable's current valuedoodle.getVariable('gold');// → 500Use case: Test shop systems, stat checks, or any mechanic that depends on variables.
Location Control
Section titled “Location Control”Instantly teleport to any location without using the map.
doodle.teleport('tavern');doodle.teleport('market');doodle.teleport('dungeon_entrance');Use case: Quickly navigate to specific locations to test content without traversing the map.
Dialogue Control
Section titled “Dialogue Control”Trigger any dialogue directly, bypassing normal game flow.
doodle.triggerDialogue('bartender_greeting');doodle.triggerDialogue('merchant_intro');Use case: Test specific dialogue trees without playing through prerequisites.
Quest Control
Section titled “Quest Control”Set quest stages directly to test quest progression.
doodle.setQuestStage('odd_jobs', 'in_progress');doodle.setQuestStage('odd_jobs', 'completed');doodle.setQuestStage('main_quest', 'chapter_2');Use case: Test quest UI, journal entries, and quest-dependent content.
Inventory Control
Section titled “Inventory Control”Add or remove items from inventory without picking them up.
// Add an itemdoodle.addItem('old_coin');doodle.addItem('rusty_sword');
// Remove an itemdoodle.removeItem('old_coin');Use case: Test inventory UI, item-dependent dialogue, or mechanics that require specific items.
Inspection
Section titled “Inspection”View the current game state and content registry.
// Show current state summary and command listdoodle.inspect();
// Return full game state object (flags, variables, inventory, etc.)const state = doodle.inspectState();console.log(state.flags);console.log(state.inventory);
// Return content registry (all loaded entities)const registry = doodle.inspectRegistry();console.log(registry.dialogues);console.log(registry.characters);Use case: Debug state issues, verify content loaded correctly, or understand what’s happening behind the scenes.
Example Debugging Workflows
Section titled “Example Debugging Workflows”Testing a Quest Dialogue Branch
Section titled “Testing a Quest Dialogue Branch”You want to test a dialogue option that only appears if the player has completed a quest:
// Set up the prerequisite quest statedoodle.setQuestStage('odd_jobs', 'completed');
// Trigger the dialoguedoodle.triggerDialogue('bartender_greeting');
// The quest-dependent choice should now appearTesting Shop Purchase Logic
Section titled “Testing Shop Purchase Logic”You’re building a shop system with conditions based on gold:
// Give yourself golddoodle.setVariable('gold', 1000);
// Verify the variable is setdoodle.getVariable('gold');
// Trigger the shop dialoguedoodle.triggerDialogue('merchant_shop');
// Try buying items and check if gold decreases correctlyTesting Item-Dependent Dialogue
Section titled “Testing Item-Dependent Dialogue”A character has different dialogue if you’re carrying a specific item:
// Add the itemdoodle.addItem('magic_amulet');
// Teleport to the character's locationdoodle.teleport('wizards_tower');
// Talk to the characterdoodle.triggerDialogue('wizard_greeting');
// Special dialogue should appearDebugging State Issues
Section titled “Debugging State Issues”Something’s not working as expected:
// Check current stateconst state = doodle.inspectState();
// Look for unexpected flag valuesconsole.log(state.flags);
// Check variable valuesconsole.log(state.variables);
// Verify inventory contentsconsole.log(state.inventory);Limitations
Section titled “Limitations”- Dev tools access internal engine state using private fields. This is intentional for debugging but means breaking changes to the engine internals won’t be considered breaking changes to the dev tools API.
- Dev tools only work in development mode (
npm run dev). They are not available in production builds. - State changes made via dev tools bypass all game logic. For example,
doodle.addItem()doesn’t trigger effects or run conditions. It directly mutates the state.
Production Safety
Section titled “Production Safety”Dev tools are completely removed from production builds:
import.meta.env.DEVis replaced withfalseby Vite during production builds- The
if (import.meta.env.DEV)block is eliminated by the minifier - The
enableDevToolsfunction and entire devtools module are tree-shaken from the bundle window.doodleis undefined in production
This works the same way across all frameworks (React, Vue, Svelte, vanilla JS). Your players will never see or access the dev tools.
- Use
doodle.inspect()as your starting point. It shows the current game state and lists all available commands. - Combine commands to set up complex scenarios: set multiple flags, add items, then trigger dialogue.
- Save console commands in a text file or browser snippet for scenarios you test repeatedly.
- Use
inspectState()andinspectRegistry()to understand how the engine represents your content internally.