Compare commits

...

111 Commits

Author SHA1 Message Date
Wilson E. Alvarez d2ca303c5e
Fix List<T> access
Due to upstream change:

	955d5affa8
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez fe597a5a46
Fix GCC warning for unhandled 'PACKED_VECTOR4_ARRAY' in switch
Due to upstream change:

	f9b488508c
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez d20d28be78
Update EditorPlugin header location
Due to upstream change:

	1bcbbe96c4
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez 2f1fd6fb62
Update EditorHelpBit calls
Due to upstream change:

	a714cb9f65
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez 022d95434d
Add missing MarginContainer header
Due to upstream change:

	7884d63281
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez ce28ed92d2
Fix bottom panel visbility calls
Due to upstream change:

	eb6ca91ba6
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez 8f294ac5ea
Migrate to Tree::set_custom_draw_callback
Due to upstream change:

	a32a2eaedc
2024-06-29 14:52:05 -04:00
Wilson E. Alvarez 9410031d45
Fix upstream EditorScale header locations
Due to upstream change:

	4b55c81eba
2024-06-29 14:52:05 -04:00
Serhii Snitsaruk 61b2db5a07
Bump version to 1.2-dev 2024-06-29 16:53:30 +02:00
Serhii Snitsaruk 15464e288c
GH: Add issue templates 2024-06-29 13:35:01 +02:00
Serhii Snitsaruk 4467ebbbd9
Update README.md 2024-06-24 21:34:35 +02:00
Serhii Snitsaruk a2dedabd77
Fix potential rare var name conflict in BTCooldown 2024-06-23 12:14:15 +02:00
Serhii Snitsaruk 2356e6c967
Merge pull request #147 from limbonaut/gha-gdext-android
GHA: Build Android platform libs in GDExtension workflow
2024-06-21 11:38:31 +02:00
Serhii Snitsaruk b712fe25dc
GHA: Don't strip arm Android libs 2024-06-21 10:34:36 +02:00
Serhii Snitsaruk 5bdec804b3
Add 32-bit Android libs to GDExtension manifest 2024-06-21 10:31:30 +02:00
Serhii Snitsaruk 2784937ca8
GHA: Build Android libs for arm32 & x86_32 2024-06-21 10:18:15 +02:00
Serhii Snitsaruk d383e9d1a4
GHA: Don't build web and android libs in test builds 2024-06-20 19:55:23 +02:00
Serhii Snitsaruk 4f1b22c668
GHA: Add GDExtension Android libs to build workflow 2024-06-20 19:54:29 +02:00
Serhii Snitsaruk 8fa609ef9e
Bump version to 1.1.0 2024-06-18 14:11:04 +02:00
Serhii Snitsaruk c6851259e8
Doc: Mention mapping in BTSubtree class doc 2024-06-18 13:25:33 +02:00
Serhii Snitsaruk 5a60ad9308
Doc: Add section on "Debugging behavior trees" in index.rst 2024-06-18 13:12:05 +02:00
Serhii Snitsaruk 7a90fdb113
Merge pull request #145 from limbonaut/gha-gdext-convert-icon-colors
Support editor light themes for icons in GDExtension
2024-06-15 09:05:46 +02:00
Serhii Snitsaruk 3bc8343e8b
GHA: Set `convert_colors_with_editor_theme=true` in .import 2024-06-15 08:42:41 +02:00
Serhii Snitsaruk 1c7eb994d4
Merge pull request #142 from limbonaut/fix-task-tree-column
Fix task tree warnings column always taking up space
2024-06-12 18:36:08 +02:00
Serhii Snitsaruk c76278b735
Fix task tree warnings column always taking up space 2024-06-10 08:26:18 +02:00
Serhii Snitsaruk fc53078130
Demo: Remove strange hsm.set_guard() call 2024-06-07 17:43:46 +02:00
Serhii Snitsaruk 88583a8a2e
Merge pull request #136 from limbonaut/fix-unfolding
Fix folding/unfolding selected composite task is not working
2024-06-06 13:19:11 +02:00
Serhii Snitsaruk d6076d131f
Fix folding/unfolding selected composite task is not working 2024-06-06 12:58:18 +02:00
Serhii Snitsaruk 2d1c5fa4ba
Merge pull request #133 from limbonaut/fix-performance-monitoring-crash
Fix crash when freeing agent with performance monitoring enabled
2024-06-05 23:04:14 +02:00
Serhii Snitsaruk 7eaebc4e7c
Fix crash when freeing agent with performance monitoring enabled 2024-06-04 09:30:42 +02:00
Serhii Snitsaruk db73133f1b
Bump version to 1.1-rc 2024-06-03 22:41:46 +02:00
Serhii Snitsaruk 7c0c81d141
Hide .0 patch version for builds with pre-release version status only 2024-06-03 22:35:51 +02:00
Serhii Snitsaruk adff6bd08c
Merge pull request #131 from limbonaut/fix-reparenting-issue
Fix reparenting an agent deactivates its HSM
2024-06-03 15:50:35 +02:00
Serhii Snitsaruk 06de52492a
Fix reparenting an agent deactivates its HSM 2024-06-03 14:55:00 +02:00
Serhii Snitsaruk 2c8e0d2da0
Merge pull request #130 from limbonaut/fix-custom-task-issues
Fix custom task issues - a rare crash & a sticky name/script issue, and improve error handling
2024-06-03 11:08:21 +02:00
Serhii Snitsaruk ce5f012101
Fix sticky name issues in custom tasks due to script errors 2024-06-03 10:45:01 +02:00
Serhii Snitsaruk 3bca05bc50
Fix crash on adding a task with bad script base type and improve error handling 2024-06-02 21:58:50 +02:00
Serhii Snitsaruk 56bb91df0a
Merge pull request #127 from limbonaut/blackboard-tests
Add unit tests for `Blackboard` and fix `Blackboard.unbind_var()`
2024-06-01 16:08:35 +02:00
Serhii Snitsaruk c793c8ba53
Fix `Blackboard.unbind_var` failing for existing bound variable 2024-06-01 10:53:56 +02:00
Serhii Snitsaruk 808ca1de7f
Add unit tests for Blackboard 2024-06-01 10:52:31 +02:00
Serhii Snitsaruk 809efc1be2
Merge pull request #125 from limbonaut/api-new-blackboard-methods
API: Add Blackboard methods to enable iteration, state inspection & serialization
2024-06-01 10:26:21 +02:00
Serhii Snitsaruk 29532113c0
Change to typed array in `BlackboardPlan::list_vars` 2024-05-31 11:57:27 +02:00
Serhii Snitsaruk 1e6c3fa92b
Files reformatted by pre-commit 2024-05-31 11:46:16 +02:00
Serhii Snitsaruk 35f2c3c142
API: Add `Blackboard` methods to enable iteration, state inspection and serialization 2024-05-31 11:46:11 +02:00
Serhii Snitsaruk 49f5e3be79
Merge pull request #124 from limbonaut/show-version
Show version info in the editor
2024-05-30 16:28:35 +02:00
Serhii Snitsaruk a04d4aaca4
Show version info in the editor
- Version is set in `limboai_version.py` file and hash is taken from GIT on build.
- Click to copy version info into clipboard.
2024-05-30 16:03:05 +02:00
Serhii Snitsaruk 08ad6c1d99
Merge pull request #122 from limbonaut/open-owner-scene
Editor: Add tab context menu option "Jump to Owner"
2024-05-29 21:00:48 +02:00
Serhii Snitsaruk c9825413c0
Add shortcut for "Close Tab" and clean up input code 2024-05-29 19:13:57 +02:00
Serhii Snitsaruk 990438db55
Handle built-in resources in "Jump to owner" tab action 2024-05-29 18:27:55 +02:00
Serhii Snitsaruk 2b299c0007
Accept Ctrl-J when LimboAI editor is visible and fix shortcuts not working sometimes 2024-05-29 11:58:59 +02:00
Serhii Snitsaruk 88e468c526
Editor: Add Ctrl+J shortcut for "Jump to Owner" tab action 2024-05-29 11:18:11 +02:00
Serhii Snitsaruk a0cd983927
Editor: Fix "jump to owner" from subtree and refactor 2024-05-29 10:53:54 +02:00
Serhii Snitsaruk 792502db84
Editor: Add tab context menu option "Open owner scene" 2024-05-28 21:16:26 +02:00
Serhii Snitsaruk 5f5ecc2db1
Merge pull request #123 from limbonaut/fix-compilation-errors-with-new-godotcpp
Fix compile-time errors with updated godot-cpp
2024-05-28 20:11:40 +02:00
Serhii Snitsaruk 361eb3eb15
Fix compile-time errors with updated godot-cpp 2024-05-28 19:50:57 +02:00
Serhii Snitsaruk dbb89e6883
Merge pull request #120 from limbonaut/improve-navigation
Implement tab navigation in the editor
2024-05-28 12:28:47 +02:00
Serhii Snitsaruk fa3034d190
Remove old editor navigation code 2024-05-28 12:04:02 +02:00
Serhii Snitsaruk 9e0489a034
Editor: Tab button to inspect blackboard plan 2024-05-28 11:16:37 +02:00
Serhii Snitsaruk a7d4b1e7f7
Editor: Use the full name for tabs if the short name is not unique 2024-05-28 09:46:21 +02:00
Serhii Snitsaruk 127fa678f4
Editor: Add tab context menu 2024-05-27 16:53:44 +02:00
Serhii Snitsaruk e0d47ddf08
Fix closing last tab leads to crash on exit 2024-05-27 15:36:17 +02:00
Serhii Snitsaruk 57382e10a7
Editor: Show full filename in tabs for BTs saved as built-in resources 2024-05-27 15:06:35 +02:00
Serhii Snitsaruk 0a28feb7a9
Fix GDExtension compilation issues 2024-05-27 14:27:14 +02:00
Serhii Snitsaruk 6f8f95b5de
Editor: Replace BT resource button with tabs 2024-05-27 13:48:08 +02:00
Serhii Snitsaruk e9eec23c3e
Update README.md
Put first steps earlier in the README
2024-05-26 12:39:04 +02:00
Serhii Snitsaruk d0fcea5574
Merge pull request #118 from limbonaut/doc-first-steps
Doc: Add first steps
2024-05-26 12:36:42 +02:00
Serhii Snitsaruk e059429760
Doc: Add first steps 2024-05-26 12:34:18 +02:00
Serhii Snitsaruk 918095622c
Merge pull request #117 from limbonaut/gha-toolchain-mirror
GHA: Use godotengine buildroot mirror
2024-05-26 12:16:14 +02:00
Serhii Snitsaruk 23c19c11e6
GHA: Use godotengine buildroot mirror 2024-05-26 09:32:49 +02:00
Serhii Snitsaruk bc2c4a26ac
GHA: Restore toolchain links
Toolchain contents should be cached now.
2024-05-25 16:07:00 +02:00
Serhii Snitsaruk 83214fc93b
GHA: Temporarily change toolchain link while tuxfamily.org is down (32-bit) 2024-05-25 15:05:34 +02:00
Serhii Snitsaruk e062669cf7
GHA: Temporarily change toolchain link while tuxfamily.org is down 2024-05-25 14:05:15 +02:00
Serhii Snitsaruk 2e94bf8bf2
Merge pull request #116 from limbonaut/gha-import-files
GHA: Generate .import files for GDExtension icons
2024-05-25 14:01:16 +02:00
Serhii Snitsaruk e8816091e6
GHA: Verify toolchain checksum 2024-05-25 13:33:48 +02:00
Serhii Snitsaruk fc1b99a5b1
GHA: Action that downloads & caches Linux toolchain
TuxFamily is often down lately, maybe caching could help.
2024-05-25 13:33:42 +02:00
Serhii Snitsaruk 04300e5222
GHA: Generate .import files for GDExtension icons 2024-05-22 22:33:21 +02:00
Serhii Snitsaruk 2f326e5b6f
Merge pull request #110 from limbonaut/type-validation
Editor: Check if variable is of correct type for BBParam subtypes
2024-05-18 15:33:39 +02:00
Serhii Snitsaruk 064d00fdfa
Editor: Check if variable is of correct type for BBParam subtypes 2024-05-18 14:12:00 +02:00
Serhii Snitsaruk ff61d55c44
Merge pull request #106 from limbonaut/mapping
Mapping variables
2024-05-18 13:47:42 +02:00
Serhii Snitsaruk 7ab7a9d098
BlackboardPlan: Improve inspector update while manually typing in mappings
Fixes property glitches observed while a map variable is typed in.
2024-05-17 21:54:50 +02:00
Serhii Snitsaruk b54f3696ff
Fix crash upon adding variable in the plan editor with empty default name
Also default to float when expected type is not specified.
2024-05-17 11:08:58 +02:00
Serhii Snitsaruk dc77ecd2b2
Fix `parent_scope` argument in `create_blackboard` should have a default value 2024-05-17 10:36:25 +02:00
Serhii Snitsaruk 026272f7f7
Fix BBParam subtypes incorrectly display type error in inspector 2024-05-17 10:27:22 +02:00
Serhii Snitsaruk d08018b7b1
BlackboardPlan: Update mapping after variable renamed
Currently, only for owned variables, not parent's.
2024-05-17 10:00:12 +02:00
Serhii Snitsaruk 00396dce61
Merge pull request #107 from limbonaut/steamdeck-support
GHA: Use older toolchain for SteamDeck compatibility
2024-05-16 17:48:33 +02:00
Serhii Snitsaruk 69201fa877
GHA: Use older toolchain for SteamDeck compatibility 2024-05-16 12:08:13 +02:00
Serhii Snitsaruk d920060dee
Update class documentation 2024-05-15 20:58:38 +02:00
Serhii Snitsaruk 2718271bb1
Clean up & renames 2024-05-15 20:43:24 +02:00
Serhii Snitsaruk 3cf90f9387
BlackboardPlan: Don't show mapping in root plans 2024-05-15 13:13:27 +02:00
Serhii Snitsaruk c6b1a40627
BlackboardPlan: Update inspector upon mapping editing finished 2024-05-15 12:52:50 +02:00
Serhii Snitsaruk d36f8f1122
Fix variables missing from BTState blackboard
Non-overridden variables could be missing at runtime in the BTState blackboard due to a bug this commit fixes.
2024-05-15 11:43:31 +02:00
Serhii Snitsaruk a572613001
BlackboardPlan: Utilize mapping in LimboHSM
Also changes how parent plan providing is implemented (used for editor hints).
2024-05-15 11:38:53 +02:00
Serhii Snitsaruk c30c5a4d7a
BlackboardPlan: Auto-fill type info when adding a missing variable for a mapping 2024-05-14 22:55:25 +02:00
Serhii Snitsaruk ef1c1e5192
Fix circular ref & non-tools compilation errors 2024-05-14 22:03:29 +02:00
Serhii Snitsaruk 3b12288ae0
BlackboardPlan: Serialize only non-empty mapping values 2024-05-14 20:25:18 +02:00
Serhii Snitsaruk a1cdff2e2e
Fix issues with mapping in BTSubtree 2024-05-14 19:47:05 +02:00
Serhii Snitsaruk e43bc25d82
BlackboardPlan: Provide editor hints for mappings 2024-05-14 13:29:04 +02:00
Ola S. 549d73b8fc
Add icons for empty & error variable statuses 2024-05-14 13:25:24 +02:00
Serhii Snitsaruk 0d1e846d93
BlackboardPlan: Use mapping with BTSubtree 2024-05-14 11:39:32 +02:00
Serhii Snitsaruk 2d493a76bd
BlackboardPlan: Improve mapping and serialize 2024-05-14 09:29:56 +02:00
Serhii Snitsaruk bdfe5f52c2
BlackboardPlan: Implement rudimentary mapping 2024-05-13 23:21:55 +02:00
Serhii Snitsaruk e55611a1a9
Bump doc version 2024-05-12 17:34:12 +02:00
Serhii Snitsaruk ba467ad62c
Merge pull request #102 from limbonaut/btplayer-agent-property
Allow specifying agent in `BTPlayer` node via inspector
2024-05-12 17:29:05 +02:00
Serhii Snitsaruk e5d04b9eda
Doc: Update doc pages and examples 2024-05-02 19:38:05 +02:00
Serhii Snitsaruk e36ea6d3e6
Better error handling in BTState, BTPlayer & BehaviorTree 2024-05-02 14:10:29 +02:00
Serhii Snitsaruk 803da63fa8
Doc: Update class docs 2024-05-02 13:39:19 +02:00
Serhii Snitsaruk 84c89356a6
Print error if agent node is set after initialization 2024-05-02 12:49:32 +02:00
Serhii Snitsaruk a2dae24b99
Rename BTPlayer's agent => agent_node 2024-05-02 12:11:59 +02:00
Serhii Snitsaruk af23272e3d
Fix unnamed arguments in method bindings 2024-05-02 01:27:14 +02:00
Serhii Snitsaruk 506d8aa967
Use `scene_root` with `BBParam` 2024-05-01 23:39:09 +02:00
Serhii Snitsaruk 5dff2e537b
Add `agent` parameter to `BTPlayer` to propagate upon `BehaviorTree` initialization, and add `scene_root` property to `BTTask`
`scene_root` is useful to resolve exported NodePath properties in `BTTask` instances (and for BBNode parameters).
2024-05-01 23:20:17 +02:00
155 changed files with 2260 additions and 549 deletions

