├── .gitignore ├── BTSK.sln ├── BehaviorTree.cpp ├── BehaviorTreeEvent.cpp ├── BehaviorTreeOptimized.cpp ├── BehaviorTreeShared.cpp ├── BehaviorTreeStarterKit.vcxproj ├── BehaviorTreeStarterKit.vcxproj.filters ├── LICENSE.md ├── README.md ├── Shared.h ├── Test.cpp └── Test.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | /Debug 14 | /Release 15 | *.opensdf 16 | *.sdf 17 | *.suo 18 | *.user 19 | /ipch 20 | -------------------------------------------------------------------------------- /BTSK.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BehaviorTreeStarterKit", "BehaviorTreeStarterKit.vcxproj", "{157B27E5-12E2-43F4-A99C-703B54052DE3}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {157B27E5-12E2-43F4-A99C-703B54052DE3}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {157B27E5-12E2-43F4-A99C-703B54052DE3}.Debug|Win32.Build.0 = Debug|Win32 14 | {157B27E5-12E2-43F4-A99C-703B54052DE3}.Release|Win32.ActiveCfg = Release|Win32 15 | {157B27E5-12E2-43F4-A99C-703B54052DE3}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /BehaviorTree.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * This file is part of the Behavior Tree Starter Kit. 3 | * 4 | * Copyright (c) 2012, AiGameDev.com. 5 | * 6 | * Credits: Alex J. Champandard 7 | *****************************************************************************/ 8 | 9 | #include 10 | #include "Shared.h" 11 | #include "Test.h" 12 | 13 | namespace bt1 14 | { 15 | 16 | // ============================================================================ 17 | 18 | enum Status 19 | /** 20 | * Return values of and valid states for behaviors. 21 | */ 22 | { 23 | BH_INVALID, 24 | BH_SUCCESS, 25 | BH_FAILURE, 26 | BH_RUNNING, 27 | BH_ABORTED, 28 | }; 29 | 30 | class Behavior 31 | /** 32 | * Base class for actions, conditions and composites. 33 | */ 34 | { 35 | public: 36 | virtual Status update() = 0; 37 | 38 | virtual void onInitialize() {} 39 | virtual void onTerminate(Status) {} 40 | 41 | Behavior() 42 | : m_eStatus(BH_INVALID) 43 | { 44 | } 45 | 46 | virtual ~Behavior() 47 | { 48 | } 49 | 50 | Status tick() 51 | { 52 | if (m_eStatus != BH_RUNNING) 53 | { 54 | onInitialize(); 55 | } 56 | 57 | m_eStatus = update(); 58 | 59 | if (m_eStatus != BH_RUNNING) 60 | { 61 | onTerminate(m_eStatus); 62 | } 63 | return m_eStatus; 64 | } 65 | 66 | void reset() 67 | { 68 | m_eStatus = BH_INVALID; 69 | } 70 | 71 | void abort() 72 | { 73 | onTerminate(BH_ABORTED); 74 | m_eStatus = BH_ABORTED; 75 | } 76 | 77 | bool isTerminated() const 78 | { 79 | return m_eStatus == BH_SUCCESS || m_eStatus == BH_FAILURE; 80 | } 81 | 82 | bool isRunning() const 83 | { 84 | return m_eStatus == BH_RUNNING; 85 | } 86 | 87 | Status getStatus() const 88 | { 89 | return m_eStatus; 90 | } 91 | 92 | private: 93 | Status m_eStatus; 94 | }; 95 | 96 | // ---------------------------------------------------------------------------- 97 | 98 | struct MockBehavior : public Behavior 99 | { 100 | int m_iInitializeCalled; 101 | int m_iTerminateCalled; 102 | int m_iUpdateCalled; 103 | Status m_eReturnStatus; 104 | Status m_eTerminateStatus; 105 | 106 | MockBehavior() 107 | : m_iInitializeCalled(0) 108 | , m_iTerminateCalled(0) 109 | , m_iUpdateCalled(0) 110 | , m_eReturnStatus(BH_RUNNING) 111 | , m_eTerminateStatus(BH_INVALID) 112 | { 113 | } 114 | 115 | virtual ~MockBehavior() 116 | { 117 | } 118 | 119 | virtual void onInitialize() 120 | { 121 | ++m_iInitializeCalled; 122 | } 123 | 124 | virtual void onTerminate(Status s) 125 | { 126 | ++m_iTerminateCalled; 127 | m_eTerminateStatus = s; 128 | } 129 | 130 | virtual Status update() 131 | { 132 | ++m_iUpdateCalled; 133 | return m_eReturnStatus; 134 | } 135 | }; 136 | 137 | TEST(StarterKit1, TaskInitialize) 138 | { 139 | MockBehavior t; 140 | CHECK_EQUAL(0, t.m_iInitializeCalled); 141 | 142 | t.tick(); 143 | CHECK_EQUAL(1, t.m_iInitializeCalled); 144 | }; 145 | 146 | TEST(StarterKit1, TaskUpdate) 147 | { 148 | MockBehavior t; 149 | CHECK_EQUAL(0, t.m_iUpdateCalled); 150 | 151 | t.tick(); 152 | CHECK_EQUAL(1, t.m_iUpdateCalled); 153 | }; 154 | 155 | TEST(StarterKit1, TaskTerminate) 156 | { 157 | MockBehavior t; 158 | t.tick(); 159 | CHECK_EQUAL(0, t.m_iTerminateCalled); 160 | 161 | t.m_eReturnStatus = BH_SUCCESS; 162 | t.tick(); 163 | CHECK_EQUAL(1, t.m_iTerminateCalled); 164 | }; 165 | 166 | // ============================================================================ 167 | 168 | class Decorator : public Behavior 169 | { 170 | protected: 171 | Behavior* m_pChild; 172 | 173 | public: 174 | Decorator(Behavior* child) : m_pChild(child) {} 175 | }; 176 | 177 | class Repeat : public Decorator 178 | { 179 | public: 180 | Repeat(Behavior* child) 181 | : Decorator(child) 182 | { 183 | } 184 | 185 | void setCount(int count) 186 | { 187 | m_iLimit = count; 188 | } 189 | 190 | void onInitialize() 191 | { 192 | m_iCounter = 0; 193 | } 194 | 195 | Status update() 196 | { 197 | for (;;) 198 | { 199 | m_pChild->tick(); 200 | if (m_pChild->getStatus() == BH_RUNNING) break; 201 | if (m_pChild->getStatus() == BH_FAILURE) return BH_FAILURE; 202 | if (++m_iCounter == m_iLimit) return BH_SUCCESS; 203 | m_pChild->reset(); 204 | } 205 | return BH_INVALID; 206 | } 207 | 208 | protected: 209 | int m_iLimit; 210 | int m_iCounter; 211 | }; 212 | 213 | // ============================================================================ 214 | 215 | class Composite : public Behavior 216 | { 217 | public: 218 | void addChild(Behavior* child) { m_Children.push_back(child); } 219 | void removeChild(Behavior*); 220 | void clearChildren(); 221 | protected: 222 | typedef std::vector Behaviors; 223 | Behaviors m_Children; 224 | }; 225 | 226 | class Sequence : public Composite 227 | { 228 | protected: 229 | virtual ~Sequence() 230 | { 231 | } 232 | 233 | virtual void onInitialize() 234 | { 235 | m_CurrentChild = m_Children.begin(); 236 | } 237 | 238 | virtual Status update() 239 | { 240 | // Keep going until a child behavior says it's running. 241 | for (;;) 242 | { 243 | Status s = (*m_CurrentChild)->tick(); 244 | 245 | // If the child fails, or keeps running, do the same. 246 | if (s != BH_SUCCESS) 247 | { 248 | return s; 249 | } 250 | 251 | // Hit the end of the array, job done! 252 | if (++m_CurrentChild == m_Children.end()) 253 | { 254 | return BH_SUCCESS; 255 | } 256 | } 257 | } 258 | 259 | Behaviors::iterator m_CurrentChild; 260 | }; 261 | 262 | // ---------------------------------------------------------------------------- 263 | 264 | template 265 | class MockComposite : public COMPOSITE 266 | { 267 | public: 268 | MockComposite(size_t size) 269 | { 270 | for (size_t i=0; i(COMPOSITE::m_Children[index]); 288 | } 289 | }; 290 | 291 | typedef MockComposite MockSequence; 292 | 293 | TEST(StarterKit1, SequenceTwoChildrenFails) 294 | { 295 | MockSequence seq(2); 296 | 297 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 298 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 299 | 300 | seq[0].m_eReturnStatus = BH_FAILURE; 301 | CHECK_EQUAL(seq.tick(), BH_FAILURE); 302 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 303 | CHECK_EQUAL(0, seq[1].m_iInitializeCalled); 304 | } 305 | 306 | TEST(StarterKit1, SequenceTwoChildrenContinues) 307 | { 308 | MockSequence seq(2); 309 | 310 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 311 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 312 | CHECK_EQUAL(0, seq[1].m_iInitializeCalled); 313 | 314 | seq[0].m_eReturnStatus = BH_SUCCESS; 315 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 316 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 317 | CHECK_EQUAL(1, seq[1].m_iInitializeCalled); 318 | } 319 | 320 | TEST(StarterKit1, SequenceOneChildPassThrough) 321 | { 322 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 323 | for (int i=0; i<2; ++i) 324 | { 325 | MockSequence seq(1); 326 | 327 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 328 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 329 | 330 | seq[0].m_eReturnStatus = status[i]; 331 | CHECK_EQUAL(seq.tick(), status[i]); 332 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 333 | } 334 | } 335 | 336 | 337 | // ============================================================================ 338 | 339 | class Selector : public Composite 340 | { 341 | protected: 342 | virtual ~Selector() 343 | { 344 | } 345 | 346 | virtual void onInitialize() 347 | { 348 | m_Current = m_Children.begin(); 349 | } 350 | 351 | virtual Status update() 352 | { 353 | // Keep going until a child behavior says its running. 354 | for (;;) 355 | { 356 | Status s = (*m_Current)->tick(); 357 | 358 | // If the child succeeds, or keeps running, do the same. 359 | if (s != BH_FAILURE) 360 | { 361 | return s; 362 | } 363 | 364 | // Hit the end of the array, it didn't end well... 365 | if (++m_Current == m_Children.end()) 366 | { 367 | return BH_FAILURE; 368 | } 369 | } 370 | } 371 | 372 | Behaviors::iterator m_Current; 373 | }; 374 | 375 | // ---------------------------------------------------------------------------- 376 | 377 | typedef MockComposite MockSelector; 378 | 379 | TEST(StarterKit1, SelectorTwoChildrenContinues) 380 | { 381 | MockSelector seq(2); 382 | 383 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 384 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 385 | 386 | seq[0].m_eReturnStatus = BH_FAILURE; 387 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 388 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 389 | } 390 | 391 | TEST(StarterKit1, SelectorTwoChildrenSucceeds) 392 | { 393 | MockSelector seq(2); 394 | 395 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 396 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 397 | 398 | seq[0].m_eReturnStatus = BH_SUCCESS; 399 | CHECK_EQUAL(seq.tick(), BH_SUCCESS); 400 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 401 | } 402 | 403 | TEST(StarterKit1, SelectorOneChildPassThrough) 404 | { 405 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 406 | for (int i=0; i<2; ++i) 407 | { 408 | MockSelector seq(1); 409 | 410 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 411 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 412 | 413 | seq[0].m_eReturnStatus = status[i]; 414 | CHECK_EQUAL(seq.tick(), status[i]); 415 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 416 | } 417 | } 418 | 419 | class Parallel : public Composite 420 | { 421 | public: 422 | enum Policy 423 | { 424 | RequireOne, 425 | RequireAll, 426 | }; 427 | 428 | Parallel(Policy forSuccess, Policy forFailure) 429 | : m_eSuccessPolicy(forSuccess) 430 | , m_eFailurePolicy(forFailure) 431 | { 432 | } 433 | 434 | virtual ~Parallel() {} 435 | 436 | protected: 437 | Policy m_eSuccessPolicy; 438 | Policy m_eFailurePolicy; 439 | 440 | virtual Status update() 441 | { 442 | size_t iSuccessCount = 0, iFailureCount = 0; 443 | 444 | for (Behaviors::iterator it = m_Children.begin(); it != m_Children.end(); ++it) 445 | { 446 | Behavior& b = **it; 447 | if (!b.isTerminated()) 448 | { 449 | b.tick(); 450 | } 451 | 452 | if (b.getStatus() == BH_SUCCESS) 453 | { 454 | ++iSuccessCount; 455 | if (m_eSuccessPolicy == RequireOne) 456 | { 457 | return BH_SUCCESS; 458 | } 459 | } 460 | 461 | if (b.getStatus() == BH_FAILURE) 462 | { 463 | ++iFailureCount; 464 | if (m_eFailurePolicy == RequireOne) 465 | { 466 | return BH_FAILURE; 467 | } 468 | } 469 | } 470 | 471 | if (m_eFailurePolicy == RequireAll && iFailureCount == m_Children.size()) 472 | { 473 | return BH_FAILURE; 474 | } 475 | 476 | if (m_eSuccessPolicy == RequireAll && iSuccessCount == m_Children.size()) 477 | { 478 | return BH_SUCCESS; 479 | } 480 | 481 | return BH_RUNNING; 482 | } 483 | 484 | virtual void onTerminate(Status) 485 | { 486 | for (Behaviors::iterator it = m_Children.begin(); it != m_Children.end(); ++it) 487 | { 488 | Behavior& b = **it; 489 | if (b.isRunning()) 490 | { 491 | b.abort(); 492 | } 493 | } 494 | } 495 | }; 496 | 497 | 498 | TEST(StarterKit1, ParallelSucceedRequireAll) 499 | { 500 | Parallel parallel(Parallel::RequireAll, Parallel::RequireOne); 501 | MockBehavior children[2]; 502 | parallel.addChild(&children[0]); 503 | parallel.addChild(&children[1]); 504 | 505 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 506 | children[0].m_eReturnStatus = BH_SUCCESS; 507 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 508 | children[1].m_eReturnStatus = BH_SUCCESS; 509 | CHECK_EQUAL(BH_SUCCESS, parallel.tick()); 510 | } 511 | 512 | TEST(StarterKit1, ParallelSucceedRequireOne) 513 | { 514 | Parallel parallel(Parallel::RequireOne, Parallel::RequireAll); 515 | MockBehavior children[2]; 516 | parallel.addChild(&children[0]); 517 | parallel.addChild(&children[1]); 518 | 519 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 520 | children[0].m_eReturnStatus = BH_SUCCESS; 521 | CHECK_EQUAL(BH_SUCCESS, parallel.tick()); 522 | } 523 | 524 | TEST(StarterKit1, ParallelFailureRequireAll) 525 | { 526 | Parallel parallel(Parallel::RequireOne, Parallel::RequireAll); 527 | MockBehavior children[2]; 528 | parallel.addChild(&children[0]); 529 | parallel.addChild(&children[1]); 530 | 531 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 532 | children[0].m_eReturnStatus = BH_FAILURE; 533 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 534 | children[1].m_eReturnStatus = BH_FAILURE; 535 | CHECK_EQUAL(BH_FAILURE, parallel.tick()); 536 | } 537 | 538 | TEST(StarterKit1, ParallelFailureRequireOne) 539 | { 540 | Parallel parallel(Parallel::RequireAll, Parallel::RequireOne); 541 | MockBehavior children[2]; 542 | parallel.addChild(&children[0]); 543 | parallel.addChild(&children[1]); 544 | 545 | CHECK_EQUAL(BH_RUNNING, parallel.tick()); 546 | children[0].m_eReturnStatus = BH_FAILURE; 547 | CHECK_EQUAL(BH_FAILURE, parallel.tick()); 548 | } 549 | 550 | class Monitor : public Parallel 551 | { 552 | public: 553 | Monitor() 554 | : Parallel(Parallel::RequireOne, Parallel::RequireOne) 555 | { 556 | } 557 | 558 | void addCondition(Behavior* condition) 559 | { 560 | m_Children.insert(m_Children.begin(), condition); 561 | } 562 | 563 | void addAction(Behavior* action) 564 | { 565 | m_Children.push_back(action); 566 | } 567 | }; 568 | 569 | 570 | // ============================================================================ 571 | 572 | class ActiveSelector : public Selector 573 | { 574 | protected: 575 | 576 | virtual void onInitialize() 577 | { 578 | m_Current = m_Children.end(); 579 | } 580 | 581 | virtual Status update() 582 | { 583 | Behaviors::iterator previous = m_Current; 584 | 585 | Selector::onInitialize(); 586 | Status result = Selector::update(); 587 | 588 | if (previous != m_Children.end() && m_Current != previous) 589 | { 590 | (*previous)->onTerminate(BH_ABORTED); 591 | } 592 | return result; 593 | } 594 | }; 595 | 596 | typedef MockComposite MockActiveSelector; 597 | 598 | 599 | TEST(StarterKit1, ActiveBinarySelector) 600 | { 601 | MockActiveSelector sel(2); 602 | 603 | sel[0].m_eReturnStatus = BH_FAILURE; 604 | sel[1].m_eReturnStatus = BH_RUNNING; 605 | 606 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 607 | CHECK_EQUAL(1, sel[0].m_iInitializeCalled); 608 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 609 | CHECK_EQUAL(1, sel[1].m_iInitializeCalled); 610 | CHECK_EQUAL(0, sel[1].m_iTerminateCalled); 611 | 612 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 613 | CHECK_EQUAL(2, sel[0].m_iInitializeCalled); 614 | CHECK_EQUAL(2, sel[0].m_iTerminateCalled); 615 | CHECK_EQUAL(1, sel[1].m_iInitializeCalled); 616 | CHECK_EQUAL(0, sel[1].m_iTerminateCalled); 617 | 618 | sel[0].m_eReturnStatus = BH_RUNNING; 619 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 620 | CHECK_EQUAL(3, sel[0].m_iInitializeCalled); 621 | CHECK_EQUAL(2, sel[0].m_iTerminateCalled); 622 | CHECK_EQUAL(1, sel[1].m_iInitializeCalled); 623 | CHECK_EQUAL(1, sel[1].m_iTerminateCalled); 624 | 625 | sel[0].m_eReturnStatus = BH_SUCCESS; 626 | CHECK_EQUAL(sel.tick(), BH_SUCCESS); 627 | CHECK_EQUAL(3, sel[0].m_iInitializeCalled); 628 | CHECK_EQUAL(3, sel[0].m_iTerminateCalled); 629 | CHECK_EQUAL(1, sel[1].m_iInitializeCalled); 630 | CHECK_EQUAL(1, sel[1].m_iTerminateCalled); 631 | } 632 | 633 | } // namespace bt1 634 | -------------------------------------------------------------------------------- /BehaviorTreeEvent.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * This file is part of the Behavior Tree Starter Kit. 3 | * 4 | * Copyright (c) 2012, AiGameDev.com 5 | * 6 | * Credits: Alex J. Champandard 7 | *****************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Shared.h" 13 | #include "Test.h" 14 | 15 | namespace bt4 16 | { 17 | 18 | // ============================================================================ 19 | 20 | enum Status 21 | { 22 | BH_INVALID, 23 | BH_SUCCESS, 24 | BH_FAILURE, 25 | BH_RUNNING, 26 | BH_SUSPENDED, 27 | }; 28 | 29 | typedef std::function BehaviorObserver; 30 | 31 | class Behavior 32 | { 33 | public: 34 | Behavior() 35 | : m_eStatus(BH_INVALID) 36 | { 37 | } 38 | 39 | virtual ~Behavior() 40 | { 41 | } 42 | 43 | Status tick() 44 | { 45 | if (m_eStatus == BH_INVALID) 46 | { 47 | onInitialize(); 48 | } 49 | 50 | m_eStatus = update(); 51 | 52 | if (m_eStatus != BH_RUNNING) 53 | { 54 | onTerminate(m_eStatus); 55 | } 56 | return m_eStatus; 57 | } 58 | 59 | virtual Status update() = 0; 60 | 61 | virtual void onInitialize() {} 62 | virtual void onTerminate(Status) {} 63 | 64 | Status m_eStatus; 65 | BehaviorObserver m_Observer; 66 | }; 67 | 68 | class BehaviorTree 69 | { 70 | public: 71 | 72 | void start(Behavior& bh, BehaviorObserver* observer = NULL) 73 | { 74 | if (observer != NULL) 75 | { 76 | bh.m_Observer = *observer; 77 | } 78 | m_Behaviors.push_front(&bh); 79 | } 80 | 81 | void stop(Behavior& bh, Status result) 82 | { 83 | ASSERT(result != BH_RUNNING); 84 | bh.m_eStatus = result; 85 | 86 | if (bh.m_Observer) 87 | { 88 | bh.m_Observer(result); 89 | } 90 | } 91 | 92 | void tick() 93 | { 94 | // Insert an end-of-update marker into the list of tasks. 95 | m_Behaviors.push_back(NULL); 96 | 97 | // Keep going updating tasks until we encounter the marker. 98 | while (step()) 99 | { 100 | continue; 101 | } 102 | } 103 | 104 | bool step() 105 | { 106 | Behavior* current = m_Behaviors.front(); 107 | m_Behaviors.pop_front(); 108 | 109 | // If this is the end-of-update marker, stop processing. 110 | if (current == NULL) 111 | { 112 | return false; 113 | } 114 | 115 | // Perform the update on this individual task. 116 | current->tick(); 117 | 118 | // Process the observer if the task terminated. 119 | if (current->m_eStatus != BH_RUNNING && current->m_Observer) 120 | { 121 | current->m_Observer(current->m_eStatus); 122 | } 123 | else // Otherwise drop it into the queue for the next tick(). 124 | { 125 | m_Behaviors.push_back(current); 126 | } 127 | return true; 128 | } 129 | 130 | protected: 131 | std::deque m_Behaviors; 132 | }; 133 | 134 | // ---------------------------------------------------------------------------- 135 | 136 | struct MockBehavior : public Behavior 137 | { 138 | int m_iInitializeCalled; 139 | int m_iTerminateCalled; 140 | int m_iUpdateCalled; 141 | Status m_eReturnStatus; 142 | Status m_eTerminateStatus; 143 | 144 | MockBehavior() 145 | : m_iInitializeCalled(0) 146 | , m_iTerminateCalled(0) 147 | , m_iUpdateCalled(0) 148 | , m_eReturnStatus(BH_RUNNING) 149 | , m_eTerminateStatus(BH_INVALID) 150 | { 151 | } 152 | 153 | virtual ~MockBehavior() 154 | { 155 | } 156 | 157 | virtual void onInitialize() 158 | { 159 | ++m_iInitializeCalled; 160 | } 161 | 162 | virtual void onTerminate(Status s) 163 | { 164 | ++m_iTerminateCalled; 165 | m_eTerminateStatus = s; 166 | } 167 | 168 | virtual Status update() 169 | { 170 | ++m_iUpdateCalled; 171 | return m_eReturnStatus; 172 | } 173 | }; 174 | 175 | TEST(StarterKit4, TaskInitialize) 176 | { 177 | MockBehavior t; 178 | BehaviorTree bt; 179 | 180 | bt.start(t); 181 | CHECK_EQUAL(0, t.m_iInitializeCalled); 182 | 183 | bt.tick(); 184 | CHECK_EQUAL(1, t.m_iInitializeCalled); 185 | }; 186 | 187 | TEST(StarterKit4, TaskUpdate) 188 | { 189 | MockBehavior t; 190 | BehaviorTree bt; 191 | 192 | bt.start(t); 193 | bt.tick(); 194 | CHECK_EQUAL(1, t.m_iUpdateCalled); 195 | 196 | t.m_eReturnStatus = BH_SUCCESS; 197 | bt.tick(); 198 | CHECK_EQUAL(2, t.m_iUpdateCalled); 199 | }; 200 | 201 | TEST(StarterKit4, TaskTerminate) 202 | { 203 | MockBehavior t; 204 | BehaviorTree bt; 205 | 206 | bt.start(t); 207 | bt.tick(); 208 | CHECK_EQUAL(0, t.m_iTerminateCalled); 209 | 210 | t.m_eReturnStatus = BH_SUCCESS; 211 | bt.tick(); 212 | CHECK_EQUAL(1, t.m_iTerminateCalled); 213 | }; 214 | 215 | 216 | // ============================================================================ 217 | 218 | class Composite : public Behavior 219 | { 220 | public: 221 | typedef std::vector Behaviors; 222 | Behaviors m_Children; 223 | }; 224 | 225 | class Sequence : public Composite 226 | { 227 | public: 228 | Sequence(BehaviorTree& bt) 229 | : m_pBehaviorTree(&bt) 230 | { 231 | } 232 | 233 | protected: 234 | BehaviorTree* m_pBehaviorTree; 235 | 236 | virtual void onInitialize() 237 | { 238 | m_Current = m_Children.begin(); 239 | BehaviorObserver observer = std::bind(&Sequence::onChildComplete, this, std::placeholders::_1); 240 | m_pBehaviorTree->start(**m_Current, &observer); 241 | } 242 | 243 | void onChildComplete(Status) 244 | { 245 | Behavior& child = **m_Current; 246 | if (child.m_eStatus == BH_FAILURE) 247 | { 248 | m_pBehaviorTree->stop(*this, BH_FAILURE); 249 | return; 250 | } 251 | 252 | ASSERT(child.m_eStatus == BH_SUCCESS); 253 | if (++m_Current == m_Children.end()) 254 | { 255 | m_pBehaviorTree->stop(*this, BH_SUCCESS); 256 | } 257 | else 258 | { 259 | BehaviorObserver observer = std::bind(&Sequence::onChildComplete, this, std::placeholders::_1); 260 | m_pBehaviorTree->start(**m_Current, &observer); 261 | } 262 | } 263 | 264 | virtual Status update() 265 | { 266 | return BH_RUNNING; 267 | } 268 | 269 | Behaviors::iterator m_Current; 270 | }; 271 | 272 | // ---------------------------------------------------------------------------- 273 | 274 | template 275 | class MockComposite : public COMPOSITE 276 | { 277 | public: 278 | MockComposite(BehaviorTree& bt, size_t size) 279 | : COMPOSITE(bt) 280 | { 281 | for (size_t i=0; i(COMPOSITE::m_Children[index]); 299 | } 300 | }; 301 | 302 | typedef MockComposite MockSequence; 303 | 304 | TEST(StarterKit4, SequenceOnePassThrough) 305 | { 306 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 307 | for (int i=0; i<2; ++i) 308 | { 309 | BehaviorTree bt; 310 | MockSequence seq(bt, 1); 311 | 312 | bt.start(seq); 313 | bt.tick(); 314 | CHECK_EQUAL(seq.m_eStatus, BH_RUNNING); 315 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 316 | 317 | seq[0].m_eReturnStatus = status[i]; 318 | bt.tick(); 319 | CHECK_EQUAL(seq.m_eStatus, status[i]); 320 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 321 | } 322 | } 323 | 324 | TEST(StarterKit4, SequenceTwoFails) 325 | { 326 | BehaviorTree bt; 327 | MockSequence seq(bt, 2); 328 | 329 | bt.start(seq); 330 | bt.tick(); 331 | CHECK_EQUAL(seq.m_eStatus, BH_RUNNING); 332 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 333 | 334 | seq[0].m_eReturnStatus = BH_FAILURE; 335 | bt.tick(); 336 | CHECK_EQUAL(seq.m_eStatus, BH_FAILURE); 337 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 338 | } 339 | 340 | TEST(StarterKit4, SequenceTwoContinues) 341 | { 342 | BehaviorTree bt; 343 | MockSequence seq(bt, 2); 344 | 345 | bt.start(seq); 346 | bt.tick(); 347 | CHECK_EQUAL(seq.m_eStatus, BH_RUNNING); 348 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 349 | 350 | seq[0].m_eReturnStatus = BH_SUCCESS; 351 | bt.tick(); 352 | CHECK_EQUAL(seq.m_eStatus, BH_RUNNING); 353 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 354 | } 355 | 356 | } // namespace bt4 357 | -------------------------------------------------------------------------------- /BehaviorTreeOptimized.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * This file is part of the Behavior Tree Starter Kit. 3 | * 4 | * Copyright (c) 2012, AiGameDev.com 5 | * 6 | * Credits: Alex J. Champandard 7 | *****************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Shared.h" 13 | #include "Test.h" 14 | 15 | namespace bt3 16 | { 17 | 18 | // ============================================================================ 19 | 20 | enum Status 21 | { 22 | BH_INVALID, 23 | BH_SUCCESS, 24 | BH_FAILURE, 25 | BH_RUNNING, 26 | }; 27 | 28 | class Behavior 29 | { 30 | public: 31 | Behavior() 32 | : m_eStatus(BH_INVALID) 33 | { 34 | } 35 | 36 | virtual ~Behavior() 37 | { 38 | } 39 | 40 | Status tick() 41 | { 42 | if (m_eStatus == BH_INVALID) 43 | onInitialize(); 44 | 45 | m_eStatus = update(); 46 | 47 | if (m_eStatus != BH_RUNNING) 48 | onTerminate(m_eStatus); 49 | 50 | return m_eStatus; 51 | } 52 | 53 | protected: 54 | virtual Status update() = 0; 55 | 56 | virtual void onInitialize() {} 57 | virtual void onTerminate(Status) {} 58 | 59 | Status m_eStatus; 60 | }; 61 | 62 | typedef std::vector Behaviors; 63 | 64 | // ---------------------------------------------------------------------------- 65 | 66 | const size_t k_MaxBehaviorTreeMemory = 8192; 67 | 68 | class BehaviorTree 69 | { 70 | public: 71 | BehaviorTree() 72 | : m_pBuffer(new uint8_t[k_MaxBehaviorTreeMemory]) 73 | , m_iOffset(0) 74 | { 75 | } 76 | 77 | ~BehaviorTree() 78 | { 79 | delete [] m_pBuffer; 80 | } 81 | 82 | template 83 | T& allocate() 84 | { 85 | T* node = new ((void*)((uintptr_t)m_pBuffer + m_iOffset)) T; 86 | m_iOffset += sizeof(T); 87 | ASSERT(m_iOffset < k_MaxBehaviorTreeMemory); 88 | return *node; 89 | } 90 | 91 | protected: 92 | uint8_t* m_pBuffer; 93 | size_t m_iOffset; 94 | }; 95 | 96 | // ---------------------------------------------------------------------------- 97 | 98 | struct MockBehavior : public Behavior 99 | { 100 | int m_iInitializeCalled; 101 | int m_iTerminateCalled; 102 | int m_iUpdateCalled; 103 | Status m_eReturnStatus; 104 | Status m_eTerminateStatus; 105 | 106 | MockBehavior() 107 | : m_iInitializeCalled(0) 108 | , m_iTerminateCalled(0) 109 | , m_iUpdateCalled(0) 110 | , m_eReturnStatus(BH_RUNNING) 111 | , m_eTerminateStatus(BH_INVALID) 112 | { 113 | } 114 | 115 | virtual ~MockBehavior() 116 | { 117 | } 118 | 119 | virtual void onInitialize() 120 | { 121 | ++m_iInitializeCalled; 122 | } 123 | 124 | virtual void onTerminate(Status s) 125 | { 126 | ++m_iTerminateCalled; 127 | m_eTerminateStatus = s; 128 | } 129 | 130 | virtual Status update() 131 | { 132 | ++m_iUpdateCalled; 133 | return m_eReturnStatus; 134 | } 135 | }; 136 | 137 | TEST(StarterKit2, TaskInitialize) 138 | { 139 | BehaviorTree bt; 140 | MockBehavior& t = bt.allocate(); 141 | CHECK_EQUAL(0, t.m_iInitializeCalled); 142 | 143 | t.tick(); 144 | CHECK_EQUAL(1, t.m_iInitializeCalled); 145 | }; 146 | 147 | TEST(StarterKit2, TaskUpdate) 148 | { 149 | BehaviorTree bt; 150 | MockBehavior& t = bt.allocate(); 151 | CHECK_EQUAL(0, t.m_iUpdateCalled); 152 | 153 | t.tick(); 154 | CHECK_EQUAL(1, t.m_iUpdateCalled); 155 | }; 156 | 157 | TEST(StarterKit2, TaskTerminate) 158 | { 159 | BehaviorTree bt; 160 | MockBehavior& t = bt.allocate(); 161 | 162 | t.tick(); 163 | CHECK_EQUAL(0, t.m_iTerminateCalled); 164 | 165 | t.m_eReturnStatus = BH_SUCCESS; 166 | t.tick(); 167 | CHECK_EQUAL(1, t.m_iTerminateCalled); 168 | }; 169 | 170 | 171 | // ============================================================================ 172 | 173 | const size_t k_MaxChildrenPerComposite = 7; 174 | 175 | class Composite : public Behavior 176 | { 177 | public: 178 | Composite() 179 | : m_ChildCount(0) 180 | { 181 | } 182 | 183 | void addChild(Behavior& child) 184 | { 185 | ASSERT(m_ChildCount < k_MaxChildrenPerComposite); 186 | ptrdiff_t p = (uintptr_t)&child - (uintptr_t)this; 187 | ASSERT(p < std::numeric_limits::max()); 188 | m_Children[m_ChildCount++] = static_cast(p); 189 | } 190 | 191 | Behavior& getChild(size_t index) 192 | { 193 | ASSERT(index < m_ChildCount); 194 | return *(Behavior*)((uintptr_t)this + m_Children[index]); 195 | } 196 | 197 | size_t getChildCount() const 198 | { 199 | return m_ChildCount; 200 | } 201 | 202 | private: 203 | uint16_t m_Children[k_MaxChildrenPerComposite]; 204 | uint16_t m_ChildCount; 205 | }; 206 | 207 | class Sequence : public Composite 208 | { 209 | protected: 210 | virtual ~Sequence() 211 | { 212 | } 213 | 214 | virtual void onInitialize() 215 | { 216 | m_Current = 0; 217 | } 218 | 219 | virtual Status update() 220 | { 221 | for (;;) 222 | { 223 | Status s = getChild(m_Current).tick(); 224 | if (s != BH_SUCCESS) 225 | { 226 | return s; 227 | } 228 | ASSERT(m_Current < std::numeric_limits::max() - 1); 229 | if (++m_Current == getChildCount()) 230 | { 231 | return BH_SUCCESS; 232 | } 233 | } 234 | } 235 | 236 | uint16_t m_Current; 237 | }; 238 | 239 | // ---------------------------------------------------------------------------- 240 | 241 | template 242 | class MockComposite : public COMPOSITE 243 | { 244 | public: 245 | void initialize(BehaviorTree& tree, size_t size) 246 | { 247 | for (size_t i=0; i(); 250 | COMPOSITE::addChild(t); 251 | } 252 | } 253 | 254 | MockBehavior& operator[](size_t index) 255 | { 256 | ASSERT(index < COMPOSITE::getChildCount()); 257 | return *static_cast(&COMPOSITE::getChild(index)); 258 | } 259 | }; 260 | 261 | typedef MockComposite MockSequence; 262 | 263 | TEST(StarterKit2, SequenceOnePassThrough) 264 | { 265 | BehaviorTree bt; 266 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 267 | for (int i=0; i<2; ++i) 268 | { 269 | MockSequence& seq = bt.allocate(); 270 | seq.initialize(bt, 1); 271 | 272 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 273 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 274 | 275 | seq[0].m_eReturnStatus = status[i]; 276 | CHECK_EQUAL(seq.tick(), status[i]); 277 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 278 | } 279 | } 280 | 281 | TEST(StarterKit2, SequenceTwoFails) 282 | { 283 | BehaviorTree bt; 284 | MockSequence& seq = bt.allocate(); 285 | seq.initialize(bt, 2); 286 | 287 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 288 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 289 | 290 | seq[0].m_eReturnStatus = BH_FAILURE; 291 | CHECK_EQUAL(seq.tick(), BH_FAILURE); 292 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 293 | } 294 | 295 | TEST(StarterKit2, SequenceTwoContinues) 296 | { 297 | BehaviorTree bt; 298 | MockSequence& seq = bt.allocate(); 299 | seq.initialize(bt, 2); 300 | 301 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 302 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 303 | 304 | seq[0].m_eReturnStatus = BH_SUCCESS; 305 | CHECK_EQUAL(seq.tick(), BH_RUNNING); 306 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 307 | } 308 | 309 | 310 | // ============================================================================ 311 | 312 | class Selector : public Composite 313 | { 314 | protected: 315 | virtual ~Selector() 316 | { 317 | } 318 | 319 | virtual void onInitialize() 320 | { 321 | m_Current = 0; 322 | } 323 | 324 | virtual Status update() 325 | { 326 | for (;;) 327 | { 328 | Status s = getChild(m_Current).tick(); 329 | if (s != BH_FAILURE) 330 | { 331 | return s; 332 | } 333 | ASSERT(m_Current < std::numeric_limits::max() - 1); 334 | if (++m_Current == getChildCount()) 335 | { 336 | return BH_FAILURE; 337 | } 338 | } 339 | } 340 | 341 | uint16_t m_Current; 342 | }; 343 | 344 | typedef MockComposite MockSelector; 345 | 346 | // ---------------------------------------------------------------------------- 347 | 348 | TEST(StarterKit2, SelectorOnePassThrough) 349 | { 350 | BehaviorTree bt; 351 | 352 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 353 | for (int i=0; i<2; ++i) 354 | { 355 | MockSelector& sel = bt.allocate(); 356 | sel.initialize(bt, 1); 357 | 358 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 359 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 360 | 361 | sel[0].m_eReturnStatus = status[i]; 362 | CHECK_EQUAL(sel.tick(), status[i]); 363 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 364 | } 365 | } 366 | 367 | TEST(StarterKit2, SelectorTwoContinues) 368 | { 369 | BehaviorTree bt; 370 | MockSelector& sel = bt.allocate(); 371 | sel.initialize(bt, 2); 372 | 373 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 374 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 375 | 376 | sel[0].m_eReturnStatus = BH_FAILURE; 377 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 378 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 379 | } 380 | 381 | TEST(StarterKit2, SelectorTwoSucceeds) 382 | { 383 | BehaviorTree bt; 384 | MockSelector& sel = bt.allocate(); 385 | sel.initialize(bt, 2); 386 | 387 | CHECK_EQUAL(sel.tick(), BH_RUNNING); 388 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 389 | 390 | sel[0].m_eReturnStatus = BH_SUCCESS; 391 | CHECK_EQUAL(sel.tick(), BH_SUCCESS); 392 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 393 | } 394 | 395 | } // namespace bt3 396 | -------------------------------------------------------------------------------- /BehaviorTreeShared.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * This file is part of the Behavior Tree Starter Kit. 3 | * 4 | * Copyright (c) 2012, AiGameDev.com 5 | * 6 | * Credits: Alex J. Champandard 7 | *****************************************************************************/ 8 | 9 | #include 10 | #include "Shared.h" 11 | #include "Test.h" 12 | 13 | namespace bt2 14 | { 15 | 16 | // ============================================================================ 17 | 18 | class Node; 19 | class Task; 20 | 21 | enum Status 22 | { 23 | BH_INVALID, 24 | BH_SUCCESS, 25 | BH_FAILURE, 26 | BH_RUNNING, 27 | }; 28 | 29 | class Node 30 | { 31 | public: 32 | virtual Task* create() = 0; 33 | virtual void destroy(Task*) = 0; 34 | 35 | virtual ~Node() {} 36 | }; 37 | 38 | class Task 39 | { 40 | public: 41 | Task(Node& node) 42 | : m_pNode(&node) 43 | { 44 | } 45 | 46 | virtual ~Task() {} 47 | 48 | virtual Status update() = 0; 49 | 50 | virtual void onInitialize() {} 51 | virtual void onTerminate(Status) {} 52 | 53 | protected: 54 | Node* m_pNode; 55 | }; 56 | 57 | class Behavior 58 | { 59 | protected: 60 | Task* m_pTask; 61 | Node* m_pNode; 62 | Status m_eStatus; 63 | 64 | public: 65 | Behavior() 66 | : m_pTask(NULL) 67 | , m_pNode(NULL) 68 | , m_eStatus(BH_INVALID) 69 | { 70 | } 71 | Behavior(Node& node) 72 | : m_pTask(NULL) 73 | , m_pNode(NULL) 74 | , m_eStatus(BH_INVALID) 75 | { 76 | setup(node); 77 | } 78 | ~Behavior() 79 | { 80 | m_eStatus = BH_INVALID; 81 | teardown(); 82 | } 83 | 84 | void setup(Node& node) 85 | { 86 | teardown(); 87 | 88 | m_pNode = &node; 89 | m_pTask = node.create(); 90 | } 91 | void teardown() 92 | { 93 | if (m_pTask == NULL) 94 | { 95 | return; 96 | } 97 | 98 | ASSERT(m_eStatus != BH_RUNNING); 99 | m_pNode->destroy(m_pTask); 100 | m_pTask = NULL; 101 | } 102 | 103 | Status tick() 104 | { 105 | if (m_eStatus == BH_INVALID) 106 | { 107 | m_pTask->onInitialize(); 108 | } 109 | 110 | m_eStatus = m_pTask->update(); 111 | 112 | if (m_eStatus != BH_RUNNING) 113 | { 114 | m_pTask->onTerminate(m_eStatus); 115 | } 116 | 117 | return m_eStatus; 118 | } 119 | 120 | template 121 | TASK* get() const 122 | { 123 | // NOTE: It's safe to static_cast, the caller almost 124 | // always knows what kind of task type it is. 125 | return dynamic_cast(m_pTask); 126 | } 127 | }; 128 | 129 | // ---------------------------------------------------------------------------- 130 | 131 | struct MockTask : public Task 132 | { 133 | int m_iInitializeCalled; 134 | int m_iTerminateCalled; 135 | int m_iUpdateCalled; 136 | Status m_eReturnStatus; 137 | Status m_eTerminateStatus; 138 | 139 | MockTask(Node& node) 140 | : Task(node) 141 | , m_iInitializeCalled(0) 142 | , m_iTerminateCalled(0) 143 | , m_iUpdateCalled(0) 144 | , m_eReturnStatus(BH_RUNNING) 145 | , m_eTerminateStatus(BH_INVALID) 146 | { 147 | } 148 | 149 | virtual void onInitialize() 150 | { 151 | ++m_iInitializeCalled; 152 | } 153 | 154 | virtual void onTerminate(Status s) 155 | { 156 | ++m_iTerminateCalled; 157 | m_eTerminateStatus = s; 158 | } 159 | 160 | virtual Status update() 161 | { 162 | ++m_iUpdateCalled; 163 | return m_eReturnStatus; 164 | } 165 | }; 166 | 167 | struct MockNode : public Node 168 | { 169 | virtual void destroy(Task*) {} 170 | virtual Task* create() 171 | { 172 | m_pTask = new MockTask(*this); 173 | return m_pTask; 174 | } 175 | 176 | virtual ~MockNode() 177 | { 178 | delete m_pTask; 179 | } 180 | 181 | MockNode() 182 | : m_pTask(NULL) 183 | { 184 | } 185 | 186 | MockTask* m_pTask; 187 | }; 188 | 189 | TEST(StarterKit3, TaskInitialize) 190 | { 191 | MockNode n; 192 | Behavior b(n); 193 | 194 | MockTask* t = b.get(); 195 | CHECK_EQUAL(0, t->m_iInitializeCalled); 196 | 197 | b.tick(); 198 | CHECK_EQUAL(1, t->m_iInitializeCalled); 199 | }; 200 | 201 | TEST(StarterKit3, TaskUpdate) 202 | { 203 | MockNode n; 204 | Behavior b(n); 205 | 206 | MockTask* t = b.get(); 207 | CHECK_EQUAL(0, t->m_iUpdateCalled); 208 | 209 | b.tick(); 210 | CHECK_EQUAL(1, t->m_iUpdateCalled); 211 | }; 212 | 213 | TEST(StarterKit3, TaskTerminate) 214 | { 215 | MockNode n; 216 | Behavior b(n); 217 | b.tick(); 218 | 219 | MockTask* t = b.get(); 220 | CHECK_EQUAL(0, t->m_iTerminateCalled); 221 | 222 | t->m_eReturnStatus = BH_SUCCESS; 223 | b.tick(); 224 | CHECK_EQUAL(1, t->m_iTerminateCalled); 225 | }; 226 | 227 | 228 | // ============================================================================ 229 | 230 | typedef std::vector Nodes; 231 | 232 | class Composite : public Node 233 | { 234 | public: 235 | Nodes m_Children; 236 | }; 237 | 238 | class Sequence : public Task 239 | { 240 | public: 241 | Sequence(Composite& node) 242 | : Task(node) 243 | { 244 | } 245 | 246 | Composite& getNode() 247 | { 248 | return *static_cast(m_pNode); 249 | } 250 | 251 | virtual void onInitialize() 252 | { 253 | m_CurrentChild = getNode().m_Children.begin(); 254 | m_CurrentBehavior.setup(**m_CurrentChild); 255 | } 256 | 257 | virtual Status update() 258 | { 259 | for (;;) 260 | { 261 | Status s = m_CurrentBehavior.tick(); 262 | if (s != BH_SUCCESS) 263 | { 264 | return s; 265 | } 266 | if (++m_CurrentChild == getNode().m_Children.end()) 267 | { 268 | return BH_SUCCESS; 269 | } 270 | m_CurrentBehavior.setup(**m_CurrentChild); 271 | } 272 | } 273 | 274 | protected: 275 | Nodes::iterator m_CurrentChild; 276 | Behavior m_CurrentBehavior; 277 | }; 278 | 279 | // ---------------------------------------------------------------------------- 280 | 281 | template 282 | class MockComposite : public Composite 283 | { 284 | public: 285 | MockComposite(size_t size) 286 | { 287 | for (size_t i=0; i(m_Children[index])->m_pTask; 305 | ASSERT(task != NULL); 306 | return *task; 307 | } 308 | 309 | virtual Task* create() 310 | { 311 | return new TASK(*this); 312 | } 313 | 314 | virtual void destroy(Task* task) 315 | { 316 | delete task; 317 | } 318 | }; 319 | 320 | typedef MockComposite MockSequence; 321 | 322 | TEST(StarterKit3, SequenceTwoFails) 323 | { 324 | MockSequence seq(2); 325 | Behavior bh(seq); 326 | 327 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 328 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 329 | 330 | seq[0].m_eReturnStatus = BH_FAILURE; 331 | CHECK_EQUAL(bh.tick(), BH_FAILURE); 332 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 333 | } 334 | 335 | TEST(StarterKit3, SequenceTwoContinues) 336 | { 337 | MockSequence seq(2); 338 | Behavior bh(seq); 339 | 340 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 341 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 342 | 343 | seq[0].m_eReturnStatus = BH_SUCCESS; 344 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 345 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 346 | } 347 | 348 | TEST(StarterKit3, SequenceOnePassThrough) 349 | { 350 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 351 | for (int i=0; i<2; ++i) 352 | { 353 | MockSequence seq(1); 354 | Behavior bh(seq); 355 | 356 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 357 | CHECK_EQUAL(0, seq[0].m_iTerminateCalled); 358 | 359 | seq[0].m_eReturnStatus = status[i]; 360 | CHECK_EQUAL(bh.tick(), status[i]); 361 | CHECK_EQUAL(1, seq[0].m_iTerminateCalled); 362 | } 363 | } 364 | 365 | 366 | // ============================================================================ 367 | 368 | class Selector : public Task 369 | { 370 | public: 371 | Selector(Composite& node) 372 | : Task(node) 373 | { 374 | } 375 | 376 | Composite& getNode() 377 | { 378 | return *static_cast(m_pNode); 379 | } 380 | 381 | virtual void onInitialize() 382 | { 383 | m_CurrentChild = getNode().m_Children.begin(); 384 | m_Behavior.setup(**m_CurrentChild); 385 | } 386 | 387 | virtual Status update() 388 | { 389 | for (;;) 390 | { 391 | Status s = m_Behavior.tick(); 392 | if (s != BH_FAILURE) 393 | { 394 | return s; 395 | } 396 | if (++m_CurrentChild == getNode().m_Children.end()) 397 | { 398 | return BH_FAILURE; 399 | } 400 | m_Behavior.setup(**m_CurrentChild); 401 | } 402 | } 403 | 404 | protected: 405 | Nodes::iterator m_CurrentChild; 406 | Behavior m_Behavior; 407 | }; 408 | 409 | typedef MockComposite MockSelector; 410 | 411 | TEST(StarterKit3, SelectorOnePassThrough) 412 | { 413 | Status status[2] = { BH_SUCCESS, BH_FAILURE }; 414 | for (int i=0; i<2; ++i) 415 | { 416 | MockSelector sel(1); 417 | Behavior bh(sel); 418 | 419 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 420 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 421 | 422 | sel[0].m_eReturnStatus = status[i]; 423 | CHECK_EQUAL(bh.tick(), status[i]); 424 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 425 | } 426 | } 427 | 428 | TEST(StarterKit3, SelectorTwoContinues) 429 | { 430 | MockSelector sel(2); 431 | Behavior bh(sel); 432 | 433 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 434 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 435 | 436 | sel[0].m_eReturnStatus = BH_FAILURE; 437 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 438 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 439 | } 440 | 441 | TEST(StarterKit3, SelectorTwoSucceeds) 442 | { 443 | MockSelector sel(2); 444 | Behavior bh(sel); 445 | 446 | CHECK_EQUAL(bh.tick(), BH_RUNNING); 447 | CHECK_EQUAL(0, sel[0].m_iTerminateCalled); 448 | 449 | sel[0].m_eReturnStatus = BH_SUCCESS; 450 | CHECK_EQUAL(bh.tick(), BH_SUCCESS); 451 | CHECK_EQUAL(1, sel[0].m_iTerminateCalled); 452 | } 453 | 454 | } // namespace bt2 455 | -------------------------------------------------------------------------------- /BehaviorTreeStarterKit.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {157B27E5-12E2-43F4-A99C-703B54052DE3} 15 | BehaviorTrees 16 | BehaviorTreeStarterKit 17 | 18 | 19 | 20 | Application 21 | true 22 | MultiByte 23 | 24 | 25 | Application 26 | false 27 | true 28 | MultiByte 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | AllRules.ruleset 42 | true 43 | 44 | 45 | AllRules.ruleset 46 | true 47 | 48 | 49 | 50 | Level4 51 | Disabled 52 | true 53 | true 54 | 55 | 56 | true 57 | 58 | 59 | 60 | 61 | Level4 62 | MaxSpeed 63 | true 64 | true 65 | true 66 | true 67 | 68 | 69 | true 70 | true 71 | true 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /BehaviorTreeStarterKit.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014, AiGameDev.com 2 | 3 | This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 4 | 5 | 6 | Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 7 | 8 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 9 | 10 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 11 | 12 | 3. This notice may not be removed or altered from any source distribution. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | btsk 2 | ==== 3 | 4 | Behavior Tree Starter Kit -------------------------------------------------------------------------------- /Shared.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARED_H 2 | #define SHARED_H 3 | 4 | #include 5 | 6 | #if defined(_MSC_VER) && (_MSC_VER >= 1600) // VS2010 7 | #define ASSERT(X) assert(X); __analysis_assume(X) 8 | #define ASSERT_MSG(X, M) ASSERT(X) 9 | #else 10 | #define ASSERT(X) assert(X) 11 | #define ASSERT_MSG(X, M) ASSERT(X) 12 | #endif 13 | 14 | #endif // SHARED_H 15 | -------------------------------------------------------------------------------- /Test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Test.h" 3 | 4 | using namespace test; 5 | 6 | int main() 7 | { 8 | TestSuite& testSuite = TestSuite::getInstance(); 9 | // testSuite.setVerbose(true); 10 | 11 | bool allPassed = testSuite.runAllTests(); 12 | std::cerr << (allPassed ? "Success." : "Failure!") << std::endl; 13 | return allPassed ? 0 : 1; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Shared.h" 10 | 11 | namespace test 12 | { 13 | typedef std::function Test; 14 | 15 | class TestFailedException 16 | { 17 | public: 18 | TestFailedException(std::string message) 19 | : m_message(message) 20 | { 21 | } 22 | 23 | std::string m_message; 24 | }; 25 | 26 | class TestSuite 27 | { 28 | private: 29 | typedef std::pair RegisteredTest; 30 | typedef std::vector RegisteredTests; 31 | 32 | public: 33 | TestSuite() 34 | : m_verbose(false) 35 | { 36 | } 37 | 38 | void registerTest(const std::string& testName, Test test) 39 | { 40 | m_tests.push_back(RegisteredTest(testName, test)); 41 | } 42 | 43 | void setVerbose(bool b) 44 | { 45 | m_verbose = b; 46 | } 47 | 48 | bool runAllTests() 49 | { 50 | bool allPassed = true; 51 | for (RegisteredTests::const_iterator test = m_tests.begin(); test != m_tests.end(); ++test) 52 | { 53 | if (m_verbose) 54 | { 55 | std::cout << test->first << " " << std::flush; 56 | } 57 | try 58 | { 59 | test->second(); 60 | if (m_verbose) 61 | { 62 | std::cout << "PASS" << std::endl << std::flush; 63 | } 64 | else 65 | { 66 | std::cout << "." << std::flush; 67 | } 68 | } 69 | catch (TestFailedException e) 70 | { 71 | allPassed = false; 72 | if (!m_verbose) 73 | { 74 | std::cout << test->first << " "; 75 | } 76 | std::cout << "FAIL: " << e.m_message << std::endl << std::flush; 77 | } 78 | } 79 | if (!m_verbose) 80 | { 81 | std::cout << std::endl << std::flush; 82 | } 83 | return allPassed; 84 | } 85 | 86 | static TestSuite& getInstance() 87 | { 88 | static TestSuite instance; 89 | return instance; 90 | } 91 | 92 | private: 93 | bool m_verbose; 94 | RegisteredTests m_tests; 95 | }; 96 | 97 | class AutoTestRegister 98 | { 99 | public: 100 | AutoTestRegister(const std::string& name, Test test) 101 | { 102 | TestSuite::getInstance().registerTest(name, test); 103 | } 104 | }; 105 | } 106 | 107 | #define TEST(SUITENAME, TESTNAME) \ 108 | void SUITENAME##_##TESTNAME(); \ 109 | static test::AutoTestRegister autoTestRegister_##SUITENAME##_##TESTNAME(#SUITENAME "_" #TESTNAME, SUITENAME##_##TESTNAME); \ 110 | void SUITENAME##_##TESTNAME() 111 | 112 | #define CHECK(X) \ 113 | if (!(X)) \ 114 | { \ 115 | std::ostringstream os; \ 116 | os << #X << " "; \ 117 | os << __FILE__ << ":" << __LINE__; \ 118 | throw test::TestFailedException(os.str()); \ 119 | } 120 | 121 | #define CHECK_EQUAL(X, Y) \ 122 | if (!((X) == (Y))) \ 123 | { \ 124 | std::ostringstream os; \ 125 | os << "(" << #X << " == " << #Y << ") "; \ 126 | os << "expected:" << (X) << " actual:" << (Y) << " "; \ 127 | os << __FILE__ << ":" << __LINE__; \ 128 | throw test::TestFailedException(os.str()); \ 129 | } 130 | 131 | #endif // TEST_H 132 | --------------------------------------------------------------------------------