[{"data":1,"prerenderedAt":2091},["ShallowReactive",2],{"post-/posts/bevy-behave":3},{"id":4,"title":5,"body":6,"created":2082,"description":2083,"extension":2084,"meta":2085,"navigation":688,"path":2086,"seo":2087,"stem":2088,"updated":2089,"__hash__":2090},"posts/2.posts/20250411.Bevy Behave.md","Modelling Agent Behaviour with Bevy Behave",{"type":7,"value":8,"toc":2069},"minimark",[9,15,27,47,50,53,56,59,64,75,78,81,84,87,90,95,98,104,107,110,270,277,349,359,365,453,457,460,463,467,470,473,505,508,786,789,792,796,799,804,807,815,955,958,980,983,994,1001,1048,1051,1402,1405,1408,1413,1416,1591,1594,1598,1601,1605,1608,1611,1615,1618,1621,1624,1629,1632,1731,1748,1752,1755,1762,1766,1769,1772,1777,1780,1973,1976,1984,1987,1991,1994,2002,2005,2020,2024,2065],[10,11,12],"full-screen-sticky",{},[13,14],"behave-demo",{},[16,17,18,19,26],"p",{},"Recently, I've been building a game with ",[20,21,25],"a",{"href":22,"rel":23},"https://bevyengine.org",[24],"nofollow","Bevy"," in my spare time.",[16,28,29,30,35,36],{},"I was looking to add interesting behaviour to the agents in my game, and came across a crate called ",[20,31,34],{"href":32,"rel":33},"https://github.com/RJ/bevy_behave",[24],"Bevy Behave",". ",[37,38,39],"sup",{},[20,40,46],{"href":41,"ariaDescribedBy":42,"dataFootnoteRef":44,"id":45},"#user-content-fn-1",[43],"footnote-label","","user-content-fnref-1","1",[16,48,49],{},"I thought I'd spend a few hours to look into how it works by creating a little interactive demo, and some explanatory writing to go along with it.",[16,51,52],{},"It quickly got out of hand, and the little demo I was planning to make became bigger than I intended.",[16,54,55],{},"It was a fun adventure, and I am glad to have it running interactively inside this very blog post.",[16,57,58],{},"The code examples assume familiarity with Rust and Bevy, but feel free to explore even if you're not — it's still entertaining to play around!",[60,61,63],"h2",{"id":62},"the-demo","The demo",[16,65,66,67],{},"Starting it requires fetching a bunch of WebAssembly (around 12 MB uncompressed), so take care if you're tight on data.",[37,68,69],{},[20,70,74],{"href":71,"ariaDescribedBy":72,"dataFootnoteRef":44,"id":73},"#user-content-fn-2",[43],"user-content-fnref-2","2",[16,76,77],{},"To start the demo, tap this button:",[79,80],"start-demo-button",{},[16,82,83],{},"If everything worked correctly (🤞) you should see a bunch of grey squares in the background.",[16,85,86],{},"This sets the stage for my demo.",[16,88,89],{},"To adjust the opacity of the demo, you can use the slider in the bottom right of the screen, in the toolbar.",[91,92,94],"h3",{"id":93},"spawn-an-agent","Spawn an agent",[16,96,97],{},"Now, let's spawn something in the world:",[99,100],"demo-button",{"id":101,"label":102,"icon":103},"spawn-agent","Spawn agent","lucide:user-plus",[16,105,106],{},"What just happened? A green square appeared in the middle of the screen.\nIf it's not visible, something might be in the way — just scroll a bit and it should pop into view.",[16,108,109],{},"It appeared because the following code was triggered:",[111,112,116],"pre",{"className":113,"code":114,"language":115,"meta":44,"style":44},"language-rust shiki shiki-themes one-dark-pro","// Spawn an entity with:\n//   - the `Agent` marker component,\n//   - a mesh (the rectangle),\n//   - and a colour (green)\ncommands.spawn((\n  Agent,\n  Mesh2d(r_meshes.add(Rectangle::new(0.9, 0.9))),\n  MeshMaterial2d(\n    r_materials.add(Color::from(tw::GREEN_600))\n  ),\n));\n","rust",[117,118,119,128,134,140,146,164,174,216,225,258,264],"code",{"__ignoreMap":44},[120,121,124],"span",{"class":122,"line":123},"line",1,[120,125,127],{"class":126},"sV9Aq","// Spawn an entity with:\n",[120,129,131],{"class":122,"line":130},2,[120,132,133],{"class":126},"//   - the `Agent` marker component,\n",[120,135,137],{"class":122,"line":136},3,[120,138,139],{"class":126},"//   - a mesh (the rectangle),\n",[120,141,143],{"class":122,"line":142},4,[120,144,145],{"class":126},"//   - and a colour (green)\n",[120,147,149,153,157,161],{"class":122,"line":148},5,[120,150,152],{"class":151},"sVyAn","commands",[120,154,156],{"class":155},"sn6KH",".",[120,158,160],{"class":159},"sVbv2","spawn",[120,162,163],{"class":155},"((\n",[120,165,167,171],{"class":122,"line":166},6,[120,168,170],{"class":169},"sU0A5","  Agent",[120,172,173],{"class":155},",\n",[120,175,177,180,183,186,188,191,193,196,199,202,204,208,211,213],{"class":122,"line":176},7,[120,178,179],{"class":159},"  Mesh2d",[120,181,182],{"class":155},"(",[120,184,185],{"class":151},"r_meshes",[120,187,156],{"class":155},[120,189,190],{"class":159},"add",[120,192,182],{"class":155},[120,194,195],{"class":169},"Rectangle",[120,197,198],{"class":155},"::",[120,200,201],{"class":159},"new",[120,203,182],{"class":155},[120,205,207],{"class":206},"sVC51","0.9",[120,209,210],{"class":155},", ",[120,212,207],{"class":206},[120,214,215],{"class":155},"))),\n",[120,217,219,222],{"class":122,"line":218},8,[120,220,221],{"class":159},"  MeshMaterial2d",[120,223,224],{"class":155},"(\n",[120,226,228,231,233,235,237,240,242,245,247,250,252,255],{"class":122,"line":227},9,[120,229,230],{"class":151},"    r_materials",[120,232,156],{"class":155},[120,234,190],{"class":159},[120,236,182],{"class":155},[120,238,239],{"class":169},"Color",[120,241,198],{"class":155},[120,243,244],{"class":159},"from",[120,246,182],{"class":155},[120,248,249],{"class":169},"tw",[120,251,198],{"class":155},[120,253,254],{"class":206},"GREEN_600",[120,256,257],{"class":155},"))\n",[120,259,261],{"class":122,"line":260},10,[120,262,263],{"class":155},"  ),\n",[120,265,267],{"class":122,"line":266},11,[120,268,269],{"class":155},"));\n",[16,271,272,273,276],{},"It appears on the screen because I've set up the ",[117,274,275],{},"Agent"," component like this:",[111,278,280],{"className":113,"code":279,"language":115,"meta":44,"style":44},"// `Agent` is a marker component that requires the\n// `Transform` and `GridCell` components\n#[derive(Component)]\n#[require(\n  Transform(|| Transform::from_xyz(0.0, 0.0, 0.1)),\n  GridCell\n)]\npub struct Agent;\n",[117,281,282,287,292,303,308,325,330,334],{"__ignoreMap":44},[120,283,284],{"class":122,"line":123},[120,285,286],{"class":126},"// `Agent` is a marker component that requires the\n",[120,288,289],{"class":122,"line":130},[120,290,291],{"class":126},"// `Transform` and `GridCell` components\n",[120,293,294,297,300],{"class":122,"line":136},[120,295,296],{"class":155},"#[derive(",[120,298,299],{"class":169},"Component",[120,301,302],{"class":155},")]\n",[120,304,305],{"class":122,"line":142},[120,306,307],{"class":155},"#[require(\n",[120,309,310,313,315,319,322],{"class":122,"line":148},[120,311,312],{"class":169},"  Transform",[120,314,182],{"class":155},[120,316,318],{"class":317},"sjrmR","||",[120,320,321],{"class":169}," Transform",[120,323,324],{"class":155},"::from_xyz(0.0, 0.0, 0.1)),\n",[120,326,327],{"class":122,"line":166},[120,328,329],{"class":169},"  GridCell\n",[120,331,332],{"class":122,"line":176},[120,333,302],{"class":155},[120,335,336,340,343,346],{"class":122,"line":218},[120,337,339],{"class":338},"seHd6","pub",[120,341,342],{"class":338}," struct",[120,344,345],{"class":169}," Agent",[120,347,348],{"class":155},";\n",[16,350,351,354,355,358],{},[117,352,353],{},"Transform"," is a Bevy component that makes the entity have a position in the world.\nFor the agent, I've configured it to have a slightly higher ",[117,356,357],{},"z"," value so that it appears above the grey cells.",[16,360,361,364],{},[117,362,363],{},"GridCell"," is a component I made:",[111,366,368],{"className":113,"code":367,"language":115,"meta":44,"style":44},"// `GridCell` represents a position on the grid.\n// It requires a `Transform` because it needs to\n// have a position in the Bevy world.\n#[derive(Component, Default)]\n#[require(Transform)]\npub struct GridCell {\n  pub x: isize,\n  pub y: isize,\n}\n",[117,369,370,375,380,385,398,407,419,435,448],{"__ignoreMap":44},[120,371,372],{"class":122,"line":123},[120,373,374],{"class":126},"// `GridCell` represents a position on the grid.\n",[120,376,377],{"class":122,"line":130},[120,378,379],{"class":126},"// It requires a `Transform` because it needs to\n",[120,381,382],{"class":122,"line":136},[120,383,384],{"class":126},"// have a position in the Bevy world.\n",[120,386,387,389,391,393,396],{"class":122,"line":142},[120,388,296],{"class":155},[120,390,299],{"class":169},[120,392,210],{"class":155},[120,394,395],{"class":169},"Default",[120,397,302],{"class":155},[120,399,400,403,405],{"class":122,"line":148},[120,401,402],{"class":155},"#[require(",[120,404,353],{"class":169},[120,406,302],{"class":155},[120,408,409,411,413,416],{"class":122,"line":166},[120,410,339],{"class":338},[120,412,342],{"class":338},[120,414,415],{"class":169}," GridCell",[120,417,418],{"class":155}," {\n",[120,420,421,424,427,430,433],{"class":122,"line":176},[120,422,423],{"class":338},"  pub",[120,425,426],{"class":151}," x",[120,428,429],{"class":155},": ",[120,431,432],{"class":169},"isize",[120,434,173],{"class":155},[120,436,437,439,442,444,446],{"class":122,"line":218},[120,438,423],{"class":338},[120,440,441],{"class":151}," y",[120,443,429],{"class":155},[120,445,432],{"class":169},[120,447,173],{"class":155},[120,449,450],{"class":122,"line":227},[120,451,452],{"class":155},"}\n",[91,454,456],{"id":455},"moving-the-agents","Moving the agents",[16,458,459],{},"Now, a green square and a few grey squares might entertain you for about three seconds before you realize you're staring at some motionless coloured boxes.",[16,461,462],{},"Let's make it a little more interesting by making the agent move around:",[99,464],{"id":465,"label":466},"walk-lr-naive","Walk left-right (naive)",[16,468,469],{},"The agent should be moving from left to right endlessly now.",[16,471,472],{},"It started moving because the following component was added to the agent entity:",[111,474,476],{"className":113,"code":475,"language":115,"meta":44,"style":44},"// walk to the left until out of bounds\nWalkInDirectionUntilOutOfBounds::new(-1, 0)\n",[117,477,478,483],{"__ignoreMap":44},[120,479,480],{"class":122,"line":123},[120,481,482],{"class":126},"// walk to the left until out of bounds\n",[120,484,485,488,490,492,495,497,499,502],{"class":122,"line":130},[120,486,487],{"class":169},"WalkInDirectionUntilOutOfBounds",[120,489,198],{"class":155},[120,491,201],{"class":159},[120,493,494],{"class":155},"(-",[120,496,46],{"class":206},[120,498,210],{"class":155},[120,500,501],{"class":206},"0",[120,503,504],{"class":155},")\n",[16,506,507],{},"I process the movement with this system:",[111,509,511],{"className":113,"code":510,"language":115,"meta":44,"style":44},"fn process_left_right_walk(\n  mut q_walkers: Query\u003C(\n      &mut GridCell,\n      &mut WalkInDirectionUntilOutOfBounds\n  ), With\u003CAgent>>,\n  r_grid_bounds: Res\u003CGridBounds>,\n) {\n  // loop over all grid cells & walk components that\n  // are attached to agents\n  for (mut grid_cell, mut walk) in q_walkers.iter_mut() {\n    // determine the next step, and update the agent's\n    // grid cell (make it move there)\n    *grid_cell = walk.step_from(&grid_cell);\n\n    // let's see if the next step will put us out of bounds\n    let next_target = walk.step_from(&grid_cell);\n    if !r_grid_bounds.contains(&next_target) {\n      // it would have, so we reverse (basically just flip\n      // -1 to +1 or vice versa)\n      walk.reverse();\n    }\n  }\n}\n",[117,512,513,523,539,551,560,576,594,599,604,609,645,650,656,683,690,696,719,743,749,755,769,775,781],{"__ignoreMap":44},[120,514,515,518,521],{"class":122,"line":123},[120,516,517],{"class":338},"fn",[120,519,520],{"class":159}," process_left_right_walk",[120,522,224],{"class":155},[120,524,525,528,531,533,536],{"class":122,"line":130},[120,526,527],{"class":338},"  mut",[120,529,530],{"class":151}," q_walkers",[120,532,429],{"class":155},[120,534,535],{"class":169},"Query",[120,537,538],{"class":155},"\u003C(\n",[120,540,541,544,547,549],{"class":122,"line":136},[120,542,543],{"class":155},"      &",[120,545,546],{"class":338},"mut",[120,548,415],{"class":169},[120,550,173],{"class":155},[120,552,553,555,557],{"class":122,"line":142},[120,554,543],{"class":155},[120,556,546],{"class":338},[120,558,559],{"class":169}," WalkInDirectionUntilOutOfBounds\n",[120,561,562,565,568,571,573],{"class":122,"line":148},[120,563,564],{"class":155},"  ), ",[120,566,567],{"class":169},"With",[120,569,570],{"class":155},"\u003C",[120,572,275],{"class":169},[120,574,575],{"class":155},">>,\n",[120,577,578,581,583,586,588,591],{"class":122,"line":166},[120,579,580],{"class":151},"  r_grid_bounds",[120,582,429],{"class":155},[120,584,585],{"class":169},"Res",[120,587,570],{"class":155},[120,589,590],{"class":169},"GridBounds",[120,592,593],{"class":155},">,\n",[120,595,596],{"class":122,"line":176},[120,597,598],{"class":155},") {\n",[120,600,601],{"class":122,"line":218},[120,602,603],{"class":126},"  // loop over all grid cells & walk components that\n",[120,605,606],{"class":122,"line":227},[120,607,608],{"class":126},"  // are attached to agents\n",[120,610,611,614,617,619,622,624,626,629,632,635,637,639,642],{"class":122,"line":260},[120,612,613],{"class":338},"  for",[120,615,616],{"class":155}," (",[120,618,546],{"class":338},[120,620,621],{"class":151}," grid_cell",[120,623,210],{"class":155},[120,625,546],{"class":338},[120,627,628],{"class":151}," walk",[120,630,631],{"class":155},") ",[120,633,634],{"class":338},"in",[120,636,530],{"class":151},[120,638,156],{"class":155},[120,640,641],{"class":159},"iter_mut",[120,643,644],{"class":155},"() {\n",[120,646,647],{"class":122,"line":266},[120,648,649],{"class":126},"    // determine the next step, and update the agent's\n",[120,651,653],{"class":122,"line":652},12,[120,654,655],{"class":126},"    // grid cell (make it move there)\n",[120,657,659,662,665,668,670,672,675,678,680],{"class":122,"line":658},13,[120,660,661],{"class":155},"    *",[120,663,664],{"class":151},"grid_cell",[120,666,667],{"class":317}," =",[120,669,628],{"class":151},[120,671,156],{"class":155},[120,673,674],{"class":159},"step_from",[120,676,677],{"class":155},"(&",[120,679,664],{"class":151},[120,681,682],{"class":155},");\n",[120,684,686],{"class":122,"line":685},14,[120,687,689],{"emptyLinePlaceholder":688},true,"\n",[120,691,693],{"class":122,"line":692},15,[120,694,695],{"class":126},"    // let's see if the next step will put us out of bounds\n",[120,697,699,702,705,707,709,711,713,715,717],{"class":122,"line":698},16,[120,700,701],{"class":338},"    let",[120,703,704],{"class":151}," next_target",[120,706,667],{"class":317},[120,708,628],{"class":151},[120,710,156],{"class":155},[120,712,674],{"class":159},[120,714,677],{"class":155},[120,716,664],{"class":151},[120,718,682],{"class":155},[120,720,722,725,728,731,733,736,738,741],{"class":122,"line":721},17,[120,723,724],{"class":338},"    if",[120,726,727],{"class":317}," !",[120,729,730],{"class":151},"r_grid_bounds",[120,732,156],{"class":155},[120,734,735],{"class":159},"contains",[120,737,677],{"class":155},[120,739,740],{"class":151},"next_target",[120,742,598],{"class":155},[120,744,746],{"class":122,"line":745},18,[120,747,748],{"class":126},"      // it would have, so we reverse (basically just flip\n",[120,750,752],{"class":122,"line":751},19,[120,753,754],{"class":126},"      // -1 to +1 or vice versa)\n",[120,756,758,761,763,766],{"class":122,"line":757},20,[120,759,760],{"class":151},"      walk",[120,762,156],{"class":155},[120,764,765],{"class":159},"reverse",[120,767,768],{"class":155},"();\n",[120,770,772],{"class":122,"line":771},21,[120,773,774],{"class":155},"    }\n",[120,776,778],{"class":122,"line":777},22,[120,779,780],{"class":155},"  }\n",[120,782,784],{"class":122,"line":783},23,[120,785,452],{"class":155},[16,787,788],{},"All in all, this works, but try implementing anything complex and you'll quickly find yourself wrestling with spaghetti code.",[16,790,791],{},"At this point, I should mention you can spawn additional agents either by tapping the spawn button again or by using the button in the toolbar (below the opacity slider).",[91,793,795],{"id":794},"moving-the-agent-with-bevy-behave","Moving the agent with Bevy Behave",[16,797,798],{},"Now, before doing more complex behaviour, let's first do the same behaviour with Bevy Behave:",[99,800],{"id":801,"label":802,"icon":803},"walk-lr","Walk left-right (behave)","lucide:arrow-left-right",[16,805,806],{},"Wow, other than maybe switching direction, nothing happened!",[16,808,809,810,814],{},"With Bevy Behave, I implemented the behaviour with a ",[811,812,813],"em",{},"behaviour tree",":",[111,816,818],{"className":113,"code":817,"language":115,"meta":44,"style":44},"let tree = behave! {\n  // repeat forever\n  Behave::Forever => {\n    Behave::Sequence => {\n      // walk left until success\n      Behave::spawn((\n        WalkInDirectionUntilOutOfBounds::new(-1, 0),\n      )),\n      // walk right until success\n      Behave::spawn((\n        WalkInDirectionUntilOutOfBounds::new(1, 0),\n      )),\n    }\n  }\n}\n",[117,819,820,835,840,853,865,870,881,901,906,911,921,939,943,947,951],{"__ignoreMap":44},[120,821,822,825,828,830,833],{"class":122,"line":123},[120,823,824],{"class":338},"let",[120,826,827],{"class":151}," tree",[120,829,667],{"class":317},[120,831,832],{"class":159}," behave!",[120,834,418],{"class":155},[120,836,837],{"class":122,"line":130},[120,838,839],{"class":126},"  // repeat forever\n",[120,841,842,845,847,850],{"class":122,"line":136},[120,843,844],{"class":169},"  Behave",[120,846,198],{"class":155},[120,848,849],{"class":169},"Forever",[120,851,852],{"class":155}," => {\n",[120,854,855,858,860,863],{"class":122,"line":142},[120,856,857],{"class":169},"    Behave",[120,859,198],{"class":155},[120,861,862],{"class":169},"Sequence",[120,864,852],{"class":155},[120,866,867],{"class":122,"line":148},[120,868,869],{"class":126},"      // walk left until success\n",[120,871,872,875,877,879],{"class":122,"line":166},[120,873,874],{"class":169},"      Behave",[120,876,198],{"class":155},[120,878,160],{"class":159},[120,880,163],{"class":155},[120,882,883,886,888,890,892,894,896,898],{"class":122,"line":176},[120,884,885],{"class":169},"        WalkInDirectionUntilOutOfBounds",[120,887,198],{"class":155},[120,889,201],{"class":159},[120,891,494],{"class":155},[120,893,46],{"class":206},[120,895,210],{"class":155},[120,897,501],{"class":206},[120,899,900],{"class":155},"),\n",[120,902,903],{"class":122,"line":218},[120,904,905],{"class":155},"      )),\n",[120,907,908],{"class":122,"line":227},[120,909,910],{"class":126},"      // walk right until success\n",[120,912,913,915,917,919],{"class":122,"line":260},[120,914,874],{"class":169},[120,916,198],{"class":155},[120,918,160],{"class":159},[120,920,163],{"class":155},[120,922,923,925,927,929,931,933,935,937],{"class":122,"line":266},[120,924,885],{"class":169},[120,926,198],{"class":155},[120,928,201],{"class":159},[120,930,182],{"class":155},[120,932,46],{"class":206},[120,934,210],{"class":155},[120,936,501],{"class":206},[120,938,900],{"class":155},[120,940,941],{"class":122,"line":652},[120,942,905],{"class":155},[120,944,945],{"class":122,"line":658},[120,946,774],{"class":155},[120,948,949],{"class":122,"line":685},[120,950,780],{"class":155},[120,952,953],{"class":122,"line":692},[120,954,452],{"class":155},[16,956,957],{},"Behave trees use control flow nodes:",[959,960,961,968,974],"ul",{},[962,963,964,967],"li",{},[117,965,966],{},"Behave::Forever"," makes its child node loop forever.",[962,969,970,973],{},[117,971,972],{},"Behave::Sequence"," processes the steps inside it in sequence.",[962,975,976,979],{},[117,977,978],{},"Behave::spawn"," spawns a behaviour entity with the specified components, and waits until it reports a successful or unsuccessful result.",[16,981,982],{},"So, what the tree does is:",[959,984,985,988,991],{},[962,986,987],{},"Walk left until success is reported.",[962,989,990],{},"Walk right until success is reported.",[962,992,993],{},"Repeat.",[16,995,996,997,1000],{},"An important difference between Bevy Behave and my previous \"naive\" solution is that the behaviour components don't belong to the agent's entity—they're attached to a completely ",[811,998,999],{},"separate"," behaviour entity.\nSo, to spawn the tree you add it as a child entity of the agent that you want it to control:",[111,1002,1004],{"className":113,"code":1003,"language":115,"meta":44,"style":44},"commands\n  .spawn(BehaveTree::new(tree))\n  .set_parent(agent_entity);\n",[117,1005,1006,1011,1034],{"__ignoreMap":44},[120,1007,1008],{"class":122,"line":123},[120,1009,1010],{"class":151},"commands\n",[120,1012,1013,1016,1018,1020,1023,1025,1027,1029,1032],{"class":122,"line":130},[120,1014,1015],{"class":155},"  .",[120,1017,160],{"class":159},[120,1019,182],{"class":155},[120,1021,1022],{"class":169},"BehaveTree",[120,1024,198],{"class":155},[120,1026,201],{"class":159},[120,1028,182],{"class":155},[120,1030,1031],{"class":151},"tree",[120,1033,257],{"class":155},[120,1035,1036,1038,1041,1043,1046],{"class":122,"line":136},[120,1037,1015],{"class":155},[120,1039,1040],{"class":159},"set_parent",[120,1042,182],{"class":155},[120,1044,1045],{"class":151},"agent_entity",[120,1047,682],{"class":155},[16,1049,1050],{},"The system that processes the movement also changes slightly:",[111,1052,1054],{"className":113,"code":1053,"language":115,"meta":44,"style":44},"\nfn process_walk_in_direction(\n  q_walks: Query\u003C(\n    &WalkInDirectionUntilOutOfBounds,\n    &BehaveCtx,\n  )>,\n  mut q_agent_cells: Query\u003C&mut GridCell, With\u003CAgent>>,\n  r_bounds: Res\u003CGridBounds>,\n  mut commands: Commands,\n) {\n  for (walk, ctx) in q_walks.iter() {\n    // retrieve the target entity from the `BehaveCtx`\n    // (usually the parent of the tree)\n    let Ok(mut agent_cell) = q_agent_cells\n      .get_mut(ctx.target_entity()) else {\n      // skip if entity is not found\n      continue;\n    };\n\n    // make the agent take the step\n    *agent_cell = walk.step_from(&agent_cell);\n\n    // see if we can take another step next time\n    let next_target = walk.step_from(&agent_cell);\n    if !r_bounds.contains(&next_target) {\n      // the next step would've put the agent out of bounds,\n      // so we report that this behaviour step was\n      // successfully completed\n      commands.trigger(ctx.success());\n    }\n  }\n}\n",[117,1055,1056,1060,1069,1080,1089,1098,1103,1131,1146,1160,1164,1192,1197,1202,1224,1249,1254,1261,1266,1270,1275,1296,1300,1305,1326,1346,1352,1358,1364,1387,1392,1397],{"__ignoreMap":44},[120,1057,1058],{"class":122,"line":123},[120,1059,689],{"emptyLinePlaceholder":688},[120,1061,1062,1064,1067],{"class":122,"line":130},[120,1063,517],{"class":338},[120,1065,1066],{"class":159}," process_walk_in_direction",[120,1068,224],{"class":155},[120,1070,1071,1074,1076,1078],{"class":122,"line":136},[120,1072,1073],{"class":151},"  q_walks",[120,1075,429],{"class":155},[120,1077,535],{"class":169},[120,1079,538],{"class":155},[120,1081,1082,1085,1087],{"class":122,"line":142},[120,1083,1084],{"class":155},"    &",[120,1086,487],{"class":169},[120,1088,173],{"class":155},[120,1090,1091,1093,1096],{"class":122,"line":148},[120,1092,1084],{"class":155},[120,1094,1095],{"class":169},"BehaveCtx",[120,1097,173],{"class":155},[120,1099,1100],{"class":122,"line":166},[120,1101,1102],{"class":155},"  )>,\n",[120,1104,1105,1107,1110,1112,1114,1117,1119,1121,1123,1125,1127,1129],{"class":122,"line":176},[120,1106,527],{"class":338},[120,1108,1109],{"class":151}," q_agent_cells",[120,1111,429],{"class":155},[120,1113,535],{"class":169},[120,1115,1116],{"class":155},"\u003C&",[120,1118,546],{"class":338},[120,1120,415],{"class":169},[120,1122,210],{"class":155},[120,1124,567],{"class":169},[120,1126,570],{"class":155},[120,1128,275],{"class":169},[120,1130,575],{"class":155},[120,1132,1133,1136,1138,1140,1142,1144],{"class":122,"line":218},[120,1134,1135],{"class":151},"  r_bounds",[120,1137,429],{"class":155},[120,1139,585],{"class":169},[120,1141,570],{"class":155},[120,1143,590],{"class":169},[120,1145,593],{"class":155},[120,1147,1148,1150,1153,1155,1158],{"class":122,"line":227},[120,1149,527],{"class":338},[120,1151,1152],{"class":151}," commands",[120,1154,429],{"class":155},[120,1156,1157],{"class":169},"Commands",[120,1159,173],{"class":155},[120,1161,1162],{"class":122,"line":260},[120,1163,598],{"class":155},[120,1165,1166,1168,1170,1173,1175,1178,1180,1182,1185,1187,1190],{"class":122,"line":266},[120,1167,613],{"class":338},[120,1169,616],{"class":155},[120,1171,1172],{"class":151},"walk",[120,1174,210],{"class":155},[120,1176,1177],{"class":151},"ctx",[120,1179,631],{"class":155},[120,1181,634],{"class":338},[120,1183,1184],{"class":151}," q_walks",[120,1186,156],{"class":155},[120,1188,1189],{"class":159},"iter",[120,1191,644],{"class":155},[120,1193,1194],{"class":122,"line":652},[120,1195,1196],{"class":126},"    // retrieve the target entity from the `BehaveCtx`\n",[120,1198,1199],{"class":122,"line":658},[120,1200,1201],{"class":126},"    // (usually the parent of the tree)\n",[120,1203,1204,1206,1209,1211,1213,1216,1218,1221],{"class":122,"line":685},[120,1205,701],{"class":338},[120,1207,1208],{"class":169}," Ok",[120,1210,182],{"class":155},[120,1212,546],{"class":338},[120,1214,1215],{"class":151}," agent_cell",[120,1217,631],{"class":155},[120,1219,1220],{"class":317},"=",[120,1222,1223],{"class":151}," q_agent_cells\n",[120,1225,1226,1229,1232,1234,1236,1238,1241,1244,1247],{"class":122,"line":692},[120,1227,1228],{"class":155},"      .",[120,1230,1231],{"class":159},"get_mut",[120,1233,182],{"class":155},[120,1235,1177],{"class":151},[120,1237,156],{"class":155},[120,1239,1240],{"class":159},"target_entity",[120,1242,1243],{"class":155},"()) ",[120,1245,1246],{"class":338},"else",[120,1248,418],{"class":155},[120,1250,1251],{"class":122,"line":698},[120,1252,1253],{"class":126},"      // skip if entity is not found\n",[120,1255,1256,1259],{"class":122,"line":721},[120,1257,1258],{"class":338},"      continue",[120,1260,348],{"class":155},[120,1262,1263],{"class":122,"line":745},[120,1264,1265],{"class":155},"    };\n",[120,1267,1268],{"class":122,"line":751},[120,1269,689],{"emptyLinePlaceholder":688},[120,1271,1272],{"class":122,"line":757},[120,1273,1274],{"class":126},"    // make the agent take the step\n",[120,1276,1277,1279,1282,1284,1286,1288,1290,1292,1294],{"class":122,"line":771},[120,1278,661],{"class":155},[120,1280,1281],{"class":151},"agent_cell",[120,1283,667],{"class":317},[120,1285,628],{"class":151},[120,1287,156],{"class":155},[120,1289,674],{"class":159},[120,1291,677],{"class":155},[120,1293,1281],{"class":151},[120,1295,682],{"class":155},[120,1297,1298],{"class":122,"line":777},[120,1299,689],{"emptyLinePlaceholder":688},[120,1301,1302],{"class":122,"line":783},[120,1303,1304],{"class":126},"    // see if we can take another step next time\n",[120,1306,1308,1310,1312,1314,1316,1318,1320,1322,1324],{"class":122,"line":1307},24,[120,1309,701],{"class":338},[120,1311,704],{"class":151},[120,1313,667],{"class":317},[120,1315,628],{"class":151},[120,1317,156],{"class":155},[120,1319,674],{"class":159},[120,1321,677],{"class":155},[120,1323,1281],{"class":151},[120,1325,682],{"class":155},[120,1327,1329,1331,1333,1336,1338,1340,1342,1344],{"class":122,"line":1328},25,[120,1330,724],{"class":338},[120,1332,727],{"class":317},[120,1334,1335],{"class":151},"r_bounds",[120,1337,156],{"class":155},[120,1339,735],{"class":159},[120,1341,677],{"class":155},[120,1343,740],{"class":151},[120,1345,598],{"class":155},[120,1347,1349],{"class":122,"line":1348},26,[120,1350,1351],{"class":126},"      // the next step would've put the agent out of bounds,\n",[120,1353,1355],{"class":122,"line":1354},27,[120,1356,1357],{"class":126},"      // so we report that this behaviour step was\n",[120,1359,1361],{"class":122,"line":1360},28,[120,1362,1363],{"class":126},"      // successfully completed\n",[120,1365,1367,1370,1372,1375,1377,1379,1381,1384],{"class":122,"line":1366},29,[120,1368,1369],{"class":151},"      commands",[120,1371,156],{"class":155},[120,1373,1374],{"class":159},"trigger",[120,1376,182],{"class":155},[120,1378,1177],{"class":151},[120,1380,156],{"class":155},[120,1382,1383],{"class":159},"success",[120,1385,1386],{"class":155},"());\n",[120,1388,1390],{"class":122,"line":1389},30,[120,1391,774],{"class":155},[120,1393,1395],{"class":122,"line":1394},31,[120,1396,780],{"class":155},[120,1398,1400],{"class":122,"line":1399},32,[120,1401,452],{"class":155},[16,1403,1404],{},"The beauty here is that system logic can focus solely on its specific task, then hand control back to the behaviour tree once it's finished.",[16,1406,1407],{},"With this setup, we can easily implement slightly more interesting behaviour:",[99,1409],{"id":1410,"label":1411,"icon":1412},"walk-clockwise","Walk clockwise","lucide:repeat-2",[16,1414,1415],{},"The agents now use this behaviour tree:",[111,1417,1419],{"className":113,"code":1418,"language":115,"meta":44,"style":44},"// repeat forever\nBehave::Forever => {\n  Behave::Sequence => {\n    Behave::spawn(\n      // walk left until success\n      WalkInDirectionUntilOutOfBounds((-1, 0)),\n    ),\n    Behave::spawn(\n      // walk up until success\n      WalkInDirectionUntilOutOfBounds((0, 1)),\n    ),\n    Behave::spawn(\n      // walk right until success\n      WalkInDirectionUntilOutOfBounds((1, 0)),\n    ),\n    Behave::spawn(\n      // walk down until success\n      WalkInDirectionUntilOutOfBounds((0, -1)),\n    ),\n  }\n}\n",[117,1420,1421,1426,1437,1447,1457,1461,1478,1483,1493,1498,1513,1517,1527,1531,1545,1549,1559,1564,1579,1583,1587],{"__ignoreMap":44},[120,1422,1423],{"class":122,"line":123},[120,1424,1425],{"class":126},"// repeat forever\n",[120,1427,1428,1431,1433,1435],{"class":122,"line":130},[120,1429,1430],{"class":169},"Behave",[120,1432,198],{"class":155},[120,1434,849],{"class":169},[120,1436,852],{"class":155},[120,1438,1439,1441,1443,1445],{"class":122,"line":136},[120,1440,844],{"class":169},[120,1442,198],{"class":155},[120,1444,862],{"class":169},[120,1446,852],{"class":155},[120,1448,1449,1451,1453,1455],{"class":122,"line":142},[120,1450,857],{"class":169},[120,1452,198],{"class":155},[120,1454,160],{"class":159},[120,1456,224],{"class":155},[120,1458,1459],{"class":122,"line":148},[120,1460,869],{"class":126},[120,1462,1463,1466,1469,1471,1473,1475],{"class":122,"line":166},[120,1464,1465],{"class":159},"      WalkInDirectionUntilOutOfBounds",[120,1467,1468],{"class":155},"((-",[120,1470,46],{"class":206},[120,1472,210],{"class":155},[120,1474,501],{"class":206},[120,1476,1477],{"class":155},")),\n",[120,1479,1480],{"class":122,"line":176},[120,1481,1482],{"class":155},"    ),\n",[120,1484,1485,1487,1489,1491],{"class":122,"line":218},[120,1486,857],{"class":169},[120,1488,198],{"class":155},[120,1490,160],{"class":159},[120,1492,224],{"class":155},[120,1494,1495],{"class":122,"line":227},[120,1496,1497],{"class":126},"      // walk up until success\n",[120,1499,1500,1502,1505,1507,1509,1511],{"class":122,"line":260},[120,1501,1465],{"class":159},[120,1503,1504],{"class":155},"((",[120,1506,501],{"class":206},[120,1508,210],{"class":155},[120,1510,46],{"class":206},[120,1512,1477],{"class":155},[120,1514,1515],{"class":122,"line":266},[120,1516,1482],{"class":155},[120,1518,1519,1521,1523,1525],{"class":122,"line":652},[120,1520,857],{"class":169},[120,1522,198],{"class":155},[120,1524,160],{"class":159},[120,1526,224],{"class":155},[120,1528,1529],{"class":122,"line":658},[120,1530,910],{"class":126},[120,1532,1533,1535,1537,1539,1541,1543],{"class":122,"line":685},[120,1534,1465],{"class":159},[120,1536,1504],{"class":155},[120,1538,46],{"class":206},[120,1540,210],{"class":155},[120,1542,501],{"class":206},[120,1544,1477],{"class":155},[120,1546,1547],{"class":122,"line":692},[120,1548,1482],{"class":155},[120,1550,1551,1553,1555,1557],{"class":122,"line":698},[120,1552,857],{"class":169},[120,1554,198],{"class":155},[120,1556,160],{"class":159},[120,1558,224],{"class":155},[120,1560,1561],{"class":122,"line":721},[120,1562,1563],{"class":126},"      // walk down until success\n",[120,1565,1566,1568,1570,1572,1575,1577],{"class":122,"line":745},[120,1567,1465],{"class":159},[120,1569,1504],{"class":155},[120,1571,501],{"class":206},[120,1573,1574],{"class":155},", -",[120,1576,46],{"class":206},[120,1578,1477],{"class":155},[120,1580,1581],{"class":122,"line":751},[120,1582,1482],{"class":155},[120,1584,1585],{"class":122,"line":757},[120,1586,780],{"class":155},[120,1588,1589],{"class":122,"line":771},[120,1590,452],{"class":155},[16,1592,1593],{},"As you can see, this makes the agents move around the grid's boundaries.",[91,1595,1597],{"id":1596},"a-more-challenging-environment","A more challenging environment",[16,1599,1600],{},"So far, our agent doesn't really have a goal, apart from moving.\nLet's make it a little more challenging by adding a hunger / eating system:",[99,1602],{"id":1603,"label":1604},"enable-hunger","Enable hunger & eating",[16,1606,1607],{},"Now, an agent will \"disappear\" when their hunger indicator runs out.",[16,1609,1610],{},"Quick! Let's spawn some fruit to sustain them:",[99,1612],{"id":1613,"label":1614},"spawn-fruit-spawner","Make fruit appear",[16,1616,1617],{},"Now, you'll see little red squares appearing that represent tasty fruit for the agents.\nWhen an agent is on the same cell as a piece of fruit, they eat the fruit.",[16,1619,1620],{},"Unless the agents are lucky and fruit keeps spawning on their predetermined path, they will still starve at some point. 😢",[16,1622,1623],{},"Let's give them smarter behaviour that makes them look for fruit and move to it:",[99,1625],{"id":1626,"label":1627,"icon":1628},"move-to-fruit","Move to fruit","lucide:cherry",[16,1630,1631],{},"The behaviour tree now looks like this:",[111,1633,1635],{"className":113,"code":1634,"language":115,"meta":44,"style":44},"Behave::Forever => {\n  Behave::Sequence => {\n    Behave::spawn(\n      // find fruit target\n      FindTarget::new(TargetKind::Fruit),\n    ),\n    Behave::spawn(\n      // go to target\n      GoToTarget,\n    ),\n  }\n}\n",[117,1636,1637,1647,1657,1667,1672,1693,1697,1707,1712,1719,1723,1727],{"__ignoreMap":44},[120,1638,1639,1641,1643,1645],{"class":122,"line":123},[120,1640,1430],{"class":169},[120,1642,198],{"class":155},[120,1644,849],{"class":169},[120,1646,852],{"class":155},[120,1648,1649,1651,1653,1655],{"class":122,"line":130},[120,1650,844],{"class":169},[120,1652,198],{"class":155},[120,1654,862],{"class":169},[120,1656,852],{"class":155},[120,1658,1659,1661,1663,1665],{"class":122,"line":136},[120,1660,857],{"class":169},[120,1662,198],{"class":155},[120,1664,160],{"class":159},[120,1666,224],{"class":155},[120,1668,1669],{"class":122,"line":142},[120,1670,1671],{"class":126},"      // find fruit target\n",[120,1673,1674,1677,1679,1681,1683,1686,1688,1691],{"class":122,"line":148},[120,1675,1676],{"class":169},"      FindTarget",[120,1678,198],{"class":155},[120,1680,201],{"class":159},[120,1682,182],{"class":155},[120,1684,1685],{"class":169},"TargetKind",[120,1687,198],{"class":155},[120,1689,1690],{"class":169},"Fruit",[120,1692,900],{"class":155},[120,1694,1695],{"class":122,"line":166},[120,1696,1482],{"class":155},[120,1698,1699,1701,1703,1705],{"class":122,"line":176},[120,1700,857],{"class":169},[120,1702,198],{"class":155},[120,1704,160],{"class":159},[120,1706,224],{"class":155},[120,1708,1709],{"class":122,"line":218},[120,1710,1711],{"class":126},"      // go to target\n",[120,1713,1714,1717],{"class":122,"line":227},[120,1715,1716],{"class":169},"      GoToTarget",[120,1718,173],{"class":155},[120,1720,1721],{"class":122,"line":260},[120,1722,1482],{"class":155},[120,1724,1725],{"class":122,"line":266},[120,1726,780],{"class":155},[120,1728,1729],{"class":122,"line":652},[120,1730,452],{"class":155},[16,1732,1733,1734,1737,1738,1741,1742,1747],{},"Of course, these behaviour steps (",[117,1735,1736],{},"FindTarget"," and ",[117,1739,1740],{},"GoToTarget",") also need to be processed in systems, but I won't bother you with those details here.\nInstead, take a look at ",[20,1743,1746],{"href":1744,"rel":1745},"https://github.com/HanKruiger/behave-blog-demo/blob/main/src/behaviours/target_finding.rs",[24],"the source code"," if you're interested.",[91,1749,1751],{"id":1750},"finding-coins-while-not-hungry","Finding coins while not hungry",[16,1753,1754],{},"This is very cool and all, but you can do much more with Bevy Behave.",[16,1756,1757,1758,1761],{},"Let's make the environment more interesting by adding ",[811,1759,1760],{},"coins","! 🤑",[99,1763],{"id":1764,"label":1765},"spawn-coin-spawner","Make coins appear",[16,1767,1768],{},"Now, when an agent is in the same cell as a coin, they fill up their point indicator.",[16,1770,1771],{},"With this environment, we can make the agents find coins while they're not really hungry, and find fruit otherwise:",[99,1773],{"id":1774,"label":1775,"icon":1776},"move-hunger-based","Hunger-based targeting","lucide:brain",[16,1778,1779],{},"The behaviour tree looks like this:",[111,1781,1783],{"className":113,"code":1782,"language":115,"meta":44,"style":44},"Behave::Forever => {\n  Behave::Sequence => {\n    Behave::IfThen => {\n      // this check reports success if energy\n      // is below 40%\n      Behave::trigger(HungerCheck(0.4)),\n\n      // spawned if hunger check succeeded\n      Behave::spawn(\n        FindTarget::new(TargetKind::Fruit),\n      ),\n\n      // spawned if hunger check failed\n      Behave::spawn(\n        FindTarget::new(TargetKind::Coins),\n      ),\n    },\n\n    // go to the target we just found\n    Behave::spawn(\n      GoToTarget,\n    ),\n  }\n}\n",[117,1784,1785,1795,1805,1816,1821,1826,1846,1850,1855,1865,1884,1889,1893,1898,1908,1927,1931,1936,1940,1945,1955,1961,1965,1969],{"__ignoreMap":44},[120,1786,1787,1789,1791,1793],{"class":122,"line":123},[120,1788,1430],{"class":169},[120,1790,198],{"class":155},[120,1792,849],{"class":169},[120,1794,852],{"class":155},[120,1796,1797,1799,1801,1803],{"class":122,"line":130},[120,1798,844],{"class":169},[120,1800,198],{"class":155},[120,1802,862],{"class":169},[120,1804,852],{"class":155},[120,1806,1807,1809,1811,1814],{"class":122,"line":136},[120,1808,857],{"class":169},[120,1810,198],{"class":155},[120,1812,1813],{"class":169},"IfThen",[120,1815,852],{"class":155},[120,1817,1818],{"class":122,"line":142},[120,1819,1820],{"class":126},"      // this check reports success if energy\n",[120,1822,1823],{"class":122,"line":148},[120,1824,1825],{"class":126},"      // is below 40%\n",[120,1827,1828,1830,1832,1834,1836,1839,1841,1844],{"class":122,"line":166},[120,1829,874],{"class":169},[120,1831,198],{"class":155},[120,1833,1374],{"class":159},[120,1835,182],{"class":155},[120,1837,1838],{"class":159},"HungerCheck",[120,1840,182],{"class":155},[120,1842,1843],{"class":206},"0.4",[120,1845,1477],{"class":155},[120,1847,1848],{"class":122,"line":176},[120,1849,689],{"emptyLinePlaceholder":688},[120,1851,1852],{"class":122,"line":218},[120,1853,1854],{"class":126},"      // spawned if hunger check succeeded\n",[120,1856,1857,1859,1861,1863],{"class":122,"line":227},[120,1858,874],{"class":169},[120,1860,198],{"class":155},[120,1862,160],{"class":159},[120,1864,224],{"class":155},[120,1866,1867,1870,1872,1874,1876,1878,1880,1882],{"class":122,"line":260},[120,1868,1869],{"class":169},"        FindTarget",[120,1871,198],{"class":155},[120,1873,201],{"class":159},[120,1875,182],{"class":155},[120,1877,1685],{"class":169},[120,1879,198],{"class":155},[120,1881,1690],{"class":169},[120,1883,900],{"class":155},[120,1885,1886],{"class":122,"line":266},[120,1887,1888],{"class":155},"      ),\n",[120,1890,1891],{"class":122,"line":652},[120,1892,689],{"emptyLinePlaceholder":688},[120,1894,1895],{"class":122,"line":658},[120,1896,1897],{"class":126},"      // spawned if hunger check failed\n",[120,1899,1900,1902,1904,1906],{"class":122,"line":685},[120,1901,874],{"class":169},[120,1903,198],{"class":155},[120,1905,160],{"class":159},[120,1907,224],{"class":155},[120,1909,1910,1912,1914,1916,1918,1920,1922,1925],{"class":122,"line":692},[120,1911,1869],{"class":169},[120,1913,198],{"class":155},[120,1915,201],{"class":159},[120,1917,182],{"class":155},[120,1919,1685],{"class":169},[120,1921,198],{"class":155},[120,1923,1924],{"class":169},"Coins",[120,1926,900],{"class":155},[120,1928,1929],{"class":122,"line":698},[120,1930,1888],{"class":155},[120,1932,1933],{"class":122,"line":721},[120,1934,1935],{"class":155},"    },\n",[120,1937,1938],{"class":122,"line":745},[120,1939,689],{"emptyLinePlaceholder":688},[120,1941,1942],{"class":122,"line":751},[120,1943,1944],{"class":126},"    // go to the target we just found\n",[120,1946,1947,1949,1951,1953],{"class":122,"line":757},[120,1948,857],{"class":169},[120,1950,198],{"class":155},[120,1952,160],{"class":159},[120,1954,224],{"class":155},[120,1956,1957,1959],{"class":122,"line":771},[120,1958,1716],{"class":169},[120,1960,173],{"class":155},[120,1962,1963],{"class":122,"line":777},[120,1964,1482],{"class":155},[120,1966,1967],{"class":122,"line":783},[120,1968,780],{"class":155},[120,1970,1971],{"class":122,"line":1307},[120,1972,452],{"class":155},[16,1974,1975],{},"I've introduced a new control flow node:",[959,1977,1978],{},[962,1979,1980,1983],{},[117,1981,1982],{},"Behave::IfThen"," runs the first child. If that child reports success, it runs the second child. Otherwise it runs the third child.",[16,1985,1986],{},"With this behaviour tree, the agent first checks if they're hungry.\nIf they are, they'll go look for food. If not, they'll look for coins instead!",[60,1988,1990],{"id":1989},"conclusion","Conclusion",[16,1992,1993],{},"We've only scratched the surface of what's possible.",[16,1995,1996,1997,1737,1999,2001],{},"I really like how you can define small building blocks like ",[117,1998,1736],{},[117,2000,1740],{},", and compose them into complex behaviour trees.",[16,2003,2004],{},"I look forward to building more behaviours with Bevy Behave!",[16,2006,2007,2008,2013,2014,2019],{},"You can follow me on ",[20,2009,2012],{"href":2010,"rel":2011},"https://mastodon.nl/@hankruiger",[24],"Mastodon"," or ",[20,2015,2018],{"href":2016,"rel":2017},"https://bsky.app/profile/hankruiger.com",[24],"Bluesky"," if you want to stay updated on my game development journey.",[91,2021,2023],{"id":2022},"footnotes","Footnotes",[2025,2026,2028,2032],"section",{"className":2027,"dataFootnotes":44},[2022],[60,2029,2023],{"className":2030,"id":43},[2031],"sr-only",[2033,2034,2035,2052],"ol",{},[962,2036,2038,2039,2044,2045],{"id":2037},"user-content-fn-1","Fun fact: crate maintainer ",[20,2040,2043],{"href":2041,"rel":2042},"https://www.metabrew.com",[24],"RJ"," founded Audioscrobbler, which evolved into Last.fm. ",[20,2046,2051],{"href":2047,"ariaLabel":2048,"className":2049,"dataFootnoteBackref":44},"#user-content-fnref-1","Back to reference 1",[2050],"data-footnote-backref","↩",[962,2053,2055,2056,2059,2060],{"id":2054},"user-content-fn-2","This is expected to be improved soon, as Bevy 0.16 will support ",[117,2057,2058],{},"no-std",", making it possible to ship much smaller binaries. ",[20,2061,2051],{"href":2062,"ariaLabel":2063,"className":2064,"dataFootnoteBackref":44},"#user-content-fnref-2","Back to reference 2",[2050],[2066,2067,2068],"style",{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}",{"title":44,"searchDepth":130,"depth":130,"links":2070},[2071,2078,2081],{"id":62,"depth":130,"text":63,"children":2072},[2073,2074,2075,2076,2077],{"id":93,"depth":136,"text":94},{"id":455,"depth":136,"text":456},{"id":794,"depth":136,"text":795},{"id":1596,"depth":136,"text":1597},{"id":1750,"depth":136,"text":1751},{"id":1989,"depth":130,"text":1990,"children":2079},[2080],{"id":2022,"depth":136,"text":2023},{"id":43,"depth":130,"text":2023},"2025-04-11","Interactive blog post about modelling agent behaviour with Bevy Behave","md",{},"/posts/bevy-behave",{"title":5,"description":2083},"2.posts/20250411.Bevy Behave",null,"ao3W-BZ-n6ulJvCyzkJ00qy8WHrLaUXP6U88x3T1i9w",1775234851922]