Lesson 03

Mouse Aiming

Turn the player to face wherever the mouse goes, swap the cursor for a crosshair, and click the targets to pop them.

Loading the game engine. This takes a moment the first time.

The player still walks with WASD, but now it also turns to face the mouse every single frame. The normal mouse arrow is hidden, and a crosshair drawn in the scene follows the mouse in its place. Click a red target and it pops: that half is target.gd, which listens to the mouse instead of the keyboard.

New code in this lesson

None of the earlier lessons use these. Each one shows the line where it appears here.

look_at()

Turns a node so its front, the side facing right in the editor, points at a spot.

player.gd
look_at(get_global_mouse_position())

get_global_mouse_position()

Where the mouse is right now, as a spot in the game world.

player.gd
look_at(get_global_mouse_position())

rotation

How far a node is turned, in radians. A full turn is TAU, about 6.28.

player.gd
sprite.flip_v = abs(rotation) > PI / 2

flip_v

Mirrors a sprite top to bottom.

player.gd
sprite.flip_v = abs(rotation) > PI / 2

abs()

A number without its minus sign: abs(-5) is 5.

player.gd
sprite.flip_v = abs(rotation) > PI / 2

PI

The number pi, about 3.14: half a turn, in radians.

player.gd
sprite.flip_v = abs(rotation) > PI / 2

Input.mouse_mode

Shows or hides the mouse arrow.

crosshair.gd
Input.mouse_mode = Input.MOUSE_MODE_HIDDEN

_process(delta)

Godot runs this every frame the screen draws. For things that are not physics, like moving a crosshair.

crosshair.gd
func _process(_delta: float) -> void:

global_position

Where a node is in the whole game world, not just compared to its parent. Use it to compare two nodes that sit in different places in the tree.

crosshair.gd
global_position = get_global_mouse_position()

_exit_tree()

Godot runs this when the node is about to leave the scene, so it can tidy up after itself.

crosshair.gd
func _exit_tree() -> void:

input_event

The signal an Area2D sends when the mouse clicks or moves over it. Needs Input Pickable ticked.

target.gd
input_event.connect(_on_input_event)

InputEventMouseButton

What Godot sends when a mouse button goes down or comes back up. event.pressed is true on the way down.

target.gd
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:

queue_free()

Removes a node and everything under it from the game, safely, at the end of the frame.

target.gd
queue_free()

The scenes

■ UI node · ■ 2D node · % unique name · .tscn instanced scene

game.tscn
  • Game Node2D
  • Player CharacterBody2D player.tscn
  • Target1 Area2D target.tscn
  • Target2 Area2D target.tscn
  • Target3 Area2D target.tscn
  • Target4 Area2D target.tscn
  • Target5 Area2D target.tscn
  • Crosshair Node2D crosshair.tscn
crosshair.tscn
  • Crosshair Node2D crosshair.gd
  • HLine Polygon2D
  • VLine Polygon2D
  • Circle Polygon2D
player.tscn
  • Player CharacterBody2D player.gd
  • Sprite2D
  • Pointer Polygon2D
  • CollisionShape2D
target.tscn
  • Target Area2D target.gd
  • Outer Polygon2D
  • Inner Polygon2D
  • CollisionShape2D

FileSystem

  • game.tscn
  • crosshair.tscn
  • crosshair.gd
  • player.tscn
  • player.gd
  • target.tscn
  • target.gd

The code

extends CharacterBody2D

## How fast the player moves.
@export var speed: float = 180.0

@onready var sprite: Sprite2D = $Sprite2D

# "_physics_process()" runs about 60 times a second. Godot calls it for you.
func _physics_process(_delta: float) -> void:
	# Move with WASD, exactly like every other lesson.
	var direction: Vector2 = Input.get_vector(
		"move_left", "move_right", "move_up", "move_down"
	)
	velocity = direction * speed
	move_and_slide()

	# "get_global_mouse_position()" asks where the mouse is on screen.
	# "look_at()" then turns the player to face that spot. Doing it every
	# frame is what makes the player track the mouse everywhere it goes.
	look_at(get_global_mouse_position())

	# Aiming left would turn the picture upside down, so mirror it back.
	sprite.flip_v = abs(rotation) > PI / 2