How to optimize a get_mask() function?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Diet Estus

In order to detect sprite overlap between my player and enemies, I am trying to create a mask of all non-transparent pixels of my sprite.

I have been doing it with the following method, which returns an array of coordinates of non-transparent pixels.

func get_mask():

    # mask is initially an empty array
    var mask = []

    # get image data
    var image = sprite.texture.get_data()

    # lock the image so get_pixel() can be called on it
    image.lock()

	# add non-transparent pixel coordinates to mask
    for x in image.get_width():
	    for y in image.get_height():
		    if image.get_pixel(x,y)[3] != 0:
			    mask.append(global_position + Vector2(x,y))

    return mask

This works and allows me to do pixel-perfect collision detection between my player and enemies. But it is not efficient. Even though I only call it when my player intersects the hit boxes of my enemies, it is still very inefficient and causes lag.

So, my question: How can I optimize my get_mask() function?

I am open to radically changing the function, so long as the result is a mask of non-transparent pixels that I can use to check for overlap between two sprites.

I would love to be able to generate the mask on the gpu using Godot’s shader language, but there’s no way to get a shader to output data in a way that can be used in non-shader scripts.

:bust_in_silhouette: Reply From: hilfazer

You could calculate mask only when texture changes.

var mask

func get_mask():
    assert(mask != null)
    return mask


func calculate_mask():
    # your current get_mask()'s code goes here

Call mask = calculate_mask() when necessary. Have a look at texture_changed() and frame_changed() signals of Sprite. These may not be enough depending on what you do with your Sprites.

Don’t be expecting much performance from pixel perfect collision detection. It’s just… not fast.

Thanks for the response, and good idea! The reason I’m so interested in pixel-perfect collision detection is because I used to work in Pygame, a Python module for game dev, which had a built-in mask collision detection which was very speedy. I’ve always wanted this in Godot. I may have to look into the backend to see how Pygame does it so well.

Diet Estus | 2018-06-09 15:06