0 votes

https://ibb.co/Fq07xpJ
-heres the image

Note: Tilemap comes in different shapes and sizes. Area2D can be positioned anywhere in the map

Godot version 3.2
in Engine by (19 points)
edited by

There is no image attached and we'll probably need more information than just an image.

Sorry. Fixed the image.

3 Answers

+1 vote
Best answer

Node structure:
Node Structure

Node2D Script:

extends Node2D

func _ready() -> void:
    $Area2D.connect("placed", $TileMap, "on_area2D_placed")

Area2D Script:

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.

by (3,285 points)
selected by

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()

Wow. Works like a charm. This is what i needed. Thank you very much

0 votes

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.

by (576 points)

Where do i add another collisionshape? on the tilemap?

0 votes

I Answered this live on youtube! https://www.youtube.com/watch?v=ZCcZ3-esGJ4

here is the github with the solution: https://github.com/ACB-prgm/QA_Session_01

by (576 points)
edited by
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.