Add a collision shape to a scene and create the area you want to spawn the objects in.
Then in a script:
onready var spawnArea = $CollisionShape2D.shape.extents
onready var origin = $CollisionShape2D.global_position - spawnArea
func gen_random_pos():
var x = rand_range(origin.x, spawnArea.x)
var y = rand_range(origin.y, spawnArea.y)
return Vector2(x, y)
this will generate a random point within an area. If you don’t know how to spawn/instantiate objects, I recommend going through HeartBeast’s series on youtube.
Where do i add another collisionshape? on the tilemap?
extends Area2D
signal placed(object)
onready var collision_shape = $CollisionShape2D
func _input(event: InputEvent) -> void:
if event.is_action_released("ui_accept"):
emit_signal("placed", self)
TileMap Script:
extends TileMap
func on_area2D_placed(obj:Area2D):
var radius_squared = pow(obj.collision_shape.shape.radius, 2)
var area_position = obj.position
var tiles_colliding = []
for tile in get_used_cells():
if map_to_world(tile).distance_squared_to(area_position) <= radius_squared:
tiles_colliding.append(tile)
print(tiles_colliding)
The Node2D just connects the signal between the two. The Area2D provides access to its collision shape (for calculations) and emits the placed signal when you press enter, but this can be emitted at any time. The tile map iterates over all of its tiles and determines whether each tile is within the circle, if so it adds that tile to the tiles_colliding array.
Tile tiles_colliding array contains the map positions of all the tiles within your Area2D’s circle. It is not as precise as the colored area you drew but it’s the best you’re going to get without doing annoying geometry to find the secant line between your tile map and circle and creating a polygon based on that. Using the tiles_colliding array you can generate some kind of invisible object spawner object and place it in that tile.
Here was my attempt at making an actual polygon because I thought it would be fun… it works… somewhat…
extends Area2D
var ray_cast
onready var collision_shape = $CollisionShape2D
func _ready() -> void:
collision_shape.position = Vector2.ZERO
ray_cast = RayCast2D.new()
self.add_child(ray_cast)
ray_cast.position = Vector2.ZERO
ray_cast.enabled = true
ray_cast.collide_with_areas = true
func get_shape_points() -> Array:
var col_shape_radius = collision_shape.shape.radius
var points = []
# contains direction vectors to connect initial point to last touching point
var vectors_for_points = []
var counter = 1
var last_collided_point = null
for degree in range(359):
var direction_vector = Vector2.RIGHT.rotated(deg2rad(degree)).normalized()
ray_cast.set_cast_to(direction_vector * col_shape_radius)
ray_cast.force_raycast_update()
if ray_cast.is_colliding():
var collision_point = ray_cast.get_collision_point()
last_collided_point = collision_point
# first collision? that's our starting point
if points.empty():
points.append(collision_point)
# otherwise only grab every 5th point
elif counter % 5 == 0:
vectors_for_points.append(direction_vector)
counter += 1
else:
counter += 1
else:
# if we just stopped colliding, append our last collision point
if last_collided_point:
points.append(last_collided_point)
last_collided_point = null
# if we had at least one collision, interpolate the vectors to points for a polygon
if not points.empty():
var last_point = points.pop_back()
for direction_vector in vectors_for_points:
points.append(self.position + (direction_vector * col_shape_radius))
points.append(last_point)
return points
func _input(event: InputEvent) -> void:
if event.is_action_released("mb_left"):
var shape = Polygon2D.new()
shape.set_polygon(PoolVector2Array(get_shape_points()))
get_parent().add_child(shape)
shape.color = Color.red
elif event.is_action_released("mb_right"):
self.position = get_global_mouse_position()
timothybrentwood | 2021-05-04 23:51
Wow. Works like a charm. This is what i needed. Thank you very much