How to test whether a point lies inside a 2D Sprite or CollisionObject2D?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By chanon
:warning: Old Version Published before Godot 3 was released.

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

:bust_in_silhouette: Reply From: lukas

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

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

	poly_corners = vertices.size()
	poly_x.resize(poly_corners)
	poly_y.resize(poly_corners)
	constant.resize(poly_corners)
	multiple.resize(poly_corners)
	vertices_pos.resize(poly_corners)
	
	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
		else:
			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
	else:
		return true
:bust_in_silhouette: Reply From: chanon

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

trying to find way to do that in godot 3

Wender Alves Libório | 2018-12-11 21:17