I am working on the input and it feels like its getting out of hand. I wanted to check in with people and see how people structure their input handling.

Currently I have 1 function _unhandled_unput(event) and inside there I have a ton of elif statements trying to handle every possible situation and event. Its manageable at the moment but I only have like 4 events so its going to get very out of hand if I continue.

I need to have 100s of these events based on whats selected and what mouse/keyboard buttons are being pressed and I need some way to resuse the actions.

spoiler
func _unhandled_input(event):
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == MOUSE_BUTTON_RIGHT:
			clear_selection()
			gui.queue_redraw()
			get_viewport().set_input_as_handled()
			return
	if selected_item == "colonist": #broken
		if event is InputEventKey:
			if event.OS.get_keycode_string() == "r":
				for colonist in selected_group:
					colonist.set_state("DRAFT")
					get_viewport().set_input_as_handled()
					gui.queue_redraw()
	#nothing selected dragbox to select things and single click to select things - does not work at the moment
	elif selected_type == "" or selected_type == "basic":
		if is_dragging and event is InputEventMouseMotion:
				drag_end = camera.get_global_mouse_position()
				cam_drag_end= get_viewport().get_mouse_position()
				get_selection(drag_start, drag_end)
				gui.queue_redraw()
				get_viewport().set_input_as_handled()
				return
		elif event is InputEventMouseButton and not event.pressed:
			is_dragging = false
			gui.queue_redraw()
			drag_start = null
			drag_end = null
			get_viewport().set_input_as_handled()
			return
		elif event is InputEventMouseButton and event.pressed:
			if event.button_index == MOUSE_BUTTON_LEFT:
				selected_type = "basic"
				is_dragging = true
				drag_start = camera.get_global_mouse_position()
				cam_drag_start = get_viewport().get_mouse_position()
				gui.queue_redraw()
				get_viewport().set_input_as_handled()
				return
	#command flow for dragging a selection box
	elif selected_type == "command":
		if selected_item == "structure_dict_growing":
			if is_dragging and event is InputEventMouseMotion:
				var grid_pos = tilemap.local_to_map(camera.get_global_mouse_position())
				var local_pos = tilemap.map_to_local(grid_pos)
				drag_end = local_pos + Vector2(32, 32)
				cam_drag_end = get_viewport().get_mouse_position()
				gui.queue_redraw()
				get_viewport().set_input_as_handled()
				return
			elif event is InputEventMouseButton and not event.pressed:
				if event.button_index == MOUSE_BUTTON_LEFT:
					is_dragging = false
					gui.queue_redraw()
					get_viewport().set_input_as_handled()
					MessageBus.rpc_id(1, "request_zone_growing", selected_item ,drag_start, drag_end, multiplayer.get_unique_id())
					drag_start = null
					drag_end = null
					return
			elif event is InputEventMouseButton and event.pressed:
				if event.button_index == MOUSE_BUTTON_LEFT:
					is_dragging = true
					#to snap to grid
					var grid_pos = tilemap.local_to_map(camera.get_global_mouse_position())
					var local_pos = tilemap.map_to_local(grid_pos)
					drag_start = local_pos - Vector2(32, 32)
					cam_drag_start = get_viewport().get_mouse_position() #this is broken cbf fixing maybe one day after selection is working 
					gui.queue_redraw()
					get_viewport().set_input_as_handled()
					return
	elif selected_type == "floor":
		if event is InputEventMouseButton and event.pressed:
			if event.button_index == MOUSE_BUTTON_LEFT:
				var global_mouse_pos = camera.get_global_mouse_position()
				var grid_pos = tilemap.local_to_map(global_mouse_pos)
				if selected_item == "":
					return
				MessageBus.rpc_id(1, "request_build_floor", selected_item, grid_pos, multiplayer.get_unique_id())
				get_viewport().set_input_as_handled()
				return
	elif selected_type == "building":
		if event is InputEventMouseButton and event.pressed:
			if event.button_index == MOUSE_BUTTON_LEFT:
				var global_mouse_pos = camera.get_global_mouse_position()
				var grid_pos = tilemap.local_to_map(global_mouse_pos)
				if selected_item == "":
					return
				MessageBus.rpc_id(1, "request_build_structure", selected_item, grid_pos, multiplayer.get_unique_id())
				get_viewport().set_input_as_handled()
				return
  • Fizz@lemmy.nzOP
    link
    fedilink
    English
    arrow-up
    2
    ·
    13 days ago

    The reason for checking keys is it is the only way i know how to do it. This input handling script is what im finding the most trouble with, a lot of the lines are added because I read a 6yr old reddit comment that said to use it and im not sure if I should remove it because then bugs start appearing.

    The reason for unhandled input was the first input handling code example I saw and honestly I forgot about input(). I’ll look into the creating input actions but at a glance it doesn’t seem like it could work for my game. This game is pretty much left click right click on a 2d space and depending on what tool is selected.

    I really do want to move some of the code into other functions but im not sure how to make a function that handles the input over multiple different states.

    For example dragging a box. needs to check for the left click then check for the dragging state and draw on drag then check for a release. I have a lot of tools that will need to implement this so I’d really like to get this logic out of the main function and into a nice reusable block.

    code example
    	elif selected_type == "command":
    		if selected_item == "structure_dict_growing":
    			if is_dragging and event is InputEventMouseMotion:
    				var grid_pos = tilemap.local_to_map(camera.get_global_mouse_position())
    				var local_pos = tilemap.map_to_local(grid_pos)
    				drag_end = local_pos + Vector2(32, 32)
    				cam_drag_end = get_viewport().get_mouse_position()
    				gui.queue_redraw()
    				get_viewport().set_input_as_handled()
    				return
    			elif event is InputEventMouseButton and not event.pressed:
    				if event.button_index == MOUSE_BUTTON_LEFT:
    					is_dragging = false
    					gui.queue_redraw()
    					get_viewport().set_input_as_handled()
    					MessageBus.rpc_id(1, "request_zone_growing", selected_item ,drag_start, drag_end, multiplayer.get_unique_id())
    					drag_start = null
    					drag_end = null
    					return
    			elif event is InputEventMouseButton and event.pressed:
    				if event.button_index == MOUSE_BUTTON_LEFT:
    					is_dragging = true
    					#to snap to grid
    					var grid_pos = tilemap.local_to_map(camera.get_global_mouse_position())
    					var local_pos = tilemap.map_to_local(grid_pos)
    					drag_start = local_pos - Vector2(32, 32)
    					cam_drag_start = get_viewport().get_mouse_position()
    					gui.queue_redraw()
    					get_viewport().set_input_as_handled()
    					return
    
    • JaN0h4ck@feddit.org
      link
      fedilink
      arrow-up
      5
      ·
      13 days ago

      You could have your Drag Box use the Area2D class and listen for it’s events like this: (input_pickable need to be set to true)

      class_name DraggableBox
      extend Area2D
      
      var mouse_inside: bool = false
      
      func _ready():
        mouse_entered.connect(_on_mouse_entered)
        mouse_exited.connect(_on_mouse_exited)
      
      func _process(delta: float):
        if (not mouse_inside): reuturn
        if (Input.is_action_pressed("name_for_action_with_mouse_click"):
          #handle your object movement
        if (Input.is_action_just_released("name_for_action_with_mouse_click"):
          #handle letting go of the object
      
      func _on_mouse_entered():
        mouse_inside = true
      
      func _on_mouse_exited():
        mouse_inside = false
      
      • hosaka@programming.dev
        link
        fedilink
        arrow-up
        4
        ·
        12 days ago

        This is a solid advice. In the last Unreal Engine project that I was working, this is the approach I went for, entities handle their own input, with a few exceptions of course. Harder to find input handling but at least it’s contained in a class the input actually needs to effect.

    • neco arc@lemmy.world
      link
      fedilink
      arrow-up
      2
      ·
      12 days ago

      yea honestly JaN0h4ck’s advice by moving the input check into is probably what you’d want to have a crack into trying otherwise for cleaning up code I would recommend 100% using Godot’s actions so instead of doing an if statement for checking InputEventMouseButton and if its pressed or not and then checking the button index, just use an event.is_action_[pressed/released], or you check if the event is an action type and then call a separate function passing the event in to check the action just to make your _input function a little cleaner by only having the logic for the non button events happening in there

      also considering how much youre calling camera.get_global_mouse_position() and whatnot, I feel like you may as well just set a variable at the top of the func for it, having the space for that would also help if you were to look into any other “mouse” position stuff such as touch screen inputs or virtual pointers from a controller

    • FooBarrington@lemmy.world
      link
      fedilink
      arrow-up
      2
      ·
      edit-2
      11 days ago

      IMO this is a great case for Finite State Machines. Unfortunately I can’t provide a GDScript example since I write in C#, but I think there are some assets for this in the asset library. The basic idea is that you define states, and valid transitions between those states (e.g. for a drag’n’drop system given the states idle, dragging and dropped it makes sense for the user to go idle -> dragging, dragging -> dropped and dropped -> idle, but not from idle -> dropped). Then you can separate the state transition logic from the main handler for each state, which I think is what you want, right?

      This pattern can also easily be made reusable, either by having no dedicated logic in the state machine (-> everything is handled through events), or by using an adapter interface that components create and pass into the state machine.

      Let me know if you have questions, or would like to see an example - I can at least provide pseudo code :)