Merge pull request 'behavior-tree-implementation' (#2) from behavior-tree-implementation into main
Reviewed-on: #2pull/3/head
commit
208dfd20d5
|
@ -9,6 +9,8 @@
|
||||||
- Temperature layer (normal and different levels of coldness, transparent solid color)
|
- Temperature layer (normal and different levels of coldness, transparent solid color)
|
||||||
- Design a tilemap for the game
|
- Design a tilemap for the game
|
||||||
- Player (Dome)
|
- Player (Dome)
|
||||||
|
- As a layer in the tilemap
|
||||||
|
- Implemented in the PlayerManager
|
||||||
- Stats (see document)
|
- Stats (see document)
|
||||||
- Inventory
|
- Inventory
|
||||||
- with one slot
|
- with one slot
|
||||||
|
@ -18,7 +20,7 @@
|
||||||
- Use tilemap layers to compute route
|
- Use tilemap layers to compute route
|
||||||
- Support obstacles
|
- Support obstacles
|
||||||
- Result must be an array of tile coordinates, length (array length) and the total cost (sum of weights)
|
- Result must be an array of tile coordinates, length (array length) and the total cost (sum of weights)
|
||||||
- Decision Tree (Classes, etc.) (Luca, Cool in)
|
- Decision Tree (Classes, etc.) (Yan)
|
||||||
- Reference Food Gatherer for implementation
|
- Reference Food Gatherer for implementation
|
||||||
- Child of player, serves as controller
|
- Child of player, serves as controller
|
||||||
- Script needs access to the scene, player and other objects/data
|
- Script needs access to the scene, player and other objects/data
|
||||||
|
|
|
@ -1,48 +1,48 @@
|
||||||
{
|
{
|
||||||
|
"filename": "tilemaps.aseprite",
|
||||||
"height": 320,
|
"height": 320,
|
||||||
|
"width": 320,
|
||||||
"layers": [
|
"layers": [
|
||||||
{
|
{
|
||||||
|
"name": "ground",
|
||||||
"cels": [
|
"cels": [
|
||||||
{
|
{
|
||||||
"image": "tilemaps\\tilemap_ground.png",
|
"image": "tilemaps\\tilemap_ground.png",
|
||||||
"frame": 0
|
"frame": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"name": "ground"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "objects",
|
||||||
"cels": [
|
"cels": [
|
||||||
{
|
{
|
||||||
"image": "tilemaps\\tilemap_objects.png",
|
"image": "tilemaps\\tilemap_objects.png",
|
||||||
"frame": 0
|
"frame": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"name": "objects"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "temperature",
|
||||||
"cels": [
|
"cels": [
|
||||||
{
|
{
|
||||||
"image": "tilemaps\\tilemap_temperature.png",
|
"image": "tilemaps\\tilemap_temperature.png",
|
||||||
"frame": 0
|
"frame": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"name": "temperature"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "player",
|
||||||
"cels": [
|
"cels": [
|
||||||
{
|
{
|
||||||
"image": "tilemaps\\tilemap_player.png",
|
"image": "tilemaps\\tilemap_player.png",
|
||||||
"frame": 0
|
"frame": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"name": "player"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"width": 320,
|
|
||||||
"frames": [
|
"frames": [
|
||||||
{
|
{
|
||||||
"duration": 0.1
|
"duration": 0.1
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"filename": "tilemaps.aseprite"
|
|
||||||
}
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
[gd_scene load_steps=6 format=4 uid="uid://b88asko1ugyd2"]
|
[gd_scene load_steps=10 format=4 uid="uid://b88asko1ugyd2"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/global/GameManager.gd" id="1_eeg2d"]
|
[ext_resource type="Script" path="res://scripts/global/GameManager.gd" id="1_eeg2d"]
|
||||||
[ext_resource type="Script" path="res://scripts/tilemap/World.gd" id="1_k0rw8"]
|
[ext_resource type="Script" path="res://scripts/tilemap/World.gd" id="1_k0rw8"]
|
||||||
[ext_resource type="TileSet" uid="uid://bi836ygcmyvhb" path="res://assets/tilemap/tileset.tres" id="1_vlccq"]
|
[ext_resource type="TileSet" uid="uid://bi836ygcmyvhb" path="res://assets/tilemap/tileset.tres" id="1_vlccq"]
|
||||||
[ext_resource type="Script" path="res://scripts/global/Camera.gd" id="2_1vbjl"]
|
[ext_resource type="Script" path="res://scripts/global/Camera.gd" id="2_1vbjl"]
|
||||||
[ext_resource type="Script" path="res://scripts/player/PlayerManager.gd" id="4_1xqo1"]
|
[ext_resource type="Script" path="res://scripts/player/PlayerManager.gd" id="4_1xqo1"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/player/tree/BehaviorTree.gd" id="6_efs30"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/player/tree/impl/base/TaskSelector.gd" id="7_1jajd"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/player/tree/impl/context/Task5050Success.gd" id="8_xrswf"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/player/tree/impl/context/Task5050Running.gd" id="9_r13cd"]
|
||||||
|
|
||||||
[node name="Island-scene" type="Node2D"]
|
[node name="Island-scene" type="Node2D"]
|
||||||
script = ExtResource("1_eeg2d")
|
script = ExtResource("1_eeg2d")
|
||||||
|
@ -33,3 +37,18 @@ tile_set = ExtResource("1_vlccq")
|
||||||
|
|
||||||
[node name="PlayerManager" type="Node" parent="."]
|
[node name="PlayerManager" type="Node" parent="."]
|
||||||
script = ExtResource("4_1xqo1")
|
script = ExtResource("4_1xqo1")
|
||||||
|
|
||||||
|
[node name="BehaviorTree" type="Node" parent="PlayerManager"]
|
||||||
|
script = ExtResource("6_efs30")
|
||||||
|
|
||||||
|
[node name="sl_Root" type="Node" parent="PlayerManager/BehaviorTree"]
|
||||||
|
script = ExtResource("7_1jajd")
|
||||||
|
|
||||||
|
[node name="Node" type="Node" parent="PlayerManager/BehaviorTree/sl_Root"]
|
||||||
|
script = ExtResource("7_1jajd")
|
||||||
|
|
||||||
|
[node name="5050Success" type="Node" parent="PlayerManager/BehaviorTree/sl_Root/Node"]
|
||||||
|
script = ExtResource("8_xrswf")
|
||||||
|
|
||||||
|
[node name="5050Running" type="Node" parent="PlayerManager/BehaviorTree/sl_Root/Node"]
|
||||||
|
script = ExtResource("9_r13cd")
|
||||||
|
|
|
@ -76,6 +76,11 @@ key_9={
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":57,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":57,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
force_game_tick={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ extends Node
|
||||||
@onready var camera: Camera2D = $Camera2D
|
@onready var camera: Camera2D = $Camera2D
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
player.world = world
|
player.game_manager = self
|
||||||
player.camera = camera
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if Input.is_action_just_pressed("key_1"):
|
if Input.is_action_just_pressed("key_1"):
|
||||||
|
@ -16,3 +15,5 @@ func _process(delta: float) -> void:
|
||||||
camera.go_to_zooming(Vector2(789.883972167969, 450.102813720703), 0.56015348434448)
|
camera.go_to_zooming(Vector2(789.883972167969, 450.102813720703), 0.56015348434448)
|
||||||
if Input.is_action_just_pressed("key_9"):
|
if Input.is_action_just_pressed("key_9"):
|
||||||
camera.print_config()
|
camera.print_config()
|
||||||
|
if Input.is_action_just_pressed("force_game_tick"):
|
||||||
|
player.game_tick()
|
||||||
|
|
|
@ -1,19 +1,32 @@
|
||||||
class_name PlayerManager
|
class_name PlayerManager
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var board_position: Vector2 = Vector2(0, 0)
|
|
||||||
var world: World = null
|
|
||||||
var camera: Camera2D = null
|
|
||||||
#
|
|
||||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||||
|
#
|
||||||
|
var game_manager: GameManager = null
|
||||||
|
var board_position: Vector2 = Vector2(0, 0)
|
||||||
|
|
||||||
|
@onready var behavior_tree: BehaviorTree = $BehaviorTree
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
call_deferred("update_board")
|
call_deferred("defer_ready")
|
||||||
|
|
||||||
|
|
||||||
|
func defer_ready() -> void:
|
||||||
|
behavior_tree.game_manager = game_manager
|
||||||
|
update_board()
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if Input.is_action_just_pressed("key_3"):
|
if Input.is_action_just_pressed("key_3"):
|
||||||
camera.go_to_zooming(world.tilemap_player.cell_to_local(board_position), 2)
|
game_manager.camera.go_to_zooming(game_manager.world.tilemap_player.cell_to_local(board_position), 2)
|
||||||
|
|
||||||
|
|
||||||
func update_board() -> void:
|
func update_board() -> void:
|
||||||
world.tilemap_player.clear_cells()
|
game_manager.world.tilemap_player.clear_cells()
|
||||||
world.tilemap_player.set_cell(board_position, tilemap_types.PLAYER)
|
game_manager.world.tilemap_player.set_cell(board_position, tilemap_types.PLAYER)
|
||||||
|
|
||||||
|
|
||||||
|
func game_tick() -> void:
|
||||||
|
behavior_tree.game_tick()
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
class_name BehaviorTree
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var game_manager: GameManager = null
|
||||||
|
#
|
||||||
|
var blackboard: Dictionary = {}
|
||||||
|
var behavior_tree: Task = null
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if get_child_count() == 0 or get_child_count() > 1:
|
||||||
|
push_error("This controller needs exactly one Task child, got " + str(get_child_count()))
|
||||||
|
|
||||||
|
var child: Node = get_child(0)
|
||||||
|
if not (child is Task):
|
||||||
|
push_error("Child is not a task: " + child.name)
|
||||||
|
|
||||||
|
behavior_tree = child as Task
|
||||||
|
|
||||||
|
|
||||||
|
func populate_blackboard():
|
||||||
|
blackboard["world"] = game_manager.world
|
||||||
|
blackboard["player"] = game_manager.player
|
||||||
|
blackboard["camera"] = game_manager.camera
|
||||||
|
|
||||||
|
|
||||||
|
func game_tick() -> void:
|
||||||
|
print("game_tick:")
|
||||||
|
populate_blackboard()
|
||||||
|
behavior_tree.internal_run(blackboard)
|
||||||
|
print(" ==> [active state=", blackboard["current_task"], "] [status=", behavior_tree.status, "]")
|
|
@ -0,0 +1,56 @@
|
||||||
|
class_name Task
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
enum {FAILURE = -1, SUCCESS = 1, RUNNING = 0}
|
||||||
|
var status: int = FAILURE
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
for c in get_children():
|
||||||
|
if not c is Task:
|
||||||
|
push_error("Child is not a task: " + c.name + " in " + name)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
func internal_run(p_blackboard: Dictionary) -> void:
|
||||||
|
p_blackboard["current_task"] = self
|
||||||
|
|
||||||
|
if status == RUNNING:
|
||||||
|
var running_child: Task = find_running_child()
|
||||||
|
if running_child != null:
|
||||||
|
running_child.internal_run(p_blackboard)
|
||||||
|
status = running_child.status
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
run(p_blackboard)
|
||||||
|
else:
|
||||||
|
run(p_blackboard)
|
||||||
|
print(" - ", name, " ", status)
|
||||||
|
|
||||||
|
|
||||||
|
func find_running_child() -> Task:
|
||||||
|
for c in get_children():
|
||||||
|
if c.status == RUNNING:
|
||||||
|
return c
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
func run_child(p_blackboard: Dictionary, p_child: Task) -> void:
|
||||||
|
p_child.internal_run(p_blackboard)
|
||||||
|
if p_child.status != RUNNING:
|
||||||
|
status = RUNNING
|
||||||
|
|
||||||
|
|
||||||
|
func run(p_blackboard: Dictionary) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func cancel(p_blackboard: Dictionary):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func get_first_child() -> Task:
|
||||||
|
if get_child_count() == 0:
|
||||||
|
push_error("Task does not have any children: " + name)
|
||||||
|
return null
|
||||||
|
return get_children()[0] as Task
|
|
@ -0,0 +1,11 @@
|
||||||
|
class_name TaskSelector
|
||||||
|
extends Task
|
||||||
|
|
||||||
|
func run(blackboard: Dictionary) -> void:
|
||||||
|
for c in self.get_children():
|
||||||
|
run_child(blackboard, c)
|
||||||
|
if c.status != FAILURE:
|
||||||
|
status = c.status
|
||||||
|
return
|
||||||
|
status = FAILURE
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
class_name TaskSequence
|
||||||
|
extends Task
|
||||||
|
|
||||||
|
func run(blackboard: Dictionary) -> void:
|
||||||
|
for c in self.get_children():
|
||||||
|
run_child(blackboard, c)
|
||||||
|
if c.status != SUCCESS:
|
||||||
|
status = c.status
|
||||||
|
return
|
||||||
|
status = SUCCESS
|
|
@ -0,0 +1,9 @@
|
||||||
|
class_name Task5050Running
|
||||||
|
extends Task
|
||||||
|
|
||||||
|
func run(blackboard: Dictionary) -> void:
|
||||||
|
var random: int = randi() % 2
|
||||||
|
if random == 0:
|
||||||
|
status = RUNNING
|
||||||
|
else:
|
||||||
|
status = SUCCESS
|
|
@ -0,0 +1,9 @@
|
||||||
|
class_name Task5050Success
|
||||||
|
extends Task
|
||||||
|
|
||||||
|
func run(blackboard: Dictionary) -> void:
|
||||||
|
var random: int = randi() % 2
|
||||||
|
if random == 0:
|
||||||
|
status = SUCCESS
|
||||||
|
else:
|
||||||
|
status = FAILURE
|
Loading…
Reference in New Issue