64
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: Bug report
description: Report a bug in LimboAI
labels:
- bug
body:
- type: markdown
attributes:
value: |
- Write a descriptive title above.
- Search [open](https://github.com/limbonaut/limboai/issues) and [closed](https://github.com/limbonaut/limboai/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- type: input
attributes:
label: Godot version
description: |
- Specify the Godot version and hardware information if relevant.
- You can copy the version info by clicking on it in the Godot status bar.
- Alternatively, you can copy the version and hardware info in Godot using the main menu command "Help -> Copy System Info".
placeholder: v4.2.2.limboai+v1.1.0.gha [15073afe3]
validations:
required: true
- type: input
attributes:
label: LimboAI version
description: |
- Specify the LimboAI version.
- You can copy the version info by clicking on it in the toolbar of the LimboAI editor (top-right corner).
placeholder: v1.1.0 [8fa609e]
validations:
required: true
- type: dropdown
id: variant
attributes:
label: LimboAI variant
description: Which variant of our plugin are you running?
options:
- GDExtension / AssetLib
- Module (custom editor or template build)
default: 0
validations:
required: true
- type: textarea
attributes:
label: Issue description
description: |
- Describe your issue in detail. What doesn't work and how do you expect it to work instead?
- Provide screenshots and/or a console output if it helps to convey the problem.
validations:
required: true
- type: textarea
attributes:
label: How to reproduce
description: |
- Provide a list of steps or sample code that reproduces the issue.
- You can provide a small Godot project which reproduces the issue, with no unnecessary files included.
- Drag and drop a ZIP archive to upload it (10Mb limit).
- Don't include the `.godot` folder in the archive.
- Redroduction project helps to find the bug more quickly!
validations:
required: true

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Documentation
url: https://limboai.readthedocs.io/en/stable/
about: Please see our documentation for more information.
- name: Discussions
url: https://github.com/limbonaut/limboai/discussions
about: Need help? Ask questions in Discussions or on our Discord server.
- name: Discord Server
url: https://discord.gg/N5MGC95GpP
about: Share your experience or get help on our Discord server.

View File

@ -0,0 +1,38 @@
name: Feature or improvement proposal
description: Propose a new feature to be added or improved in LimboAI
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
- Write a descriptive title above.
- Search [open](https://github.com/limbonaut/limboai/issues) and [closed](https://github.com/limbonaut/limboai/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- type: textarea
attributes:
label: Problem statement
description: |
- Describe the problem or limitation you're currently facing with the LimboAI plugin.
- If it helps, describe the project you're working on and how it relates to the problem or limitation.
validations:
required: true
- type: textarea
attributes:
label: Proposed solution
description: |
- Describe your proposed solution and how it helps to overcome the problem or limitation.
- If it helps, show how it will work with code, pseudo-code, mock-ups, and/or diagrams.
- You can include any images or videos with drag'n'drop, and sample code blocks with <code>```</code> tags.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: |
- Describe alternative solutions and features you've considered.
- If this enhancement will not be used often, can it be worked around with a few clicks or lines of code?
validations:
required: true

View File

@ -0,0 +1,47 @@
name: Setup Linux toolchain
inputs:
arch:
required: true
runs:
using: "composite"
steps:
- name: Set up environment
shell: bash
run: |
# ! Settings:
TOOLCHAIN_64_URL=https://github.com/godotengine/buildroot/releases/download/godot-2020.11.x-2/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
TOOLCHAIN_64_SHA=16c8302fcb676c1f0fb9df73d6cff250ba1f4286
TOOLCHAIN_32_URL=https://github.com/godotengine/buildroot/releases/download/godot-2020.11.x-2/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
TOOLCHAIN_32_SHA=6171652abc54ef219e5187bc53660ee4e2f796f4
# ! Export variables:
if [[ "${{ inputs.arch }}" == "x86_64" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_64_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_64_SHA}" >> "$GITHUB_ENV"
elif [[ "${{ inputs.arch }}" == "x86_32" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_32_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_32_SHA}" >> "$GITHUB_ENV"
fi
- name: Cache buildroot
id: cache-buildroot
uses: actions/cache@v4
with:
path: buildroot
key: ${{env.TOOLCHAIN_SHA}}
- name: Set up buildroot
if: steps.cache-buildroot.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir buildroot
wget ${TOOLCHAIN_URL} -O buildroot/buildroot.tar.bz2
cd buildroot
echo "${TOOLCHAIN_SHA} buildroot.tar.bz2"
echo "${TOOLCHAIN_SHA} buildroot.tar.bz2" | shasum --check
tar -xjf buildroot.tar.bz2 --strip-components=1
ls -l
rm buildroot.tar.bz2
./relocate-sdk.sh
cd ..

View File

@ -36,6 +36,7 @@ env:
SCONSFLAGS: dev_build=no debug_symbols=no
EM_VERSION: 3.1.45
EM_CACHE_FOLDER: "emsdk-cache"
GODOT_VERSION: 4.2-stable
jobs:
gdextension:
@ -94,14 +95,70 @@ jobs:
platform: web
target: template_release
arch: wasm32
should-build: true
should-build: ${{ !inputs.test-build }}
- name: 🌐 Web (wasm32, debug)
runner: ubuntu-20.04
platform: web
target: template_debug
arch: wasm32
should-build: true
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm64, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm64, debug)
runner: ubuntu-20.04
platform: android
target: template_debug
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm32, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: arm32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm32, debug)
runner: ubuntu-20.04
platform: android
target: template_debug
arch: arm32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_64, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: x86_64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_64, debug)
runner: ubuntu-20.04
platform: android
target: template_debug
arch: x86_64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_32, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: x86_32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_32, debug)
runner: ubuntu-20.04
platform: android
target: template_debug
arch: x86_32
should-build: ${{ !inputs.test-build }}
exclude:
- { opts: { should-build: false } }
@ -132,25 +189,11 @@ jobs:
id: output-name-prefix
run: echo "name-prefix=${NAME_PREFIX}" >> $GITHUB_OUTPUT
- name: Set up Linux buildroot x86_64
if: matrix.opts.platform == 'linux' && matrix.opts.arch == 'x86_64'
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Set up Linux buildroot x86_32
if: matrix.opts.platform == 'linux' && matrix.opts.arch == 'x86_32'
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf i686-godot-linux-gnu_sdk-buildroot.tar.bz2
mv i686-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Setup Linux toolchain
if: matrix.opts.platform == 'linux'
uses: ./limboai/.github/actions/setup-linux-toolchain
with:
arch: ${{matrix.opts.arch}}
- name: Set up Python 3.x
if: matrix.opts.platform == 'windows' || matrix.opts.platform == 'macos'
@ -179,13 +222,27 @@ jobs:
emcc -v
- name: Set up scons
if: matrix.opts.platform == 'windows' || matrix.opts.platform == 'macos' || matrix.opts.platform == 'web'
if: matrix.opts.platform != 'linux'
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons==4.4.0
python --version
scons --version
- name: Set up Java 17
if: matrix.opts.platform == 'android'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- uses: nttld/setup-ndk@v1
if: matrix.opts.platform == 'android'
id: setup-ndk
with:
ndk-version: r23c
link-to-sdk: true
- name: Set up MSVC problem matcher on Windows
if: matrix.opts.platform == 'windows'
uses: ammaraskar/msvc-problem-matcher@master
@ -231,7 +288,7 @@ jobs:
ls -R out/
- name: Strip lib
if: matrix.opts.platform != 'windows' && matrix.opts.platform != 'web'
if: matrix.opts.platform != 'windows' && matrix.opts.platform != 'web' && (matrix.opts.platform != 'android' || startsWith(matrix.opts.arch, 'x86'))
run: |
ls -l -R out/addons/limboai/bin/
echo "---"
@ -251,8 +308,8 @@ jobs:
name: ${{ env.NAME }}
path: out/*
merge-artifacts:
name: Merge artifacts
package-extension:
name: 📦 Package extension
runs-on: ubuntu-latest
needs: gdextension
@ -263,3 +320,48 @@ jobs:
name: ${{needs.gdextension.outputs.name-prefix}}
pattern: tmp-gdextension.*
delete-merged: true
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ${{needs.gdextension.outputs.name-prefix}}
path: out/
- name: Setup Godot
shell: bash
run: |
echo "Downloading Godot ${GODOT_VERSION}"
mkdir bin
cd bin
wget "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_linux.x86_64.zip" -O godot.zip
unzip godot.zip
rm godot.zip
mv Godot_* godot
chmod u+x godot
ls -l
cd ..
./bin/godot --version
- name: Generate icon .import files
shell: bash
run: |
touch out/project.godot
timeout 20s ./bin/godot --headless --editor --path ./out/ || /bin/true
rm out/project.godot
rm -rf out/.godot/
- name: Change editor icon import settings
shell: bash
run: |
echo "--- Listing icons dir:"
ls out/addons/limboai/icons/
echo "--- (end of listing)"
sed -i 's|editor/scale_with_editor_scale=false|editor/scale_with_editor_scale=true|' out/addons/limboai/icons/*.import
sed -i 's|editor/convert_colors_with_editor_theme=false|editor/convert_colors_with_editor_theme=true|' out/addons/limboai/icons/*.import
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{needs.gdextension.outputs.name-prefix}}
path: out/*
overwrite: true

View File

@ -139,27 +139,10 @@ jobs:
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version
# About sed see: https://github.com/godotengine/buildroot/issues/6
- name: Set up buildroot x86_64
if: matrix.opts.arch == 'x86_64'
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
sed -i x86_64-godot-linux-gnu/sysroot/usr/lib/pkgconfig/dbus-1.pc -e "s@/lib@/lib64@g"
./relocate-sdk.sh
cd ..
- name: Set up buildroot x86_32
if: matrix.opts.arch == 'x86_32'
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf i686-godot-linux-gnu_sdk-buildroot.tar.bz2
mv i686-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Set up Linux toolchain
uses: ./modules/limboai/.github/actions/setup-linux-toolchain
with:
arch: ${{matrix.opts.arch}}
- name: Set up scons cache
uses: actions/cache@v4

View File

@ -26,8 +26,8 @@ concurrency:
# Global Settings.
env:
GODOT_REF: "4.2"
GODOT_CPP_REF: "4.2"
GODOT_REF: "master"
GODOT_CPP_REF: "master"
jobs:
unit-tests:
@ -52,16 +52,10 @@ jobs:
with:
path: modules/limboai
# About sed see: https://github.com/godotengine/buildroot/issues/6
- name: Set up buildroot x86_64
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
sed -i x86_64-godot-linux-gnu/sysroot/usr/lib/pkgconfig/dbus-1.pc -e "s@/lib@/lib64@g"
./relocate-sdk.sh
cd ..
- name: Set up Linux toolchain
uses: ./modules/limboai/.github/actions/setup-linux-toolchain
with:
arch: x86_64
- name: Set up scons cache
uses: actions/cache@v4

View File

@ -29,13 +29,14 @@ Behavior Trees are powerful hierarchical structures used to model and control th
![Charger from Demo](doc/images/demo_charger.gif)
### Videos
[![Demonstration](https://img.youtube.com/vi/NWaMArUg7mY/0.jpg)](https://www.youtube.com/watch?v=NWaMArUg7mY)
>**🛈 Demo project** lives in the `demo` folder and is available separately in [**Releases**](https://github.com/limbonaut/limboai/releases).
> Run `demo/scenes/showcase.tscn` to get started.
> It also contains a tutorial that introduces behavior trees using examples.
> It also includes a tutorial that introduces behavior trees through illustrative examples.
### Videos
<a href="https://www.youtube.com/watch?v=NWaMArUg7mY"><img src="https://img.youtube.com/vi/NWaMArUg7mY/0.jpg" width=410></a>
<a href="https://www.youtube.com/watch?v=aP0Aacdxmno"><img src="https://img.youtube.com/vi/aP0Aacdxmno/0.jpg" width=410></a>
## Features
@ -72,6 +73,10 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- **Demo + Tutorial:** Check out our extensive demo project, which includes an introduction to behavior trees using examples.
## First steps
Follow the [First steps](https://limboai.readthedocs.io/en/latest/index.html#first-steps) guide to learn how to get started with LimboAI and the demo project.
## Getting LimboAI
LimboAI can be used as either a C++ module or as a GDExtension shared library. GDExtension version is more convenient to use but somewhat limited in features. Whichever you choose to use, your project will stay compatible with both and you can switch from one to the other any time. See [Using GDExtension](https://limboai.readthedocs.io/en/latest/getting-started/gdextension.html).
@ -93,6 +98,7 @@ LimboAI can be used as either a C++ module or as a GDExtension shared library. G
## Using the plugin
- [Online Documentation](https://limboai.readthedocs.io/en/latest/index.html)
- [First steps](https://limboai.readthedocs.io/en/latest/index.html#first-steps)
- [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/latest/getting-started/introduction.html)
- [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/latest/getting-started/custom-tasks.html)
- [Sharing data using Blackboard](https://limboai.readthedocs.io/en/latest/getting-started/using-blackboard.html)
@ -114,18 +120,6 @@ Need help? We have a Discord server: https://discord.gg/N5MGC95GpP
I write about LimboAI development on Mastodon: https://mastodon.gamedev.place/@limbo.
## Roadmap
Features and improvements that may be implemented in the future:
- ~~Providing precompiled builds for download.~~ 🗸
- ~~Tests and CI.~~ 🗸
- ~~GDExtension support.~~ 🗸
- ~~Extensive demo project.~~ 🗸
- Expanding the library of commonly useful tasks.
- Exploring the execution history of behavior trees in the visual debugger.
- Per-project ignore list for tasks that users may want to hide in the task palette.
- GUI editor for state machines.
## License
Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.

4
SCsub
View File

@ -7,6 +7,10 @@ module_env = env.Clone()
module_env.Append(CPPDEFINES=["LIMBOAI_MODULE"])
import limboai_version
limboai_version.generate_module_version_header()
module_env.add_source_files(env.modules_sources, "*.cpp")
module_env.add_source_files(env.modules_sources, "blackboard/*.cpp")
module_env.add_source_files(env.modules_sources, "blackboard/bb_param/*.cpp")

View File

@ -18,6 +18,8 @@ class BBAabb : public BBParam {
GDCLASS(BBAabb, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::AABB; }
};

View File

@ -18,6 +18,8 @@ class BBArray : public BBParam {
GDCLASS(BBArray, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBBasis : public BBParam {
GDCLASS(BBBasis, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::BASIS; }
};

View File

@ -18,6 +18,8 @@ class BBBool : public BBParam {
GDCLASS(BBBool, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::BOOL; }
};

View File

@ -18,6 +18,8 @@ class BBByteArray : public BBParam {
GDCLASS(BBByteArray, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_BYTE_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBColor : public BBParam {
GDCLASS(BBColor, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::COLOR; }
};

View File

@ -18,6 +18,8 @@ class BBColorArray : public BBParam {
GDCLASS(BBColorArray, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_COLOR_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBDictionary : public BBParam {
GDCLASS(BBDictionary, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::DICTIONARY; }
};

View File

@ -18,6 +18,8 @@ class BBFloat : public BBParam {
GDCLASS(BBFloat, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::FLOAT; }
};

View File

@ -18,6 +18,8 @@ class BBFloat32Array : public BBParam {
GDCLASS(BBFloat32Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_FLOAT32_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBFloat64Array : public BBParam {
GDCLASS(BBFloat64Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_FLOAT64_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBInt : public BBParam {
GDCLASS(BBInt, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::INT; }
};

View File

@ -18,6 +18,8 @@ class BBInt32Array : public BBParam {
GDCLASS(BBInt32Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_INT32_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBInt64Array : public BBParam {
GDCLASS(BBInt64Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_INT64_ARRAY; }
};

View File

@ -20,9 +20,9 @@
#include <godot_cpp/classes/node.hpp>
#endif // LIMBOAI_GDEXTENSION
Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(p_agent == nullptr, Variant());
ERR_FAIL_COND_V(!p_blackboard.is_valid(), Variant());
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_NULL_V_MSG(p_scene_root, Variant(), "BBNode: get_value() failed - scene_root is null.");
ERR_FAIL_NULL_V_MSG(p_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
Variant val;
if (get_value_source() == SAVED_VALUE) {
@ -32,9 +32,7 @@ Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard,
}
if (val.get_type() == Variant::NODE_PATH) {
Node *agent = Object::cast_to<Node>(p_agent);
ERR_FAIL_COND_V_MSG(agent == nullptr, Variant(), "BBNode: p_agent must be a Node.");
return agent->get_node_or_null(val);
return p_scene_root->get_node_or_null(val);
} else {
Object *obj = val;
if (unlikely(obj == nullptr && val.get_type() != Variant::NIL)) {

View File

@ -18,10 +18,11 @@ class BBNode : public BBParam {
GDCLASS(BBNode, BBParam);
protected:
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
static void _bind_methods() {}
public:
virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
};
#endif // BB_NODE_H

View File

@ -75,7 +75,7 @@ String BBParam::_to_string() {
}
}
Variant BBParam::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
Variant BBParam::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(!p_blackboard.is_valid(), p_default);
if (value_source == SAVED_VALUE) {
@ -105,7 +105,7 @@ void BBParam::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_variable", "variable_name"), &BBParam::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BBParam::get_variable);
ClassDB::bind_method(D_METHOD("get_type"), &BBParam::get_type);
ClassDB::bind_method(D_METHOD("get_value", "agent", "blackboard", "default"), &BBParam::get_value, Variant());
ClassDB::bind_method(D_METHOD("get_value", "scene_root", "blackboard", "default"), &BBParam::get_value, Variant());
ADD_PROPERTY(PropertyInfo(Variant::INT, "value_source", PROPERTY_HINT_ENUM, "Saved Value,Blackboard Var"), "set_value_source", "get_value_source");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable", PROPERTY_HINT_NONE, "", 0), "set_variable", "get_variable");

View File

@ -49,8 +49,6 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
virtual Variant::Type get_type() const { return Variant::NIL; }
void set_value_source(ValueSource p_value);
ValueSource get_value_source() const { return value_source; }
@ -66,7 +64,9 @@ public:
virtual String _to_string();
#endif
virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
virtual Variant::Type get_type() const { return Variant::NIL; }
virtual Variant::Type get_variable_expected_type() const { return get_type(); }
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
BBParam();
};

View File

@ -18,6 +18,8 @@ class BBPlane : public BBParam {
GDCLASS(BBPlane, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PLANE; }
};

View File

@ -18,6 +18,8 @@ class BBProjection : public BBParam {
GDCLASS(BBProjection, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PROJECTION; }
};

View File

@ -18,6 +18,8 @@ class BBQuaternion : public BBParam {
GDCLASS(BBQuaternion, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::QUATERNION; }
};

View File

@ -18,6 +18,8 @@ class BBRect2 : public BBParam {
GDCLASS(BBRect2, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::RECT2; }
};

View File

@ -18,6 +18,8 @@ class BBRect2i : public BBParam {
GDCLASS(BBRect2i, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::RECT2I; }
};

View File

@ -18,6 +18,8 @@ class BBString : public BBParam {
GDCLASS(BBString, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::STRING; }
};

View File

@ -18,6 +18,8 @@ class BBStringArray : public BBParam {
GDCLASS(BBStringArray, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_STRING_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBStringName : public BBParam {
GDCLASS(BBStringName, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::STRING_NAME; }
};

View File

@ -18,6 +18,8 @@ class BBTransform2D : public BBParam {
GDCLASS(BBTransform2D, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::TRANSFORM2D; }
};

View File

@ -18,6 +18,8 @@ class BBTransform3D : public BBParam {
GDCLASS(BBTransform3D, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::TRANSFORM3D; }
};

View File

@ -27,6 +27,8 @@ public:
virtual Variant::Type get_type() const override;
void set_type(Variant::Type p_type);
virtual Variant::Type get_variable_expected_type() const override { return Variant::NIL; }
BBVariant(const Variant &p_value);
BBVariant();
};

View File

@ -18,6 +18,8 @@ class BBVector2 : public BBParam {
GDCLASS(BBVector2, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR2; }
};

View File

@ -18,6 +18,8 @@ class BBVector2Array : public BBParam {
GDCLASS(BBVector2Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR2_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBVector2i : public BBParam {
GDCLASS(BBVector2i, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR2I; }
};

View File

@ -18,6 +18,8 @@ class BBVector3 : public BBParam {
GDCLASS(BBVector3, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR3; }
};

View File

@ -18,6 +18,8 @@ class BBVector3Array : public BBParam {
GDCLASS(BBVector3Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR3_ARRAY; }
};

View File

@ -18,6 +18,8 @@ class BBVector3i : public BBParam {
GDCLASS(BBVector3i, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR3I; }
};

View File

@ -18,6 +18,8 @@ class BBVector4 : public BBParam {
GDCLASS(BBVector4, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR4; }
};

View File

@ -18,6 +18,8 @@ class BBVector4i : public BBParam {
GDCLASS(BBVector4i, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR4I; }
};

View File

@ -64,6 +64,36 @@ void Blackboard::erase_var(const StringName &p_name) {
data.erase(p_name);
}
TypedArray<StringName> Blackboard::list_vars() const {
TypedArray<StringName> var_names;
var_names.resize(data.size());
int idx = 0;
for (const KeyValue<StringName, BBVariable> &kv : data) {
var_names[idx] = kv.key;
idx += 1;
}
return var_names;
}
Dictionary Blackboard::get_vars_as_dict() const {
Dictionary dict;
for (const KeyValue<StringName, BBVariable> &kv : data) {
dict[kv.key] = kv.value.get_value();
}
return dict;
}
void Blackboard::populate_from_dict(const Dictionary &p_dictionary) {
Array keys = p_dictionary.keys();
for (int i = 0; i < keys.size(); i++) {
if (keys[i].get_type() == Variant::STRING_NAME || keys[i].get_type() == Variant::STRING) {
set_var(keys[i], p_dictionary[keys[i]]);
} else {
ERR_PRINT("Blackboard: Invalid key type in dictionary to populate blackboard. Must be StringName or String.");
}
}
}
void Blackboard::bind_var_to_property(const StringName &p_name, Object *p_object, const StringName &p_property, bool p_create) {
if (!data.has(p_name)) {
if (p_create) {
@ -76,7 +106,7 @@ void Blackboard::bind_var_to_property(const StringName &p_name, Object *p_object
}
void Blackboard::unbind_var(const StringName &p_name) {
ERR_FAIL_COND_MSG(data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist (var: " + p_name + ").");
ERR_FAIL_COND_MSG(!data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist (var: " + p_name + ").");
data[p_name].unbind();
}
@ -98,12 +128,16 @@ void Blackboard::link_var(const StringName &p_name, const Ref<Blackboard> &p_tar
}
void Blackboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_var", "var_name", "default", "complain"), &Blackboard::get_var, Variant(), true);
ClassDB::bind_method(D_METHOD("get_var", "var_name", "default", "complain"), &Blackboard::get_var, DEFVAL(Variant()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_var", "var_name", "value"), &Blackboard::set_var);
ClassDB::bind_method(D_METHOD("has_var", "var_name"), &Blackboard::has_var);
ClassDB::bind_method(D_METHOD("set_parent", "blackboard"), &Blackboard::set_parent);
ClassDB::bind_method(D_METHOD("get_parent"), &Blackboard::get_parent);
ClassDB::bind_method(D_METHOD("erase_var", "var_name"), &Blackboard::erase_var);
ClassDB::bind_method(D_METHOD("clear"), &Blackboard::clear);
ClassDB::bind_method(D_METHOD("list_vars"), &Blackboard::list_vars);
ClassDB::bind_method(D_METHOD("get_vars_as_dict"), &Blackboard::get_vars_as_dict);
ClassDB::bind_method(D_METHOD("populate_from_dict", "dictionary"), &Blackboard::populate_from_dict);
ClassDB::bind_method(D_METHOD("top"), &Blackboard::top);
ClassDB::bind_method(D_METHOD("bind_var_to_property", "var_name", "object", "property", "create"), &Blackboard::bind_var_to_property, DEFVAL(false));
ClassDB::bind_method(D_METHOD("unbind_var", "var_name"), &Blackboard::unbind_var);

View File

@ -46,10 +46,15 @@ public:
Ref<Blackboard> top() const;
Variant get_var(const StringName &p_name, const Variant &p_default, bool p_complain = true) const;
Variant get_var(const StringName &p_name, const Variant &p_default = Variant(), bool p_complain = true) const;
void set_var(const StringName &p_name, const Variant &p_value);
bool has_var(const StringName &p_name) const;
void erase_var(const StringName &p_name);
void clear() { data.clear(); }
TypedArray<StringName> list_vars() const;
Dictionary get_vars_as_dict() const;
void populate_from_dict(const Dictionary &p_dictionary);
void bind_var_to_property(const StringName &p_name, Object *p_object, const StringName &p_property, bool p_create = false);
void unbind_var(const StringName &p_name);
@ -57,8 +62,6 @@ public:
void assign_var(const StringName &p_name, const BBVariable &p_var);
void link_var(const StringName &p_name, const Ref<Blackboard> &p_target_blackboard, const StringName &p_target_var, bool p_create = false);
// TODO: Add serialization API.
};
#endif // BLACKBOARD_H

View File

@ -11,6 +11,8 @@
#include "blackboard_plan.h"
#include "../util/limbo_utility.h"
bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
String name_str = p_name;
@ -25,6 +27,28 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
StringName value = p_value;
bool properties_changed = false;
if (value == StringName()) {
if (parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
parent_scope_mapping.erase(mapped_var_name);
}
} else {
if (!parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
}
parent_scope_mapping[mapped_var_name] = value;
}
if (properties_changed) {
notify_property_list_changed();
}
return true;
}
// * Storage
if (name_str.begins_with("var/")) {
StringName var_name = name_str.get_slicec('/', 1);
@ -56,7 +80,23 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
// * Editor
if (var_map.has(p_name)) {
r_ret = var_map[p_name].get_value();
if (has_mapping(p_name)) {
r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]);
} else {
r_ret = var_map[p_name].get_value();
}
return true;
}
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(mapped_var_name == StringName(), false);
if (parent_scope_mapping.has(mapped_var_name)) {
r_ret = parent_scope_mapping[mapped_var_name];
} else {
r_ret = StringName();
}
return true;
}
@ -64,7 +104,6 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
if (!name_str.begins_with("var/")) {
return false;
}
StringName var_name = name_str.get_slicec('/', 1);
String what = name_str.get_slicec('/', 2);
ERR_FAIL_COND_V(!var_map.has(var_name), false);
@ -90,29 +129,49 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
// * Editor
if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
if (has_mapping(var_name)) {
p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
}
}
// * Storage
if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) {
// Don't store variable if it's not modified in a derived plan.
// Variable is considered modified when it's marked as changed and its value is different from the base plan.
continue;
}
// * Storage
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(var.get_type(), "var/" + var_name + "/value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/hint_string", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
// * Mapping
if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
// Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
}
}
}
bool BlackboardPlan::_property_can_revert(const StringName &p_name) const {
if (String(p_name).begins_with("mapping/")) {
return true;
}
return base.is_valid() && base->var_map.has(p_name);
}
bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const {
if (String(p_name).begins_with("mapping/")) {
r_property = StringName();
return true;
}
if (base->var_map.has(p_name)) {
r_property = base->var_map[p_name].get_value();
return true;
@ -131,6 +190,15 @@ void BlackboardPlan::set_base_plan(const Ref<BlackboardPlan> &p_base) {
notify_property_list_changed();
}
void BlackboardPlan::set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider) {
parent_scope_plan_provider = p_parent_scope_plan_provider;
notify_property_list_changed();
}
bool BlackboardPlan::has_mapping(const StringName &p_name) const {
return is_mapping_enabled() && parent_scope_mapping.has(p_name) && parent_scope_mapping[p_name] != StringName();
}
void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) {
prefetch_nodepath_vars = p_enable;
emit_changed();
@ -169,11 +237,11 @@ BBVariable BlackboardPlan::get_var(const StringName &p_name) {
Pair<StringName, BBVariable> BlackboardPlan::get_var_by_index(int p_index) {
Pair<StringName, BBVariable> ret;
ERR_FAIL_INDEX_V(p_index, (int)var_map.size(), ret);
return var_list[p_index];
return var_list.get(p_index);
}
PackedStringArray BlackboardPlan::list_vars() const {
PackedStringArray ret;
TypedArray<StringName> BlackboardPlan::list_vars() const {
TypedArray<StringName> ret;
for (const Pair<StringName, BBVariable> &p : var_list) {
ret.append(p.first);
}
@ -214,6 +282,11 @@ void BlackboardPlan::rename_var(const StringName &p_name, const StringName &p_ne
var_map.erase(p_name);
var_map.insert(p_new_name, var);
if (parent_scope_mapping.has(p_name)) {
parent_scope_mapping[p_new_name] = parent_scope_mapping[p_name];
parent_scope_mapping.erase(p_name);
}
notify_property_list_changed();
emit_changed();
}
@ -335,12 +408,11 @@ inline void bb_add_var_dup_with_prefetch(const Ref<Blackboard> &p_blackboard, co
}
}
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node) {
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackboard> &p_parent_scope) {
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard);
for (const Pair<StringName, BBVariable> &p : var_list) {
bb_add_var_dup_with_prefetch(bb, p.first, p.second, prefetch_nodepath_vars, p_node);
}
bb->set_parent(p_parent_scope);
populate_blackboard(bb, true, p_node);
return bb;
}
@ -351,6 +423,13 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
continue;
}
bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, prefetch_nodepath_vars, p_node);
if (parent_scope_mapping.has(p.first)) {
StringName target_var = parent_scope_mapping[p.first];
if (target_var != StringName()) {
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable $%s to parent scope because the parent scope is not set.", p.first));
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
}
}
}
}
@ -362,7 +441,9 @@ void BlackboardPlan::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_plan"), &BlackboardPlan::get_base_plan);
ClassDB::bind_method(D_METHOD("is_derived"), &BlackboardPlan::is_derived);
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
ClassDB::bind_method(D_METHOD("create_blackboard", "node"), &BlackboardPlan::create_blackboard);
ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()));
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard);
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.

View File

@ -36,6 +36,13 @@ private:
// and only the values can be different in those variables.
Ref<BlackboardPlan> base;
// Mapping between variables in this plan and their parent scope names.
// Used for linking variables to their parent scope counterparts upon Blackboard creation/population.
HashMap<StringName, StringName> parent_scope_mapping;
// Fetcher function for the parent scope plan. Funtion should return a Ref<BlackboardPlan>.
// Used in the inspector. When set, mapping feature becomes available.
Callable parent_scope_plan_provider;
// If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population).
bool prefetch_nodepath_vars = true;
@ -52,6 +59,12 @@ public:
void set_base_plan(const Ref<BlackboardPlan> &p_base);
Ref<BlackboardPlan> get_base_plan() const { return base; }
void set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider);
Callable get_parent_scope_plan_provider() const { return parent_scope_plan_provider; }
bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref<BlackboardPlan>()); }
bool has_mapping(const StringName &p_name) const;
void set_prefetch_nodepath_vars(bool p_enable);
bool is_prefetching_nodepath_vars() const;
@ -63,7 +76,7 @@ public:
_FORCE_INLINE_ bool is_empty() const { return var_map.is_empty(); }
int get_var_count() const { return var_map.size(); }
PackedStringArray list_vars() const;
TypedArray<StringName> list_vars() const;
StringName get_var_name(const BBVariable &p_var) const;
bool is_valid_var_name(const StringName &p_name) const;
void rename_var(const StringName &p_name, const StringName &p_new_name);
@ -72,7 +85,7 @@ public:
void sync_with_base_plan();
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }
Ref<Blackboard> create_blackboard(Node *p_agent);
Ref<Blackboard> create_blackboard(Node *p_agent, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>());
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node);
BlackboardPlan();

View File

@ -52,7 +52,13 @@ void BehaviorTree::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
}
void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) {
#ifdef TOOLS_ENABLED
_unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
root_task = p_value;
#ifdef TOOLS_ENABLED
_set_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
emit_changed();
}
@ -71,10 +77,12 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
root_task = p_other->get_root_task();
}
Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const {
Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) const {
ERR_FAIL_COND_V_MSG(root_task == nullptr, memnew(BTTask), "Trying to instance a behavior tree with no valid root task.");
ERR_FAIL_NULL_V_MSG(p_agent, memnew(BTTask), "Trying to instance a behavior tree with no valid agent.");
ERR_FAIL_NULL_V_MSG(p_scene_root, memnew(BTTask), "Trying to instance a behavior tree with no valid scene root.");
Ref<BTTask> inst = root_task->clone();
inst->initialize(p_agent, p_blackboard);
inst->initialize(p_agent, p_blackboard, p_scene_root);
return inst;
}
@ -83,6 +91,22 @@ void BehaviorTree::_plan_changed() {
emit_changed();
}
#ifdef TOOLS_ENABLED
void BehaviorTree::_set_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = this->get_instance_id();
}
}
void BehaviorTree::_unset_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = ObjectID();
}
}
#endif // TOOLS_ENABLED
void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);
@ -92,7 +116,7 @@ void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task);
ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone);
ClassDB::bind_method(D_METHOD("copy_other", "other"), &BehaviorTree::copy_other);
ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard"), &BehaviorTree::instantiate);
ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "scene_root"), &BehaviorTree::instantiate);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan");

View File

@ -34,6 +34,11 @@ private:
void _plan_changed();
#ifdef TOOLS_ENABLED
void _set_editor_behavior_tree_hint();
void _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
protected:
static void _bind_methods();
@ -53,7 +58,7 @@ public:
Ref<BehaviorTree> clone() const;
void copy_other(const Ref<BehaviorTree> &p_other);
Ref<BTTask> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const;
Ref<BTTask> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) const;
BehaviorTree();
~BehaviorTree();

View File

@ -50,9 +50,13 @@ void BTPlayer::_load_tree() {
}
#endif
tree_instance.unref();
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Behavior tree has no valid root task.");
tree_instance = behavior_tree->instantiate(get_owner(), blackboard);
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
Node *agent = GET_NODE(this, agent_node);
ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node));
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set).");
tree_instance = behavior_tree->instantiate(agent, blackboard, scene_root);
#ifdef DEBUG_ENABLED
if (IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
@ -89,6 +93,13 @@ void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
}
}
void BTPlayer::set_agent_node(const NodePath &p_agent_node) {
agent_node = p_agent_node;
if (tree_instance.is_valid()) {
ERR_PRINT("BTPlayer: Agent node cannot be set after the behavior tree is instantiated. This change will not affect the behavior tree instance.");
}
}
void BTPlayer::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan;
_update_blackboard_plan();
@ -142,21 +153,31 @@ void BTPlayer::restart() {
void BTPlayer::_set_monitor_performance(bool p_monitor_performance) {
monitor_performance = p_monitor_performance;
if (!get_owner()) {
if (!get_owner() && monitor_performance) {
// Don't add custom monitor if not in scene.
return;
}
Performance *perf = Performance::get_singleton();
if (monitor_performance) {
if (monitor_id == StringName()) {
monitor_id = vformat("limboai/update_ms|%s_%s_%s", get_owner()->get_name(), get_name(),
String(itos(get_instance_id())).md5_text().substr(0, 4));
}
if (!perf->has_custom_monitor(monitor_id)) {
PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTPlayer::_get_mean_update_time_msec));
}
} else if (monitor_id != StringName() && perf->has_custom_monitor(monitor_id)) {
perf->remove_custom_monitor(monitor_id);
_add_custom_monitor();
} else {
_remove_custom_monitor();
}
}
void BTPlayer::_add_custom_monitor() {
if (monitor_id == StringName()) {
monitor_id = vformat("LimboAI/update_ms|%s_%s_%s", get_owner()->get_name(), get_name(),
String(itos(get_instance_id())).md5_text().substr(0, 4));
}
if (!Performance::get_singleton()->has_custom_monitor(monitor_id)) {
PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTPlayer::_get_mean_update_time_msec));
}
}
void BTPlayer::_remove_custom_monitor() {
if (monitor_id != StringName() && Performance::get_singleton()->has_custom_monitor(monitor_id)) {
Performance::get_singleton()->remove_custom_monitor(monitor_id);
}
}
@ -194,9 +215,6 @@ void BTPlayer::_notification(int p_notification) {
_load_tree();
}
set_active(active);
#ifdef DEBUG_ENABLED
_set_monitor_performance(monitor_performance);
#endif
} else {
_update_blackboard_plan();
}
@ -206,6 +224,9 @@ void BTPlayer::_notification(int p_notification) {
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
if (monitor_performance) {
_add_custom_monitor();
}
#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_EXIT_TREE: {
@ -213,6 +234,9 @@ void BTPlayer::_notification(int p_notification) {
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
}
if (monitor_performance) {
_remove_custom_monitor();
}
#endif // DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@ -220,7 +244,6 @@ void BTPlayer::_notification(int p_notification) {
behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
}
}
} break;
}
}
@ -228,6 +251,8 @@ void BTPlayer::_notification(int p_notification) {
void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTPlayer::set_behavior_tree);
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTPlayer::get_behavior_tree);
ClassDB::bind_method(D_METHOD("set_agent_node", "agent_node"), &BTPlayer::set_agent_node);
ClassDB::bind_method(D_METHOD("get_agent_node"), &BTPlayer::get_agent_node);
ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &BTPlayer::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &BTPlayer::get_update_mode);
ClassDB::bind_method(D_METHOD("set_active", "active"), &BTPlayer::set_active);
@ -245,6 +270,7 @@ void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTPlayer::get_tree_instance);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "agent_node"), "set_agent_node", "get_agent_node");
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard");
@ -266,6 +292,7 @@ void BTPlayer::_bind_methods() {
BTPlayer::BTPlayer() {
blackboard = Ref<Blackboard>(memnew(Blackboard));
agent_node = LW_NAME(node_pp);
}
BTPlayer::~BTPlayer() {

View File

@ -37,6 +37,7 @@ public:
private:
Ref<BehaviorTree> behavior_tree;
NodePath agent_node;
Ref<BlackboardPlan> blackboard_plan;
UpdateMode update_mode = UpdateMode::PHYSICS;
bool active = true;
@ -57,6 +58,9 @@ public:
void set_behavior_tree(const Ref<BehaviorTree> &p_tree);
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; };
void set_agent_node(const NodePath &p_agent_node);
NodePath get_agent_node() const { return agent_node; }
void set_blackboard_plan(const Ref<BlackboardPlan> &p_plan);
Ref<BlackboardPlan> get_blackboard_plan() const { return blackboard_plan; }
@ -88,6 +92,10 @@ private:
void _set_monitor_performance(bool p_monitor_performance);
bool _get_monitor_performance() const { return monitor_performance; }
void _add_custom_monitor();
void _remove_custom_monitor();
double _get_mean_update_time_msec();
#endif // DEBUG_ENABLED

View File

@ -32,10 +32,10 @@ void BTState::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
}
behavior_tree = p_tree;
_update_blackboard_plan();
} else {
behavior_tree = p_tree;
}
_update_blackboard_plan();
}
void BTState::_update_blackboard_plan() {
@ -52,8 +52,9 @@ void BTState::_update_blackboard_plan() {
void BTState::_setup() {
LimboState::_setup();
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
// TODO: BBNode relies on agent to be scene owner, so if the user provides anything else, the behavior tree can break.
tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard());
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - can't get scene root (make sure the BTState's owner property is set).");
tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), scene_root);
#ifdef DEBUG_ENABLED
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
@ -73,7 +74,7 @@ void BTState::_exit() {
void BTState::_update(double p_delta) {
VCALL_ARGS(_update, p_delta);
if (!active) {
if (!is_active()) {
// Bail out if a transition happened in the meantime.
return;
}

View File

@ -57,7 +57,7 @@ BT::Status BTCheckVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(!get_blackboard()->has_var(variable), FAILURE, vformat("BTCheckVar: Blackboard variable doesn't exist: \"%s\". Returning FAILURE.", variable));
Variant left_value = get_blackboard()->get_var(variable, Variant());
Variant right_value = value->get_value(get_agent(), get_blackboard());
Variant right_value = value->get_value(get_scene_root(), get_blackboard());
return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE;
}

View File

@ -26,7 +26,7 @@ BT::Status BTSetVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetVar: `value` is not set.");
Variant result;
Variant error_result = LW_NAME(error_value);
Variant right_value = value->get_value(get_agent(), get_blackboard(), error_result);
Variant right_value = value->get_value(get_scene_root(), get_blackboard(), error_result);
ERR_FAIL_COND_V_MSG(right_value == error_result, FAILURE, "BTSetVar: Failed to get parameter value. Returning FAILURE.");
if (operation == LimboUtility::OPERATION_NONE) {
result = right_value;

View File

@ -17,6 +17,9 @@
class BTAction : public BTTask {
GDCLASS(BTAction, BTTask);
protected:
static void _bind_methods() {}
public:
virtual PackedStringArray get_configuration_warnings() override;
};

View File

@ -18,6 +18,9 @@ class BTComment : public BTTask {
GDCLASS(BTComment, BTTask);
TASK_CATEGORY(Utility);
protected:
static void _bind_methods() {}
public:
virtual Ref<BTTask> clone() const override;
virtual PackedStringArray get_configuration_warnings() override;

View File

@ -17,6 +17,9 @@
class BTComposite : public BTTask {
GDCLASS(BTComposite, BTTask);
protected:
static void _bind_methods() {}
public:
virtual PackedStringArray get_configuration_warnings() override;
};

View File

@ -17,6 +17,9 @@
class BTCondition : public BTTask {
GDCLASS(BTCondition, BTTask);
protected:
static void _bind_methods() {}
public:
virtual PackedStringArray get_configuration_warnings() override;
};

View File

@ -18,6 +18,8 @@ class BTDecorator : public BTTask {
GDCLASS(BTDecorator, BTTask)
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
public:

View File

@ -14,6 +14,7 @@
#include "../../blackboard/blackboard.h"
#include "../../util/limbo_string_names.h"
#include "../../util/limbo_utility.h"
#include "../behavior_tree.h"
#include "bt_comment.h"
#ifdef LIMBOAI_MODULE
@ -117,31 +118,31 @@ bool BTTask::is_displayed_collapsed() const {
}
String BTTask::get_task_name() {
if (data.custom_name.is_empty()) {
#ifdef LIMBOAI_MODULE
if (get_script_instance() && get_script_instance()->has_method(LW_NAME(_generate_name))) {
if (unlikely(!get_script_instance()->get_script()->is_tool())) {
ERR_PRINT(vformat("BTTask: Task script should be a \"tool\" script!"));
} else {
return get_script_instance()->call(LimboStringNames::get_singleton()->_generate_name);
}
}
return _generate_name();
#elif LIMBOAI_GDEXTENSION
Ref<Script> task_script = get_script();
if (task_script.is_valid() && task_script->is_tool()) {
Variant call_result;
VCALL_OR_NATIVE_V(_generate_name, Variant, call_result);
ERR_FAIL_COND_V(call_result.get_type() == Variant::NIL, _generate_name());
String task_name = call_result;
ERR_FAIL_COND_V(task_name.is_empty(), _generate_name());
return task_name;
} else {
return _generate_name();
}
#endif
if (!data.custom_name.is_empty()) {
return data.custom_name;
}
return data.custom_name;
Ref<Script> task_script = get_script();
if (task_script.is_valid()) {
bool has_generate_method = has_method(LW_NAME(_generate_name));
ERR_FAIL_COND_V_MSG(has_generate_method && !task_script->is_tool(), _generate_name(), vformat("BTTask: @tool annotation is required if _generate_name is defined: %s", task_script->get_path()));
if (task_script->is_tool() && has_generate_method) {
String call_result;
VCALL_V(_generate_name, call_result);
if (call_result.is_empty() || call_result == "<null>") {
// Force reset script instance.
set_script(Variant());
set_script(task_script);
// Retry.
VCALL_V(_generate_name, call_result);
}
ERR_FAIL_COND_V_MSG(call_result.is_empty() || call_result == "<null>", _generate_name(), vformat("BTTask: _generate_name() failed to return a proper name string (%s)", task_script->get_path()));
return call_result;
}
}
return _generate_name();
}
Ref<BTTask> BTTask::get_root() const {
@ -159,13 +160,15 @@ void BTTask::set_custom_name(const String &p_name) {
}
};
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr);
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_NULL(p_agent);
ERR_FAIL_NULL(p_blackboard);
ERR_FAIL_NULL(p_scene_root);
data.agent = p_agent;
data.blackboard = p_blackboard;
data.scene_root = p_scene_root;
for (int i = 0; i < data.children.size(); i++) {
get_child(i)->initialize(p_agent, p_blackboard);
get_child(i)->initialize(p_agent, p_blackboard, p_scene_root);
}
VCALL_OR_NATIVE(_setup);
@ -374,11 +377,27 @@ void BTTask::print_tree(int p_initial_tabs) {
}
}
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> BTTask::editor_get_behavior_tree() {
BTTask *task = this;
while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) {
task = task->data.parent;
}
return Object::cast_to<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
}
void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) {
data.behavior_tree_id = p_bt->get_instance_id();
}
#endif // TOOLS_ENABLED
void BTTask::_bind_methods() {
// Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
ClassDB::bind_method(D_METHOD("get_root"), &BTTask::get_root);
ClassDB::bind_method(D_METHOD("initialize", "agent", "blackboard"), &BTTask::initialize);
ClassDB::bind_method(D_METHOD("initialize", "agent", "blackboard", "scene_root"), &BTTask::initialize);
ClassDB::bind_method(D_METHOD("clone"), &BTTask::clone);
ClassDB::bind_method(D_METHOD("execute", "delta"), &BTTask::execute);
ClassDB::bind_method(D_METHOD("get_child", "idx"), &BTTask::get_child);
@ -395,10 +414,14 @@ void BTTask::_bind_methods() {
ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0));
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort);
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree);
#endif // TOOLS_ENABLED
// Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);
ClassDB::bind_method(D_METHOD("set_agent", "agent"), &BTTask::set_agent);
ClassDB::bind_method(D_METHOD("get_scene_root"), &BTTask::get_scene_root);
ClassDB::bind_method(D_METHOD("_get_children"), &BTTask::_get_children);
ClassDB::bind_method(D_METHOD("_set_children", "children"), &BTTask::_set_children);
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTTask::get_blackboard);
@ -410,6 +433,7 @@ void BTTask::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_name"), "set_custom_name", "get_custom_name");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_agent", "get_agent");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_NODE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_scene_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", PROPERTY_USAGE_NONE), "", "get_blackboard");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_children", "_get_children");
ADD_PROPERTY(PropertyInfo(Variant::INT, "status", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_status");

View File

@ -42,6 +42,8 @@
using namespace godot;
#endif // LIMBOAI_GDEXTENSION
class BehaviorTree;
/**
* Base class for BTTask.
* Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...)
@ -75,12 +77,16 @@ private:
int index = -1;
String custom_name;
Node *agent = nullptr;
Node *scene_root = nullptr;
Ref<Blackboard> blackboard;
BTTask *parent = nullptr;
Vector<Ref<BTTask>> children;
Status status = FRESH;
double elapsed = 0.0;
bool display_collapsed = false;
#ifdef TOOLS_ENABLED
ObjectID behavior_tree_id;
#endif
} data;
Array _get_children() const;
@ -116,6 +122,8 @@ public:
_FORCE_INLINE_ Node *get_agent() const { return data.agent; }
void set_agent(Node *p_agent) { data.agent = p_agent; }
_FORCE_INLINE_ Node *get_scene_root() const { return data.scene_root; }
void set_display_collapsed(bool p_display_collapsed);
bool is_displayed_collapsed() const;
@ -126,7 +134,7 @@ public:
Ref<BTTask> get_root() const;
virtual Ref<BTTask> clone() const;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard);
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root);
virtual PackedStringArray get_configuration_warnings(); // ! Native version.
Status execute(double p_delta);
@ -159,6 +167,11 @@ public:
void print_tree(int p_initial_tabs = 0);
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> editor_get_behavior_tree();
void editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt);
#endif
BTTask();
~BTTask();
};

View File

@ -22,6 +22,8 @@ private:
int last_running_idx = 0;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -22,6 +22,8 @@ private:
int last_running_idx = 0;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -23,6 +23,8 @@ private:
Array indicies;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -23,6 +23,8 @@ private:
Array indicies;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -22,6 +22,8 @@ private:
int last_running_idx = 0;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -22,6 +22,8 @@ private:
int last_running_idx = 0;
protected:
static void _bind_methods() {}
virtual void _enter() override;
virtual Status _tick(double p_delta) override;
};

View File

@ -19,6 +19,8 @@ class BTAlwaysFail : public BTDecorator {
TASK_CATEGORY(Decorators);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -19,6 +19,8 @@ class BTAlwaysSucceed : public BTDecorator {
TASK_CATEGORY(Decorators);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -54,7 +54,7 @@ String BTCooldown::_generate_name() {
void BTCooldown::_setup() {
if (cooldown_state_var == StringName()) {
cooldown_state_var = vformat("cooldown_%d", rand());
cooldown_state_var = vformat("cooldown_%d", get_instance_id());
}
get_blackboard()->set_var(cooldown_state_var, false);
if (start_cooled) {

View File

@ -19,6 +19,8 @@ class BTInvert : public BTDecorator {
TASK_CATEGORY(Decorators);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -11,29 +11,46 @@
#include "bt_new_scope.h"
#include "../../behavior_tree.h"
void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan;
if (blackboard_plan.is_null()) {
blackboard_plan.instantiate();
}
_update_blackboard_plan();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
callable_mp(this, &BTNewScope::_set_parent_scope_plan_from_bt).call_deferred();
}
#endif // TOOLS_ENABLED
emit_changed();
}
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
#ifdef TOOLS_ENABLED
void BTNewScope::_set_parent_scope_plan_from_bt() {
ERR_FAIL_NULL(get_blackboard_plan());
Ref<BehaviorTree> bt = get_root()->editor_get_behavior_tree();
ERR_FAIL_NULL(bt);
get_blackboard_plan()->set_parent_scope_plan_provider(callable_mp(bt.ptr(), &BehaviorTree::get_blackboard_plan));
}
#endif // TOOLS_ENABLED
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr);
Ref<Blackboard> bb;
if (blackboard_plan.is_valid()) {
bb = blackboard_plan->create_blackboard(p_agent);
bb = blackboard_plan->create_blackboard(p_agent, p_blackboard);
} else {
bb = Ref<Blackboard>(memnew(Blackboard));
bb->set_parent(p_blackboard);
}
bb->set_parent(p_blackboard);
BTDecorator::initialize(p_agent, bb);
BTDecorator::initialize(p_agent, bb, p_scene_root);
}
BT::Status BTNewScope::_tick(double p_delta) {

View File

@ -23,6 +23,10 @@ class BTNewScope : public BTDecorator {
private:
Ref<BlackboardPlan> blackboard_plan;
#ifdef TOOLS_ENABLED
void _set_parent_scope_plan_from_bt();
#endif // TOOLS_ENABLED
protected:
static void _bind_methods();
@ -34,7 +38,7 @@ protected:
virtual Status _tick(double p_delta) override;
public:
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) override;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) override;
};
#endif // BT_NEW_SCOPE_H

View File

@ -19,6 +19,8 @@ class BTRepeatUntilFailure : public BTDecorator {
TASK_CATEGORY(Decorators);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -19,6 +19,8 @@ class BTRepeatUntilSuccess : public BTDecorator {
TASK_CATEGORY(Decorators);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -44,14 +44,14 @@ String BTSubtree::_generate_name() {
return vformat("Subtree %s", s);
}
void BTSubtree::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
void BTSubtree::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND_MSG(!subtree.is_valid(), "Subtree is not assigned.");
ERR_FAIL_COND_MSG(!subtree->get_root_task().is_valid(), "Subtree root task is not valid.");
ERR_FAIL_COND_MSG(get_child_count() != 0, "Subtree task shouldn't have children during initialization.");
add_child(subtree->get_root_task()->clone());
BTNewScope::initialize(p_agent, p_blackboard);
BTNewScope::initialize(p_agent, p_blackboard, p_scene_root);
}
BT::Status BTSubtree::_tick(double p_delta) {

View File

@ -35,7 +35,7 @@ public:
void set_subtree(const Ref<BehaviorTree> &p_value);
Ref<BehaviorTree> get_subtree() const { return subtree; }
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) override;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) override;
virtual PackedStringArray get_configuration_warnings() override;
BTSubtree() = default;

View File

@ -62,7 +62,7 @@ String BTAwaitAnimation::_generate_name() {
void BTAwaitAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTAwaitAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTAwaitAnimation: Failed to get AnimationPlayer.");
ERR_FAIL_COND_MSG(animation_name == StringName(), "BTAwaitAnimation: Animation Name is not set.");
ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTAwaitAnimation: Animation not found: %s", animation_name));

View File

@ -62,7 +62,7 @@ BT::Status BTCheckAgentProperty::_tick(double p_delta) {
Variant left_value = get_agent()->get(property);
#endif
Variant right_value = value->get_value(get_agent(), get_blackboard());
Variant right_value = value->get_value(get_scene_root(), get_blackboard());
return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE;
}

View File

@ -44,7 +44,7 @@ String BTPauseAnimation::_generate_name() {
void BTPauseAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPauseAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPauseAnimation: Failed to get AnimationPlayer.");
setup_failed = false;
}

View File

@ -77,7 +77,7 @@ String BTPlayAnimation::_generate_name() {
void BTPlayAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPlayAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPlayAnimation: Failed to get AnimationPlayer.");
ERR_FAIL_COND_MSG(animation_name != StringName() && !animation_player->has_animation(animation_name), vformat("BTPlayAnimation: Animation not found: %s", animation_name));
if (animation_name == StringName() && await_completion > 0.0) {

View File

@ -55,7 +55,7 @@ BT::Status BTSetAgentProperty::_tick(double p_delta) {
Variant result;
StringName error_value = LW_NAME(error_value);
Variant right_value = value->get_value(get_agent(), get_blackboard(), error_value);
Variant right_value = value->get_value(get_scene_root(), get_blackboard(), error_value);
ERR_FAIL_COND_V_MSG(right_value == Variant(error_value), FAILURE, "BTSetAgentProperty: Couldn't get value of value-parameter.");
bool r_valid;
if (operation == LimboUtility::OPERATION_NONE) {

View File

@ -56,7 +56,7 @@ String BTStopAnimation::_generate_name() {
void BTStopAnimation::_setup() {
setup_failed = true;
ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTStopAnimation: AnimationPlayer parameter is not set.");
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_agent(), get_blackboard()));
animation_player = Object::cast_to<AnimationPlayer>(animation_player_param->get_value(get_scene_root(), get_blackboard()));
ERR_FAIL_COND_MSG(animation_player == nullptr, "BTStopAnimation: Failed to get AnimationPlayer.");
if (animation_name != StringName()) {
ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTStopAnimation: Animation not found: %s", animation_name));

View File

@ -83,7 +83,7 @@ String BTCallMethod::_generate_name() {
BT::Status BTCallMethod::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(method == StringName(), FAILURE, "BTCallMethod: Method Name is not set.");
ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTCallMethod: Node parameter is not set.");
Object *obj = node_param->get_value(get_agent(), get_blackboard());
Object *obj = node_param->get_value(get_scene_root(), get_blackboard());
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTCallMethod: Failed to get object: " + node_param->to_string());
Variant result;
@ -101,7 +101,7 @@ BT::Status BTCallMethod::_tick(double p_delta) {
}
for (int i = 0; i < args.size(); i++) {
Ref<BBVariant> param = args[i];
call_args.push_back(param->get_value(get_agent(), get_blackboard()));
call_args.push_back(param->get_value(get_scene_root(), get_blackboard()));
argptrs[i + int(include_delta)] = &call_args[i];
}
}
@ -117,7 +117,7 @@ BT::Status BTCallMethod::_tick(double p_delta) {
}
for (int i = 0; i < args.size(); i++) {
Ref<BBVariant> param = args[i];
call_args.push_back(param->get_value(get_agent(), get_blackboard()));
call_args.push_back(param->get_value(get_scene_root(), get_blackboard()));
}
// TODO: Unsure how to detect call error, so we return SUCCESS for now...

View File

@ -107,7 +107,7 @@ String BTEvaluateExpression::_generate_name() {
BT::Status BTEvaluateExpression::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(expression_string.is_empty(), FAILURE, "BTEvaluateExpression: Expression String is not set.");
ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTEvaluateExpression: Node parameter is not set.");
Object *obj = node_param->get_value(get_agent(), get_blackboard());
Object *obj = node_param->get_value(get_scene_root(), get_blackboard());
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTEvaluateExpression: Failed to get object: " + node_param->to_string());
ERR_FAIL_COND_V_MSG(is_parsed != Error::OK, FAILURE, "BTEvaluateExpression: Failed to parse expression: " + expression.get_error_text());
@ -116,7 +116,7 @@ BT::Status BTEvaluateExpression::_tick(double p_delta) {
}
for (int i = 0; i < input_values.size(); ++i) {
const Ref<BBVariant> &bb_variant = input_values[i];
processed_input_values[i + int(input_include_delta)] = bb_variant->get_value(get_agent(), get_blackboard());
processed_input_values[i + int(input_include_delta)] = bb_variant->get_value(get_scene_root(), get_blackboard());
}
Variant result = expression.execute(processed_input_values, obj, false);

View File

@ -19,6 +19,8 @@ class BTFail : public BTAction {
TASK_CATEGORY(Utility);
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
};

View File

@ -67,7 +67,6 @@ func _init_state_machine() -> void:
hsm.initialize(self)
hsm.set_active(true)
hsm.set_guard(_can_dodge)
func _init_input_events() -> void:

View File

@ -49,11 +49,11 @@ Methods
.. table::
:widths: auto
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Variant.Type<enum_@GlobalScope_Variant.Type>` | :ref:`get_type<class_BBParam_method_get_type>` **(** **)** |const| |
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_value<class_BBParam_method_get_value>` **(** Object agent, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)** |
+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Variant.Type<enum_@GlobalScope_Variant.Type>` | :ref:`get_type<class_BBParam_method_get_type>` **(** **)** |const| |
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_value<class_BBParam_method_get_value>` **(** Node scene_root, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)** |
+-----------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -167,7 +167,7 @@ Returns the expected data type of the parameter.
.. rst-class:: classref-method
Variant **get_value** **(** Object agent, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)**
Variant **get_value** **(** Node scene_root, :ref:`Blackboard<class_Blackboard>` blackboard, Variant default=null **)**
Returns the value of the parameter.

View File

@ -55,17 +55,17 @@ Methods
.. table::
:widths: auto
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`clone<class_BehaviorTree_method_clone>` **(** **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`copy_other<class_BehaviorTree_method_copy_other>` **(** :ref:`BehaviorTree<class_BehaviorTree>` other **)** |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root_task<class_BehaviorTree_method_get_root_task>` **(** **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`instantiate<class_BehaviorTree_method_instantiate>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |const| |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_root_task<class_BehaviorTree_method_set_root_task>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`clone<class_BehaviorTree_method_clone>` **(** **)** |const| |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`copy_other<class_BehaviorTree_method_copy_other>` **(** :ref:`BehaviorTree<class_BehaviorTree>` other **)** |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root_task<class_BehaviorTree_method_get_root_task>` **(** **)** |const| |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`instantiate<class_BehaviorTree_method_instantiate>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |const| |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_root_task<class_BehaviorTree_method_set_root_task>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -172,9 +172,9 @@ Returns the root task of the BehaviorTree resource.
.. rst-class:: classref-method
:ref:`BTTask<class_BTTask>` **instantiate** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |const|
:ref:`BTTask<class_BTTask>` **instantiate** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |const|
Instantiates the Behavior Tree and returns the root :ref:`BTTask<class_BTTask>`.
Instantiates the behavior tree and returns the root :ref:`BTTask<class_BTTask>`. ``scene_root`` should be the root node of the scene that the Behavior Tree will be used in (e.g., the owner of the node that contains the behavior tree).
.. rst-class:: classref-item-separator

View File

@ -33,27 +33,35 @@ Methods
.. table::
:widths: auto
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`bind_var_to_property<class_Blackboard_method_bind_var_to_property>` **(** StringName var_name, Object object, StringName property, bool create **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`erase_var<class_Blackboard_method_erase_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`get_parent<class_Blackboard_method_get_parent>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_var<class_Blackboard_method_get_var>` **(** StringName var_name, Variant default=null, bool complain=true **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_var<class_Blackboard_method_has_var>` **(** StringName var_name **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`link_var<class_Blackboard_method_link_var>` **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_parent<class_Blackboard_method_set_parent>` **(** :ref:`Blackboard<class_Blackboard>` blackboard **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_var<class_Blackboard_method_set_var>` **(** StringName var_name, Variant value **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`top<class_Blackboard_method_top>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`unbind_var<class_Blackboard_method_unbind_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`bind_var_to_property<class_Blackboard_method_bind_var_to_property>` **(** StringName var_name, Object object, StringName property, bool create=false **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`clear<class_Blackboard_method_clear>` **(** **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`erase_var<class_Blackboard_method_erase_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`get_parent<class_Blackboard_method_get_parent>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variant | :ref:`get_var<class_Blackboard_method_get_var>` **(** StringName var_name, Variant default=null, bool complain=true **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Dictionary | :ref:`get_vars_as_dict<class_Blackboard_method_get_vars_as_dict>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_var<class_Blackboard_method_has_var>` **(** StringName var_name **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`link_var<class_Blackboard_method_link_var>` **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create=false **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| StringName[] | :ref:`list_vars<class_Blackboard_method_list_vars>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`populate_from_dict<class_Blackboard_method_populate_from_dict>` **(** Dictionary dictionary **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_parent<class_Blackboard_method_set_parent>` **(** :ref:`Blackboard<class_Blackboard>` blackboard **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_var<class_Blackboard_method_set_var>` **(** StringName var_name, Variant value **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`top<class_Blackboard_method_top>` **(** **)** |const| |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`unbind_var<class_Blackboard_method_unbind_var>` **(** StringName var_name **)** |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -68,7 +76,7 @@ Method Descriptions
.. rst-class:: classref-method
void **bind_var_to_property** **(** StringName var_name, Object object, StringName property, bool create **)**
void **bind_var_to_property** **(** StringName var_name, Object object, StringName property, bool create=false **)**
Establish a binding between a variable and the object's property specified by ``property`` and ``object``. Changes to the variable update the property, and vice versa. If ``create`` is ``true``, the variable will be created if it doesn't exist.
@ -76,6 +84,18 @@ Establish a binding between a variable and the object's property specified by ``
----
.. _class_Blackboard_method_clear:
.. rst-class:: classref-method
void **clear** **(** **)**
Removes all variables from the Blackboard. Parent scopes are not affected.
.. rst-class:: classref-item-separator
----
.. _class_Blackboard_method_erase_var:
.. rst-class:: classref-method
@ -112,6 +132,18 @@ Returns variable value or ``default`` if variable doesn't exist. If ``complain``
----
.. _class_Blackboard_method_get_vars_as_dict:
.. rst-class:: classref-method
Dictionary **get_vars_as_dict** **(** **)** |const|
Returns all variables in the Blackboard as a dictionary. Keys are the variable names, values are the variable values. Parent scopes are not included.
.. rst-class:: classref-item-separator
----
.. _class_Blackboard_method_has_var:
.. rst-class:: classref-method
@ -128,7 +160,7 @@ Returns ``true`` if the Blackboard contains the ``var_name`` variable, including
.. rst-class:: classref-method
void **link_var** **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create **)**
void **link_var** **(** StringName var_name, :ref:`Blackboard<class_Blackboard>` target_blackboard, StringName target_var, bool create=false **)**
Links a variable to another Blackboard variable. If a variable is linked to another variable, their state will always be identical, and any change to one will be reflected in the other. If ``create`` is ``true``, the variable will be created if it doesn't exist.
@ -138,6 +170,30 @@ You can use this method to link a variable in the current scope to a variable in
----
.. _class_Blackboard_method_list_vars:
.. rst-class:: classref-method
StringName[] **list_vars** **(** **)** |const|
Returns all variable names in the Blackboard. Parent scopes are not included.
.. rst-class:: classref-item-separator
----
.. _class_Blackboard_method_populate_from_dict:
.. rst-class:: classref-method
void **populate_from_dict** **(** Dictionary dictionary **)**
Fills the Blackboard with multiple variables from a dictionary. The dictionary keys must be variable names and the dictionary values must be variable values. Keys must be StringName or String.
.. rst-class:: classref-item-separator
----
.. _class_Blackboard_method_set_parent:
.. rst-class:: classref-method

View File

@ -35,16 +35,20 @@ Methods
:widths: auto
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`create_blackboard<class_BlackboardPlan_method_create_blackboard>` **(** Node node **)** |
| :ref:`Blackboard<class_Blackboard>` | :ref:`create_blackboard<class_BlackboardPlan_method_create_blackboard>` **(** Node node, :ref:`Blackboard<class_Blackboard>` parent_scope=null **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`get_base_plan<class_BlackboardPlan_method_get_base_plan>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Callable | :ref:`get_parent_scope_plan_provider<class_BlackboardPlan_method_get_parent_scope_plan_provider>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_derived<class_BlackboardPlan_method_is_derived>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`populate_blackboard<class_BlackboardPlan_method_populate_blackboard>` **(** :ref:`Blackboard<class_Blackboard>` blackboard, bool overwrite, Node node **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_base_plan<class_BlackboardPlan_method_set_base_plan>` **(** :ref:`BlackboardPlan<class_BlackboardPlan>` blackboard_plan **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_parent_scope_plan_provider<class_BlackboardPlan_method_set_parent_scope_plan_provider>` **(** Callable callable **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`sync_with_base_plan<class_BlackboardPlan_method_sync_with_base_plan>` **(** **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
@ -83,7 +87,7 @@ Method Descriptions
.. rst-class:: classref-method
:ref:`Blackboard<class_Blackboard>` **create_blackboard** **(** Node node **)**
:ref:`Blackboard<class_Blackboard>` **create_blackboard** **(** Node node, :ref:`Blackboard<class_Blackboard>` parent_scope=null **)**
Constructs a new instance of a :ref:`Blackboard<class_Blackboard>` using this plan. If ``NodePath`` prefetching is enabled, ``node`` will be used to retrieve node instances for ``NodePath`` variables and substitute their values.
@ -103,6 +107,18 @@ Returns the base plan. See :ref:`is_derived<class_BlackboardPlan_method_is_deriv
----
.. _class_BlackboardPlan_method_get_parent_scope_plan_provider:
.. rst-class:: classref-method
Callable **get_parent_scope_plan_provider** **(** **)** |const|
Returns the parent scope plan provider - a callable that returns a **BlackboardPlan**.
.. rst-class:: classref-item-separator
----
.. _class_BlackboardPlan_method_is_derived:
.. rst-class:: classref-method
@ -141,6 +157,18 @@ Use with caution, as it will remove variables not present in the base plan. Only
----
.. _class_BlackboardPlan_method_set_parent_scope_plan_provider:
.. rst-class:: classref-method
void **set_parent_scope_plan_provider** **(** Callable callable **)**
Sets the parent scope plan provider - a callable that returns a **BlackboardPlan**. Used to provide hints in the inspector. When set, mapping feature becomes available.
.. rst-class:: classref-item-separator
----
.. _class_BlackboardPlan_method_sync_with_base_plan:
.. rst-class:: classref-method

View File

@ -19,7 +19,7 @@ Player of :ref:`BehaviorTree<class_BehaviorTree>` resources.
Description
-----------
BTPlayer node is used for the instantiation and playback of :ref:`BehaviorTree<class_BehaviorTree>` resources at runtime. During instantiation, the behavior tree instance is initialized with a reference to the agent and the :ref:`blackboard<class_BTPlayer_property_blackboard>`. Agent is the owner of the BTPlayer node (see :ref:`Node.owner<class_Node_member_owner>`).
**BTPlayer** node is used to instantiate and play :ref:`BehaviorTree<class_BehaviorTree>` resources at runtime. During initialization, the behavior tree instance is given references to the agent, the :ref:`blackboard<class_BTPlayer_property_blackboard>`, and the current scene root. The agent can be specified by the :ref:`agent_node<class_BTPlayer_property_agent_node>` property (defaults to the BTPlayer's parent node).
For an introduction to behavior trees, see :ref:`BehaviorTree<class_BehaviorTree>`.
@ -31,19 +31,21 @@ Properties
.. table::
:widths: auto
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| bool | :ref:`active<class_BTPlayer_property_active>` | ``true`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`behavior_tree<class_BTPlayer_property_behavior_tree>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`blackboard<class_BTPlayer_property_blackboard>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`blackboard_plan<class_BTPlayer_property_blackboard_plan>` | |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| bool | :ref:`monitor_performance<class_BTPlayer_property_monitor_performance>` | ``false`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
| :ref:`UpdateMode<enum_BTPlayer_UpdateMode>` | :ref:`update_mode<class_BTPlayer_property_update_mode>` | ``1`` |
+---------------------------------------------+-------------------------------------------------------------------------+-----------+
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| bool | :ref:`active<class_BTPlayer_property_active>` | ``true`` |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| NodePath | :ref:`agent_node<class_BTPlayer_property_agent_node>` | ``NodePath("..")`` |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`behavior_tree<class_BTPlayer_property_behavior_tree>` | |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`blackboard<class_BTPlayer_property_blackboard>` | |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`blackboard_plan<class_BTPlayer_property_blackboard_plan>` | |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| bool | :ref:`monitor_performance<class_BTPlayer_property_monitor_performance>` | ``false`` |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
| :ref:`UpdateMode<enum_BTPlayer_UpdateMode>` | :ref:`update_mode<class_BTPlayer_property_update_mode>` | ``1`` |
+---------------------------------------------+-------------------------------------------------------------------------+--------------------+
.. rst-class:: classref-reftable-group
@ -161,6 +163,23 @@ If ``true``, the behavior tree will be executed during update.
----
.. _class_BTPlayer_property_agent_node:
.. rst-class:: classref-property
NodePath **agent_node** = ``NodePath("..")``
.. rst-class:: classref-property-setget
- void **set_agent_node** **(** NodePath value **)**
- NodePath **get_agent_node** **(** **)**
Path to the node that will be used as the agent. Setting it after instantiation will have no effect.
.. rst-class:: classref-item-separator
----
.. _class_BTPlayer_property_behavior_tree:
.. rst-class:: classref-property

View File

@ -23,6 +23,8 @@ BTSubtree instantiates a :ref:`BehaviorTree<class_BehaviorTree>` and includes it
Returns the status of the subtree's execution.
Subtree blackboard variables can be mapped to the main tree blackboard plan variables. Check out mapping section in the inspector.
Note: BTSubTree is designed as a simpler loader, and does not support updating :ref:`subtree<class_BTSubtree_property_subtree>` at runtime. A custom subtree decorator is better suited and `somewhat trivial <https://github.com/limbonaut/limboai/issues/94#issuecomment-2068833610>`__ to implement.
.. rst-class:: classref-reftable-group

View File

@ -46,6 +46,8 @@ Properties
+-------------------------------------+---------------------------------------------------------+--------+
| float | :ref:`elapsed_time<class_BTTask_property_elapsed_time>` | |
+-------------------------------------+---------------------------------------------------------+--------+
| Node | :ref:`scene_root<class_BTTask_property_scene_root>` | |
+-------------------------------------+---------------------------------------------------------+--------+
| :ref:`Status<enum_BT_Status>` | :ref:`status<class_BTTask_property_status>` | |
+-------------------------------------+---------------------------------------------------------+--------+
@ -57,59 +59,61 @@ Methods
.. table::
:widths: auto
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_enter<class_BTTask_private_method__enter>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_exit<class_BTTask_private_method__exit>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`_generate_name<class_BTTask_private_method__generate_name>` **(** **)** |virtual| |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| PackedStringArray | :ref:`_get_configuration_warnings<class_BTTask_private_method__get_configuration_warnings>` **(** **)** |virtual| |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_setup<class_BTTask_private_method__setup>` **(** **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`_tick<class_BTTask_private_method__tick>` **(** float delta **)** |virtual| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`abort<class_BTTask_method_abort>` **(** **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child<class_BTTask_method_add_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child_at_index<class_BTTask_method_add_child_at_index>` **(** :ref:`BTTask<class_BTTask>` task, int idx **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`clone<class_BTTask_method_clone>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`execute<class_BTTask_method_execute>` **(** float delta **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_child<class_BTTask_method_get_child>` **(** int idx **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count<class_BTTask_method_get_child_count>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count_excluding_comments<class_BTTask_method_get_child_count_excluding_comments>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_index<class_BTTask_method_get_index>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_parent<class_BTTask_method_get_parent>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root<class_BTTask_method_get_root>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`get_task_name<class_BTTask_method_get_task_name>` **(** **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_child<class_BTTask_method_has_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_descendant_of<class_BTTask_method_is_descendant_of>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_root<class_BTTask_method_is_root>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`next_sibling<class_BTTask_method_next_sibling>` **(** **)** |const| |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`print_tree<class_BTTask_method_print_tree>` **(** int initial_tabs=0 **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child<class_BTTask_method_remove_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child_at_index<class_BTTask_method_remove_child_at_index>` **(** int idx **)** |
+-------------------------------+---------------------------------------------------------------------------------------------------------------------------+
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_enter<class_BTTask_private_method__enter>` **(** **)** |virtual| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_exit<class_BTTask_private_method__exit>` **(** **)** |virtual| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`_generate_name<class_BTTask_private_method__generate_name>` **(** **)** |virtual| |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| PackedStringArray | :ref:`_get_configuration_warnings<class_BTTask_private_method__get_configuration_warnings>` **(** **)** |virtual| |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_setup<class_BTTask_private_method__setup>` **(** **)** |virtual| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`_tick<class_BTTask_private_method__tick>` **(** float delta **)** |virtual| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`abort<class_BTTask_method_abort>` **(** **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child<class_BTTask_method_add_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child_at_index<class_BTTask_method_add_child_at_index>` **(** :ref:`BTTask<class_BTTask>` task, int idx **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`clone<class_BTTask_method_clone>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`editor_get_behavior_tree<class_BTTask_method_editor_get_behavior_tree>` **(** **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`execute<class_BTTask_method_execute>` **(** float delta **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_child<class_BTTask_method_get_child>` **(** int idx **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count<class_BTTask_method_get_child_count>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count_excluding_comments<class_BTTask_method_get_child_count_excluding_comments>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_index<class_BTTask_method_get_index>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_parent<class_BTTask_method_get_parent>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root<class_BTTask_method_get_root>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`get_task_name<class_BTTask_method_get_task_name>` **(** **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_child<class_BTTask_method_has_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_descendant_of<class_BTTask_method_is_descendant_of>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_root<class_BTTask_method_is_root>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`next_sibling<class_BTTask_method_next_sibling>` **(** **)** |const| |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`print_tree<class_BTTask_method_print_tree>` **(** int initial_tabs=0 **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child<class_BTTask_method_remove_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child_at_index<class_BTTask_method_remove_child_at_index>` **(** int idx **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator
@ -131,7 +135,7 @@ Node **agent**
- void **set_agent** **(** Node value **)**
- Node **get_agent** **(** **)**
The agent is a contextual object for the task's :ref:`BehaviorTree<class_BehaviorTree>` instance. Usually, agent is the owner of the :ref:`BTPlayer<class_BTPlayer>` node containing the :ref:`BehaviorTree<class_BehaviorTree>` resource.
The agent is the contextual object for the :ref:`BehaviorTree<class_BehaviorTree>` instance. This is usually the parent of the :ref:`BTPlayer<class_BTPlayer>` node that utilizes the :ref:`BehaviorTree<class_BehaviorTree>` resource.
.. rst-class:: classref-item-separator
@ -190,6 +194,33 @@ Returns ``0`` when task is not ``RUNNING``.
----
.. _class_BTTask_property_scene_root:
.. rst-class:: classref-property
Node **scene_root**
.. rst-class:: classref-property-setget
- Node **get_scene_root** **(** **)**
Root node of the scene the behavior tree is used in (e.g., the owner of the :ref:`BTPlayer<class_BTPlayer>` node). Can be uses to retrieve ``NodePath`` references.
\ **Example:**\
::
extends BTAction
@export var node_path: NodePath
func _setup():
var node: Node = scene_root.get_node(node_path)
.. rst-class:: classref-item-separator
----
.. _class_BTTask_property_status:
.. rst-class:: classref-property
@ -337,6 +368,18 @@ Duplicates the task and its children, copying the exported members. Sub-resource
----
.. _class_BTTask_method_editor_get_behavior_tree:
.. rst-class:: classref-method
:ref:`BehaviorTree<class_BehaviorTree>` **editor_get_behavior_tree** **(** **)**
Returns the behavior tree that owns this task. This is only available in the editor.
.. rst-class:: classref-item-separator
----
.. _class_BTTask_method_execute:
.. rst-class:: classref-method
@ -457,11 +500,11 @@ Returns ``true`` if ``task`` is a child of this task.
.. rst-class:: classref-method
void **initialize** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard **)**
void **initialize** **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)**
Initilizes the task. Assigns :ref:`agent<class_BTTask_property_agent>` and :ref:`blackboard<class_BTTask_property_blackboard>`, and calls :ref:`_setup<class_BTTask_private_method__setup>` for the task and its children.
The method is called recursively for each child task.
The method is called recursively for each child task. ``scene_root`` should be the root node of the scene the behavior tree is used in (e.g., the owner of the node that contains the behavior tree).
.. rst-class:: classref-item-separator

View File

@ -3,9 +3,10 @@
Accessing nodes in the scene tree
=================================
There are several ways to access nodes in the agent's scene tree.
There are several ways to access nodes in the agent's scene tree from a :ref:`BTTask<class_BTTask>`.
**🛈 Note:** Agent is the owner of :ref:`BTPlayer<class_BTPlayer>` node.
**🛈 Note:** The root node of the agent's scene tree can be accessed with the
:ref:`scene_root<class_BTTask_property_scene_root>` property.
With ``BBNode`` property
@ -16,7 +17,7 @@ With ``BBNode`` property
@export var cast_param: BBNode
func _tick(delta) -> Status:
var node: ShapeCast3D = cast_param.get_value(agent, blackboard)
var node: ShapeCast3D = cast_param.get_value(scene_root, blackboard)
With ``NodePath`` property
@ -27,14 +28,16 @@ With ``NodePath`` property
@export var cast_path: NodePath
func _tick(delta) -> Status:
var node: ShapeCast3D = agent.get_node(cast_path)
var node: ShapeCast3D = scene_root.get_node(cast_path)
Using blackboard plan
---------------------
You can :ref:`create a blackboard variable<editing_plan>` in the editor with the type ``NodePath``
and point it to the proper node in the :ref:`BTPlayer<class_BTPlayer>` blackboard plan.
and point it to the proper node in the :ref:`BTPlayer<class_BTPlayer>` blackboard plan. By default,
any ``NodePath`` variable will be replaced with the node instance when the blackboard is instantiated
at runtime (see :ref:`BlackboardPlan.prefetch_nodepath_vars<class_BlackboardPlan_property_prefetch_nodepath_vars>`).
.. code:: gdscript

View File

@ -92,7 +92,7 @@ Example 1: A simple action
# Called each time this task is ticked (aka executed).
func _tick(p_delta: float) -> Status:
var n: CanvasItem = agent.get_node_or_null(node_path)
var n: CanvasItem = scene_root.get_node_or_null(node_path)
if is_instance_valid(n):
n.visible = visible
return SUCCESS

View File

@ -91,7 +91,7 @@ Usage example:
@export var speed: BBFloat
func _tick(delta: float) -> Status:
var current_speed: float = speed.get_value(agent, blackboard, 0.0)
var current_speed: float = speed.get_value(scene_root, blackboard, 0.0)
...
Advanced topic: Blackboard scopes

Some files were not shown because too many files have changed in this diff Show More