Do people not know that Input.get_vector() exists? đź’€

So someone pointed out to me that the Input.get_vector() function limits the length of the vector to 1, so there is no reason to normalize it. Which made me wonder why all the tutorials I saw normalized the vector. So I went and looked up some top-down movement tutorials on YouTube, and the reason people were calling Vector2.normalized() is because they were not using the Input.get_vector() function at all!

They were using Input.get_axis() two times for the x and y axes separately (which isn’t necessary for a 2D top-down game) or using Input.get_action_strength() four times for each direction, or even Input.is_action_pressed(). Using this method means that they have to normalize the input because the method won’t limit the length for them.

I’m not sure why this is. Input.get_vector() was added in 3.4, which was released in Nov. 2021 (all the tutorials I looked at were for Godot 4). Before that, the tutorial on the docs did call Vector2.normalized(), so maybe people are getting their knowledge from that tutorial, or maybe other game engines where you have to use a normalizing function as well?

Something to take into consideration when normalizing the input vector is that while it won’t change anything for keyboard movement, joystick movement will be affected. The action strength of a key press is either 1.0 or 0.0, but a joystick can be any number in between. Normalizing the input vector from a joystick will result in the length being 1.0 or 0.0, so the player can’t halfway move the joystick to walk slower.

The tutorials I watched are for beginners, so they probably aren’t going to be connecting their XBox controller or whatever and trying to move their character with the joystick, but I think it would be better to put simpler code in the tutorial that also won’t break with different forms of input.

Basically, this code:

var input := Vector2(
    Input.get_action_strength("right") - Input.get_action_strength("left"),
    Input.get_action_strength("down") - Input.get_action_strength("up")
).limit_length(1.0)

can be shortened into this code:

var input := Input.get_vector("left", "right", "up", "down")
11 Likes

Maybe lol

4 Likes

In a similar vein, every so often there’s people who ask for help because their exported variables do not appear in the inspector. Then you look at their code, and you see they’re using the old export keyword instead of @export. Most of the time they also connect to signals in the old-timey stringly-typed way.

I guess it makes sense in a way. If you’re the first person to write a guide for something that quickly gets popular, it’s likely your guide is also going to become popular. And if you’re just starting out with Godot and game development in general, you might just think to use the most popular guide rather than the most up-to-date one, especially if you’re unaware software goes bad faster than bananas do.

8 Likes

I fight similar battle advocating the use of multiplayer Nodes over RPCs. A lot of the early 4.0 - 4.2 tutorials (and legacy RPC tutorials for 3.x) that still stand, use workarounds instead utulizing the available features. I will say that the current documentation is lacking, scattered and should be updated. (Which i may get to if no one else does. )

I think in some sense it is the entropy of change, and people just putting themselves out there for recognition in the attention economy not really caring if they are completely right or wrong. There is something to be said that perfectionism is a progress killer. And its time consuming to re-edit/re-shoot videos.

4 Likes

Another benefit of Input.get_vector() is that it uses a circular deadzone, which is usually desired for top-down movement. The tutorials you mentioned use a square deadzone.

2 Likes

Yup, I guess that is another thing to take into consideration.

Some of the tutorials would be good entries in a “reverse code golf” contest, though. For example, something like:

	var input := Vector2()
	if Input.is_action_pressed("move_left"):
		input.x = -1
	if Input.is_action_pressed("move_right"):
		input.x = 1
	if Input.is_action_pressed("move_up"):
		input.y = -1
	if Input.is_action_pressed("move_down"):
		input.y = 1
	input = input.normalized()

That’s 11 lines of code that could just be one line :sob: and it wouldn’t even move in an intuitive way for the player

3 Likes

most godot tutorials found online are awful, even Brakeys, he does a lot of questionable stuff. some could be understandable knowing that it’s directed at people with no understanding of programming, but recommending these methods makes people catch bad habits. and others just make no sense. one example would be his awful character controller tool node that takes strings to set inputs.
instead he should’ve made people use the Input maps in project settings.
another would be the way he made collisions in the 2D tutorial, where the enemies would tell the player what to do instead of the player having a state machine and deciding based on what it collided with.

ironically, the best tutorials are in the docs, but very few read them, specially people who make tutorials.

I think this is more of a ChatGPT problem, as the code generated uses obsolete or just non-existent methods and practices. just like when they made it play chess and it made-up chess pieces and moves.
I mean, anyone could realize the tutorial they are using says godot 3, or is 4 years old.

but I haven’t seen many godot 3 guides recently, they are being pushed to the bottom of the searches, with godot 4 tuts at the top, or maybe this is just filter bubble at play.

