Skip to content

Decorators in a tree stored in the register exhibit global state #63

@Tresky

Description

@Tresky

Consider the following code where I've created a decorator that will add a custom cooldown timer to a BT that will start as soon as the tree experiences a SUCCESS in the sequence that it is wrapping. This concept works excellent when there is 1 tree present.

However, as soon as I instance the class a second time, the two trees exhibit shared behavior. Meaning, as soon as one of the trees experiences a SUCCESS, both of their decorators fire and the cooldown begins for BOTH trees instead of just the one that had the SUCCESS as you would expect.

// Custom decorator that adds a cooldown after a SUCCESS
// Modified from the CooldownDecorator in the repo.
// Original CooldownDecorator times the cooldown from the START of
// the sequence. I need to time from the end of the sequence getting a SUCCESS.
class SuccessCooldownDecorator extends Decorator {
  nodeType = 'SuccessCooldownDecorator'

  setConfig ({ cooldown }) {
    this.config = { cooldown }
  }

  decorate (run) {
    if (this.lock) {
      return FAILURE
    }
    const result = run()
    if (result === SUCCESS) {
      this.lock = true
      setTimeout(() => {
        this.lock = false
      }, this.config.cooldown * 1000)
    }
    return result
  }
}

// Create a new sequence
BehaviorTree.register('my-sequence', new Sequence({
  nodes: [ ... ]
}))

// Implement a custom Decorator around the Sequence
BehaviorTree.register('decorated', new SuccessCooldownDecorator({
  node: 'my-sequence',
  config: { cooldown: 2 }
}))

// Place the BT inside of a class
class MyContainer {
  contructor () {
    this.tree = new BehaviorTree({
      node: 'decorated',
      blackboard: { ... }
    })
  }

  update () {
    this.tree.step()
  }
}

const inst1 = new MyContainer()
const inst2 = new MyContainer()

If I move the decorator being created out of the register and into the class constructor, it works as expected.

class MyContainer {
  contructor () {
    this.tree = new BehaviorTree({
      node: new SuccessCooldownDecorator({
        node: 'my-sequence',
        config: { cooldown: 2 }
      }),
      blackboard: { ... }
    })
  }
  ...
}

I tried looking through the Decorator and Node classes to see if I could identify the issue I'm having, but I couldn't find any instances of any global state being used. I'm not sure why doing new Sequence in the global scope works fine, but doing new Decorator in the global scope doesn't allow this use case.

Is my custom decorator implementation incorrect? Am I doing something else wrong?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions