Click detection and transparency

I am looking to move a large project from Flex/ActionScript. Mouse detection was relatively straightforward on all of the objects. From the docs here, " gui_node will return true or false depending on if the specified coordinate is within the bounds of a gui node or not" but what is really needed is detection based on non-transparent textures (tilesource in my case) for the node(s). In fact if the node has children, it would be best if I make the call with the top level node and let the internal logic walk the relationships. I’ve searched everywhere for a nice, clean solution. Even the wonderful defold-input package doesn’t seem to cover “odd shapes”. Note: polygons won’t be enough for what I need. Consider an object such as a chair. Thanks for suggestions.

Ok, so you have images of irregular shaped objects and you need to detect input? How large are the individual images?

Furniture objects are 50x150 pixels in a tile of 16 parts (8 directions as well as a forward and backward component - eg a chair has a portion behind a player when sitting as well as in front). One use case is clicking on a player vs the chair they are sitting in. ( I use the term chair loosely, as it could be a roller coaster cart, bumper car, canoe, …)

I even tried using clipping but the pick_node still used the bounding box.

Yes, the gui nodes and picking is always rectangular.

If you put them in a tilesource and use the same image as collision source you’ll get something that is at least not rectangular in shape, but you’d have to use tilemaps to make use of the traced collision shapes.

@britzl Could he theoretically load the images separately and get the rgba value of a pixel from the buffer?

Of course the simple solution is to use game objects and build accurate collision out of however many shapes you need. This could be a lot of work depending on how picky you are and how many objects you have.

I’ve heard of plenty of programs that generate collision polygons from a texture’s alpha, but none that can deal with internal holes.

Yes, that could possibly work. Or render the scene to a render target with a special shader that assigns a color per clickable item and then do picking by checking the pixels in the render target somehow.

1 Like

The project has thousands of images so pre-building collisions is rather out of the question. Support for holes may not be critical. Can you give me references to these programs? Are any usable at runtime as an asset is changed?

Would anyone have a simple example of leveraging tilemaps? I tried to follow the documentation on masking, hoping it would “spill over” to the pick_node logic but no luck. (future enhancement?) All of the images are already in a format suitable to make tilesources.

All of this assumes I can load assets, ie images, from http calls to act as textures for the tilesources and tilemaps. Until I can get clicking to work, no sense trying the rest of the aspects to convert, and there is a lot!

I’ve got high hopes for Defold.

PhysicsEditor is the first thing that came to mind, and I’m pretty sure Unity has this built into their sprite importer or something. Defold’s physics only allows convex polygons with 16 vertices max though.

But if you want to do this dynamically at runtime, that changes things . . . You can’t dynamically generate collision shapes with Defold’s built-in physics. Load images from the web, sure, there’s an code snippet of doing that for GUI here. Getting that into a tilemap…I’m not so sure. IMO all the things you want to do are Defold’s weak points. You should expect to do a lot of work and experimentation yourself if you want to get this project to work.

Oh, so you’re downloading images on the fly?! That puts things in a different perspective. Your best bet is to load the image (using image.load), create a texture, assign it to a gui node but also keep the pixel data from image.load() and read from that when the user clicks. Start with gui.pick_node() and if that detects a click then go and sample pixel data to know for sure.

Since the image really needs to apply to a tilesource, I can’t directly assign it to the gui node’s texture. I couldn’t find how to alter the texture for a tilesource. I don’t seen an id or url for tilesources.

Also, is there any further info on the structure of the image buffer? I am guessing it is height * width * 4 (RGBA).

Why? Can you share an example image that you wish to use?

It returns the type: image.load()

Tilesource for directions the item faces as well as animations (player clothing).BanquetChair

Using the image buffer, the location of the click, the placement of the object and the current animation (in my case the direction the item is facing), it only took 2 lines of code to return whether the pixel under the mouse event is transparent or not. Now pick_node can return a real true or false. Now if the buffer could be obtained directly from the objects, this would make for a great enhancement to pick_node, perhaps add an optional boolean argument - consider transparency. This would handle ANY shaped object. No polygons to approximate the shape.

1 Like

Well, it’s a complete new feature, and while it is surely useful, it is not as simple as this flag might imply :slight_smile:

Or render the scene to a render target with a special shader that assigns a color per clickable item and then do picking by checking the pixels in the render target somehow.

And if we were to implement it, it would probably be something along the lines what @britzl suggested.
I don’t think it will be in our most immediate road map at least.

So how would I get access to a tilesource in order to load a new image into it? (or should I start a new thread for this topic?) The sprite already points to the proper image (tilesource in this case) and has selected the proper animation. The tilesource already has the proper dimensions and defined set of animations.
By using the image buffer, placement and click information, I can now detect whether the clicked spot is transparent or not. Making progress.

If you wish to change the entire texture used by a sprite you need to load the new one into a buffer object and set that. @sergey.lerg has an ImageLoader extension that loads an image from disk or string/bytes:

Use it like this:

local image_resource = imageloader.load{ data = data }
local image_resource = imageloader.load{ filename = "D:\0001.jpg" }
resource.set_texture(go.get('#spite', 'texture0'), image_resource.header, image_resource.buffer)

You’re always downloading the images from a server or? You could maybe also look into our LiveUpdate feature and load new assets (game objects, sprites etc) on the fly and use them as needed.

1 Like

Thanks so much for the info on the extension. I was hoping the following would handle my use case.
http.request(imgurl, “GET”, function(self, id, response)
img = image.load(response.response)
print(string.format(“img size %d by %d”, img.height, img.width))

		local image_resource = imageloader.load{data = img.buffer}
		pprint(image_resource)
		
		resource.set_texture(go.get('#car2Back', 'texture0'), 
		image_resource.header, 
		image_resource.buffer)

However I am getting a fatal error and notice the resource dimensions are both 0. That can’t be good!

DEBUG:SCRIPT: img size 1200 by 72
DEBUG:SCRIPT:
{ --[[000000000309D550]]
header = { --[[000000000309D5A0]]
num_mip_maps = 1,
width = 0,
channels = 0,
height = 0,
type = 3553
}
}
ERROR:SCRIPT: /car2/car2.script:110: Wrong type for table attribute ‘format’. Expected number, got nil
stack traceback:
[C]: in function ‘set_texture’

Also a minor nuisance, with the extension added as a dependency, I can no longer simply rebuild the project with an open running window and have it work properly. I must close the window with the active executable before rebuilding.

You shouldn’t do image.load() and the imageloader.load()

It should be enough to do imageloader.load({ data = response.response })

Thanks, the header looks better. I was using image.load since I need to keep the buffer for my transparency logic.
However, the extension does produce the same results. The first picture is the sprite using a tilesource loaded into the project. The same image file is loaded via http and processed by the extension.

before

after

code
– change texture
http.request(imgurl, “GET”, function(self, id, response)

					local image_resource = imageloader.load{data = response.response}
					pprint(image_resource)

					resource.set_texture(go.get('#car2Back', 'texture0'), 
					image_resource.header, 
					image_resource.buffer)