3 Likes

Are you sure you’re talking about Brackeys? He’s made like, three Godot tutorials and I saw that he opens the InputMap in one and explains how to add actions. And I couldn’t find the enemies thing either. Maybe you mixed him up with another tutorial maker with millions of views?

So I decided to go through a tutorial series to see what newcomers to the engine are watching. It had hundreds of thousands of views so it couldn’t be that bad right? Unfortunately I think the tutorial maker made a bunch of mistakes. A lot of the way they set up the scenes will cause problems later down the line, after the developer finishes the tutorial and wants to add their own stuff.

For example, the tutorial does not set up a good way of differentiating between different CharacterBodies. The way I would do it would be to put all the players on one layer and all the enemies on another, and set up the layers and masks of the Areas accordingly. Another way is to use groups. The tutorial doesn’t do either of those, however. The tutorial puts an empty (except for pass) function in the player script called player() and uses the has_method() function to check if the body is a player. I think the tutorial should have used that opportunity to teach the viewer about groups instead of… that.

I think these tutorials railroad the viewer into making exactly the game in the tutorial and nothing more. I get that you can’t just throw in a bunch of complicated functions that a newcomer would not understand, but there are a lot of bad practices in there that wouldn’t make things more complicated if they were replaced with better practices.

2 Likes

yes.

he made a Killzone scene, this creates a timer and then calls a function that restarts the game.
this approach is not extendable and will lead to problems when trying to implement it for local mechanics, like a power-up.
he does the same with the coin.

these are things that should be handled by the player, when the player interacts with the world.

look at killzone.gd from his github:

extends Area2D

@onready var timer = $Timer

func _on_body_entered(body):
	print("You died!")
	Engine.time_scale = 0.5
	body.get_node("CollisionShape2D").queue_free()
	timer.start()


func _on_timer_timeout():
	Engine.time_scale = 1.0
	get_tree().reload_current_scene()

body is the player and we are killing it from the enemy.

instead, killzone and coin should be in groups or use metadata, and we get collision from the player. because when you try to implement a state machine, which is what you need to get more complex mechanics, and animations, you would be changing those from the killzone and causing all sorts of bugs.

func _on_collide_with_enemy(body):
	if body.is_in_group("killzone"):
		print("you died")
		#play animation

in fact, I answered a post recently where they had a problem that was a direct result of the Brakeys method:

func _on_area_2d_body_entered(body: PlayerController):
	if body.is_in_group('Player'):
		var animList = body.animation_player.get_animation_list()		
		print(animList)
		body.animation_player.play("electricity")

yes, he does that, but then he gives his proto_controller that has a list of actions by string that have to be replaced by the name of the custom action.

@export_group("Input Actions")
## Name of Input Action to move Left.
@export var input_left : String = "ui_left"
## Name of Input Action to move Right.
@export var input_right : String = "ui_right"
## Name of Input Action to move Forward.
@export var input_forward : String = "ui_up"
## Name of Input Action to move Backward.
@export var input_back : String = "ui_down"
## Name of Input Action to Jump.
@export var input_jump : String = "ui_accept"
## Name of Input Action to Sprint.
@export var input_sprint : String = "sprint"
## Name of Input Action to toggle freefly mode.
@export var input_freefly : String = "freefly"

I don’t know, this feels like it makes it too complex for a new user to edit the names of the inputs in the script themselves, which would take them closer to the code.

the default movement script that godot provides is by contrast, a much better way to learn the code and provides most of what is needed for a character to move.
also the proto_controller does the bad practice of bundling everything into the same script, where the camera should be controlled separately for a third person game.

he also calls Input in _unhandled_input_event instead of using event.


func _unhandled_input(event: InputEvent) -> void:
	# Mouse capturing
	if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
		capture_mouse()

the problem is if people trust him so much they take his methods over those that would make the most sense . we must look at things in a critical way when learning, but there are still those who don’t do that.

4 Likes

Oh, I see now, yeah that is a bad way of teaching stuff. But the problem is, newcomers don’t know what is “good” and what is “bad” so they will not be able to tell if Brackeys or whoever is doing something that is bad practice.

Personally I prefer shorter tutorials over multi-hour long tutorials. Like, if you make a tutorial that is over an hour, and you make a mistake, the only way to correct it is to pin a comment with the correction because remaking the entire video would take a while. I also prefer text tutorials too, but video tutorials seem to be more popular and accessible.

Definitely agree, the killzone.gd script is not what a new user should be seeing IMHO.

Should probably be just a signal in there.

2 Likes