From f0c0a74c14b5ce3d2703d6c2bb4723c295ad1195 Mon Sep 17 00:00:00 2001 From: Adrien Quillet Date: Sun, 29 Jan 2023 12:58:10 +0100 Subject: [PATCH] #19 : add an example how to share a blackboard in multiple trees --- examples/blackboard_sharing/README.MD | 12 +++ examples/blackboard_sharing/main.gd | 42 ++++++++ examples/blackboard_sharing/main.tscn | 23 +++++ examples/blackboard_sharing/player.gd | 63 ++++++++++++ examples/blackboard_sharing/player.tscn | 124 ++++++++++++++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 examples/blackboard_sharing/README.MD create mode 100644 examples/blackboard_sharing/main.gd create mode 100644 examples/blackboard_sharing/main.tscn create mode 100644 examples/blackboard_sharing/player.gd create mode 100644 examples/blackboard_sharing/player.tscn diff --git a/examples/blackboard_sharing/README.MD b/examples/blackboard_sharing/README.MD new file mode 100644 index 0000000..fe0d594 --- /dev/null +++ b/examples/blackboard_sharing/README.MD @@ -0,0 +1,12 @@ +# 🗣️ Sharing blackboard : the talking game ! + +This example demonstrates how to share a single blackboard in multiple behavior trees. + +Sharing a blackboard can be necessary if you want to easily share data between trees, for example, to synchronize multiple NPCs. + +The blackboard can be instantiate in a Scene Tree via the editor, or in a script (as in this example). + +## Technical elements + +- `main.tscn` : scene to run. The main scene instantiates a unique blackboard and give it to all player trees, +- `player.tscn` : a player in the talking game. They all share the same blackboard to known who is the next to talk. \ No newline at end of file diff --git a/examples/blackboard_sharing/main.gd b/examples/blackboard_sharing/main.gd new file mode 100644 index 0000000..4ea5349 --- /dev/null +++ b/examples/blackboard_sharing/main.gd @@ -0,0 +1,42 @@ +extends Node2D + +#------------------------------------------ +# Signaux +#------------------------------------------ + +#------------------------------------------ +# Exports +#------------------------------------------ + +#------------------------------------------ +# Variables publiques +#------------------------------------------ + +#------------------------------------------ +# Variables privées +#------------------------------------------ + +var _shared_blackboard:BTBlackboard + +#------------------------------------------ +# Fonctions Godot redéfinies +#------------------------------------------ + +func _ready() -> void: + _shared_blackboard = BTBlackboard.new() + _shared_blackboard.set_data("next_talking", "Alice") + _shared_blackboard.set_data("players", []) + + $"PlayerAlice/BTRoot/".blackboard = _shared_blackboard + $"PlayerBob/BTRoot/".blackboard = _shared_blackboard + $"PlayerCharles/BTRoot/".blackboard = _shared_blackboard + $"PlayerEmily/BTRoot/".blackboard = _shared_blackboard + +#------------------------------------------ +# Fonctions publiques +#------------------------------------------ + +#------------------------------------------ +# Fonctions privées +#------------------------------------------ + diff --git a/examples/blackboard_sharing/main.tscn b/examples/blackboard_sharing/main.tscn new file mode 100644 index 0000000..8317600 --- /dev/null +++ b/examples/blackboard_sharing/main.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=3 format=3 uid="uid://d1s5iyoblr4cm"] + +[ext_resource type="Script" path="res://examples/blackboard_sharing/main.gd" id="1_g7q7h"] +[ext_resource type="PackedScene" uid="uid://7k8r0e84lcr3" path="res://examples/blackboard_sharing/player.tscn" id="2_y55mt"] + +[node name="World" type="Node2D"] +script = ExtResource("1_g7q7h") + +[node name="PlayerAlice" parent="." instance=ExtResource("2_y55mt")] +position = Vector2(509, 181) +player_name = "Alice" + +[node name="PlayerBob" parent="." instance=ExtResource("2_y55mt")] +position = Vector2(515, 477) +player_name = "Bob" + +[node name="PlayerCharles" parent="." instance=ExtResource("2_y55mt")] +position = Vector2(309, 324) +player_name = "Charles" + +[node name="PlayerEmily" parent="." instance=ExtResource("2_y55mt")] +position = Vector2(733, 322) +player_name = "Emily" diff --git a/examples/blackboard_sharing/player.gd b/examples/blackboard_sharing/player.gd new file mode 100644 index 0000000..b65b39b --- /dev/null +++ b/examples/blackboard_sharing/player.gd @@ -0,0 +1,63 @@ +extends Node2D + +#------------------------------------------ +# Signaux +#------------------------------------------ + +#------------------------------------------ +# Exports +#------------------------------------------ + +@export var player_name:String = "": + set(value): + player_name = value + if _name_label: + _name_label.text = player_name + +#------------------------------------------ +# Variables publiques +#------------------------------------------ + +#------------------------------------------ +# Variables privées +#------------------------------------------ + +@onready var _text_label:Label = $GUI/Text +@onready var _name_label:Label = $GUI/Name + +#------------------------------------------ +# Fonctions Godot redéfinies +#------------------------------------------ + +func _ready() -> void: + _name_label.text = player_name + +#------------------------------------------ +# Fonctions publiques +#------------------------------------------ + +func declare_presence(blackboard:BTBlackboard) -> int: + blackboard.get_data("players").append(player_name) + return BTTickResult.SUCCESS + +func is_my_turn_to_talk(next_talking:String) -> bool: + return player_name == next_talking + +func talk() -> int: + _text_label.visible = true + get_tree().create_timer(1).timeout.connect(func():_text_label.visible = false) + return BTTickResult.SUCCESS + +func choose_next_player_talking(blackboard:BTBlackboard) -> int: + blackboard.set_data("next_talking", _pick_next_player(blackboard.get_data("players"))) + return BTTickResult.SUCCESS + +#------------------------------------------ +# Fonctions privées +#------------------------------------------ + +func _pick_next_player(players:Array) -> String: + var result:String = players.pick_random() + while result == player_name: + result = players.pick_random() + return result diff --git a/examples/blackboard_sharing/player.tscn b/examples/blackboard_sharing/player.tscn new file mode 100644 index 0000000..45ac42c --- /dev/null +++ b/examples/blackboard_sharing/player.tscn @@ -0,0 +1,124 @@ +[gd_scene load_steps=11 format=3 uid="uid://7k8r0e84lcr3"] + +[ext_resource type="Script" path="res://examples/blackboard_sharing/player.gd" id="1_1ti0g"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/BTRoot.gd" id="2_sdd0w"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Composite/BTSelector.gd" id="3_2v3m1"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Composite/BTSequence.gd" id="4_fjw5l"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Decorators/BTSuccess.gd" id="4_qnbqx"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Leaves/BTConditionCallable.gd" id="5_8tfvp"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Decorators/BTLimiter.gd" id="5_d3jao"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Leaves/BTActionCallable.gd" id="6_i6lan"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Leaves/BTActionWait.gd" id="10_1e6f0"] +[ext_resource type="Script" path="res://addons/yet_another_behavior_tree/src/Nodes/Decorators/BTInverter.gd" id="11_fvy66"] + +[node name="Player" type="Node2D"] +script = ExtResource("1_1ti0g") + +[node name="Shape" type="Node2D" parent="."] + +[node name="Ahahahah" type="Polygon2D" parent="Shape"] +color = Color(0.188235, 0.490196, 0.505882, 1) +polygon = PackedVector2Array(-27, -55, 22, -55, 22, -25, 10, -18, 24, 17, -30, 17, -30, 17, -30, 17, -13, -18, -13, -18, -13, -18, -27, -26, -27, -26, -27, -26) + +[node name="GUI" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 0 +offset_left = -61.0 +offset_top = -84.0 +offset_right = 61.0 +offset_bottom = -58.0 + +[node name="Text" type="Label" parent="GUI"] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "My turn !" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Name" type="Label" parent="GUI"] +layout_mode = 0 +offset_left = 24.0 +offset_top = 104.0 +offset_right = 98.0 +offset_bottom = 127.0 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="BTRoot" type="Node" parent="."] +script = ExtResource("2_sdd0w") +actor_path = NodePath("..") + +[node name="Do" type="Node" parent="BTRoot"] +script = ExtResource("4_fjw5l") + +[node name="Init" type="Node" parent="BTRoot/Do"] +script = ExtResource("4_fjw5l") +save_progression = true + +[node name="Declare I am playing" type="Node" parent="BTRoot/Do/Init"] +script = ExtResource("4_qnbqx") + +[node name="Do once" type="Node" parent="BTRoot/Do/Init/Declare I am playing"] +script = ExtResource("5_d3jao") + +[node name="I\'m here !" type="Node" parent="BTRoot/Do/Init/Declare I am playing/Do once"] +script = ExtResource("6_i6lan") +method_owner_path = NodePath("../../../../../..") +method_name = "declare_presence" +method_arguments = ["blackboard"] + +[node name="Invert" type="Node" parent="BTRoot/Do/Init"] +script = ExtResource("11_fvy66") + +[node name="Do once" type="Node" parent="BTRoot/Do/Init/Invert"] +script = ExtResource("5_d3jao") + +[node name="Wait 1s" type="Node" parent="BTRoot/Do/Init/Invert/Do once"] +script = ExtResource("10_1e6f0") + +[node name="One Between" type="Node" parent="BTRoot/Do"] +script = ExtResource("3_2v3m1") +save_progression = true + +[node name="I must talk" type="Node" parent="BTRoot/Do/One Between"] +script = ExtResource("4_fjw5l") +save_progression = true + +[node name="Is my Turn ?" type="Node" parent="BTRoot/Do/One Between/I must talk"] +script = ExtResource("5_8tfvp") +method_owner_path = NodePath("../../../../..") +method_name = "is_my_turn_to_talk" +method_arguments = ["blackboard.get_data(\"next_talking\")"] + +[node name="Talk" type="Node" parent="BTRoot/Do/One Between/I must talk"] +script = ExtResource("6_i6lan") +method_owner_path = NodePath("../../../../..") +method_name = "talk" +method_arguments = [] + +[node name="Wait a little bit" type="Node" parent="BTRoot/Do/One Between/I must talk"] +script = ExtResource("10_1e6f0") +random_deviation_ms = 300 + +[node name="Set Next Player To Talk" type="Node" parent="BTRoot/Do/One Between/I must talk"] +script = ExtResource("6_i6lan") +method_owner_path = NodePath("../../../../..") +method_name = "choose_next_player_talking" +method_arguments = ["blackboard"] + +[node name="I must listen" type="Node" parent="BTRoot/Do/One Between"] +script = ExtResource("4_fjw5l") + +[node name="Not my turn" type="Node" parent="BTRoot/Do/One Between/I must listen"] +script = ExtResource("11_fvy66") + +[node name="Is my Turn ?" type="Node" parent="BTRoot/Do/One Between/I must listen/Not my turn"] +script = ExtResource("5_8tfvp") +method_owner_path = NodePath("../../../../../..") +method_name = "is_my_turn_to_talk" +method_arguments = ["blackboard.get_data(\"next_talking\")"]