keepcalculator

06 Numbers and Powers

Godot Random Moving Objects: How to Add

Card No. 06·49432 · Updated · Standard formula, shown below

To add random moving objects in Godot, you create a script that assigns a random direction and speed to an object’s velocity, then updates its position each frame. You can do this with a few lines of GDScript in the object’s _ready() and _physics_process() functions, or by using a RandomNumberGenerator for more control. This guide covers both simple and advanced approaches, including how to keep objects inside the screen, add collisions, and avoid common pitfalls.

Why Random Movement Is Useful

Random movement is a core mechanic in many games and simulations. It’s used for:

  • Ambient creatures (fish, birds, insects) that wander without a set path.
  • Particles or debris that scatter after an explosion.
  • Non-player characters (NPCs) that patrol or wander to feel more alive.
  • Pickups or power-ups that drift around the level.
  • Simple AI for enemies that move unpredictably to challenge the player.

In Godot, you have full control over how “random” the movement feels. You can use pure randomness (each frame a new direction) or smoothed randomness (changes direction occasionally), depending on the effect you want.

Basic Setup: A Node and a Script

The simplest way is to attach a script to any Node2D (or RigidBody2D, CharacterBody2D, etc.) that has a position. Here’s a minimal GDScript for a Node2D:

extends Node2D

var speed = 200
var direction = Vector2.RIGHT

func _ready():
    randomize()  # Ensure different results each run
    direction = Vector2.RIGHT.rotated(randf() * TAU)

func _process(delta):
    position += direction * speed * delta

This moves the object in a straight line forever. To make it change direction, you can add a timer or check a random chance each frame.

Using RandomNumberGenerator for Better Control

Godot’s global randf() works, but for more control (seeds, reproducible sequences), use the RandomNumberGenerator class. Create one as a member variable:

extends Node2D

var rng = RandomNumberGenerator.new()
var speed = 200
var direction = Vector2.RIGHT
var change_timer = 0.0

func _ready():
    rng.randomize()
    _new_direction()

func _process(delta):
    change_timer -= delta
    if change_timer <= 0:
        _new_direction()
        change_timer = rng.randf_range(0.5, 2.0)
    position += direction * speed * delta

func _new_direction():
    direction = Vector2.RIGHT.rotated(rng.randf_range(0, TAU))

This changes direction every 0.5 to 2 seconds. The rng object lets you set a seed (rng.seed = 12345) to reproduce the same sequence, which is useful for testing.

Keeping Objects on Screen

Random movers often leave the visible area. To keep them inside, you can wrap around the screen edges or bounce off them. Here’s a bounce example for a 2D viewport:

func _process(delta):
    position += direction * speed * delta
    var viewport = get_viewport_rect().size
    if position.x < 0 or position.x > viewport.x:
        direction.x = -direction.x
        position.x = clamp(position.x, 0, viewport.x)
    if position.y < 0 or position.y > viewport.y:
        direction.y = -direction.y
        position.y = clamp(position.y, 0, viewport.y)

For wrapping, replace the clamp with position.x = wrapf(position.x, 0, viewport.x) and similarly for y.

Using Physics: RigidBody2D and CharacterBody2D

If your object needs to collide with walls or other objects, use a physics body. For a RigidBody2D, you can set a random linear velocity in _ready():

extends RigidBody2D

func _ready():
    randomize()
    linear_velocity = Vector2.RIGHT.rotated(randf() * TAU) * randf_range(100, 300)

The physics engine handles collisions and bouncing automatically. For a CharacterBody2D, you need to call move_and_slide():

extends CharacterBody2D

var speed = 200
var direction = Vector2.RIGHT

func _ready():
    randomize()
    direction = Vector2.RIGHT.rotated(randf() * TAU)

func _physics_process(delta):
    velocity = direction * speed
    move_and_slide()
    if get_slide_collision_count() > 0:
        direction = direction.bounce(get_slide_collision(0).get_normal())

This makes the object bounce off walls realistically.

Common Problems and Solutions

Here are frequent issues and how to fix them:

  • Object doesn’t move: Check that the script is attached and the function is _process or _physics_process, not _process spelled wrong.
  • Object moves too fast/slow: Adjust the speed value. Remember _process is frame-rate dependent; use _physics_process for consistent speed across devices.
  • Same movement every time: Call randomize() (or rng.randomize()) in _ready() to seed the generator with a random value.
  • Object leaves the screen: Add boundary checks as shown above.
  • Object jitters: If using _process, switch to _physics_process and move with position += direction * speed * delta (delta is already in physics ticks).

Frequently Asked Questions

How do I make an object move in a random direction but stay on a grid?

Instead of a continuous angle, choose from a set of directions (up, down, left, right). Use direction = [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT][rng.randi_range(0, 3)]. Then move in that direction for a fixed distance or time before choosing again.

Can I make the speed random too?

Yes. Set speed = rng.randf_range(100, 300) in _ready() or whenever you change direction. This creates more variety in movement.

How do I make the object change direction smoothly instead of abruptly?

Use a Tween to interpolate the direction vector over time, or gradually rotate the direction using direction = direction.rotated(rotation_speed * delta) where rotation_speed is a small random value.

Why is my object moving diagonally faster than straight?

This happens when you normalize the direction vector. Always use direction.normalized() if you combine multiple inputs. If you set a fixed vector like Vector2(1,1), its length is sqrt(2) ≈ 1.41, so it moves faster. Normalize it.

Can I use this for 3D objects?

Yes. For a Node3D, use Vector3 for direction and position. For example: direction = Vector3.RIGHT.rotated(Vector3.UP, rng.randf_range(0, TAU)) for horizontal movement, or add a random Y component for full 3D.

How do I make the object stop after a certain time?

Use a timer. In _ready(), start a Timer node with a random wait time, and in its timeout signal, set speed = 0 or queue_free(). Alternatively, use a countdown variable that decreases each frame.

Alternatives and Advanced Techniques

For more complex behavior, consider using a NavigationAgent2D to move toward random points on a navigation mesh, or a Path2D with random speed along the path. For wandering AI, a state machine (idle, move, pause) gives more natural movement. You can also use noise (like FastNoiseLite) to generate smooth random directions over time, which is great for fish or clouds.

Remember that random movement is just a starting point. Combine it with other behaviors (seeking, fleeing, following) to create rich, engaging gameplay.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →