Rez 0.9: the Behaviour Tree release

At the end of October, I released v0.8 of Rez, a game authoring system I built for myself to replace my use of Twine. Today I have published v0.9.

Originally meant to be a “quick & dirty” replacement for Twine, over a year, it has become a complete system for building HTML games. It has built-in support for things I wanted, like flexible items & inventories, NPC actors, and a scene & card based rendering system. Rez is declarative, with Javascript callbacks providing dynamic behaviour.

However, for my own use, it was missing a key component: behaviour trees. I hate trying to implement behaviour in a programming language (esp. one like Javascript). I have used behaviour trees before, so I ported a behaviour tree library I wrote for Clojure and here we are.

Rez now natively supports behaviour trees as an attribute type and provides a helpful selection of tasks in the standard library (composites & decorators) and a @task element to define custom condition tasks & action tasks yourself (see stdlib source for how the core tasks are implemented).

The result: you can write something like:

@actor sam_spade begin
   ...
   behaviours: ^[SELECT [
      [SEQUENCE [
        [ACTOR_IN location=sams_office]
        [ACTOR_SEES item=booze]
        [MAYBE p=20 [ACTOR_DRINKS]]]
      [SEQUENCE [
        [ACTOR_SEES actor=wilmer]
        [ACTOR_RELATIONSHIP actor=wilmer rel=antagonist]
        [ACTOR_HAS item=handgun]
        [SELECT [
          [SEQUENCE [
            [ACTOR_HAS tag=drunk]
            [ACTOR_SHOOTS_AND_MISSES]]]
          [MAYBE p=20 [
            [ACTOR_SHOOTS target=wilmer item=handgun]]
      [SEQUENCE [
        [ACTOR_SEES tagged=dame]
        [RANDOM_EACH [
          [ACTOR_SAYS line="You know, that's good, because if you actually were as innocent as you pretend to be, we'd never get anywhere"]
          [ACTOR_SAYS line="We didn't exactly believe your story, Miss. We believed your 200 dollars"]
          [ACTOR_SAYS line="You won't need much of anybody's help. You're good. Chiefly your eyes, I think, and that throb you get in your voice when you say things like "Be generous, Mr. Spade"]]]]]
@end

Okay, very simple and silly example, but hopefully illustrative of what is possible. SEQUENCE, SELECT, MAYBE, and RANDOM_EACH are core tasks from the standard library. The author of this game would implement condition tasks like ACTOR_IN and ACTOR_SEES and action tasks such as ACTOR_DRINKS, ACTOR_SAYS, and so on.

With the addition of behaviour trees, Rez is now conceptually complete, although there are still a lot of holes in my implementation of some of those concepts. I plan to fill those in as I switch my focus from tooling to game development.

I realise there are already a lot of authoring systems out there, but if anyone fancies trying Rez drop me a line. The source is all available, but I guess it might require my help to set it up.

9 Likes

Very nice. I hope to give this a try. Thank you.

I will be trying it on an RPi 4 using RaspiOS. (A derivative of Debian for the Arm Processor.)

I’d be interested to know if that would work. I think you can get Erlang working on the Pi, so who knows… please report back either way :slight_smile:

1 Like

Will do. I am going to try tonight or tomorrow.

Thanks.

I game it a shot. I installed all of the dependencies. The Elixir version is only 1.10.13 in the RPi repositories. I changed the dependency in the dependency in the mix.exs file. I had to execute mix dep.git to install the Hex package.

It seemed to compile with several warnings until it got to the Poison section and then failed to compile

I looked at the Elixir website. I may try to manually install the proper version of Elixir tomorrow.

PS. There is an install procedure for Elixir 1.13 on the Elixir page. That may do the trick. More tomorrow.

If that’s Elixir 1.10.13, then I would be surprised if it would work; that’s pretty old.

Although I target 1.14 now, most of the development was done on 1.13, which should work.

M.

2 Likes

1.14 is available. I will try that later today.

PS. Still no luck. Tried to manually install the recent versions of Elixir and Erlang. Multiple dependencies that cannot be overcome with RaspiOS.

Maybe RaspberryPi.org will update their OS soon or I may try to do an install with Ubuntu. ?

Alas, I know nothing about RaspiOS, so I can be of little help to you in getting Erlang or Elixir to work. So far as I am aware, you should have no problems with Ubuntu.

1 Like

I am going to give it a try. Just have to get an SD card to set it up.

I’ve updated some dependencies and tested my own installation instructions in the updated README. The only macOS dependency I make now is on Homebrew to install ASDF. So as long as you can install ASDF on your platform, I think it should work. But I think a lot of things that don’t turn out to be true :slight_smile:

1 Like

As I’ve begun working on my two games, I’ve fleshed out some areas of Rez that either weren’t there or didn’t work. In v0.9.2

1. I’ve added @rel that established relationships between entities, e.g.

@rel begin
  source: #ronin_faction
  target: #player_faction
  affinity: -0.5
end

Affinity scores range from -5.0 (hate) to +5.0 (love), and relationships can be tagged to add further options. In this case, the ‘ronin faction’ starts out mildly disliking the ‘player faction’, but they don’t hate them and won’t take negative actions towards them, but they won’t help them either. When that starts to hit -2.0, things will get worse.

In this case, I’ve set up a relationship between two factions so that I don’t have to set up separate actor->actor relationships. But I could do that if it made sense. Or do both so that a specific relationship could override a faction-based relationship (could be helpful in a game based on Romeo & Juliet, for example).

2. Assets and Groups (dynamic sets of assets) now work. So, for example, I have 100 background images tagged #background_image and a group background_group with include_tags: #{:background_image}:

const group = $("background_group");
const asset_id = group.randomAssetId();
const asset = $(asset_id);
return asset.tag();

This code will give me the <img src='...' /> tag for a background image chosen randomly (without replacement).

3. The Handlebars helpers that were included in the runtime have been turned into an element @helper with the existing helpers moved into the standard library:

For example, the old r_asset helper is now called atag:

@helper ASSET_TAG_HELPER begin
  %% The $atag helper returns a tag that embeds the file (be it image, movie,
  %% or audio) referenced by the asset.

  name: "atag"
  args: ["asset_id"]
  handler: (asset_id) => {
    const asset = $(asset_id);
    if(asset.game_object_type != "asset") {
      throw "Attempt to retrieve asset with id '" + asset_id + "' but found '" + asset.game_object_type + "'!";
    } else {
      return Handlebars.SafeString(asset.tag());
    }
  }
end

The author can now easily create their own helpers, although, at present, it’s not easy to override the built-in helpers. I may switch from Handlebars at some point, but it’s good enough right now.

4. Added the @object element.

The object element is an escape hatch allowing the author to define their own things for which there isn’t a built-in concept. This can be useful along with @alias so you can do the following:

@alias skill = object # begin
  template: true
  description: "Something an actor has learned how to do"
  min: 0
  max: 5
  level: 0
end

@skill fisticuffs begin
  name: "Fisticuffs"
  type: :martial
  description: "Marquis of Queensbury be damned, hit 'em where it hurts."
end

Rez knows nothing about skills, but presumably, your custom code & behaviour tasks will.

I added this late in the day because I hadn’t yet decided where to draw the line. I have actors and items and all sorts that have pre-defined behaviours.

But as I’ve been playing with my own designs, I feel like I have a reasonable balance between built-in but flexible base concepts and now want to be able to add game-specific concepts.

There are many ways to do things like skills, classes, perks, jobs, etc.; how they play out is likely to be very game-design-specific. So now it’s time to add @object and allow that process to start. There may be changes here as I learn what works and doesn’t.

5. Numerous big-fixes.

Of course, now I try to use it in anger, I’m finding all the problems and rough patches!

2 Likes

I’ve merged your topics together because I don’t think we need a new topic for each incremental update. My rule of thumb would be only to start a new topic for really major updates (a 1.0 release, or if it supports a different type of game (like adding parser support), or if it supports a new way of publishing games, new OSes etc), or if it’s been 6+ months since the last update. (Though even then it’s not a problem to update the older topic!) Thank you for understanding.

No worries. I posted in a different place because I wasn’t sure I’d put the first post in the right place (in tech rather than dev systems). Thanks. M

1 Like

should the relations span [-1,1], then you’d scale by 0.5 in your calculation?
Just so it’s a bit easier to author.

I could have used [-1, 1], and in some ways, it would be more natural.

But it didn’t seem likely authors would be applying the kind of function where that domain would matter to an affinity score. By contrast, I felt that [-5, 5] felt somehow more expressive.

If you have particular arguments for [-1, 1] I’d be pleased to hear them. It’s hardly set in stone.

Apologies if I’m not following this too closely, but this is something I can weigh in on.

Mathematically, there are a lot more convenient and easy formulas for scaling, curving, and measuring things when continuous values span between [-1, 1]

For x, where it spans [-1, 1], x*x gives a bias curve toward zero, without causing the maximum value to go beyond the limits of the span.

If z=237 out of a maximum of 255, then you simply need to do x=z/255 to get a gradient between [0, 1].

If you want to distribute x across, say, 7 modes, you simple do modeIndex=x*7.

It’s less cognitively intuitive than [-5,5], but it’s more mathematically simple and useful.

2 Likes

Hi Joey. Thanks, you’re quite right; a range of functions works well on a domain of [-1,1].

What was in my mind was whether the likelihood of anyone using them on an affinity score outweighs what I felt was, as you put it, a more cognitively intuitive range.

If someone did, scaling between [-5,5] and [-1,1] isn’t very difficult. But at the same time, I don’t want to make life unnecessarily more complicated.

1 Like