Skip to content

Commit 8724ce7

Browse files
committed
Changing TimeData to StateData, allow passing state
1 parent c31e998 commit 8724ce7

16 files changed

+170
-156
lines changed

src/BehaviorTreeBuilder.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import ParallelNode from "./Node/ParallelNode";
99
import ParentBehaviorTreeNodeInterface from "./Node/ParentBehaviorTreeNodeInterface";
1010
import SelectorNode from "./Node/SelectorNode";
1111
import SequenceNode from "./Node/SequenceNode";
12-
import TimeData from "./TimeData";
12+
import StateData from "./StateData";
1313

1414
export default class BehaviorTreeBuilder {
1515
/**
@@ -28,10 +28,10 @@ export default class BehaviorTreeBuilder {
2828
* Create an action node.
2929
*
3030
* @param {string} name
31-
* @param {(time: TimeData) => BehaviorTreeStatus} fn
31+
* @param {(state: StateData) => BehaviorTreeStatus} fn
3232
* @returns {BehaviorTreeBuilder}
3333
*/
34-
public do(name: string, fn: (time: TimeData) => Promise<BehaviorTreeStatus>): BehaviorTreeBuilder {
34+
public do(name: string, fn: (state: StateData) => Promise<BehaviorTreeStatus>): BehaviorTreeBuilder {
3535
if (this.parentNodeStack.isEmpty()) {
3636
throw new BehaviorTreeError(Errors.UNNESTED_ACTION_NODE);
3737
}
@@ -46,10 +46,10 @@ export default class BehaviorTreeBuilder {
4646
* Like an action node... but the function can return true/false and is mapped to success/failure.
4747
*
4848
* @param {string} name
49-
* @param {(time: TimeData) => boolean} fn
49+
* @param {(state: StateData) => boolean} fn
5050
* @returns {BehaviorTreeBuilder}
5151
*/
52-
public condition(name: string, fn: (time: TimeData) => Promise<boolean>): BehaviorTreeBuilder {
52+
public condition(name: string, fn: (state: StateData) => Promise<boolean>): BehaviorTreeBuilder {
5353
return this.do(name, async (t) => await fn(t) ? BehaviorTreeStatus.Success : BehaviorTreeStatus.Failure);
5454
}
5555

src/Node/ActionNode.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
2-
import TimeData from "../TimeData";
2+
import StateData from "../StateData";
33
import BehaviorTreeNodeInterface from "./BehaviorTreeNodeInterface";
44

55
/**
66
* A behavior tree leaf node for running an action
77
*
8-
* @property {string} name - The name of the node
9-
* @property {(time: TimeData) => BehaviorTreeStatus} fn - Function to invoke for the action.
8+
* @property {string} name - The name of the node
9+
* @property {(state: StateData) => BehaviorTreeStatus} fn - Function to invoke for the action.
1010
*/
1111
export default class ActionNode implements BehaviorTreeNodeInterface {
1212
public constructor(
1313
public readonly name: string,
14-
public readonly fn: (time: TimeData) => Promise<BehaviorTreeStatus>,
14+
public readonly fn: (state: StateData) => Promise<BehaviorTreeStatus>,
1515
) {
1616
}
1717

18-
public async tick(time: TimeData): Promise<BehaviorTreeStatus> {
19-
return await this.fn(time);
18+
public async tick(state: StateData): Promise<BehaviorTreeStatus> {
19+
return await this.fn(state);
2020
}
2121
}

src/Node/BehaviorTreeNodeInterface.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
2-
import TimeData from "../TimeData";
2+
import StateData from "../StateData";
33

44
export default interface BehaviorTreeNodeInterface {
5-
tick(time: TimeData): Promise<BehaviorTreeStatus>;
5+
tick(state: StateData): Promise<BehaviorTreeStatus>;
66
}

src/Node/InverterNode.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
22
import BehaviorTreeError from "../Error/BehaviorTreeError";
33
import Errors from "../Error/Errors";
4-
import TimeData from "../TimeData";
4+
import StateData from "../StateData";
55
import BehaviorTreeNodeInterface from "./BehaviorTreeNodeInterface";
66
import ParentBehaviorTreeNodeInterface from "./ParentBehaviorTreeNodeInterface";
77

@@ -19,12 +19,12 @@ export default class InverterNode implements ParentBehaviorTreeNodeInterface {
1919
public constructor(public readonly name: string) {
2020
}
2121

22-
public async tick(time: TimeData): Promise<BehaviorTreeStatus> {
22+
public async tick(state: StateData): Promise<BehaviorTreeStatus> {
2323
if (!this.childNode) {
2424
throw new BehaviorTreeError(Errors.INVERTER_NO_CHILDREN);
2525
}
2626

27-
const result = await this.childNode.tick(time);
27+
const result = await this.childNode.tick(state);
2828
if (result === BehaviorTreeStatus.Failure) {
2929
return BehaviorTreeStatus.Success;
3030
} else if (result === BehaviorTreeStatus.Success) {

src/Node/ParallelNode.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
2-
import TimeData from "../TimeData";
2+
import StateData from "../StateData";
33
import BehaviorTreeNodeInterface from "./BehaviorTreeNodeInterface";
44
import ParentBehaviorTreeNodeInterface from "./ParentBehaviorTreeNodeInterface";
55

@@ -25,8 +25,8 @@ export default class ParallelNode implements ParentBehaviorTreeNodeInterface {
2525
) {
2626
}
2727

28-
public async tick(time: TimeData): Promise<BehaviorTreeStatus> {
29-
const statuses: BehaviorTreeStatus[] = await Promise.all(this.children.map((c) => this.tickChildren(time, c)));
28+
public async tick(state: StateData): Promise<BehaviorTreeStatus> {
29+
const statuses: BehaviorTreeStatus[] = await Promise.all(this.children.map((c) => this.tickChildren(state, c)));
3030
const succeeded = statuses.filter((x) => x === BehaviorTreeStatus.Success).length;
3131
const failed = statuses.filter((x) => x === BehaviorTreeStatus.Failure).length;
3232

@@ -44,9 +44,9 @@ export default class ParallelNode implements ParentBehaviorTreeNodeInterface {
4444
this.children.push(child);
4545
}
4646

47-
private async tickChildren(time: TimeData, child: BehaviorTreeNodeInterface): Promise<BehaviorTreeStatus> {
47+
private async tickChildren(state: StateData, child: BehaviorTreeNodeInterface): Promise<BehaviorTreeStatus> {
4848
try {
49-
return await child.tick(time);
49+
return await child.tick(state);
5050
} catch (e) {
5151
return BehaviorTreeStatus.Failure;
5252
}

src/Node/SelectorNode.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
22
import NodeEnumerator from "../NodeEnumerator";
3-
import TimeData from "../TimeData";
3+
import StateData from "../StateData";
44
import BehaviorTreeNodeInterface from "./BehaviorTreeNodeInterface";
55
import ParentBehaviorTreeNodeInterface from "./ParentBehaviorTreeNodeInterface";
66

@@ -29,7 +29,7 @@ export default class SelectorNode implements ParentBehaviorTreeNodeInterface {
2929
this.enumerator = new NodeEnumerator(this.children);
3030
}
3131

32-
public async tick(time: TimeData): Promise<BehaviorTreeStatus> {
32+
public async tick(state: StateData): Promise<BehaviorTreeStatus> {
3333
if (!this.enumerator) {
3434
this.init();
3535
}
@@ -39,7 +39,7 @@ export default class SelectorNode implements ParentBehaviorTreeNodeInterface {
3939
}
4040

4141
do {
42-
const status = await this.enumerator.current.tick(time);
42+
const status = await this.enumerator.current.tick(state);
4343
if (status !== BehaviorTreeStatus.Failure) {
4444
if (status === BehaviorTreeStatus.Success) {
4545
this.enumerator.reset();

src/Node/SequenceNode.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import BehaviorTreeStatus from "../BehaviorTreeStatus";
22
import NodeEnumerator from "../NodeEnumerator";
3-
import TimeData from "../TimeData";
3+
import StateData from "../StateData";
44
import BehaviorTreeNodeInterface from "./BehaviorTreeNodeInterface";
55
import ParentBehaviorTreeNodeInterface from "./ParentBehaviorTreeNodeInterface";
66

@@ -29,7 +29,7 @@ export default class SequenceNode implements ParentBehaviorTreeNodeInterface {
2929
this.enumerator = new NodeEnumerator(this.children);
3030
}
3131

32-
public async tick(time: TimeData): Promise<BehaviorTreeStatus> {
32+
public async tick(state: StateData): Promise<BehaviorTreeStatus> {
3333
if (!this.enumerator) {
3434
this.init();
3535
}
@@ -39,7 +39,7 @@ export default class SequenceNode implements ParentBehaviorTreeNodeInterface {
3939
}
4040

4141
do {
42-
const status = await this.enumerator.current.tick(time);
42+
const status = await this.enumerator.current.tick(state);
4343
if (status !== BehaviorTreeStatus.Success) {
4444
if (status === BehaviorTreeStatus.Failure) {
4545
this.enumerator.reset();

src/StateData.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Represents time and state. Used to pass time values to behavior tree nodes.
3+
*
4+
* @property {number} deltaTime - The current time of this state representation
5+
* @property {object} state - Any state data you would like to pass to the nodes.
6+
*/
7+
export default class StateData {
8+
public constructor(public readonly deltaTime: number = 0, public readonly state: any = {}) {
9+
}
10+
}

src/TimeData.ts

-7
This file was deleted.

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import ParallelNode from "./Node/ParallelNode";
99
import ParentBehaviorTreeNodeInterface from "./Node/ParentBehaviorTreeNodeInterface";
1010
import SelectorNode from "./Node/SelectorNode";
1111
import SequenceNode from "./Node/SequenceNode";
12-
import TimeData from "./TimeData";
12+
import TimeData from "./StateData";
1313

1414
export {
1515
BehaviorTreeBuilder,

test/BehaviorTreeBuilderTest.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import test from "ava";
2-
import * as TypeMoq from "typemoq";
32
import BehaviorTreeBuilder from "../src/BehaviorTreeBuilder";
43
import BehaviorTreeError from "../src/Error/BehaviorTreeError";
54
import BehaviorTreeStatus from "../src/BehaviorTreeStatus";
65
import InverterNode from "../src/Node/InverterNode";
7-
import TimeData from "../src/TimeData";
6+
import StateData from "../src/StateData";
87
import SequenceNode from "../src/Node/SequenceNode";
98
import ParallelNode from "../src/Node/ParallelNode";
109
import SelectorNode from "../src/Node/SelectorNode";
@@ -41,7 +40,7 @@ test("can create inverter node", async (assert) => {
4140
.build();
4241

4342
assert.is(InverterNode, node.constructor);
44-
assert.is(BehaviorTreeStatus.Failure, await node.tick(new TimeData()));
43+
assert.is(BehaviorTreeStatus.Failure, await node.tick(new StateData()));
4544
});
4645

4746
test("can't create an unbalanced behavior tree", async (assert) => {
@@ -63,7 +62,7 @@ test("condition is syntactic sugar for do", async (assert) => {
6362
.build();
6463

6564
assert.is(InverterNode, node.constructor);
66-
assert.is(BehaviorTreeStatus.Failure, await node.tick(new TimeData()));
65+
assert.is(BehaviorTreeStatus.Failure, await node.tick(new StateData()));
6766
});
6867

6968
test("can invert an inverter", async (assert) => {
@@ -77,7 +76,7 @@ test("can invert an inverter", async (assert) => {
7776
.build();
7877

7978
assert.is(InverterNode, node.constructor);
80-
assert.is(BehaviorTreeStatus.Success, await node.tick(new TimeData()));
79+
assert.is(BehaviorTreeStatus.Success, await node.tick(new StateData()));
8180
});
8281

8382
test("adding more than a single child to inverter throws exception", async (assert) => {
@@ -113,7 +112,7 @@ test("can create a sequence", async (assert) => {
113112
.build();
114113

115114
assert.is(SequenceNode, sequence.constructor);
116-
assert.is(BehaviorTreeStatus.Success, await sequence.tick(new TimeData()));
115+
assert.is(BehaviorTreeStatus.Success, await sequence.tick(new StateData()));
117116
assert.is(2, invokeCount);
118117
});
119118

@@ -136,7 +135,7 @@ test("can create a parallel", async (assert) => {
136135
.build();
137136

138137
assert.is(ParallelNode, parallel.constructor);
139-
assert.is(BehaviorTreeStatus.Success, await parallel.tick(new TimeData()));
138+
assert.is(BehaviorTreeStatus.Success, await parallel.tick(new StateData()));
140139
assert.is(2, invokeCount);
141140
});
142141

@@ -159,7 +158,7 @@ test("can create a selector", async (assert) => {
159158
.build();
160159

161160
assert.is(SelectorNode, selector.constructor);
162-
assert.is(BehaviorTreeStatus.Success, await selector.tick(new TimeData()));
161+
assert.is(BehaviorTreeStatus.Success, await selector.tick(new StateData()));
163162
assert.is(2, invokeCount);
164163
});
165164

@@ -183,7 +182,7 @@ test("can splice sub tree", async (assert) => {
183182
.end()
184183
.build();
185184

186-
await tree.tick(new TimeData);
185+
await tree.tick(new StateData);
187186

188187
assert.is(2, invokeCount);
189188
});

test/Node/ActionNodeTest.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
import test from "ava";
2-
import TimeData from "../../src/TimeData";
2+
import StateData from "../../src/StateData";
33
import ActionNode from "../../src/Node/ActionNode";
44
import BehaviorTreeStatus from "../../src/BehaviorTreeStatus";
55

66
test("can run action", async (assert) => {
7-
const time = new TimeData();
7+
const state = new StateData();
88
let invokeCount = 0;
99
const testObject = new ActionNode(
1010
"some-action",
11-
async (t) => {
12-
assert.is(time, t);
11+
async (s) => {
12+
assert.is(state, s);
1313
++invokeCount;
1414

1515
return BehaviorTreeStatus.Running;
1616
}
1717
);
1818

19-
assert.is(BehaviorTreeStatus.Running, await testObject.tick(time));
19+
assert.is(BehaviorTreeStatus.Running, await testObject.tick(state));
2020
assert.is(1, invokeCount);
2121
});
22+
23+
24+
test("state is available to nodes", async (assert) => {
25+
const state = new StateData(0, {test: 'foo', bar: 'baz'});
26+
const testObject = new ActionNode("some-action", async (s) => {
27+
assert.is(state.state.test, s.state.test);
28+
29+
return BehaviorTreeStatus.Success
30+
});
31+
32+
assert.is(BehaviorTreeStatus.Success, await testObject.tick(state));
33+
});

test/Node/InverterNodeTest.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import test from "ava";
22
import * as TypeMoq from "typemoq";
3-
import TimeData from "../../src/TimeData";
3+
import StateData from "../../src/StateData";
44
import InverterNode from "../../src/Node/InverterNode";
55
import BehaviorTreeNodeInterface from "../../src/Node/BehaviorTreeNodeInterface";
66
import BehaviorTreeStatus from "../../src/BehaviorTreeStatus";
@@ -18,50 +18,50 @@ test.afterEach.always(() => {
1818

1919
test("ticking with no child node throws error", async (assert) => {
2020
try {
21-
await testObject.tick(new TimeData());
21+
await testObject.tick(new StateData());
2222
} catch (e) {
2323
assert.throws(() => {throw e}, BehaviorTreeError, "should have thrown");
2424
assert.is(e.message, Errors.INVERTER_NO_CHILDREN);
2525
}
2626
});
2727

2828
test("inverts success of child node", async (assert) => {
29-
const time = new TimeData();
29+
const state = new StateData();
3030
const mockChildNode = TypeMoq.Mock.ofType<BehaviorTreeNodeInterface>();
3131

3232
mockChildNode
33-
.setup(async (m) => await m.tick(time))
33+
.setup(async (m) => await m.tick(state))
3434
.returns(() => Promise.resolve(BehaviorTreeStatus.Success));
3535

3636
testObject.addChild(mockChildNode.object);
37-
assert.is(BehaviorTreeStatus.Failure, await testObject.tick(time));
38-
mockChildNode.verify((m) => m.tick(time), TypeMoq.Times.once());
37+
assert.is(BehaviorTreeStatus.Failure, await testObject.tick(state));
38+
mockChildNode.verify((m) => m.tick(state), TypeMoq.Times.once());
3939
});
4040

4141
test("inverts failure of child node", async (assert) => {
42-
const time = new TimeData();
42+
const state = new StateData();
4343
const mockChildNode = TypeMoq.Mock.ofType<BehaviorTreeNodeInterface>();
4444

4545
mockChildNode
46-
.setup(async (m) => await m.tick(time))
46+
.setup(async (m) => await m.tick(state))
4747
.returns(() => Promise.resolve(BehaviorTreeStatus.Failure));
4848

4949
testObject.addChild(mockChildNode.object);
50-
assert.is(BehaviorTreeStatus.Success, await testObject.tick(time));
51-
mockChildNode.verify((m) => m.tick(time), TypeMoq.Times.once());
50+
assert.is(BehaviorTreeStatus.Success, await testObject.tick(state));
51+
mockChildNode.verify((m) => m.tick(state), TypeMoq.Times.once());
5252
});
5353

5454
test("pass through running of child node", async (assert) => {
55-
const time = new TimeData();
55+
const state = new StateData();
5656
const mockChildNode = TypeMoq.Mock.ofType<BehaviorTreeNodeInterface>();
5757

5858
mockChildNode
59-
.setup(async (m) => await m.tick(time))
59+
.setup(async (m) => await m.tick(state))
6060
.returns(() => Promise.resolve(BehaviorTreeStatus.Running));
6161

6262
testObject.addChild(mockChildNode.object);
63-
assert.is(BehaviorTreeStatus.Running, await testObject.tick(time));
64-
mockChildNode.verify((m) => m.tick(time), TypeMoq.Times.once());
63+
assert.is(BehaviorTreeStatus.Running, await testObject.tick(state));
64+
mockChildNode.verify((m) => m.tick(state), TypeMoq.Times.once());
6565
});
6666

6767
test("adding more than a single child throws exception", async (assert) => {

0 commit comments

Comments
 (0)