How do i detect 8 directions from a gamepad joystick?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Millard

So, I’m making a 2d platformer, and I’m working on the shooting mechanic. The character will be able to shoot in 8 directions. (top, right, bottom, left, and the four directions in between.) In order to aim, the player will use the right gamepad joystick. the problem is, While i know how to detect if its pointed up, down, right, or left, (those can be found in the input map project settings,) i’m not sure how to find the other directions.

thanks.

Hi,
how do you detect it by now? Do you use _input method? Or do you use _process / _physics_process with the Input singleton?

klaas | 2020-09-27 17:35

hi, i’m using the _physics_process() for now, although i might switch to input once i’m further into development.

Millard | 2020-09-28 16:31

:bust_in_silhouette: Reply From: njamster

While i know how to detect if its pointed up, down, right, or left […] i’m not sure how to find the other directions

The other directions are just combinations of the four basic ones (I called those actions stick_right, stick_left, stick_down and stick_up here):

var direction = Vector2()
direction.x = int(Input.is_action_pressed("stick_right")) - int(Input.is_action_pressed("stick_left"))
direction.y = int(Input.is_action_pressed("stick_down")) - int(Input.is_action_pressed("stick_up"))

Note that the diagonal directions will be longer than the basic ones. While that does not matter, when you’re only interested in the direction, it does matter when you use the Vector2 as a means to express speed. In that case, use direction.normalized()!

what exactly does the int() function do, I’ve never seen it before. I assume that it’s returning an integer form of the Input?

Millard | 2020-09-28 16:45

also, would this code require the player to move the joystick in exactly the right direction, or would it allow leeway like it’s supposed to?

Millard | 2020-09-28 16:54

ok, i have written this code for detecting the aiming.

var aim_left = Input.is_action_pressed("aim_left")
var aim_left_up = int(Input.is_action_pressed("aim_left")) - int(Input.is_action_pressed("aim_up") )
var aim_left_down = int(Input.is_action_pressed("aim_left")) - int(Input.is_action_pressed("aim_down") )

var aim_right = Input.is_action_pressed("aim_right")
var aim_right_up = int(Input.is_action_pressed("aim_right")) - int(Input.is_action_pressed("aim_up") )
var aim_right_down = int(Input.is_action_pressed("aim_right")) - int(Input.is_action_pressed("aim_down") )

var aim_up = Input.is_action_pressed("aim_up")
var aim_down = Input.is_action_pressed("aim_down")

and this for testing it.

if aim_left:
	print("aiming left")
if aim_left_up:
	print("aiming left up")
if aim_left_down:
	print("aiming left down")

if aim_right:
	print("aiming right")
if aim_right_up:
	print("aiming right up")
if aim_right_down:
	print("aiming right down")	
	
if aim_down:
	print("aiming down")
if aim_up:
	print("aiming up")

this almost sort of works, but it’s obviously not quite right. (and I didn’t think it would be).
so, if you could look at it and see what i’m doing wrong, or what I should do that would be great. :smiley:

thanks for your answer!

Millard | 2020-09-28 17:21

what exactly does the int() function do, I’ve never seen it before. I assume that it’s returning an integer form of the Input?

Correct. It converts the return value of Input.is_action_pressed() which happens to be a boolean (i.e. either true and false) into an integer (where true becomes 1 and false becomes 0). In more general terms this is called a “type-cast”. Because variables in GDscript have no fixed type by default, you won’t encounter it often, but if you program in a strongly-typed-language like e.g. C# you’ll use it all the time. :wink:

also, would this code require the player to move the joystick in exactly the right direction, or would it allow leeway like it’s supposed to?

That’s where the so-called “dead-zone” comes into play. You can set it individually for each input action and it defaults to 0.5, which means an axis input will be recognized once you push it over 50% of the maximum value, but not below that. So you don’t have to hit an exactly specified spot, but instead need to apply enough force to pass over the dead-zone of both input actions to make the player walk diagonally.

If you prefer that, you can also work with the exact axis values instead:

const DEVICE_ID = 0
const DEADZONE = 0.5
const FACTOR = 1

func _physics_process(delta):
    var input_direction = Vector2()
	input_direction.x = Input.get_joy_axis(DEVICE_ID, JOY_ANALOG_LX)
	input_direction.y = Input.get_joy_axis(DEVICE_ID, JOY_ANALOG_LY)
	if input_direction.length() > DEADZONE:
		var input_angle_in_degrees = rad2deg(input_direction.angle())

		# rounds the input angle to the next multiple of 45
		var dir = int(round(input_angle_in_degrees/45.0)*45)

		var move_direction

		match dir:
			0:
				move_direction = Vector2( 1,  0)
			-45:
				move_direction= Vector2( 1, -1).normalized()
			-90:
				move_direction= Vector2( 0, -1)
			-135:
				move_direction= Vector2(-1, -1).normalized()
			-180, 180:
				move_direction= Vector2(-1,  0)
			135:
				move_direction= Vector2(-1,  1).normalized()
			90:
				move_direction= Vector2( 0,  1)
			45:
				move_direction= Vector2( 1,  1).normalized()

