Mathematically Identifying Tile Clicked

Started by
16 comments, last by Daerax 20 years, 4 months ago
greetings all =D

I thought i would step in and just pose the question ''why?''

why would you want to do it mathamaticly, when it''s just as easy and sufficantly fast *IMO* when looping through the tiles and checking that the pixel your mouse is over on a given tile image isnt transparent *color key comparison, or alpha byte/bit*

now i know everyone is screaming "GAH! what if i have a 1000x1000 sized tile map? what then mr. smarty pants?"

well i would first wonder if your actualy enumerating all those tiles each frame *and I know your not drawing them all*.

so the first step is before you render your map create a subset of the map, part of the map that is visible,

now that you have that array, your probably only looking at maybe 200 tils at most *depending on tile size and display res i know but lets be realistic=)*

so then you want to what tile your mouse is over, obviously this should be done once, in response to a mouse move or frame update

so you loop through the subset of the map, checking for tiles who''s rectangular boundries fall within the point, when you find one, do a hit-test on the tile''s image *which could mean a lock ,read and unlock on vram but who cares, 1 or two isnt gonna kill ya. when you find the tile that has a positive hit test *mouse is over a part of the image that is opaque, or atleast not fully transparent, then you save the tile''s pointer, you then use that pointer *which could be null* as reference to the tile structure, simple as that, no crazy math=D
and to make things even simpler=D, for each map in the game you could simple generate a boolean hit-test mask in the shape of the sized tile this particular map is using and read from that, totaly removing the lock,read,unlock overhead. so all there is to it is to loop through some tiles and do some point-in-rect tests, sounds simple and straight forward to me=)

ok now I know im gonna get flammed for providing a lo-tech solution, so just be gentle



Raymond Jacobs,

www.EDIGames.com

www.EtherealDarkness.com

Raymond Jacobs, Owner - Ethereal Darkness Interactive
www.EDIGames.com - EDIGamesCompany - @EDIGames

Advertisement
well.. I suppose that would work too, but I am thinking of implenting zoom in my tile editor also (not done yet though). That way I would be able to see a whole bunch of tiles at one time. But that matter can probably also be solved using color key somehow.. I don''t know

Why do it the easy way when there is a hard!
_____________________________
www.OddGames.comwww.OddGames.com/daniel
Raymond:

Are you suggesting that he perform a boolean hit-test on a tile mask for each tile currently displayed on the screen, using the current mouse location, and stopping once he gets a succesful hit?

I''m assuming this process is incorporated into the screen-drawing function? (It would be pointless to enumerate the tiles on the screen twice.)

For a standard Diablo-style isometric engine this wouldn''t be a good solution obviously (it''s far easier and quicker to simply convert directly from screen co-ord''s to tile co-ord''s), but it may solve ElectronicSandClock''s problem of calculating the selected tile I guess.

Not sure how you''d solve the related problem of tile-to-screen co-ordinates though (eg. "a ring is lying on the floor at tile co-ordinates [3.5, 7.25] - where should it be drawn on the screen?"). Note that the equations used for this process can be re-arranged to find which tile the mouse is over.

-hotstone
You would do a point-in-rect hit test on each visible tile

when one of those becomes true then you do a mask test =)


as for tile to screen coords

that sounds like drawing to me =)

im sure there is some kind of transform you could apply that would map the screen space directly into tile space and back

*rotating and scaling apropriatly*

but thats way over my head =D




Raymond Jacobs,

www.EDIGames.com

www.EtherealDarkness.com

Raymond Jacobs, Owner - Ethereal Darkness Interactive
www.EDIGames.com - EDIGamesCompany - @EDIGames

So, I started doodling a bit, and here''s what I came up with:

You''re on to something with dividing a tile into the four sections ABCD, but i came to realize that section B is the same as section C, and section D is the same as section A. Really there are only two cases, not four, of these "quarter tile" coordinates.

the width of these rectangular sections is the width of the tile over two, and the height is the height of the tile over two, so it is easy enough to determine which rectangular section a particular point is by division.

//which quartertile are we in?
quartertilecolumn=worldx/(tilewidth/2);
quartertilerow=worldy/(tileheight/2);

//we''ll need these later...
quartertilex=worldx%(tilewidth/2);
quartertiley=worldy%(tileheight/2);

the only difference in the two cases is the slant of the line, one is positive (\) and the other is negative(/), according to their slopes in the coordinate system of the screen. the manner in which you determine the slant of the particular rectangle the mouse resides is to find out if quartertilecolumn plus quartertilerow is an odd number.

