+2 votes

Given a Vector2, how can I programmatically test whether it is inside
- A 2D Sprite with a texture, ignoring the alpha of the current frame's texture
- A CollisionObject2D such as an Area2D

I can't see any simple contains_point method for both

in Engine by (254 points)

2 Answers

+2 votes
Best answer

The way I finally used was to use
Physics2DDirectSpaceState's intersect_point method and look at the results.

var space = get_world_2d().get_direct_space_state()
var results = space.intersect_point( thePoint, 32, [], 2147483647, Physics2DDirectSpaceState.TYPE_MASK_AREA )
# then see if the CollisionObject2D is inside the results
by (254 points)

trying to find way to do that in godot 3

+1 vote

To what I know there is no direct way how to do it.

Sprite solution can be to add Polygon2D as child node of a sprite and mark with it non-alpha part of your sprite. Then you can attach the following script toPolygon2D node. Then you can test whether a point (Vector2D) lies inside the polygon with has_point(point) function. It uses the Ray casting algorithm.

Solution for CollisionObject2D is similar. Instead of adding Polygon2D you can use coordinates of collision object polygon.

extends Polygon2D

var poly_corners  =  0 # how many corners the polygon has (no repeats)
var poly_x = [] # horizontal coordinates of corners
var poly_y = [] # vertical coordinates of corners
var constant = [] # storage for precalculated constants (same size as poly_x)
var multiple = [] # storage for precalculated multipliers (same size as poly_x)
var vertices_pos = [] # storage for global coordinates of polygon's vertices

func _ready():

func inic():
    var poly_pos = get_global_pos() # global position of polygon
    var vertices = get_polygon() # local coordiantes of vertices

    poly_corners = vertices.size()

    for i in range(poly_corners): 
        vertices_pos[i] = poly_pos + vertices[i]
        poly_x[i] = vertices_pos[i].x
        poly_y[i] = vertices_pos[i].y

    precalc_values_has_point(poly_x, poly_y)

func precalc_values_has_point(poly_x, poly_y): # precalculation of constant and multiple
    var j = poly_corners - 1
    for i in range(poly_corners): 
        if poly_y[j] == poly_x[i]:
            constant[i] = poly_y[i]
            multiple[i] = 0.0
            constant[i] = poly_x[i] - poly_y[i] * poly_x[j] / (poly_y[j] - poly_y[i]) + poly_y[i] * poly_x[i] / (poly_y[j] - poly_y[i])
            multiple[i] = (poly_x[j] - poly_x[i]) / (poly_y[j] - poly_y[i])
        j = i

func has_point(point):
    var x = point.x
    var y = point.y
    var j = poly_corners - 1
    var odd_nodes = 0
    for i in range(poly_corners): 
        if (poly_y[i] < y and poly_y[j] >= y) or (poly_y[j] < y and poly_y[i] >= y):
            if y * multiple[i] + constant[i] < x:
                odd_nodes += 1
        j = i

    if odd_nodes % 2 == 0:
        return false
        return true
by (675 points)
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.