feat: implemented initial attack states logic
This commit is contained in:
parent
356b88948e
commit
4b43358975
9 changed files with 189 additions and 56 deletions
|
|
@ -92,13 +92,13 @@ void CharacterState::_notification(int what) {
|
|||
}
|
||||
}
|
||||
|
||||
void CharacterState::switch_state(String name) {
|
||||
void CharacterState::switch_state(NodePath path) {
|
||||
this->set_state_active(false);
|
||||
stack_state(name);
|
||||
stack_state(path);
|
||||
}
|
||||
|
||||
void CharacterState::stack_state(String name) {
|
||||
if (CharacterState * new_state{ cast_to<CharacterState>(get_parent()->get_node(NodePath(name))) }) {
|
||||
void CharacterState::stack_state(NodePath path) {
|
||||
if (CharacterState * new_state{ cast_to<CharacterState>(get_node(path)) }) {
|
||||
new_state->set_state_active(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,18 +30,19 @@ class CharacterState : public Node {
|
|||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void switch_state(NodePath path);
|
||||
inline void switch_state(String name) { switch_state(NodePath("../" + name)); }
|
||||
template <class TNewState>
|
||||
void switch_state();
|
||||
void switch_state() { switch_state(TNewState::get_class_static()); }
|
||||
void stack_state(NodePath name);
|
||||
inline void stack_state(String name) { stack_state(NodePath("../" + name)); }
|
||||
template <class TStackState>
|
||||
void stack_state();
|
||||
void switch_state(String name);
|
||||
void stack_state(String name);
|
||||
inline void stack_state() { stack_state(TStackState::get_class_static()); }
|
||||
virtual void state_entered();
|
||||
virtual void state_exited();
|
||||
|
||||
public:
|
||||
void set_state_active(bool active);
|
||||
PlayerBody *get_body() const { return cast_to<PlayerBody>(get_parent()); }
|
||||
|
||||
private:
|
||||
bool active_state{ false };
|
||||
|
|
@ -50,16 +51,3 @@ public:
|
|||
static String const sig_state_entered;
|
||||
static String const sig_state_exited;
|
||||
};
|
||||
|
||||
template <class TNewState>
|
||||
void CharacterState::switch_state() {
|
||||
this->set_state_active(false);
|
||||
this->stack_state<TNewState>();
|
||||
}
|
||||
|
||||
template <class TStackState>
|
||||
void CharacterState::stack_state() {
|
||||
if (TStackState * state{ cast_to<TStackState>(get_parent()->get_node(NodePath(TStackState::get_class_static()))) }) {
|
||||
state->set_state_active(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,39 @@
|
|||
#include "player_states.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void PlayerState::_bind_methods() {}
|
||||
|
||||
void PlayerState::ready() {
|
||||
this->anim = cast_to<AnimationTree>(get_node(NodePath("%AnimationTree")));
|
||||
this->anim_fsm = this->anim->get("parameters/playback");
|
||||
}
|
||||
|
||||
void PlayerState::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
ready();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerState::apply_root_motion(double delta) {
|
||||
PlayerBody *body{ get_body() };
|
||||
Basis basis{ body->get_global_basis() };
|
||||
Vector3 root_motion{ this->anim->get_root_motion_position() };
|
||||
body->set_velocity((root_motion.x * basis.get_column(0) + root_motion.z * basis.get_column(2)) / delta);
|
||||
}
|
||||
|
||||
void PlayerState::input_trigger_entry_actions(Ref<InputEvent> const &what) {
|
||||
if (what->is_action_pressed("light_attack")) {
|
||||
switch_state("LightAttack");
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerIdleState::_bind_methods() {}
|
||||
|
||||
|
|
@ -10,10 +44,6 @@ void PlayerIdleState::_notification(int what) {
|
|||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
this->anim = cast_to<AnimationTree>(get_node(NodePath("%AnimationTree")));
|
||||
this->anim_fsm = this->anim->get("parameters/playback");
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
get_body()->set_velocity(get_body()->get_velocity().move_toward(Vector3(), get_process_delta_time() * 50.f));
|
||||
return;
|
||||
|
|
@ -22,8 +52,8 @@ void PlayerIdleState::_notification(int what) {
|
|||
|
||||
void PlayerIdleState::state_entered() {
|
||||
set_process_unhandled_input(true);
|
||||
this->anim_fsm->travel("idle");
|
||||
set_process(true);
|
||||
get_anim_fsm()->travel("idle");
|
||||
}
|
||||
|
||||
void PlayerIdleState::state_exited() {
|
||||
|
|
@ -34,6 +64,8 @@ void PlayerIdleState::state_exited() {
|
|||
void PlayerIdleState::unhandled_input(Ref<InputEvent> const &what) {
|
||||
if (what->is_action("move_left") || what->is_action("move_right") || what->is_action("move_forward") || what->is_action("move_backward")) {
|
||||
switch_state<PlayerRunState>();
|
||||
} else {
|
||||
input_trigger_entry_actions(what);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,10 +73,7 @@ void PlayerRunState::_bind_methods() {
|
|||
}
|
||||
|
||||
void PlayerRunState::process(double delta) {
|
||||
PlayerBody *body{ get_body() };
|
||||
Basis basis{ body->get_global_basis() };
|
||||
Vector3 root_motion{ this->anim->get_root_motion_position() };
|
||||
body->set_velocity((root_motion.x * basis.get_column(0) + root_motion.z * basis.get_column(2)) / delta);
|
||||
apply_root_motion(delta);
|
||||
if (get_body()->get_transformed_movement_input().is_zero_approx()) {
|
||||
switch_state<PlayerIdleState>();
|
||||
} else {
|
||||
|
|
@ -60,9 +89,59 @@ void PlayerRunState::_notification(int what) {
|
|||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
this->anim = cast_to<AnimationTree>(get_node(NodePath("%AnimationTree")));
|
||||
this->anim_fsm = this->anim->get("parameters/playback");
|
||||
case NOTIFICATION_PROCESS:
|
||||
process(get_process_delta_time());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerRunState::unhandled_input(Ref<InputEvent> const &what) {
|
||||
input_trigger_entry_actions(what);
|
||||
}
|
||||
|
||||
void PlayerRunState::state_entered() {
|
||||
set_process(true);
|
||||
get_anim_fsm()->travel("run");
|
||||
}
|
||||
|
||||
void PlayerRunState::state_exited() {
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void PlayerBasicAttackState::_bind_methods() {
|
||||
BIND_PROPERTY(Variant::STRING, animation_name);
|
||||
BIND_HPROPERTY(Variant::NODE_PATH, next_light_attack_state, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CharacterState");
|
||||
BIND_PROPERTY(Variant::FLOAT, trigger_next_margin);
|
||||
BIND_PROPERTY(Variant::FLOAT, input_next_margin);
|
||||
}
|
||||
|
||||
void PlayerBasicAttackState::process(double delta) {
|
||||
apply_root_motion(delta);
|
||||
|
||||
if (!this->animation_started) {
|
||||
if (get_anim_fsm()->get_current_node() == this->animation_name) {
|
||||
this->animation_started = true;
|
||||
this->anim_length = get_anim_fsm()->get_current_length();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
delta_since_start += delta;
|
||||
float const trigger_next_progress{ this->anim_length - this->trigger_next_margin };
|
||||
if (this->next_queued && this->delta_since_start > trigger_next_progress && !this->next_light_attack_state.is_empty()) {
|
||||
switch_state(this->next_light_attack_state);
|
||||
}
|
||||
if (get_anim_fsm()->get_current_node() != this->animation_name) {
|
||||
switch_state<PlayerIdleState>();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerBasicAttackState::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
process(get_process_delta_time());
|
||||
|
|
@ -70,11 +149,26 @@ void PlayerRunState::_notification(int what) {
|
|||
}
|
||||
}
|
||||
|
||||
void PlayerRunState::state_entered() {
|
||||
set_process(true);
|
||||
this->anim_fsm->travel("run");
|
||||
void PlayerBasicAttackState::unhandled_input(Ref<InputEvent> const &what) {
|
||||
float const input_next_progress{ get_anim_fsm()->get_current_length() - this->input_next_margin };
|
||||
if (this->delta_since_start > input_next_progress && what->is_action_pressed("light_attack")) {
|
||||
this->next_queued = true;
|
||||
get_viewport()->set_input_as_handled();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerRunState::state_exited() {
|
||||
set_process(false);
|
||||
void PlayerBasicAttackState::state_entered() {
|
||||
set_process(true);
|
||||
set_process_unhandled_input(true);
|
||||
|
||||
this->next_queued = false;
|
||||
this->animation_started = false;
|
||||
this->delta_since_start = 0.0;
|
||||
|
||||
get_anim_fsm()->start(this->animation_name);
|
||||
}
|
||||
|
||||
void PlayerBasicAttackState::state_exited() {
|
||||
set_process(false);
|
||||
set_process_unhandled_input(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,28 @@
|
|||
#include "scene/animation/animation_node_state_machine.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class PlayerIdleState : public CharacterState {
|
||||
GDCLASS(PlayerIdleState, CharacterState);
|
||||
class PlayerState : public CharacterState {
|
||||
GDCLASS(PlayerState, CharacterState);
|
||||
static void _bind_methods();
|
||||
void ready();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void apply_root_motion(double delta);
|
||||
PlayerBody *get_body() const { return cast_to<PlayerBody>(get_parent()); }
|
||||
void input_trigger_entry_actions(Ref<InputEvent> const &what);
|
||||
|
||||
private:
|
||||
AnimationTree *anim{ nullptr };
|
||||
Ref<AnimationNodeStateMachinePlayback> anim_fsm{};
|
||||
|
||||
protected:
|
||||
GET_SET_FNS(AnimationTree *, anim);
|
||||
GET_SET_FNS(Ref<AnimationNodeStateMachinePlayback>, anim_fsm);
|
||||
};
|
||||
|
||||
class PlayerIdleState : public PlayerState {
|
||||
GDCLASS(PlayerIdleState, PlayerState);
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
|
|
@ -14,32 +34,46 @@ protected:
|
|||
void state_entered() override;
|
||||
void state_exited() override;
|
||||
void unhandled_input(Ref<InputEvent> const &what) override;
|
||||
|
||||
private:
|
||||
AnimationTree *anim{ nullptr };
|
||||
Ref<AnimationNodeStateMachinePlayback> anim_fsm{};
|
||||
};
|
||||
|
||||
class PlayerRunState : public CharacterState {
|
||||
GDCLASS(PlayerRunState, CharacterState);
|
||||
class PlayerRunState : public PlayerState {
|
||||
GDCLASS(PlayerRunState, PlayerState);
|
||||
static void _bind_methods();
|
||||
void process(double delta);
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void unhandled_input(Ref<InputEvent> const &what) override;
|
||||
void state_entered() override;
|
||||
void state_exited() override;
|
||||
};
|
||||
|
||||
class PlayerBasicAttackState : public PlayerState {
|
||||
GDCLASS(PlayerBasicAttackState, PlayerState);
|
||||
static void _bind_methods();
|
||||
void process(double delta);
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void unhandled_input(Ref<InputEvent> const &what) override;
|
||||
void state_entered() override;
|
||||
void state_exited() override;
|
||||
|
||||
private:
|
||||
AnimationTree *anim{ nullptr };
|
||||
Ref<AnimationNodeStateMachinePlayback> anim_fsm{};
|
||||
};
|
||||
String animation_name{ "swing_1" };
|
||||
float trigger_next_margin{ 0.1 };
|
||||
float input_next_margin{ 0.2 };
|
||||
NodePath next_light_attack_state{};
|
||||
|
||||
class PlayerBasicAttackState : public CharacterState {
|
||||
GDCLASS(PlayerBasicAttackState, CharacterState);
|
||||
static void _bind_methods();
|
||||
double delta_since_start{ 0.0 };
|
||||
float anim_length{};
|
||||
|
||||
private:
|
||||
String animation_name{};
|
||||
String next_light_attack{};
|
||||
bool next_queued{ false };
|
||||
bool animation_started{ false };
|
||||
|
||||
public:
|
||||
GET_SET_FNS(String, animation_name);
|
||||
GET_SET_FNS(NodePath, next_light_attack_state);
|
||||
GET_SET_FNS(float, trigger_next_margin);
|
||||
GET_SET_FNS(float, input_next_margin);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ void initialize_break_utopia_module(ModuleInitializationLevel p_level) {
|
|||
}
|
||||
ClassDB::register_class<PlayerBody>();
|
||||
ClassDB::register_class<CharacterState>();
|
||||
ClassDB::register_class<PlayerState>();
|
||||
ClassDB::register_class<PlayerRunState>();
|
||||
ClassDB::register_class<PlayerIdleState>();
|
||||
ClassDB::register_class<PlayerBasicAttackState>();
|
||||
}
|
||||
|
||||
void uninitialize_break_utopia_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -88,6 +88,16 @@ graph_offset = Vector2(-228.371, 13.6511)
|
|||
|
||||
[node name="PlayerRunState" type="PlayerRunState" parent="."]
|
||||
|
||||
[node name="LightAttack" type="PlayerBasicAttackState" parent="."]
|
||||
next_light_attack_state = NodePath("../LightAttack2")
|
||||
|
||||
[node name="LightAttack2" type="PlayerBasicAttackState" parent="."]
|
||||
animation_name = "swing_2"
|
||||
next_light_attack_state = NodePath("../LightAttack3")
|
||||
|
||||
[node name="LightAttack3" type="PlayerBasicAttackState" parent="."]
|
||||
animation_name = "swing_3"
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.949463, 0)
|
||||
shape = SubResource("CapsuleShape3D_5j0w6")
|
||||
|
|
|
|||
|
|
@ -37,3 +37,8 @@ move_right={
|
|||
"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":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
light_attack={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(276.9, 17.7383),"global_position":Vector2(276.9, 48.7383),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue