• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM [SOLVED] Mouse Movement - Snapping to Grid

J

Jonathan E. Styles

Guest
I am sure this is a quick and easy one for you pros, but, alas - I am new! In my game I am adding a "click-n-drag" element, but it is absolutely critical that it remains snapped to an imaginary isometric grid.

I have not programmatically created this grid, I have just placed my tiles in the room maker tool and added code to my objects. My current Draw Event for my "click-n-drag" object looks like this:

Code:
draw_sprite_ext(spr_Tree1_Alpha,0,mouse_x - global.offset_x,mouse_y - global.offset_y,1,1,0,c_white,.4);
if !place_snapped(16,16)
 {
 move_snap(16,16);
 }
However, the sprite drags smoothly across the screen along with the mouse. Ideally I would like the "drag sprite" to move around on the screen in a grid-like pattern and allow the mouse to freely move with the sprite trailing behind in its methodical pattern.

Here is an image of what I am working with currently:

ClickNDrag001.png

The idea is to show the user/player exactly where the item is going to be placed before they click, but it must be aligned to the "grid" to work properly. Any ideas on how I can accomplish this?

In advance, thanks for looking at this and thank you for taking any time to assist.
 

Carolusclen

Member
another way, if your whole level is on a grid that has no offsets is to have it like

Code:
draw_sprite(my_sprite,-1,floor(mouse_x/16)*16,floor(mouse_x/16)*16)
floor(mouse_x/16)*16 what this does is

floor rounds the value down to a whole number
so basically you are dividing the mouse position by 16 which will give you a decimal number of for example 10.5
floor takes the .5 off the end
then multiply by 16 again which is your grid size. this will snap the object to follow the mouse but snap to a grid :)

i hope i got that right, i am not at home but i was working on this for a while with similar issues the other day and have my work at home
 
J

Jonathan E. Styles

Guest
I know how to fix this Jonathan!
Put this in the end step event of your mouse object.

Code:
if !place_snapped(16,16)
 {
 move_snap(16,16);
 }
And put put this in the draw event.

Code:
draw_sprite_ext(spr_Tree1_Alpha,0,mouse_x - global.offset_x,mouse_y - global.offset_y,1,1,0,c_white,.4);
@JellyJake112 thanks for the quick reply! I did exactly what you said. However, it results in the same exact situation I have now. The object sprite is moving perfectly consistent with the mouse.

another way, if your whole level is on a grid that has no offsets is to have it like

Code:
draw_sprite(my_sprite,-1,floor(mouse_x/16)*16,floor(mouse_x/16)*16)
floor(mouse_x/16)*16 what this does is

floor rounds the value down to a whole number
so basically you are dividing the mouse position by 16 which will give you a decimal number of for example 10.5
floor takes the .5 off the end
then multiply by 16 again which is your grid size. this will snap the object to follow the mouse but snap to a grid :)

i hope i got that right, i am not at home but i was working on this for a while with similar issues the other day and have my work at home
@Carolusclen I think we are headed the right direction. However, 16x16 is simply the square grid size I used in the room editor to get everything to align when I was pasting objects in. Realistically the tiles are 32h x 64w isometrically. I think if I keep playing with the numbers I may get it right if I can wrap my head around how the math is calculating it.

At the moment, using your example, the object moves, but it can overlap the other tiles since they are wider than 16x16. I adjusted some of the code, but now it skips multiple tile positions. I will keep hammering away, but if you know the numbers needed to compensate before I get there, let me know!

The offsets are not necessary. I created those variables to get the exact mouse position when it clicked the "base" sprite. This way the sprite remained in that same spot on the mouse when it was dragging. (May be my issue to begin with) Getting rid of them is no big deal if the result is what I needed (and it seems thusly.)
 

Carolusclen

Member
mmm it shouldnt be an issue so keep the offsets.

in my code, replacing the 16x16 with 32x64 will work, however, i looked at the image but it didnt click that its isometric, my code is just topdown grid.
when i get home, I will fiddle and let you know :)
 

TheouAegis

Member
Draw the place marker with its coordinates nap for your group. When the player let's go with the mouse comma create whatever object at the coordinates snaps to a great. The object doesn't have to move snap to grid every step, you just have to display it snapped to the grid.
 
J

Jonathan E. Styles

Guest
Yeah, I tried changing the mouse_x math to 64 and the mouse_y math to 32, however it is gapping because it is creating squares. So, if my squares are the black ones, this tilling looks like:
diamond-pattern-clipart-1.png
This doesn't quite work for what I am trying to achieve. I am sure it is just an extra math value. Will keep hammering away. Thanks!
 

Carolusclen

Member
if its leaving gaps, try halve the numbers, but yeah. if i come up with something ill be sure to post here :)
i will remember because i have a project coming up that will require exactly that :(
 
J

Jonathan E. Styles

Guest
So, I have it working (temporarily), but I am rather unhappy with the movement. I'm detecting multiples of 32 on the y axis and offsetting the tile. However, this makes the sprite trail rather far from the mouse. Not sure if there is a way I can compensate for this.

Code:
offset_x = 0;
offset_y = 0;
if (mouse_y % 32) > 16 && (mouse_y % 32) < 31 { offset_x = 32; offset_y = 16;}
draw_sprite_ext(spr_Tree1_Alpha,0,floor(mouse_x/64)*64 + offset_x,floor(mouse_y/32)*32 + offset_y,1,1,0,c_white,.4);
Any thoughts? I may just have to settle for this during alpha phases.
 

TheouAegis

Member
If I'm understanding things correctly, then you want an isometric grid snap. As long as sprites origins are centered in the middle of the diamonds and mouse_x and mouse_y are greater than 0, then you can use

x = (mouse_x & ~31) + ((mouse_y >> 4 & 1) << 5);
y = (mouse_y & ~15) + ((mouse_x >> 5 & 1) << 4);

This is with no other restrictions (other than a positive mouse_x and mouse_y), so half the sprite would draw outside the room when on the edges. You'd need to instate some other preventative conditions to keep that from happening.

The formula above in slower-but-more-flexible terms:

var tile_half_width = sprite_width/2;
var tile_half_height= sprite_height/2;
var xx = mouse_x div tile_half_width;
var yy = mouse_y div tile_half_height;
x = (xx + (yy & 1)) * tile_half_width;
y = (yy + (xx & 1)) * tile_half_height;

I haven't figured out a working 2-line formula for negative coordinates yet and am starting to think I never will, so restrict your game to positive x and y coordinates.
 
Last edited:
J

Jonathan E. Styles

Guest
If I'm understanding things correctly, then you want an isometric grid snap. As long as sprites origins are centered in the middle of the diamonds and mouse_x and mouse_y are greater than 0, then you can use

x = (mouse_x & ~31) + ((mouse_y >> 4 & 1) << 5);
y = (mouse_y & ~15) + ((mouse_x >> 5 & 1) << 4);

This is with no other restrictions (other than a positive mouse_x and mouse_y), so half the sprite would draw outside the room when on the edges. You'd need to instate some other preventative conditions to keep that from happening.

The formula above in slower-but-more-flexible terms:

var tile_half_width = sprite_width/2;
var tile_half_height= sprite_height/2;
var xx = mouse_x div tile_half_width;
var yy = mouse_y div tile_half_height;
x = (xx * tile_half_width) + ((yy & 1) * tile_half_width);
y = (yy * tile_half_height) + ((xx & 1) * tile_half_height);

I haven't figured out a working 2-line formula for negative coordinates yet and am starting to think I never will, so restrict your game to positive x and y coordinates.
@TheouAegis , as always, thank you! Your code works brilliantly. I couldn't have come up with this myself -- I don't know what half of these operators are! If you don't mind, could you provide an elaborated breakdown explaining the portions of code? I, of course, understand that we are defining the x and y coordinates that draw the object/sprite, and I know what the mouse_x and mouse_y coordinates are. However, I do not understand what the & operator is doing, nor do I know how ~ affects the numbers in the function. Also, I've never seen the greater than and less than signs doubled like that (except when applying output in a CLI script!), what is it doing? Sorry for all of the questions, but I'll never learn if I don't understand the base principles of what is happening. :p

I appreciate everyone's help on this issue, and I think it is just about solved in this particular instance. Until the next issue pops up, see you then!
 

TheouAegis

Member
The second code was the same as the first code but without all the bitwise operators (except & 1, because I hate using mod 2 for everything). Although, I just now optimized it, so it doesn't have the same structure now.

If you work with sizes that are powers of 2 (ex. 4,8,16,32,64) then you can use the first code with minor modifications.

Code:
x = (mouse_x & ~31) + ((mouse_y >> 4 & 1) << 5);
          A                    B     C      D
y = (mouse_y & ~15) + ((mouse_x >> 5 & 1) << 4);
          E                    F     G      H
A) Snap the X coordinate to a grid based on half the width of the sprites/tiles. In your case, the width is 64, so this snaps it to a 32 wide grid.
B) Find out which cell in a grid based on half the height of the sprites/tiles the Y coordinate falls. In your case, the height is 32, so this checks a cell on a 16 high grid.
C) Check if the Y coordinate falls in an odd-number row or even-number row.
D) Scale the result of C to the grid and add it to the snapped X coordinate; a shift of 5 is the same as multiplying by 32.
E) Same as A, but this time you're snapping the Y coordinate to the grid.
F) Same as B, but this time you're finding which cell in the grid the X coordinate falls in.
G) Same as C, but this time you're checking if the X coordinate is in an odd-number column or even-number column.
H) Same as D, but this time you're scaling the result of G to the grid; a shift of 4 is the same as multiplying by 16.
 

dazza_bo

Member
If I'm understanding things correctly, then you want an isometric grid snap. As long as sprites origins are centered in the middle of the diamonds and mouse_x and mouse_y are greater than 0, then you can use

x = (mouse_x & ~31) + ((mouse_y >> 4 & 1) << 5);
y = (mouse_y & ~15) + ((mouse_x >> 5 & 1) << 4);

This is with no other restrictions (other than a positive mouse_x and mouse_y), so half the sprite would draw outside the room when on the edges. You'd need to instate some other preventative conditions to keep that from happening.

The formula above in slower-but-more-flexible terms:

var tile_half_width = sprite_width/2;
var tile_half_height= sprite_height/2;
var xx = mouse_x div tile_half_width;
var yy = mouse_y div tile_half_height;
x = (xx + (yy & 1)) * tile_half_width;
y = (yy + (xx & 1)) * tile_half_height;

I haven't figured out a working 2-line formula for negative coordinates yet and am starting to think I never will, so restrict your game to positive x and y coordinates.
I realise this thread is now almost a year old but I'm trying to get isometric snapping to work but just can't figure it out. I tried TheouAegis's code above but it doesn't snap right to my grid and I assume the reason is "As long as sprites origins are centered in the middle of the diamonds" whereas my sprites are centered at the top of the diamonds:


My code still uses 64w x 32h for the tile dimensions though. Any ideas on what I'd need to change?
 
Last edited:
Top