feat: implemented timing-based combos

This commit is contained in:
Sara Gerretsen 2025-12-10 17:04:05 +01:00
parent a2d127e6a0
commit 8564ed8290
3 changed files with 37 additions and 28 deletions

View file

@ -128,9 +128,11 @@ void PlayerRunState::state_exited() {
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);
BIND_HPROPERTY(Variant::NODE_PATH, next_fast, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CharacterState");
BIND_HPROPERTY(Variant::NODE_PATH, next_slow, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CharacterState");
BIND_PROPERTY(Variant::FLOAT, trigger_slow_margin);
BIND_PROPERTY(Variant::FLOAT, input_slow_margin);
BIND_PROPERTY(Variant::FLOAT, trigger_fast_margin);
}
void PlayerBasicAttackState::process(double delta) {
@ -145,10 +147,13 @@ void PlayerBasicAttackState::process(double delta) {
}
}
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);
} else if (this->delta_since_start > trigger_next_progress && get_anim_fsm()->get_current_node() != this->animation_name) {
float const trigger_fast_progress{ this->anim_length - this->trigger_fast_margin };
float const trigger_slow_progress{ this->anim_length - this->trigger_slow_margin };
if (this->next_queued == 2 && this->delta_since_start >= trigger_slow_progress && !this->next_slow.is_empty()) {
switch_state(this->next_slow);
} else if (this->next_queued == 1 && this->delta_since_start >= trigger_fast_progress && !this->next_fast.is_empty()) {
switch_state(this->next_fast);
} else if (this->delta_since_start > trigger_fast_progress && get_anim_fsm()->get_current_node() != this->animation_name) {
switch_state<PlayerIdleState>();
}
}
@ -167,9 +172,9 @@ void PlayerBasicAttackState::_notification(int what) {
}
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;
if (this->next_queued == 0 && what->is_action_pressed("light_attack")) {
float const wait_combo{ this->anim_length - this->input_slow_margin };
this->next_queued = wait_combo < this->delta_since_start ? 2 : 1;
get_viewport()->set_input_as_handled();
}
}
@ -178,7 +183,7 @@ void PlayerBasicAttackState::state_entered() {
set_process(true);
set_process_unhandled_input(true);
this->next_queued = false;
this->next_queued = 0;
this->animation_started = false;
this->delta_since_start = 0.0;

View file

@ -68,18 +68,22 @@ protected:
private:
String animation_name{ "swing_1" };
float trigger_next_margin{ 0.1 };
float input_next_margin{ 0.2 };
NodePath next_light_attack_state{};
float trigger_slow_margin{ 0.1 };
float trigger_fast_margin{ 0.2 };
float input_slow_margin{ 0.3 };
NodePath next_fast{};
NodePath next_slow{};
double delta_since_start{ 0.0 };
float anim_length{};
bool next_queued{ false };
int 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);
GET_SET_FNS(NodePath, next_fast);
GET_SET_FNS(NodePath, next_slow);
GET_SET_FNS(float, trigger_slow_margin);
GET_SET_FNS(float, trigger_fast_margin);
GET_SET_FNS(float, input_slow_margin);
};

View file

@ -149,9 +149,9 @@ root_motion_track = NodePath("character2/Skeleton3D:root")
tree_root = SubResource("AnimationNodeStateMachine_yaurm")
anim_player = NodePath("../character/AnimationPlayer")
[node name="character" parent="." unique_id=2008391632 instance=ExtResource("1_yaurm")]
[node name="character" parent="." unique_id=839375526 instance=ExtResource("1_yaurm")]
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="character/character2/Skeleton3D" parent_id_path=PackedInt32Array(2008391632, 1762217024) index="2" unique_id=566409344]
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="character/character2/Skeleton3D" parent_id_path=PackedInt32Array(839375526, 1323459309) index="2" unique_id=566409344]
transform = Transform3D(-0.74619925, -0.6650274, 0.030413395, 0.6645052, -0.7468186, -0.026356531, 0.040241107, 0.0005426332, 0.99918985, 0.6361852, 2.0346472, -0.14206453)
bone_name = "hammer_2"
bone_idx = 18
@ -177,22 +177,22 @@ process_mode = 3
[node name="LightAttack" type="PlayerBasicAttackState" parent="." unique_id=434134429]
process_mode = 3
next_light_attack_state = NodePath("../LightAttack2")
trigger_next_margin = 0.3
input_next_margin = 0.5
next_fast = NodePath("../LightAttack2")
next_slow = NodePath("../LightAttack2")
trigger_slow_margin = 0.3
trigger_fast_margin = 0.3
[node name="LightAttack2" type="PlayerBasicAttackState" parent="." unique_id=258424615]
process_mode = 3
animation_name = "swing_2"
next_light_attack_state = NodePath("../LightAttack3")
trigger_next_margin = 0.2
input_next_margin = 0.4
next_fast = NodePath("../LightAttack")
next_slow = NodePath("../LightAttack3")
trigger_slow_margin = 0.15
trigger_fast_margin = 0.15
[node name="LightAttack3" type="PlayerBasicAttackState" parent="." unique_id=676909200]
process_mode = 3
animation_name = "swing_3"
trigger_next_margin = 0.0
input_next_margin = 0.0
[node name="SpringArm3D" type="SpringArm3D" parent="." unique_id=304394102]
process_mode = 3