Get dimensions of a Hotspot?

Started by fernewelten, Wed 05/07/2017 01:38:49

Previous topic - Next topic

fernewelten

Hello,

Is there a way to get the dimensions of a hotspot(!) by code?
What I'm after is the bounding box that contains the hotspot.

Why: My Ego has got a smartphone and loves to take a snapshot with it of the room part that the player clicks on.
Ego can already do this with characters and objects. He wants to shoot hotspots, too.

The way I found for characters is a bit convoluted, but feasible:
Code: ags

Character *chr = Character.GetAtScreenXY(mouse_x, mouse_y);
ViewFrame* frame = Game.GetViewFrame(chr.View, chr.Loop, chr.Frame);
int width = Game.SpriteWidth[frame.Graphic] * GetScalingAt(mouse_x, mouse_y) / 100;
int height = Game.SpriteHeight[frame.Graphic] * GetScalingAt(mouse_x, mouse_y) / 100;
int x = chr.x - width / 2;
int y = chr.y - height;


The way I am using for objects is:
Code: ags

Object *obj = Object.GetAtScreenXY(mouse_x, mouse_y);
int width = Game.SpriteWidth[obj.Graphic];
int height = Game.SpriteHeight[object.Graphic];
int x = obj.X;
int y = obj.Y-height;


I haven't found a way to do this with hotspots.

Ego photographs about 20% more than the bounding box, so if my calculations are off by a little, that won't matter.

Gilbert

#1
Because unlike individual characters and objects, all the hotspots in a room are actually painted onto the same image (of the same size as the room), they are not separated entities, and every single pixel of the room could belong to either hotspot, so the shape of each hotspot area can be anything, and that they can be anywhere, making what "bounding box" means here a bit sketchy (like say, you may paint several pixels on the upper left corner of the room as hotspot 1 and other several pixels on the bottom right corner also as hotspot 1, then the "bounding box" containing hotspot 1 will be the whole room's dimension).
Though of course, you may just paint "well behaved" closed shapes as individual hotspots and "bounding box" may be useful here.

To find the dimension of the "bounding box" of a hotspot, one possible workaround is to iterate through all the pixels in a room(say by use of a loop that iterates as x from 0 to width of room - 1, and another inner loop that iterates as y from 0 to height of room - 1), using Hotspoy.GetAtScreenXY(x, y) to check whether the pixel belongs to a certain hotspot, eventually finding the minimum and maximum possible coordinates of x and y that contain a pixel belonging to that hotspot, and then compute the dimension of the "bounding box" from there. I'm not sure how well this method may perform though, and could be slow if the room is very large (there could be optimisations such as skipping some pixels that already are considered within the "box" during the loops, but let's not complicate thing for the time being).

Khris

I've used this method in a ShowHotspots module, but I only check every third pixel. That's usually sufficient, and is easily fast enough not to noticeably block the game.
Once can optimize this further by using the mouse coordinates to determine where to start "scanning" (click on right half of the screen -> scanning starts from the right edge). Plus one can stop scanning if the hotspot has already been found and the current line is blank.

(All this assumes that hotspots are basically blob shaped though and never thinner than 3 pixels).

fernewelten

Thanks for the fast reply! So I don't get ready-made dimensions, but I do get the two-timensional array exposed with the hotspot information per pixel. Great! That's something tangible to work with.

So I've googled around a bit and found an algorithm called "Theo Pavlidis' Algorithm" (descriptive, visual layman's explanation) for crawling around the outline of a pixel blob. That's similar to my problem: I'm considering the points of my hotspot as equivalent to "black" pixels and the rest of my screen as the "white" pixels.

I know a middle point of my black pixels (hotspot), because the player has clicked the mouse at it. So I travel from there in a straight line to the left until I find the first point that has a neighbour that is NOT in the hotspot. This gets me my first outline point. From there, I travel along the outline using "Theo Pavlidis' Algorithm", always keeping track of the lowest and highest X and Y value I encounter. When I'm back at the starting point, I know my bounding box values.

This seems to be efficient enough for my purpose, but there's a snag: If the hotspot happens to be donut shaped or similar, I must take care not to follow the inner boundary (the one of the hole) instead of the outer one. So I've modified the algorithm with a check that happens whenever I encounter a new minimum x value.  In these cases and only in these cases, I check whether the adjacent neighbour to the left is in my hotspot, too. If it is, I assume that I've hit an inner boundary by chance - so I travel from there again in a straight line to the left until I hit a new boundary and re-start the algorithm from that point.

I've implemented this idea in around 200 lines of code. It's working, but I haven't pounded on the code very hard, so it might still contain bugs.

In the meantime, Ego is running around the room happily taking snapshots of patches of grass and of his crashed car and of the rocky ledge that imprisons him. ;-D

Cassiebsg

Is there a reason why you don't just take a square/rectangle image out of the BG?
As in the player clicks on x,y spot to take a pic, and it generates a image that is lets's say 30 pixels to the left + 30 pixels to the right and 30 pixel up and 30 pixels down of where the player clicked, producing a 61x61 pixel image for the photo?
Kind of sounds easier and more logical.

Still, looking forward to try that camera in a future game. (nod)
There are those who believe that life here began out there...

Khris

The only downside I can think of is the fact that contour tracing requires all hotspots to be contiguous, while the grid approach doesn't.
Plus, the grid check requires more like 20 lines of code ;)

dayowlron

Since the hotspots are set up initially when programming can you have an array that contains the bounds of the hotspots and initialize it during Room_Load event?
If you do run any of these approaches I would put it in the Room Load event and just save the data in arrays.
Pro is the opposite of Con                       Kids of today are so much different
This fact can clearly be seen,                  Don't you know?
If progress means to move forward         Just ask them where they are from
Then what does congress mean?             And they tell you where you can go.  --Nipsey Russell

monkey0506

Assuming it's a state-saving room (room number < 300), then you should use the room_FirstLoad event instead, because the dimensions/locations of the hotspots are immutable at runtime and the array(s) in your room script will be saved when the room is changed. That makes it only load the data once instead of every time the player enters the room.

fernewelten

Quote from: Cassiebsg on Thu 06/07/2017 18:53:33
Is there a reason why you don't just take a square/rectangle image out of the BG?

It's an artistic decision. ;) Ego's smartphone has a zoom objective, but the length and width of its shots are fixed; so whenever he shoots something tiny, such as a patch of grass, it gets absurdly blown up and very grainy, whereas anything large gets scaled down. If that looks daft, well, it turns out that Ego isn't such a great photographer, anyway: somehow all his photos have a (randomly varying) slight tilt, or are underexposed or overexposed, or have a tint, or all of that at the same time.

And yes, all those bumbling shots get names such as "IMG_1283.bmp", similar to the names of real camera shots, and are solemly stored on disk, whilst Ego exclaims "Ah, THIS is really going to be my MASTER shot" and things like that.

Danvzare

One idea, is that you could give each hotspot two (or four) properties that mark the top left and bottom right coordinates for a "bounding box" to be created from. Of course that will require you to put in some extra information for every hotspot, but that should be the quickest and easiest option if you ask me.
The concept is that you basically just have to see if you're within those coordinates you already pre-set for the hotspot.

SMF spam blocked by CleanTalk