+1 vote

Hi, I want to select a random tile, inside in a determinate radius.
I am working in a procedural generator of islands, it work verry well :), and to place randomly tiles like trees for example, i would like use diferents ways, one of this could be put a tree, take his cor and randomly place others trees near in a radius

I thought 2 ways to do it:
1- make an array with all near tiles in a radius after, select randomly in the array
2- select a random cor point inside in a radius after, place in the nearest tile

I don't know much about maths, less about cos() and sin(), how can I do one, both or others ways

English isn't my native language, I tried to do the better that I could :3

in Engine

let's say we have one point (a center), and a radius, for example 4 tiles aways from the center, how can I get the tiles that are in this imagin circle?, do you understand me?

I put together an example script with a tilemap, showing how to obtain grid positions within a radius, and also a few different methods to pick a random position within a circle. (note that if you have an array of positions you can also pick a random item from it directlty).

``````extends Node

var _radius_in_tiles = 4.0 # Note: decimal values work too, you may try
var _center_in_tiles = Vector2(5, 5) # Hardcoded, you may choose

var positions = get_tile_positions_in_circle()
for pos in positions:
_tilemap.set_cellv(pos, 0)

# Get random position around point. There are several ways to do it.
var tile_position = pick_random_position_v1()
# Remove a tile for testing
_tilemap.set_cellv(tile_position, -1)

# Or simply use the array computed earlier.
tile_position = positions[randi() % len(positions)]
_tilemap.set_cellv(tile_position, -1)

func get_tile_positions_in_circle():
# Get the rectangle bounding the circle
var min_pos = (_center_in_tiles - radius_vec).floor()
var max_pos = (_center_in_tiles + radius_vec).ceil()

# Convert to integer so we can use range for loop
var min_x = int(min_pos.x)
var max_x = int(max_pos.x)
var min_y = int(min_pos.y)
var max_y = int(max_pos.y)

var positions = []

# Gather all points that are within the radius
for y in range(min_y, max_y):
for x in range(min_x, max_x):
var tile_pos = Vector2(x, y)
positions.append(tile_pos)

return positions

func pick_random_position_v1():
# Trigonometry solution.
# It's fast and simple, but points have a higher chance to be near the center.
# In some cases this is acceptable.
var angle = rand_range(-PI, PI)
var direction = Vector2(cos(angle), sin(angle))
return _center_in_tiles + direction * _radius_in_tiles

func pick_random_position_v2():
# Improved trigonometric solution.
# Density of results will be the same at any distance from center.
# https://programming.guide/random-point-within-circle.html
var angle = rand_range(-PI, PI)
var direction = Vector2(cos(angle), sin(angle))
var distance = _radius_in_tiles * sqrt(rand_range(0.0, 1.0))
return (_center_in_tiles + direction * distance).floor()

func pick_random_position_v3():
# So-called "Monte-carlo" solution.
# Generate random points until it is inside the radius.
# Can be used in more complex shape scenarios as simple prototyping solution.
for attempt in 100:
var pos = _center_in_tiles + Vector2(
return pos.floor()
return _center_in_tiles.floor()
``````
by (29,510 points)
selected by

Thank you very much :3

I have one problem

``````# Gather all points that are within the radius
for y in range(min_y, max_y):
for x in range(min_x, max_x):
var tile_pos = Vector2(x, y)
positions.append(tile_pos)
``````

here at the end

``````if tile_pos.distance_to(_center_in_tiles) < _radius_in_tiles:
positions.append(tile_pos)
``````

shows an error that says: Invalid operands 'Vector2' and 'float' in operator '<'
I did somethig worng, or it's a mistake of you?

``````extends TileMap

var centro = Vector2()

var positions = []

var min_pos = (centro - rv).floor()
var max_pos = (centro + rv).ceil()

var min_X = int(min_pos.x)
var min_Y = int(min_pos.y)
var max_X = int(max_pos.x)
var max_Y = int(max_pos.y)

for y in range(min_Y, max_Y):
for x in range(min_X, max_X):
var tile_pos = Vector2(x, y)
positions.append(tile_pos)

for i in range(positions.size()):
set_cellv(positions[i], 0)
``````

`if tile_pos.direction_to(centro) < radio:`

You wrote `direction_to`, it must be `distance_to`.

ho sorry, very stupid mistake