[Godot] Using Marker2D as a Management Node

Created: 2025-06-20Last updated: 2026-07-09

How to use Marker2D to manage spawn points, weapon muzzles, and effect anchors. Covers the difference from Node2D, attaching data with @export, using global_position, and tips for keeping markers organized.

Enemy spawn positions, bullet muzzles, places to play an effect: games are full of situations where you need to work with specific coordinates. Hardcode them into a script as Vector2(150, 300) and every small nudge means opening the code and retyping numbers, which wears you down fast.

Marker2D takes that pain away. It separates a coordinate from the code and turns it into a marker in the editor, so you can drag it into place the same way you position a character. This article covers Marker2D from the basics through practical uses like spawn points and weapon muzzles.

Illustration of Marker2D. Crosshair markers sit at meaningful positions in a 2D scene, such as spawn points and a gun muzzle

What You'll Learn

  • What Marker2D is (how it differs from Node2D, and its relationship to the old Position2D)
  • The practical technique of attaching data to spawn points with @export
  • Building anchor points that follow a character with global_position / global_rotation
  • Tips for keeping Marker2D nodes tidy

Sponsored

What is Marker2D? How it differs from Node2D

Marker2D is an extremely lightweight node used as a marker in 2D space. Internally it's nearly the same as Node2D, with one decisive difference: it always displays a crosshair gizmo in the editor.

Comparison of Marker2D and Node2D. The Node2D on the left displays nothing in the editor so its position is invisible, while the Marker2D on the right shows a persistent crosshair you can drag to adjust

A bare Node2D draws nothing, so you can't see where it is in the editor (you have no idea unless you select it). A Marker2D, with its crosshair always visible, lets you place and fine-tune coordinates exactly as if you were dragging an object. The position becomes something you can see and touch instead of an abstract number in code.

Note: Position2D from Godot 3 was renamed to Marker2D in Godot 4. It plays the same role as a position marker. When older articles or samples mention Position2D, read it as Marker2D.

Hands-On: building a flexible enemy spawn system

Marker2D really proves itself in managing spawn points. Enemy formations in a shoot-'em-up, invasion entrances in a tower defense, monster placement in a roguelike: let's build a system where the level designer can tune "what spawns where" without touching code.

First, place a Marker2D wherever you want enemies to appear, attach a script to each, and use @export to carry information such as which enemy to spawn.

# spawn_point.gd
extends Marker2D

@export var enemy_type: PackedScene   # The enemy to spawn here (assigned in the Inspector)
@export var enabled: bool = true      # Whether to use this point (designers can toggle it)

Now each spawn point is a marker that carries its own position, enemy, and enabled flag. Put them all in an enemies_spawn_points group and read them from the manager side.

Flow diagram of the spawn system. Several Marker2D spawn points each hold enemy_type and enabled via @export, and the GameManager fetches them all from a group and spawns enemies at the global_position of every enabled point
# game_manager.gd
func _ready() -> void:
    # Fetch the spawn points together via the group
    for sp in get_tree().get_nodes_in_group("enemies_spawn_points"):
        if sp.enabled and sp.enemy_type:          # Only enabled points that have an enemy assigned
            var enemy := sp.enemy_type.instantiate()
            enemy.global_position = sp.global_position   # Spawn at the marker's position
            add_child(enemy)

The nice part of this design is that level designers can do everything in the Inspector: "from this position," "spawn this boss," "skip it this time," all configured per marker without touching code. Adding an enemy is just placing one Marker2D and assigning an enemy scene. It's a battle-tested pattern combining the spawning mechanism (instantiation) with group management of the points.

There are two points worth noting.

  • Positions on the marker, logic in the manager: the Marker2D sticks to the "where and what" data, while GameManager owns "when and how." Because the roles are split, you can change the spawning presentation and reuse the markers as-is.
  • Spawn using global_position: as covered below, global_position gives you the correct world coordinate even when the marker is a child of another node.
Sponsored

Building an anchor point (muzzle) that follows a character

