fray icon indicating copy to clipboard operation
fray copied to clipboard

Create example of how to implement contextual inputs.

Open Pyxus opened this issue 3 years ago • 1 comments

As requested a example/tutorial on implementing contextual inputs should be created. An example of such input is the sprint/dodge button in souls games like Elden ring. When tapped the player enters a dodge state but if held the player enters a run state.

Pyxus avatar Feb 09 '23 05:02 Pyxus

I figured out how to do this and let me tell you this : It fall exactly into some forgotten crack in the system. By that I mean that the solution need to bypass some of the tools of FrayCombatStateMachine.

Conceptually, we are trying to determine if the button was pressed and released, or is it held. For the threshold I arbitrarily chose 0.35s. If the player release the button before 0.35s, it trigger a release, otherwise we simply ignore this signal and let the state machine check if the button is held.

There isn't a is_held function in FrayInput, but it's easy to implement

## Returns [code]true[/code] if the [kbd]input[/kbd] is being held for [kdb]min_time_frame[/kdb] .
func is_held(input: StringName, min_time_frame: int = 500, device: int = DEVICE_KBM_JOY1) -> bool:
match _get_input_state(input, device):
  var input_state:
    return input_state.is_pressed && (Time.get_ticks_msec() - input_state.time_pressed) > min_time_frame
  null:
    return false

However, it was difficult to setup the combat state machine to interact correctly with the buffer_button. Basically you need to filter the input_detected. Here is my implementation :

FrayInput.input_detected.connect(func (_input_event : FrayInputEvent) :
  if _input_event.input in ["R1","A",'X']: # Nothing special on those input
    if(FrayInput.is_just_pressed(_input_event.input) or FrayInput.is_just_released(_input_event.input)):
      csm.buffer_button(_input_event.input, _input_event.is_pressed())
  elif _input_event.input == 'B' and not _input_event.is_pressed() and _input_event.get_time_held_sec() < 0.34:
    csm.buffer_button(_input_event.input, false) # only send a release event if released before 0.34s
  elif _input_event.input == 'B' and _input_event.is_just_pressed() :
    csm.buffer_button(_input_event.input, true) # Only send pressed event.
,CONNECT_DEFERRED
)

# and in the CombatStateMachine
#...
# Dash using action button
.transition_button('idle','dash',{
	input='B',prereqs=[_cond_on_floor],
	is_triggered_on_release=true,
})

This is just a prototype since I just was able to make it work. I'll replace the code to send the the dash with a custom input ( probably 'b_tapped' ) in order to allow to use 'B' in other cases.

I hope I'm clear.

What was difficult to learn is the FrayInput.input_detected signal also is called on echo. I thought that it was only when an input is detected, basically no echo event.

Remi123 avatar Sep 03 '23 02:09 Remi123