As a concrete example of how I am using behaviour trees:
In my city I have stores which may, or may not, be open. Here are a couple of simplified examples (I tend to use a relevant prefix such as v_
or s_
to make it clear when I am talking about, e.g. a vendor or scene):
@vendor v_apothecary {
name: "The Apothecary"
description: "Potions, herbs, poultices and such like"
scene_id: #s_use_vendor
available_from: [08 00]
available_until: [17 00]
player_knows: true
}
@vendor v_inn {
name: "The Inn"
description: "A comfortable bed and a meal."
scene_id: #s_use_vendor
available_from: [06 30]
available_until: [23 00]
player_knows: true
}
@vendor v_black_market {
name: "Black market peddlar"
description: "Dangerous if caught by the city guard but some things you cannot acquire elsewhere."
scene_id: #s_use_vendor
available_from: [22 00]
available_until: [03 00]
player_knows: false
}
Now I want to display a list of vendors that the player knows about and a link to use them, assuming they are open. Time is represented like this:
@object clock {
time: [08 00]
day: 1
week: 1
cycle: 1
}
As you can see I am using lists to represent times, e.g. [08 00]
is 8:00am.
Rez provides a handy object called RezDecision
which exposes an API hide()
, no(reasons)
, yes()
. We can pass a RezDecision
to any code that should decide something for us.
hide()
means this option shouldn’t be seen by the player, e.g. the player doesn’t yet know about this vendor
no(reasons)
means this option should be shown to the player but disabled with an optional reason why they can’t select it, e.g. it’s too early
yes()
means this option should be shown and available to the player
So, we could write something like this:
@object t_vendor {
$template: true
is_available: function(decision) {
decision.default_yes();
if(!this.player_knowns) {
decision.hide();
} else {
const clock = $("clock");
if(clock.before(this.available_from)) {
decision.no("Not open yet");
} else if(clock.after(this.available_until)) {
decision.no("Closed for the day");
}
}
}
}
@vendor v_apothecary<t_vendor> {
...
}
And now when we go through the list of vendors and generate the appropriate markup depending on whether the player can see vendor, and visit them, or not.
$if (decision.result) -> {%
<a data-event="switch" data-target="${vendor.scene_id}" data-vendor="${vendor.id}" data-tooltip="${vendor.description}">${vendor.name}</a>
%}
(decision.hide) -> {%%}
() -> {%
<a href="javascript:void(0)" data-tooltip="${decision.reason}" class="disabled">${vendor.name}</a>
%}
Not that the is_available
function above is particularly difficult to write or understand, but the code itself slightly obscures what is going on. And if we were to add a few more conditions this could become more complex and harder to write & maintain. Using behaviour trees (which are really decision trees) it could look like:
@object t_vendor {
is_available: ^[$select
[$sequence [$invert [player_knows_vendor]] [decision_hide]]
[$sequence [vendor_before_open] [decision_no msg="Not open yet"]]
[$sequence [vendor_after_close] [decision_no msg="Closed for the day"]]
[decision_yes]
]
}
Once you understand some basic behaviours such as $select
, $sequence
, and $invert
the structure of the decision becomes much clearer.
It means implementing behaviours such as player_knows_vendor
, vendor_before_open
, decision_hide
and so on. But these can be quite compact:
@behaviour vendor_before_open {
execute: (behaviour, wmem) => {
return {
success: $clock.before(wmem["vendor"].available_from),
wmem: wmem
};
}
}
@behviour decision_no {
execute: (behaviour, wmem) => {
const decision = wmem["decision"];
decision.no(behaviour.option("msg"));
return {
success: true,
wmem: wmem
}
}
}
Once you have defined these behaviours they are easily composed and their implementation doesn’t get in the way. The behaviour tree more clearly (at least in my view) exposes the logic.
I have this view that an author with less technical expertise could assemble these behaviours to create the meaning and rely on a developer, more adept with Javascript, to build the implementation of the various behaviours for them. Madness? I don’t know.