add state machine and have player use it
This commit is contained in:
54
src/state_machine/State.cpp
Normal file
54
src/state_machine/State.cpp
Normal 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
46
src/state_machine/State.h
Normal 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
|
208
src/state_machine/StateMachine.cpp
Normal file
208
src/state_machine/StateMachine.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
src/state_machine/StateMachine.h
Normal file
95
src/state_machine/StateMachine.h
Normal 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
|
Reference in New Issue
Block a user