|
|
|
 |
Reply From: |
Zylann |
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
onready var _tilemap = $TileMap
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
func _ready():
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 radius_vec = Vector2(_radius_in_tiles, _radius_in_tiles)
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)
if tile_pos.distance_to(_center_in_tiles) < _radius_in_tiles:
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(
rand_range(-_radius_in_tiles, _radius_in_tiles),
rand_range(-_radius_in_tiles, _radius_in_tiles))
if pos.distance_to(_center_in_tiles) > _radius_in_tiles:
return pos.floor()
return _center_in_tiles.floor()
Thank you very much :3
Gagust | 2020-03-19 22:54
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)
if tile_pos.distance_to(_center_in_tiles) < _radius_in_tiles:
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 radio = 4.0
var centro = Vector2()
var positions = []
func _ready():
var rv = Vector2(radio, radio)
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)
if tile_pos.direction_to(centro) < radio:
positions.append(tile_pos)
for i in range(positions.size()):
set_cellv(positions[i], 0)
Gagust | 2020-03-20 00:31
if tile_pos.direction_to(centro) < radio:
You wrote direction_to
, it must be distance_to
.
Zylann | 2020-03-20 00:39
ho sorry, very stupid mistake
Gagust | 2020-03-20 02:09