feat: backlog code
This commit is contained in:
commit
2a4e00e6f1
16 changed files with 487 additions and 0 deletions
3
SCsub
Normal file
3
SCsub
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Import('env')
|
||||
|
||||
env.add_source_files(env.modules_sources, "*.cpp")
|
||||
31
behaviour_action.cpp
Normal file
31
behaviour_action.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "behaviour_action.h"
|
||||
#include "core/object/object.h"
|
||||
|
||||
void BehaviourAction::_bind_methods() {
|
||||
ClassDB::add_virtual_method(get_class_static(), _gdvirtual__execute_get_method_info());
|
||||
ClassDB::add_virtual_method(get_class_static(), _gdvirtual__exit_get_method_info());
|
||||
ClassDB::add_virtual_method(get_class_static(), _gdvirtual__enter_get_method_info());
|
||||
}
|
||||
|
||||
void BehaviourAction::enter() {
|
||||
GDVIRTUAL_CALL(_enter);
|
||||
}
|
||||
|
||||
void BehaviourAction::execute() {
|
||||
Status out_status{ Fail };
|
||||
GDVIRTUAL_CALL(_execute, out_status);
|
||||
set_status(out_status);
|
||||
}
|
||||
|
||||
void BehaviourAction::exit() {
|
||||
GDVIRTUAL_CALL(_exit);
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourAction::get_next() {
|
||||
switch (get_status()) {
|
||||
case Running:
|
||||
return this;
|
||||
default:
|
||||
return cast_to<BehaviourNode>(get_parent());
|
||||
}
|
||||
}
|
||||
17
behaviour_action.h
Normal file
17
behaviour_action.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
|
||||
class BehaviourAction : public BehaviourNode {
|
||||
GDCLASS(BehaviourAction, BehaviourNode);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void enter() override;
|
||||
void execute() override;
|
||||
void exit() override;
|
||||
BehaviourNode *get_next() override;
|
||||
GDVIRTUAL0R_REQUIRED(Status, _execute);
|
||||
GDVIRTUAL0(_enter);
|
||||
GDVIRTUAL0(_exit);
|
||||
};
|
||||
28
behaviour_composite.cpp
Normal file
28
behaviour_composite.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "behaviour_composite.h"
|
||||
|
||||
void BehaviourComposite::_bind_methods() {}
|
||||
|
||||
void BehaviourComposite::child_order_changed() {
|
||||
this->child_behaviours.clear();
|
||||
for (Variant var : get_children()) {
|
||||
if (BehaviourNode * node{ cast_to<BehaviourNode>(var) }) {
|
||||
this->child_behaviours.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviourComposite::_notification(int what) {
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
child_order_changed();
|
||||
set_leaf(get_child_behaviours().is_empty());
|
||||
return;
|
||||
case NOTIFICATION_CHILD_ORDER_CHANGED:
|
||||
if (is_ready()) {
|
||||
child_order_changed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
18
behaviour_composite.h
Normal file
18
behaviour_composite.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
|
||||
class BehaviourComposite : public BehaviourNode {
|
||||
GDCLASS(BehaviourComposite, BehaviourNode);
|
||||
static void _bind_methods();
|
||||
void child_order_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
private:
|
||||
Vector<BehaviourNode *> child_behaviours{};
|
||||
|
||||
public:
|
||||
Vector<BehaviourNode *> const &get_child_behaviours() const { return this->child_behaviours; }
|
||||
GET_SET_REF_FNS(Vector<BehaviourNode *>, child_behaviours);
|
||||
};
|
||||
29
behaviour_node.cpp
Normal file
29
behaviour_node.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#include "behaviour_node.h"
|
||||
#include "behaviour_nodes/behaviour_tree.h"
|
||||
#include "core/config/engine.h"
|
||||
|
||||
void BehaviourNode::_bind_methods() {
|
||||
BIND_ENUM_CONSTANT(Fail);
|
||||
BIND_ENUM_CONSTANT(Running);
|
||||
BIND_ENUM_CONSTANT(Success);
|
||||
}
|
||||
|
||||
void BehaviourNode::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
Node *parent{ get_parent() };
|
||||
this->parent = cast_to<BehaviourNode>(parent);
|
||||
while (this->behaviour_tree == nullptr && parent != nullptr) {
|
||||
if ((this->behaviour_tree = cast_to<BehaviourTree>(parent))) {
|
||||
break;
|
||||
}
|
||||
parent = parent->get_parent();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
34
behaviour_node.h
Normal file
34
behaviour_node.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "macros.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class BehaviourNode : public Node {
|
||||
GDCLASS(BehaviourNode, Node);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
GDENUM(Status, Fail, Running, Success);
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
public:
|
||||
virtual void enter() {}
|
||||
virtual void execute() {}
|
||||
virtual void exit() {}
|
||||
virtual BehaviourNode *get_next() { return this; }
|
||||
|
||||
private:
|
||||
class BehaviourTree *behaviour_tree{ nullptr };
|
||||
BehaviourNode *parent{ nullptr };
|
||||
Status status{};
|
||||
bool leaf{ false };
|
||||
|
||||
public:
|
||||
GET_SET_FNS(class BehaviourTree *, behaviour_tree);
|
||||
GET_SET_FNS(Status, status);
|
||||
GET_SET_FNS(bool, leaf);
|
||||
};
|
||||
|
||||
MAKE_TYPE_INFO(BehaviourNode::Status, Variant::INT);
|
||||
52
behaviour_tree.cpp
Normal file
52
behaviour_tree.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "behaviour_tree.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
#include "behaviour_nodes/control_nodes.h"
|
||||
#include "core/config/engine.h"
|
||||
|
||||
void BehaviourTree::_bind_methods() {}
|
||||
|
||||
void BehaviourTree::process() {
|
||||
this->current->execute();
|
||||
while (execute_next()) {
|
||||
this->current->execute();
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviourTree::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
for (Variant var : get_children()) {
|
||||
if ((this->current = cast_to<BehaviourNode>(var))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND_EDMSG(this->current == nullptr, "No valid BehaviourNode in BehaviourTree");
|
||||
set_process(true);
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
process();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool BehaviourTree::execute_next() {
|
||||
BehaviourNode *next{ this->current->get_next() };
|
||||
if (next == this->current) {
|
||||
return false;
|
||||
} else {
|
||||
ERR_FAIL_COND_V_EDMSG(next == nullptr, false, vformat("%s::get_next returned a nullptr, repeating last node", this->current->get_class()));
|
||||
if (this->current != next->get_parent()) {
|
||||
this->current->exit();
|
||||
}
|
||||
if (this->current->get_parent() != next) {
|
||||
next->enter();
|
||||
}
|
||||
this->current = next;
|
||||
return cast_to<BehaviourComposite>(this->current);
|
||||
}
|
||||
}
|
||||
18
behaviour_tree.h
Normal file
18
behaviour_tree.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class BehaviourTree : public Node {
|
||||
GDCLASS(BehaviourTree, Node);
|
||||
static void _bind_methods();
|
||||
void process();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
public:
|
||||
bool execute_next();
|
||||
|
||||
private:
|
||||
class BehaviourNode *current{ nullptr };
|
||||
};
|
||||
5
config.py
Normal file
5
config.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
def can_build(env, platform):
|
||||
return True;
|
||||
|
||||
def configure(env):
|
||||
pass;
|
||||
121
control_nodes.cpp
Normal file
121
control_nodes.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "control_nodes.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
|
||||
PackedStringArray BehaviourSequence::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
warnings.push_back("Sequence cannot have zero children");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void BehaviourSequence::execute() {
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
set_status(Fail);
|
||||
ERR_FAIL_EDMSG("BehaviourSequence executed with no children.");
|
||||
} else if (get_status() == Running && this->current >= 0 && this->current < get_child_behaviours().size()) {
|
||||
switch (get_child_behaviours().get(this->current)->get_status()) {
|
||||
case Running:
|
||||
set_status(Running);
|
||||
break;
|
||||
case Fail:
|
||||
set_status(Fail);
|
||||
break;
|
||||
case Success:
|
||||
++this->current;
|
||||
set_status(this->current >= get_child_behaviours().size()
|
||||
? Success
|
||||
: Running);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
set_status(Running);
|
||||
this->current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourSequence::get_next() {
|
||||
return get_status() == Running
|
||||
? get_child_behaviours().get(this->current)
|
||||
: cast_to<BehaviourNode>(get_parent());
|
||||
}
|
||||
|
||||
PackedStringArray BehaviourSelector::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
warnings.push_back("Selector cannot have zero children");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void BehaviourSelector::execute() {
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
set_status(Fail);
|
||||
ERR_FAIL_EDMSG("BehaviourSelector execution with no children.");
|
||||
} else if (get_status() == Running && this->current >= 0 && this->current < get_child_behaviours().size()) {
|
||||
switch (get_child_behaviours().get(this->current)->get_status()) {
|
||||
case Running:
|
||||
set_status(Running);
|
||||
break;
|
||||
case Fail:
|
||||
++this->current;
|
||||
set_status(this->current >= get_child_behaviours().size()
|
||||
? Fail
|
||||
: Running);
|
||||
break;
|
||||
case Success:
|
||||
set_status(Success);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
set_status(Running);
|
||||
this->current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourSelector::get_next() {
|
||||
return get_status() == Running
|
||||
? get_child_behaviours().get(this->current)
|
||||
: cast_to<BehaviourNode>(get_parent());
|
||||
}
|
||||
|
||||
PackedStringArray BehaviourRepeater::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (get_child_behaviours().size() != 1) {
|
||||
warnings.push_back(vformat("Repeater should have exactly one BehaviourNode child, has %d", get_child_behaviours().size()));
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void BehaviourRepeater::execute() {
|
||||
set_status(Running);
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourRepeater::get_next() {
|
||||
return get_child_behaviours().get(0);
|
||||
}
|
||||
|
||||
PackedStringArray BehaviourRepeatUntilFail::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (get_child_behaviours().size() != 1) {
|
||||
warnings.push_back("RepeatUntilFailure should have exactly one BehaviourNode child");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void BehaviourRepeatUntilFail::execute() {
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
set_status(Fail);
|
||||
ERR_FAIL_EDMSG("BehaviourRepeatUntilFail execution with no child");
|
||||
} else {
|
||||
set_status(get_child_behaviours().get(0)->get_status() == Fail
|
||||
? Success
|
||||
: Running);
|
||||
}
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourRepeatUntilFail::get_next() {
|
||||
return get_status() == Running
|
||||
? get_child_behaviours().get(0)
|
||||
: cast_to<BehaviourNode>(get_parent());
|
||||
}
|
||||
50
control_nodes.h
Normal file
50
control_nodes.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include "behaviour_nodes/behaviour_composite.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class BehaviourSequence : public BehaviourComposite {
|
||||
GDCLASS(BehaviourSequence, BehaviourComposite);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
void execute() override;
|
||||
BehaviourNode *get_next() override;
|
||||
|
||||
private:
|
||||
int current{ -1 };
|
||||
};
|
||||
|
||||
class BehaviourSelector : public BehaviourComposite {
|
||||
GDCLASS(BehaviourSelector, BehaviourComposite);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
void execute() override;
|
||||
BehaviourNode *get_next() override;
|
||||
|
||||
private:
|
||||
int current{ -1 };
|
||||
};
|
||||
|
||||
class BehaviourRepeater : public BehaviourComposite {
|
||||
GDCLASS(BehaviourRepeater, BehaviourComposite);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
void execute() override;
|
||||
BehaviourNode *get_next() override;
|
||||
};
|
||||
|
||||
class BehaviourRepeatUntilFail : public BehaviourComposite {
|
||||
GDCLASS(BehaviourRepeatUntilFail, BehaviourComposite);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
void execute() override;
|
||||
BehaviourNode *get_next() override;
|
||||
};
|
||||
30
decorator_nodes.cpp
Normal file
30
decorator_nodes.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include "decorator_nodes.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
void BehaviourAlwaysSuccess::_bind_methods() {}
|
||||
|
||||
PackedStringArray BehaviourAlwaysSuccess::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (get_child_behaviours().size() != 1) {
|
||||
warnings.push_back("BehaviourAlwaysSuccess should have exactly one child");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void BehaviourAlwaysSuccess::execute() {
|
||||
if (get_child_behaviours().is_empty()) {
|
||||
set_status(Fail);
|
||||
ERR_FAIL_EDMSG("BehaviourSequence executed with no children.");
|
||||
} else if (get_status() == Running) {
|
||||
set_status(get_child_behaviours().get(0)->get_status() == Running ? Running : Success);
|
||||
} else {
|
||||
set_status(Running);
|
||||
}
|
||||
}
|
||||
|
||||
BehaviourNode *BehaviourAlwaysSuccess::get_next() {
|
||||
return get_status() == Running
|
||||
? get_child_behaviours().get(0)
|
||||
: cast_to<BehaviourNode>(get_parent());
|
||||
}
|
||||
15
decorator_nodes.h
Normal file
15
decorator_nodes.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "behaviour_nodes/behaviour_composite.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class BehaviourAlwaysSuccess : public BehaviourComposite {
|
||||
GDCLASS(BehaviourAlwaysSuccess, BehaviourComposite);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
void execute() override;
|
||||
BehaviourNode *get_next() override;
|
||||
};
|
||||
27
register_types.cpp
Normal file
27
register_types.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "behaviour_nodes/register_types.h"
|
||||
#include "behaviour_nodes/behaviour_action.h"
|
||||
#include "behaviour_nodes/behaviour_node.h"
|
||||
#include "behaviour_nodes/behaviour_tree.h"
|
||||
#include "behaviour_nodes/control_nodes.h"
|
||||
#include "behaviour_nodes/decorator_nodes.h"
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
void initialize_behaviour_nodes_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
ClassDB::register_class<BehaviourTree>();
|
||||
ClassDB::register_abstract_class<BehaviourNode>();
|
||||
ClassDB::register_class<BehaviourSequence>();
|
||||
ClassDB::register_class<BehaviourRepeater>();
|
||||
ClassDB::register_class<BehaviourSelector>();
|
||||
ClassDB::register_class<BehaviourAction>();
|
||||
ClassDB::register_class<BehaviourRepeatUntilFail>();
|
||||
ClassDB::register_class<BehaviourAlwaysSuccess>();
|
||||
}
|
||||
|
||||
void uninitialize_behaviour_nodes_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
9
register_types.h
Normal file
9
register_types.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef BEHAVIOUR_NODES_REGISTER_TYPES_H
|
||||
#define BEHAVIOUR_NODES_REGISTER_TYPES_H
|
||||
|
||||
#include "modules/register_module_types.h"
|
||||
|
||||
void initialize_behaviour_nodes_module(ModuleInitializationLevel p_level);
|
||||
void uninitialize_behaviour_nodes_module(ModuleInitializationLevel p_level);
|
||||
|
||||
#endif // !BEHAVIOUR_NODES_REGISTER_TYPES_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue