Problems with Input system

BinaryOrangeBinaryOrange Posts: 234Member
edited August 2016 in Programming
I seem to be having some issues understanding input. <br /><br />I want to make it so that one of my keys toggles debug information. I have a Panel set up with two labels as child nodes and a script to show or hide the panel. <br /><br />I thought I could use something like this to toggle the visibility:<br />
if Input.is_action_pressed(&quot;toggle_debug&quot;):<br />		visible = !visible
<br /><br />This does work, but if you hold down the key, it will keep toggling back and forth, which is to be expected. I was hoping there would be an is_action_released() function, but alas, this does not appear to be!<br /><br />In fact, the documentation hints at there being one, but I can't seem to access it!<br /><br />It's under InputEventAction, but anytime I try to access it, it won't let me. The text stays as normal text and doesn't show as any kind of class. <br /><br />I've tried to access it as follows:<br />
if InputEventAction.is_action_released(&quot;toggle_debug&quot;):
<br />But it keeps saying that InputEventAction identifier is not found. <br /><br />So, what do I do? Any pointers please?  G: <br />
Tagged:

Tags :

Comments

  • danjodanjo Posts: 81Member
    I think the best way is to use _input(event)<br />
    <br />func _ready():<br />&nbsp; &nbsp; set_process_input(true)<br /><br />func _input(event):<br />&nbsp; &nbsp; if event.is_action_released(&quot;toggle_debug&quot;):<br />&nbsp; &nbsp; &nbsp; &nbsp; visible = !visible<br />
    
  • BinaryOrangeBinaryOrange Posts: 234Member
    Ah yes, that does work wonderfully! I didn't realize there was an _input(event): function!<br /><br />Here is my revised script:<br /><br />
    <br />extends Panel<br /><br /><br />var gravityText<br />var walkingText<br />var visible = true<br /><br /><br />func _ready():<br />&nbsp;  set_process(true)<br />&nbsp;  set_process_input(true)<br /><br /><br />func _process(delta):<br />&nbsp;  # display the panel if visible is true, hide if it isn&#039;t<br />&nbsp;  if visible == true:<br />&nbsp; &nbsp; &nbsp; self.show()<br />&nbsp;  else:<br />&nbsp; &nbsp; &nbsp; self.hide()<br />&nbsp;  # get the values for the labels<br />&nbsp;  gravityText = str(round(get_parent().get_node(&quot;KinematicBody2D&quot;).velocity.y))<br />&nbsp;  walkingText = str(round(get_parent().get_node(&quot;KinematicBody2D&quot;).velocity.x))<br />&nbsp;  <br />&nbsp;  # set the text in the labels<br />&nbsp;  get_node(&quot;Gravity&quot;).set_text(&quot;Gravity: &quot; + gravityText)<br />&nbsp;  get_node(&quot;Walking&quot;).set_text(&quot;Walking: &quot; + walkingText)<br />&nbsp;  <br /># process all input events<br />func _input(event):<br />&nbsp;  if event.is_action_released(&quot;toggle_debug&quot;):<br />&nbsp; &nbsp; &nbsp; # flip the visible bool<br />&nbsp; &nbsp; &nbsp; visible = !visible
    
    <br /><br />Do you happen to know why the documentation doesn't discuss this more clearly? I think it needs a little more work!  ;) <br /><br />Also, is there a way to access is_action_released() without having to use the _input(event): function?
  • danjodanjo Posts: 81Member
    To get use the methods you find in InputEvent and its subclasses you need an object of one of those classes, so you could do something like:<br />
    <br /># create event<br />var ev = InputEvent()<br /># set type index<br />ev.type=InputEvent.MOUSE_BUTTON<br /># button_index is only available for the above type<br />ev.button_index=BUTTON_LEFT<br />
    
    <br />As shown in the documentation<br />But I don't think that is of any help in your case. <br />
    Do you happen to know why the documentation doesn't discuss this more clearly?
    That seems to be a general problem with the documentation.
  • BinaryOrangeBinaryOrange Posts: 234Member
    I tried something like the following, but it doesn't register at all. <br /><br /><br />
    <br />var event = InputEvent()<br />event.type = InputEvent.ACTION<br />event.set_as_action(&quot;toggle_debug&quot;, true)<br />	<br />if event.is_action_released(&quot;toggle_debug&quot;):<br />	visible = !visible<br />	print (&quot;test&quot;)
    
    <br /><br />I put in under _process(delta):<br /><br />The reason I am wanting to do this is because I want to be able to use is_action_released() for things like the jump key. Granted, I could use a flag variable, but I want to be able to not have to do that.<br /><br />Using _input(event): seems to be pretty slow, at least in my experiments. It doesn't seem to update every frame, which is why I asked about accessing other functions inside of InputEvent. <br /><br />Why doesn't my above code work? Am I misunderstanding something about the Input system?
  • danjodanjo Posts: 81Member
    There are at least 2 problems with this code:<br /><br />1. You create an event every time _process(delta) is called, even if there is no input<br />2. You set the event as pressed, so the following if statement will always result in false<br /><br />So your code example does exacyly the same as <br />
    <br />func _process(delta):<br />&nbsp; if false:<br />&nbsp; &nbsp; visible = !visible<br />&nbsp; &nbsp; print (&quot;test&quot;)<br />
    
    <br />To get the desired result you first need to check if a specific input happend (e.g. Input.is_key_pressed()) and a flag to know if the key was just pressed, is hold down or was just released, than you got the needed information to create the event you need.<br />I think we both agree that this approach is stupid.<br /><br />Conclusion:<br />If you can't/don't want to use _input(event) you should use _fixed_process(delta) (which, in contrast to _process(delta), is called every frame) and deal with the limitations of Input.*<br /><br />For a platformer I did to get used to godot I used the following script to handle the states of a key input<br />
    <br />### class for input handling. Returns 4 button states<br />const RELEASED = 0<br />const JUST_PRESSED = 1<br />const PRESSED = 2<br />const JUST_RELEASED = 3<br /><br />var input_name<br />var prev_state<br />var current_state<br />var input<br /><br /><br />var output_state<br />var state_old<br /><br />### Get the input name and store it<br />func _init(var input_name):<br />&nbsp; &nbsp; &nbsp; &nbsp; self.input_name = input_name<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />### check the input and compare it with previous states<br />func check():<br />&nbsp; &nbsp; &nbsp; &nbsp; input = Input.is_action_pressed(self.input_name)<br />&nbsp; &nbsp; &nbsp; &nbsp; prev_state = current_state<br />&nbsp; &nbsp; &nbsp; &nbsp; current_state = input<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; state_old = output_state<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; if not prev_state and not current_state:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output_state = 0 ### Released<br />&nbsp; &nbsp; &nbsp; &nbsp; if not prev_state and current_state:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output_state = 1 ### Just Pressed<br />&nbsp; &nbsp; &nbsp; &nbsp; if prev_state and current_state:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output_state = 2 ### Pressed<br />&nbsp; &nbsp; &nbsp; &nbsp; if prev_state and not current_state:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output_state = 3 ### Just Released<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; return output_state<br />
    
    <br />It's used like this:<br />
    <br />var input_states = preload(&quot;res://scripts/input_states.gd&quot;)<br />var action_toggle_debug = input_states.new(&quot;toggle_debug&quot;)<br /><br />func _fixed_process(delta):<br />&nbsp; if action_toggle_debug.check() == input_states.JUST_RELEASED:<br />&nbsp; &nbsp; visible = !visible<br />
    
    Hope that helps<br /><br /><br />Edit: fixed a error in the example
  • BinaryOrangeBinaryOrange Posts: 234Member
    Ah, I see that now. I figured it probably had something to do with misunderstanding the set_as_action() command.<br /><br />I like your script for working around the limitations of the input system, thanks for sharing! In the meantime, for my character to jump I just created a flag variable that resets once they hit the ground again. I suppose I could also do this:<br /><br />
    if !Input.is_action_pressed(&quot;jump&quot;):<br />&nbsp; &nbsp; keyPressed = false<br />else:<br />&nbsp; &nbsp; keyPressed = true
    
  • RossRoss Posts: 199Member
    You're making this way too complicated. What danjo had in his first post is all you should need. Use [tt]_input(event)[/tt] with [tt]if event.is_action_pressed()[/tt] and [tt]if event.is_action_released()[/tt]. [tt]_input()[/tt] fires whenever there is an input event. If you're having some kind of delay that must be a problem with the rest of your code. I have a couple platformer prototypes and they work fine, there's no lag or missed input. <br /><br />Setting the input event to a variable every frame is the same thing only way more complicated and inefficient. That's just asking for trouble. You're going to need a few bools for if your character is moving left or right or on the ground probably, trying to get around that with 20 lines of other code is kind of ridiculous.
  • BinaryOrangeBinaryOrange Posts: 234Member
    This is all of my code, and there is quite significant lag when using the _input(event): function.<br /><br />
    <br /><br /><br />extends KinematicBody2D<br /><br /><br />export var gravity = 699.8<br />export var walkSpeed = 275.0<br />export var maxWalkSpeed = 275.0<br />export var jumpHeight = 500<br />var velocity = Vector2()<br />var canJump = false<br /><br /><br />func _ready():<br />	# set fixed process to true<br />	set_fixed_process(true)<br />	set_process_input(true)<br /><br /><br />func _fixed_process(delta):<br />	# get the space states<br />	var pos = self.get_pos()<br />	var space_state = get_world_2d().get_direct_space_state()<br />	var result = space_state.intersect_ray(pos, Vector2(pos.x, pos.y + 20), [ self ])<br />	<br />	# apply gravity if there is nothing beneath the player<br />	if result.empty():<br />		velocity.y += gravity<br />	else:<br />		velocity.y = 0<br />	<br />	# clamp velocities<br />	if velocity.x &lt;= -maxWalkSpeed:<br />		velocity.x = -maxWalkSpeed<br />	elif velocity.x &gt;= maxWalkSpeed:<br />		velocity.x = maxWalkSpeed<br />	if velocity.y &gt;= gravity:<br />		velocity.y = gravity<br />	<br />	# determine death<br />	if pos.y &gt;= 900:<br />		pos.y = 64<br />		pos.x = 64<br />		self.set_pos(pos)<br />	<br />	# now determine the motion!<br />	var motion = velocity * delta<br />	move(motion)<br />	<br />	# determine collision<br />	if(is_colliding()):<br />		var n = get_collision_normal()<br />		motion = n.slide(motion)<br />		velocity = n.slide(velocity)<br />		move(motion)<br />		<br />func _input(event):<br />	# determine what the action is<br />	if event.is_action_pressed(&quot;ui_left&quot;):<br />		velocity.x -= walkSpeed<br />	elif event.is_action_pressed(&quot;ui_right&quot;):<br />		velocity.x += walkSpeed<br />	else:<br />		velocity.x = 0<br />		<br />	if event.is_action_released(&quot;ui_up&quot;):		velocity.y -= gravity
    
    <br /><br />In fact, not only does it lag, but it will entirely stop moving the player occasionally, when it's only a few pixels away from the edges of my tiles (or in some cases, about 32 pixels away, so about halfway through my tiles).<br /><br />Could it be a bug, or is it something to do with my ray cast perhaps?<br /><br />Although, I have noticed the same lag when toggling my debug text. It can take a half-second or longer for Godot to realize I have let go of the toggle key before it updates to determine whether or not it should display the info.
  • danjodanjo Posts: 81Member
    Do you get the same experience with the 2d/kinematic_char demo?
  • BinaryOrangeBinaryOrange Posts: 234Member
    No, but that's because it doesn't use _input(event):, at least as far as I can tell. It's using vars to store things like Input.is_action_pressed("jump").<br /><br />Well, I guess for now my solution is to just use flag variables. Thankfully, I only need this for two things - jumping, and toggling debug information. Otherwise, I may just have to steal that script you previously shared!
  • danjodanjo Posts: 81Member
    My fault, I should have had a closer look at the demo first..<br /><br />I built a simple scene with your script attached to a KinematicBody2D, I didn't notice any lag.<br /><br />What I noticed is, that the player only moves a certain amount of time and then stops when I hold the key down, as soon as I release the key and press again it starts moving again. Not sure why this happens.. I always used RigidBody2D and set_linear_velocity for movement.<br />
  • BinaryOrangeBinaryOrange Posts: 234Member
    Well, yes, after holding the key down for a few seconds it is as though the input is forgotten and tossed to the side of a road, like an empty soda can.  However, there is still a slight "hiccup" which seems laggy to me.  Restarting my computer, however, removed the lag but didn't fix the previous issue of input being forgotten. <br /><br />I wonder if it is, perhaps, another Windows-related issue, much like the jitter with 2D physics objects?
  • MegalomaniakMegalomaniak Posts: 1,001Admin
    Sounds like the button press is handled as a one-of event? Hence why danjo's example of input state-machine might work better/well? Just to be clear, I haven't tested. This is just me making guesses based on what has been said in this thread so far.
  • RossRoss Posts: 199Member
    Ahh, I see the issue. Yes, input events are events, not states. They only fire once, not every frame. Try putting a print statement after each [tt]if event.is_action . . .[/tt]. You should see it print once, the instant you press or release the button.
  • RossRoss Posts: 199Member
    I've been playing with a rigidbody character, so it's a little bit different, but the general setup would be pretty much the same. I'm doing something like this:<br />
    <br />const s = 9000 # movement force per second<br />var speed = 0.0 # current speed<br />const maxspeed = 700<br /><br />func _input(event):<br />	if event.is_action_pressed(&quot;right&quot;):<br />		right = true<br />	if event.is_action_released(&quot;right&quot;):<br />		right = false<br />	if event.is_action_pressed(&quot;left&quot;):<br />		left = true<br />	if event.is_action_pressed(&quot;left&quot;):<br />		left = true<br /><br />func _process(delta):<br />	speed = get_linear_velocity().x<br />	if right:<br />		if speed &lt; maxspeed:<br />			apply_impulse( Vector2(0, 0), Vector2(delta * s, 0))<br />	if left:<br />		if -speed &lt; maxspeed:<br />			apply_impulse( Vector2(0, 0), Vector2(delta * -s, 0))<br />	else: # if no keys are pressed, apply a damping force. <br />		apply_impulse(Vector2(0,0), Vector2(get_linear_velocity().x * get_mass() * damping, 0))<br />
    
  • BinaryOrangeBinaryOrange Posts: 234Member
    Ah, Ross you are my hero!<br /><br />I don't know why I didn't think about doing it that way. As you said, I was just overcomplicating it (I tend to do that A LOT when I feel I am not understanding something). <br /><br />Thank you for the clear example, and also explaining where my error was.  8)
Sign In or Register to comment.