Move CharacterBody2D randomly till it collides

Godot Version

4.3

Question


Hello, I have this fish scene.
Here is the pond scene:

I have made working code for the fish to spawn in the pond, with random colors, etc. For the most part it is what I want, but I still have two questions:

  1. How can I pick a random location inside the Water Boundary to spawn the fish in? Right now they all spawn in the center by default. If this is not possible or is really hard it’s fine, but asking anyway.

  2. Once spawned, I want the fish to randomly move into a direction until they hit one of the boundaries. When they do, they randomly move in a different direction again. This continues on until they get hooked or die after a certain time. I have already done the hooked/death mechanic just need them to move around but I am unsure how.

The fish do not collide with each other so that does not have to be considered.
Thanks.

You could pick a random location by using randf_range for the x and y axis, you can use the ruler tool to find your pond’s bounds and set the range accordingly.

var random_x: float = randf_range(600, 1250) # tweak these numbers
var random_y: float = randf_range(600, 1250)
var new_fish_position := Vector2(random_x, random_y)

Pick a direction by selecting a random angle and using Vector2.from_angle

var random_angle: float = randf_range(0, TAU)
var direction := Vector2.from_angle(random_angle)

As your fish are CharacterBody2Ds you can detect when they collide by using move_and_collide, though your walls willl need to be StaticBody2D; then it’s probably best to bounce off the wall, or at least ensure that the next angle is facing away from the wall.

var collision := move_and_collide(direction * SPEED)
if collision:
    # pick a new random direction
1 Like

A few edits:
I made the Fish scene a Node2D:


The fish’s CharacterBody2D is in layer 1 with mask 2, while water boundaries are all in layer 2 with a mask of 1.

How would I make it continue moving in that direction until collide, THEN redirect?
This is my current code and it picks a new direction every frame:

func _physics_process(delta: float) -> void:
	if is_fishable:
		var random_angle: float = randf_range(0, TAU);
		var direction = Vector2.from_angle(random_angle);
		var collision = body.move_and_collide(direction);

		if collision:
			if collision.get_collider().is_in_group("pond_edge"):
				print("Collided")
				var normal = collision.get_normal()
				body.velocity = body.velocity.bounce(-normal) * 0.8;

You are making a new random_angle and direction variable every frame, since it’s within the func _physics_process, if you define the variable outside of the function, and assign it a starting value under func _ready then it will stay in one direction until you assign it a new direction.

Thank you, it worked. But now I have one final issue, the fish never detects colliding with the wall. It just passes through it as if nothing is there.



The layers/mask seem fine though.

For the player to collide with the walls the walls will need to be a StaticBody2D instead of a Area2D. Area2D only detects objects, it doesn’t stop them.

Thanks man it worked

Sorry, one last question, any way to make the Fish face the direction it is going?

probably set the rotation = random_angle, assuming your fish sprite is right-facing

1 Like