add state machine and have player use it

This commit is contained in:
2022-04-11 13:30:51 -04:00
parent 6d2d1178a7
commit ecd6fe45b2
28 changed files with 1303 additions and 416 deletions

View File

@@ -0,0 +1,54 @@
#include "State.h"
using namespace godot;
void State::_register_methods()
{
register_method("set_parent", &State::set_parent);
register_method("get_parent", &State::get_parent);
register_method("set_state_machine", &State::set_state_machine);
register_method("_state_enter", &State::_state_enter);
register_method("_state_exit", &State::_state_exit);
}
State::State()
{
}
State::~State()
{
}
void State::_init()
{
}
void State::_state_enter(const String state, const Array args)
{
WARN_PRINT("State " + state + " is missing its _state_enter method!");
}
void State::_state_exit(const String state, const Array args)
{
WARN_PRINT("State " + state + " is missing its _state_exit method!");
}
void State::set_parent(Node *new_parent)
{
parent = new_parent;
}
Node *State::get_parent()
{
return parent;
}
void State::set_state_machine(StateMachine *new_state_machine)
{
state_machine = new_state_machine;
}
StateMachine *State::get_state_machine()
{
return state_machine;
}

46
src/state_machine/State.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef JUEGO_STATE_H
#define JUEGO_STATE_H
#include "StateMachine.h"
#include <Godot.hpp>
#include <Node.hpp>
namespace godot
{
class State : public StateMachine
{
GODOT_CLASS(State, Node)
private:
StateMachine *state_machine;
public:
Node *parent;
static void _register_methods();
State();
~State();
/**
* @brief Initialize the class from Godot.
* @details This method is called just once when the Godot engine connects to the instance of the class.
*/
virtual void _init();
virtual void _state_enter(const String state, const Array args = Array());
virtual void _state_exit(const String state, const Array args = Array());
virtual void set_parent(Node *new_parent) final;
virtual Node *get_parent() final;
virtual void set_state_machine(StateMachine *new_state_machine) final;
virtual StateMachine *get_state_machine() final;
};
}
#endif

View File

@@ -0,0 +1,208 @@
#include "StateMachine.h"
#include "State.h"
using namespace godot;
void StateMachine::_register_methods()
{
register_method("_ready", &StateMachine::_ready);
register_method("_on_StateMachine_tree_entered", &StateMachine::_on_StateMachine_tree_entered);
register_method("_on_StateMachine_tree_exiting", &StateMachine::_on_StateMachine_tree_exiting);
register_property<StateMachine, String>("default_state", &StateMachine::set_default_state, &StateMachine::get_default_state, String());
register_property<StateMachine, bool>("debug", &StateMachine::set_debug, &StateMachine::get_debug, false);
register_signal<StateMachine>("state_entered", "state", GODOT_VARIANT_TYPE_STRING);
register_signal<StateMachine>("state_exited", "state", GODOT_VARIANT_TYPE_STRING);
register_signal<StateMachine>("state_restarted", "state", GODOT_VARIANT_TYPE_STRING);
}
StateMachine::StateMachine()
{
}
StateMachine::~StateMachine()
{
}
void StateMachine::_init()
{
set_default_state(String());
set_debug(false);
}
void StateMachine::_ready()
{
connect("tree_entered", this, "_on_StateMachine_tree_entered");
connect("tree_exiting", this, "_on_StateMachine_tree_exiting");
parent = get_parent();
set_current_state(get_default_state());
setup();
}
void StateMachine::setup()
{
auto children = get_children();
if (get_current_state() == "")
{
if (children.size() > 0)
{
WARN_PRINT("State machine doesn't have a default state set, using first child!");
auto child = Object::cast_to<Node>(children[0].operator Object*());
set_current_state(child->get_name());
}
else
{
ERR_PRINT("State machine doesn't have a default state set and has no child states!");
return;
}
}
for (uint8_t i = 0; i < children.size(); i++)
{
auto child = Object::cast_to<Node>(children[i].operator Object*());
add_state(child->get_name(), child);
child->call("set_state_machine", this);
child->call("set_parent", parent);
if (child->get_name() != get_current_state())
{
this->remove_child(child);
}
}
this->call("_state_enter", get_current_state());
}
void StateMachine::add_state(const String state, Node *child)
{
states[state] = child;
}
bool StateMachine::is_current(const String state)
{
if (get_current_state() == "")
{
return false;
}
else
{
return get_current_state() == state;
}
}
bool StateMachine::has(const String state)
{
return states.has(state);
}
void StateMachine::restart(const String state, const Array& args)
{
this->call("_state_exit", state, args);
this->call("_state_enter", state, args);
this->emit_signal("state_restarted", get_current_state());
}
void StateMachine::change(const String state, const Array &args)
{
if (is_current(state))
{
return this->restart(state, args);
}
auto previous_state = get_current_state();
auto exiting = this->call("_state_exit", state, args);
if (get_current_state() != "")
{
auto child = Object::cast_to<Node>(states[get_current_state()].operator Object*());
if (child != nullptr)
{
this->remove_child(child);
}
this->emit_signal("state_exited", get_current_state());
if (debug)
{
Godot::print(get_current_state() + " exited!");
}
}
set_current_state(state);
auto child = Object::cast_to<Node>(states[get_current_state()].operator Object*());
this->add_child(child);
this->call("_state_enter", previous_state, args);
this->emit_signal("state_entered", get_current_state());
if (debug)
{
Godot::print(get_current_state() + " entered!");
}
}
Variant StateMachine::call(const String method, const Array &args)
{
auto node = Object::cast_to<Node>(states[get_current_state()].operator Object*());
if (node != nullptr)
{
return node->call(method, args);
}
return Variant();
}
Variant StateMachine::_call(const String method, const Array &args)
{
return this->call(method, args);
}
void StateMachine::set_default_state(const String new_default_state)
{
default_state = new_default_state;
}
String StateMachine::get_default_state()
{
return default_state;
}
void StateMachine::set_current_state(const String new_current_sate)
{
current_state = new_current_sate;
}
String StateMachine::get_current_state()
{
return current_state;
}
void StateMachine::set_debug(bool debug)
{
this->debug = debug;
}
bool StateMachine::get_debug()
{
return this->debug;
}
void StateMachine::_on_StateMachine_tree_entered()
{
setup();
}
void StateMachine::_on_StateMachine_tree_exiting()
{
auto keys = states.keys();
for (uint8_t i = 0; i < keys.size(); i++)
{
auto child = Object::cast_to<Node>(states[keys[i]].operator Object*());
if (child != nullptr)
{
auto children = get_children();
if (!children.has(child))
{
this->add_child(child);
}
}
}
}

View File

@@ -0,0 +1,95 @@
#ifndef JUEGO_STATE_MACHINE_H
#define JUEGO_STATE_MACHINE_H
#include <Godot.hpp>
#include <Node.hpp>
namespace godot
{
class StateMachine : public Node
{
GODOT_CLASS(StateMachine, Node)
private:
Node *parent;
String default_state;
bool debug;
String current_state;
Dictionary states;
void add_state(const String state, Node *child);
public:
static void _register_methods();
StateMachine();
~StateMachine();
/**
* @brief Initialize the class from Godot.
* @details This method is called just once when the Godot engine connects to the instance of the class.
*/
void _init();
/**
* @brief Code to be run when ready.
* @details This method is run when all the children of this node are ready.
*/
void _ready();
void setup();
bool is_current();
bool is_current(const String state);
bool has(const String state);
void restart(const String state, const Array& args = Array());
void change(const String state, const Array &args = Array());
Variant call(const String method, const Array &args = Array());
Variant _call(const String method, const Array &args = Array());
template <class ...Args> Variant restart(Args ...args)
{
return restart(Array::make(args...));
}
template <class ...Args> Variant change(const String state, Args ...args)
{
return change(state, Array::make(args...));
}
template <class ...Args> Variant call(const String method, Args ...args)
{
return call(method, Array::make(args...));
}
template <class ...Args> Variant _call(const String method, Args ...args)
{
return _call(method, Array::make(args...));
}
void set_default_state(const String new_default_state);
String get_default_state();
void set_debug(bool debug);
bool get_debug();
void set_current_state(const String new_current_state);
String get_current_state();
void _on_StateMachine_tree_entered();
void _on_StateMachine_tree_exiting();
};
}
#endif