BREAKING CHANGES: - Relationship syntax now requires blocks for all participants - Removed self/other perspective blocks from relationships - Replaced 'guard' keyword with 'if' for behavior tree decorators Language Features: - Add tree-sitter grammar with improved if/condition disambiguation - Add comprehensive tutorial and reference documentation - Add SBIR v0.2.0 binary format specification - Add resource linking system for behaviors and schedules - Add year-long schedule patterns (day, season, recurrence) - Add behavior tree enhancements (named nodes, decorators) Documentation: - Complete tutorial series (9 chapters) with baker family examples - Complete reference documentation for all language features - SBIR v0.2.0 specification with binary format details - Added locations and institutions documentation Examples: - Convert all examples to baker family scenario - Add comprehensive working examples Tooling: - Zed extension with LSP integration - Tree-sitter grammar for syntax highlighting - Build scripts and development tools Version Updates: - Main package: 0.1.0 → 0.2.0 - Tree-sitter grammar: 0.1.0 → 0.2.0 - Zed extension: 0.1.0 → 0.2.0 - Storybook editor: 0.1.0 → 0.2.0
382 lines
18 KiB
HTML
382 lines
18 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||
<head>
|
||
<!-- Book generated using mdBook -->
|
||
<meta charset="UTF-8">
|
||
<title>Your First Behavior Tree - Storybook Language Guide</title>
|
||
|
||
|
||
<!-- Custom HTML head -->
|
||
|
||
<meta name="description" content="Comprehensive documentation for the Storybook narrative simulation language">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#ffffff">
|
||
|
||
<link rel="icon" href="../favicon.svg">
|
||
<link rel="shortcut icon" href="../favicon.png">
|
||
<link rel="stylesheet" href="../css/variables.css">
|
||
<link rel="stylesheet" href="../css/general.css">
|
||
<link rel="stylesheet" href="../css/chrome.css">
|
||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||
|
||
<!-- Fonts -->
|
||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||
|
||
<!-- Highlight.js Stylesheets -->
|
||
<link rel="stylesheet" href="../highlight.css">
|
||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||
|
||
<!-- Custom theme stylesheets -->
|
||
|
||
|
||
<!-- Provide site root to javascript -->
|
||
<script>
|
||
var path_to_root = "../";
|
||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||
</script>
|
||
<!-- Start loading toc.js asap -->
|
||
<script src="../toc.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="body-container">
|
||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||
<script>
|
||
try {
|
||
var theme = localStorage.getItem('mdbook-theme');
|
||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||
|
||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||
}
|
||
|
||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||
}
|
||
} catch (e) { }
|
||
</script>
|
||
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script>
|
||
var theme;
|
||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||
const html = document.documentElement;
|
||
html.classList.remove('light')
|
||
html.classList.add(theme);
|
||
html.classList.add("js");
|
||
</script>
|
||
|
||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script>
|
||
var sidebar = null;
|
||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||
if (document.body.clientWidth >= 1080) {
|
||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||
sidebar = sidebar || 'visible';
|
||
} else {
|
||
sidebar = 'hidden';
|
||
}
|
||
sidebar_toggle.checked = sidebar === 'visible';
|
||
html.classList.remove('sidebar-visible');
|
||
html.classList.add("sidebar-" + sidebar);
|
||
</script>
|
||
|
||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||
<!-- populated by js -->
|
||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||
<noscript>
|
||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||
</noscript>
|
||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||
<div class="sidebar-resize-indicator"></div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div id="page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<div id="menu-bar-hover-placeholder"></div>
|
||
<div id="menu-bar" class="menu-bar sticky">
|
||
<div class="left-buttons">
|
||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||
<i class="fa fa-bars"></i>
|
||
</label>
|
||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||
<i class="fa fa-paint-brush"></i>
|
||
</button>
|
||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||
</ul>
|
||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||
<i class="fa fa-search"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<h1 class="menu-title">Storybook Language Guide</h1>
|
||
|
||
<div class="right-buttons">
|
||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||
<i id="print-button" class="fa fa-print"></i>
|
||
</a>
|
||
<a href="https://github.com/r3t-studios/storybook" title="Git repository" aria-label="Git repository">
|
||
<i id="git-repository-button" class="fa fa-github"></i>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div id="search-wrapper" class="hidden">
|
||
<form id="searchbar-outer" class="searchbar-outer">
|
||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||
</form>
|
||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||
<div id="searchresults-header" class="searchresults-header"></div>
|
||
<ul id="searchresults">
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||
<script>
|
||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||
});
|
||
</script>
|
||
|
||
<div id="content" class="content">
|
||
<main>
|
||
<h1 id="your-first-behavior-tree"><a class="header" href="#your-first-behavior-tree">Your First Behavior Tree</a></h1>
|
||
<p>Behavior trees define how characters make decisions. They model the thought process: “Try this first, and if it fails, try that instead.” In this chapter, you will create your first behavior tree for Martha.</p>
|
||
<h2 id="what-is-a-behavior-tree"><a class="header" href="#what-is-a-behavior-tree">What is a Behavior Tree?</a></h2>
|
||
<p>A behavior tree is a hierarchy of nodes that executes from top to bottom. Each node either <strong>succeeds</strong> or <strong>fails</strong>, and the tree uses that result to decide what to do next.</p>
|
||
<p>There are two fundamental building blocks:</p>
|
||
<ul>
|
||
<li><strong><code>choose</code></strong> (Selector): Try children in order until one succeeds. Think “try A, else try B, else try C.”</li>
|
||
<li><strong><code>then</code></strong> (Sequence): Run children in order, stopping if any fails. Think “do A, then B, then C – all must succeed.”</li>
|
||
</ul>
|
||
<h2 id="your-first-tree"><a class="header" href="#your-first-tree">Your First Tree</a></h2>
|
||
<p>Let us give Martha a simple baking behavior:</p>
|
||
<pre><code class="language-storybook">behavior Martha_BakeRoutine {
|
||
choose what_to_do {
|
||
then fill_special_orders {
|
||
CheckSpecialOrders
|
||
PrepareSpecialIngredients
|
||
BakeSpecialItem
|
||
}
|
||
|
||
then daily_bread {
|
||
MixDough
|
||
KneadDough
|
||
BakeLoaves
|
||
}
|
||
|
||
CleanWorkstation
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Reading this as a story:</p>
|
||
<blockquote>
|
||
<p>Martha will <strong>choose</strong> what to do. First, she tries to <strong>fill special orders</strong>: she checks for orders, prepares special ingredients, and bakes the item. If that path fails (maybe there are no special orders), she tries <strong>daily bread</strong>: she mixes dough, kneads it, and bakes loaves. If even that fails, she simply cleans her workstation.</p>
|
||
</blockquote>
|
||
<h2 id="understanding-choose-selector"><a class="header" href="#understanding-choose-selector">Understanding choose (Selector)</a></h2>
|
||
<p>A <code>choose</code> node tries its children one at a time. As soon as one succeeds, it stops and returns success. If all children fail, it returns failure.</p>
|
||
<pre><code class="language-storybook">choose response {
|
||
HandleUrgentOrder // Try first: handle urgent order
|
||
ServeCustomer // If that fails: serve a customer
|
||
RestockShelves // If that fails: restock
|
||
}
|
||
</code></pre>
|
||
<p>This is like a priority list – the first successful option wins.</p>
|
||
<h2 id="understanding-then-sequence"><a class="header" href="#understanding-then-sequence">Understanding then (Sequence)</a></h2>
|
||
<p>A <code>then</code> node runs its children in order. If any child fails, the whole sequence fails and stops. All children must succeed for the sequence to succeed.</p>
|
||
<pre><code class="language-storybook">then make_sourdough {
|
||
MixDough // Must succeed
|
||
KneadDough // Must succeed
|
||
FirstRise // Must succeed
|
||
ShapeLoaves // Must succeed
|
||
}
|
||
</code></pre>
|
||
<p>If <code>MixDough</code> fails (no flour available), the whole process stops.</p>
|
||
<h2 id="naming-your-nodes"><a class="header" href="#naming-your-nodes">Naming Your Nodes</a></h2>
|
||
<p>Both <code>choose</code> and <code>then</code> accept optional labels:</p>
|
||
<pre><code class="language-storybook">choose daily_priority {
|
||
then morning_baking { ... }
|
||
then afternoon_sales { ... }
|
||
}
|
||
</code></pre>
|
||
<p>Labels are optional but highly recommended. They make your trees readable as narratives and help with debugging. Compare:</p>
|
||
<pre><code class="language-storybook">// Without labels (hard to read)
|
||
choose {
|
||
then { MixDough, BakeLoaves }
|
||
then { ServeCustomer, CollectPayment }
|
||
}
|
||
|
||
// With labels (reads like a story)
|
||
choose priority {
|
||
then baking { MixDough, BakeLoaves }
|
||
then sales { ServeCustomer, CollectPayment }
|
||
}
|
||
</code></pre>
|
||
<h2 id="combining-choose-and-then"><a class="header" href="#combining-choose-and-then">Combining choose and then</a></h2>
|
||
<p>Behavior trees become powerful when you nest selectors and sequences:</p>
|
||
<pre><code class="language-storybook">behavior Jane_PastryRoutine {
|
||
choose pastry_priorities {
|
||
// Highest priority: fill custom cake orders
|
||
then custom_orders {
|
||
ReviewCakeOrder
|
||
DesignDecoration
|
||
BakeAndDecorate
|
||
PackageForPickup
|
||
}
|
||
|
||
// If no orders: prepare display pastries
|
||
then display_pastries {
|
||
RollPastryDough
|
||
PrepareFillings
|
||
AssemblePastries
|
||
ArrangeDisplay
|
||
}
|
||
|
||
// Default: experiment with new recipes
|
||
ExperimentWithFlavors
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Reading this as narrative:</p>
|
||
<blockquote>
|
||
<p>Jane always prioritizes custom cake orders. She reviews the order, designs the decoration, bakes and decorates, then packages it. If there are no orders, she prepares display pastries. If there is nothing else to do, she experiments with new flavors.</p>
|
||
</blockquote>
|
||
<h2 id="actions"><a class="header" href="#actions">Actions</a></h2>
|
||
<p>The leaf nodes in a behavior tree are <strong>actions</strong> – concrete things a character does:</p>
|
||
<pre><code class="language-storybook">MixDough // Simple action
|
||
KneadDough // Simple action
|
||
ServeCustomer // Simple action
|
||
</code></pre>
|
||
<p>Actions are identifiers that the runtime interprets. They represent the actual behaviors executed in your simulation.</p>
|
||
<h2 id="a-complete-example"><a class="header" href="#a-complete-example">A Complete Example</a></h2>
|
||
<p>Here is a behavior tree for the morning rush at the bakery:</p>
|
||
<pre><code class="language-storybook">behavior Bakery_MorningRush {
|
||
---description
|
||
Handles the busy morning rush when customers are
|
||
lining up for fresh bread and pastries.
|
||
---
|
||
|
||
choose morning_priority {
|
||
then serve_waiting_customer {
|
||
GreetCustomer
|
||
TakeOrder
|
||
PackageItems
|
||
CollectPayment
|
||
ThankCustomer
|
||
}
|
||
|
||
then restock_display {
|
||
CheckDisplayLevels
|
||
FetchFromKitchen
|
||
ArrangeOnShelves
|
||
}
|
||
|
||
then quick_bake {
|
||
CheckInventory
|
||
StartQuickBatch
|
||
MonitorOven
|
||
}
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Notice the prose block (<code>---description ... ---</code>) at the top of the behavior. You can document what the behavior does right alongside the code.</p>
|
||
<h2 id="behavior-character-connection"><a class="header" href="#behavior-character-connection">Behavior-Character Connection</a></h2>
|
||
<p>Characters link to behaviors using the <code>uses behaviors</code> clause:</p>
|
||
<pre><code class="language-storybook">character Martha: Human {
|
||
age: 34
|
||
|
||
uses behaviors: [
|
||
{ tree: Martha_BakeRoutine },
|
||
{ tree: HandleEmergency }
|
||
]
|
||
}
|
||
</code></pre>
|
||
<p>This tells the simulation that Martha uses two behavior trees. We will cover advanced behavior linking (priorities, conditions) in <a href="./04-making-characters-act.html">Making Characters Act</a>.</p>
|
||
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
|
||
<p>Your behavior trees so far make decisions between options and run sequences of actions. In <a href="./04-making-characters-act.html">Making Characters Act</a>, you will learn how to add conditions, decorators, and parameters to create truly dynamic behaviors.</p>
|
||
<hr />
|
||
<p><strong>Reference</strong>: For complete behavior tree syntax, see the <a href="../reference/11-behavior-trees.html">Behavior Trees Reference</a>.</p>
|
||
|
||
</main>
|
||
|
||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
<!-- Mobile navigation buttons -->
|
||
<a rel="prev" href="../tutorial/02-creating-characters.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../tutorial/04-making-characters-act.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
|
||
<div style="clear: both"></div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||
<a rel="prev" href="../tutorial/02-creating-characters.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../tutorial/04-making-characters-act.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
</nav>
|
||
|
||
</div>
|
||
|
||
<!-- Livereload script (if served using the cli tool) -->
|
||
<script>
|
||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||
const socket = new WebSocket(wsAddress);
|
||
socket.onmessage = function (event) {
|
||
if (event.data === "reload") {
|
||
socket.close();
|
||
location.reload();
|
||
}
|
||
};
|
||
|
||
window.onbeforeunload = function() {
|
||
socket.close();
|
||
}
|
||
</script>
|
||
|
||
|
||
|
||
<script>
|
||
window.playground_copyable = true;
|
||
</script>
|
||
|
||
|
||
<script src="../elasticlunr.min.js"></script>
|
||
<script src="../mark.min.js"></script>
|
||
<script src="../searcher.js"></script>
|
||
|
||
<script src="../clipboard.min.js"></script>
|
||
<script src="../highlight.js"></script>
|
||
<script src="../book.js"></script>
|
||
|
||
<!-- Custom JS scripts -->
|
||
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|