within the rectangle, the line is drawn between two corners, either from (0,0) to (w,h) or from (0,h) to (w,0), where w is tilewidth/2 and h is tileheight/2.
based on the linear equations for these lines, we can determine whether a particular point is above, below, or on the line.

equation for (0,0)-(w,h)

(x2-x1)*(y-y1)=(y2-y1)*(x-x1)
(w-0)*(y-0)=(h-0)*(x-0)
w*y=h*x
-h*x+w*y=0

dropp off the =0, and you''ve got a linear equation. for any particular x and y, only 0 means "on the line", positive and negative are on opposite sides of the line, which makes for a really simple test.

say that w and h were both 4(making the tiles 8x8):
x->
y
|
v
0 1 2 3 4
0 0 - - - -
1 + 0 - - -
2 + + 0 - -
3 + + + 0 -
4 + + + + 0

the actual values do not matter, only the sign.

for the other slant (0,h)-(w,0):

(x2-x1)*(y-y1)=(y2-y1)*(x-x1)
(w-0)*(y-h)=(0-h)*(x-0)
w*(y-h)=-h*x
w*y-w*h=-h*x
h*x+w*y-w*h=0

and again drop the =0, and here''s the graph for w and h of 4 for all x and y values within

x->
y
|
v
0 1 2 3 4
0 - - - - 0
1 - - - 0 +
2 - - 0 + +
3 - 0 + + +
4 0 + + + +

how is this all useful? well, based on the quartertile row and column, you can determine which two tiles the mouse might be in, and you can determine the slope of the line bisecting it. with the equations, you can tell what side of the line you are on, which you can use to decide which tile of the two the mouse is in.

is this "better" than a mousemap? no. in fact, it is almost identical to a mousemap. really, you wouldn''t want to calculate with these equations each time the mouse moves. you''d want a lookup table, which would serve exactly the same purpose as a mousemap.

Get off my lawn!

I was certain that the method could be reduced to two sections as i noticed certain patterns, I did not however, persue it after I got the 4 section method working, as I was lazy/did not think it was worth it. Hoperfully I shall be able to pursue your method some day, TANSTAAFL.

Indeed this method is basically like a mousemap, without the mousemap, a mathematical representation of one...

Why would I not simply use a mousemap? I have always wondered how to do mouse to tile without a mousemap and thought it would be interesting. I was also lazy and did not want to do the steps required to get a mousemap up and ready. Also I only require this operation to be performed when there is a mouse down event and not during mouse move events, thus i do not require a look up table and use the method as is. Its quite fast used in that way (only in mousedown).


[edited by - Daerax on December 4, 2003 2:17:43 AM]
Raymond:
You said: "im sure there is some kind of transform you could apply that would map the screen space directly into tile space and back"

These transforms have already been given by myself and Useless Hacker in a different thread:

tx = ((sx - mapx) / tilewidth) - ((sy - mapy) / tileheight);
ty = ((sx - mapx) / tilewidth) + ((sy - mapy) / tileheight);

and:

sx = ((tilewidth / 2) * tx) - ((tilewidth / 2) * ty) + mapx;
sy = ((tileheight / 2) * tx) + ((tileheight / 2) * ty) + mapy;

where:
(sx,sy) - screenspace co-ordinates.
(tx,ty) - tilespace co-ordinates.
(mapx, mapy) - origin of tilespace in screenspace co-ordinates.
tilewidth, tileheight - dimensions of tile's bitmap (so to speak - you know what I mean I'm sure).

NB: I have tilespace x axis running down and to the right on the screen, and tilespace y axis running down and to the left.

-hotstone

[Edit: Initially referred to Raymond by his login accidentally.]

[edited by - hotstone on December 4, 2003 9:29:45 AM]
I made this today. This iso collision function is written in basic but obviously can be converted in any language. The input coordinates should be taken from zero up to the bounds. Rotation as negative. I have barely tested it but 64,32 tiles and 128,64 iso tiles work just fine. Maybe someone can improve the code to take work with any rotation and size.

Function RRectcollision(x,y,w,h,rot#)
a = ((((Cos(rot)*((x/2)-(w/2)/2.5) ) + Sin(rot) * (y-h/2.5))+(w/2))*1.4)-h
b = (((-Sin(rot) * ((x/2)-(w/2)/2.5) ) + Cos(rot) *(y-h/2.5)+h) * 1.4)-3
If a{ 0 Or a} w/2 Or b{h Or b}h+h Then Return True
Return False
End Function

collision = RRectcollision(32,32,64,32,-45)

Edit : replace { with < and } with >


[edited by - Rve on December 5, 2003 1:12:42 AM]

This topic is closed to new replies.

Advertisement