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
  • neco arc@lemmy.world
    link
    fedilink
    arrow-up
    6
    ·
    6 hours ago

    recommend using godot’s action system instead of checking for specific button inputs https://docs.godotengine.org/en/stable/getting_started/first_3d_game/02.player_input.html#creating-input-actions since it can more easily allow for controller support and remapping, unless there’s a reason you are checking for specific keys? also is there a reason to using _unhandled_input() over _input()? Otherwise I would also recommend moving some of this logic into separate functions as a start

    • Fizz@lemmy.nzOP
      link
      fedilink
      English
      arrow-up
      1
      ·
      5 hours 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
        2
        ·
        4 hours 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
        
  • null@lemmy.org
    link
    fedilink
    English
    arrow-up
    1
    ·
    4 hours ago

    inside there I have a ton of elif statements

    I don’t even use godot and I can already tell you this is not how you want to do it.

    • Fizz@lemmy.nzOP
      link
      fedilink
      English
      arrow-up
      2
      ·
      4 hours ago

      Haha, yeah it feels wrong but im pretty not an experienced programmer so its all I know.

      I am thinking if I could figure out how to put each type of command into its own function. The main input handling loop could just be calling a single line in response to an input event and that would be more managable. However im stuck because a lot of the logic requires multiple input events.

      All the godot tutorials keep talking about state and after waiting 8 I still have no idea how to use state to control this.

      • null@lemmy.org
        link
        fedilink
        English
        arrow-up
        1
        ·
        4 hours ago

        I highly, highly recommend using the built-in system for receiving inputs. From there you can build whatever abstraction you want on top of it.

  • lime!@feddit.nu
    link
    fedilink
    arrow-up
    3
    ·
    6 hours ago

    if you hardcode letters for keys, you can’t handle alternate keyboard layouts. without rebinding, that means some users will have to be contortionists to play.

  • NightFantom@slrpnk.net
    link
    fedilink
    arrow-up
    4
    ·
    edit-2
    7 hours ago

    I’m mostly experienced in c++ and python, but I would use a dictionary/map/associative array/whatever name you call it, where you put keypress/event/… as key, and a function with the result you want as value (as a first version, there’s other more specialised ways, but this will be enough for most cases).

    If there’s a case/switch statement in gdscript (that’s what you’re using I think?), you could also look into that, though I think it’s likely just syntactic sugar for either what you’re already doing or what I suggested, but it might be easier to read, so there’s that.

    Edit: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#match

    Gdscript calls it match operator, and I think it works like your elif tree but just a bit cleaner and easier to read

  • Hisse@programming.dev
    link
    fedilink
    arrow-up
    2
    ·
    7 hours ago

    I just dump a lot of if statements into _process. One thing good about it is that it gets rid of some nesting and shortens it a bit