njamster | 2020-09-28 17:44

Regarding the code that you’ve written, e.g.:

var aim_left_up = int(Input.is_action_pressed("aim_left")) - int(Input.is_action_pressed("aim_up") )

if aim_left_up:
    print("aiming left up")

This will be either…

  • 1, if aim_left is pressed (i.e. true/1) but aim_down isn’t (i.e. false/0)
  • -1, if aim_down is pressed (i.e. true/1) but aim_left isn’t (i.e. false/0)
  • 0 if both aim_down and aim_left are pressed (i.e. true/1)

However, that’s clearly not what you want! If you want to do it this way, you’d do:

var aim_left_up = Input.is_action_pressed("aim_left") and Input.is_action_pressed("aim_up")

if aim_left_up:
    print("aiming left up")

The solution that I initially provided you with already gives you the direction, though it’s in Vector2-form. So you could also do something like this:

var direction = Vector2()
direction.x = int(Input.is_action_pressed("stick_right")) - int(Input.is_action_pressed("stick_left"))
direction.y = int(Input.is_action_pressed("stick_down")) - int(Input.is_action_pressed("stick_up"))

if direction == Vector2(-1, 1):
    print("aiming left up")

njamster | 2020-09-28 17:59

Ok, I implemented the code you last added:

var direction = Vector2()
direction.x = int(Input.is_action_pressed("stick_right")) - int(Input.is_action_pressed("stick_left"))
direction.y = int(Input.is_action_pressed("stick_down")) - int(Input.is_action_pressed("stick_up"))

if direction == Vector2(-1, 1):
print("aiming left up")

(I also added the code checking all the other directions, not just left up)

And it works fine, except it doesn’t have deadzone.

So I am next going to use the deadzone code you implemented earlier, however first i need to understand a few things more. :slight_smile:

input_direction.x = Input.get_joy_axis(DEVICE_ID, JOY_ANALOG_LX) input_direction.y = Input.get_joy_axis(DEVICE_ID, JOY_ANALOG_LY)

Is this unnecessary with the code i have? I’m pretty sure my code is already doing this just differently.

var dir = int(round(input_angle_in_degrees/45.0)*45)
not quite sure what this is doing, could you explain?

and finally:

match dir:

pretty sure i’ve learned this somwhere before, but I haven’t done a lot of coding so I’m not sure what kind of statement this is.

Thanks so much for your help, and I totally get it if you don’t want to answer all this. :slight_smile:

Millard | 2020-09-28 19:13

I’m pretty sure my code is already doing this just differently.

Well, get_joy_axis will return a float-value between 0.0 and 1.0 depending on how hard you’re pushing the stick into the given direction. Whereas is_action_pressed will be either true (i.e. 1) or false (i.e. 0), but never something in between! What’s more appropriate to use depends on what exactly you’re trying to achieve. But yes, both share the common goal of finding out if the stick is pushed here.

not quite sure what this is doing, could you explain?

As the comment above that line already states, it “rounds the input angle to the next multiple of 45”. In order to do so, it first takes the value of input_angle_in_degrees and divides it by 45, the result of which will be a float that tells you how often you can fit 45 into input_angle_in_degrees e.g. 49.5/45 would be 1.1 and 85.5/45 would be 1.9. It then rounds these floats to the nearest whole number - so to stay with our examples round(1.1) == 1.0 and round(1.9) == 2.0. Now all that’s left to do for us is to multiply it again by 45 and we’ll end up with 45 and 90 - both multiples of 45! Casting it into an integer isn’t actually necessary here, but without it you need to use floats inside the match-statement as well - so I chose the lazy route. :wink:

Btw, the halfway case is always rounded up: round(1.5) == 2.0.

what kind of statement this is

The match-statement (check out the documentation!) is a way of re-writing a set of if-statements for which you know in advance that it’s not possible to meet more than one condition at the same time anyway! So instead of writing the following:

if counter == 1:
    print("It's a 1!")
elif counter == 2:
    print("It's a 2!")
elif counter == 3:
    print("It's a 3!")

you can also write it like that:

match counter:
    1:
        print("It's a 1!")
    2:
        print("It's a 2!")
    3:
        print("It's a 3!")

njamster | 2020-09-29 20:22

ok, I implemented it and it appears to be working! I’m using the is_action_pressed because it’s being used for directional aiming and a boolean makes more sense.

It might just be an illusion, but it seems like it’s a lot less forgiving when aiming down-left than when its aiming down-right. It’s likely just the difference in motion of my thumb, but have you experienced this before?

Thanks so much, for the help, I know it must have taken a lot of time! :smiley: :smiley:

Millard | 2020-09-29 23:22