Gun muzzles, sword tips, staff ends, feet: processing anchored to a specific body part is another Marker2D specialty. Place it as a child of the character and it follows the character's movement and rotation automatically.

Diagram of muzzle following. With a Muzzle marker placed at the gun barrel as a child of the player, the position and angle of the muzzle follow whichever way the character faces, and bullets fire from that position and angle

In the player scene, add a Marker2D as a child of the sprite, put it at the muzzle, and name it "Muzzle." Then, when firing, just pass that marker's global_position and global_rotation to the bullet.

# player.gd
@onready var muzzle: Marker2D = $Sprite2D/Muzzle
@export var bullet_scene: PackedScene

func _process(_delta: float) -> void:
    if Input.is_action_just_pressed("shoot"):
        var bullet := bullet_scene.instantiate()
        bullet.global_position = muzzle.global_position   # From the muzzle's position
        bullet.global_rotation = muzzle.global_rotation   # At the muzzle's angle
        get_tree().current_scene.add_child(bullet)        # Don't parent bullets to the player

Whether the character faces left or right, the Muzzle is always at the barrel, so bullets fly from the right position at the right angle. The same technique applies to any "effect anchored to a body part": dust at the feet, jet flames on the back, and so on.

Warning: if you flip the sprite with flip_h, global_rotation alone may not give the right bullet direction. Either correct the angle based on the flip, or use muzzle.global_transform.x (the marker's right-facing vector) as the travel direction to be safe.

Common mistakes and best practices

Common mistakeBest practice
Moving the position in code (marker.position.x += 10)Leave position adjustment to the editor and have code only read global_position. Moving it destroys the "what you see is what you edit" benefit
Generic names like Marker2D and Marker2D2Use names that convey the role, such as PlayerSpawn, Muzzle, or BossEntry
Scattering them directly under the rootGroup them under empty Node2D parents like SpawnPoints or EffectAnchors
Using position on a child markerAlways use global_position when you want world coordinates (position is relative to the parent)

The difference between position and global_position matters most here. A child node's position is relative to its parent, so it lands somewhere unintended once the parent moves or rotates.

Diagram of the difference between position and global_position. When the parent node moves and rotates, a child marker's position (relative coordinates) stays the same value but the actual location shifts, while global_position (world coordinates) always points at the correct absolute location on screen

When you mean "this spot on screen," as with spawning and firing, always use global_position and you'll get the correct world coordinate regardless of the parent's movement and rotation.

Organizing your markers pays off too. Gathering related markers under a parent Node2D lets you move or hide them as a group and keeps the scene tree tidy.

Contrasting diagram of Marker2D organization. The bad example scatters many Marker2D nodes directly under the root in a mess, while the good example groups them by role under parent Node2D nodes named SpawnPoints and EffectAnchors

Bonus: Good to know for later

Once you're comfortable managing coordinates with Marker2D, these come into view next.

  • Path2D / Curve2D manage "lines": if Marker2D manages points, Path2D and Curve2D manage lines (paths). You can define enemy patrol routes and camera movement paths. Growing points you placed with markers into paths is a natural progression.
  • Generating levels from placement data: reading marker information in _ready() and assembling the whole level from it leads into procedural generation.
  • Custom gizmos when a crosshair isn't enough: with a @tool script and _draw(), you can draw your own helper icons in the editor.

Summary

  • Marker2D is a position marker. Internally almost identical to Node2D, but its strength is the crosshair always visible in the editor
  • Godot 3's Position2D was renamed to Marker2D in Godot 4
  • Attaching data to spawn points with @export lets designers tune spawning entirely in the Inspector
  • Place one as a child of a character and it follows movement and rotation, giving correct firing positions and angles through global_position / global_rotation
  • Don't move coordinates in code, adjust them in the editor; read them with global_position; organize them by grouping under parent Node2D nodes

If you have even one hardcoded coordinate, try replacing it with a Marker2D. Once you know how comfortable it is to adjust a position without opening a script, there's no going back.

Further Reading