DEV Community

loading...
Cover image for Link's Movement in The Legend of Zelda (Snapping to the Grid)

Link's Movement in The Legend of Zelda (Snapping to the Grid)

robotspacefish profile image Jess Updated on ・3 min read

I recently spoke with someone about the original Legend of Zelda (my all-time fav game) and he mentioned how the developers kept Link's movement within a grid. I never thought about it before and I thought it was really cool. It's sort of subtle but you can see here how Link seems to snap into the grid when the player turns:


Link movement snapping to grid as the player turns

I wanted to know more so I googled a bit and came across a blog post called Movement Mechanics by Troy Gilbert in which he explains what's going on. To sum it up, the sprites that make up Link and the background tiles are 16x16 but Link moves along a half-tile grid that is 8x8. The player can move Link up, down, left, or right one pixel at a time and the game will always keep Link aligned to the 8x8 grid so he can walk between obstacles and through doorways without getting caught on the edges.

I also came across a YouTube video Making Zelda in Game Maker: E015 - Aligning Player to Grid by Code Workshop. I don't use Game Maker Studio but I followed what the instructor explained and adapted it for my needs so I could see how this technique works.

Code Workshop explains that when you move in a direction Link aligns to the grid on the other axis. So if you move left or right along the X-axis, Link aligns to the Y-axis and if you move up or down along the Y-Axis he aligns to the X-axis.

Since I created my demo in Pico-8 I'm going to show my re-creation of Code Workshop's align_to_grid function using Lua.

function align(val, alignTo)
  local remainder = val % alignTo
  local halfway = alignTo/2

  if (remainder > halfway) then
    return alignTo - remainder
  else
    return -remainder
  end
end
Enter fullscreen mode Exit fullscreen mode

The align function takes 2 parameters: val which is the player's current x or y location (I'll explain more in a bit), and alignTo, which is the amount of pixels you want your player to align to. Since Link aligns to an 8x8 grid, 8 will get passed in for alignTo.

remainder is what's left from the x or y location divided by the alignTo value.

If the remainder is larger than the halfway point of the pixels you want to align to, then return alignTo - remainder, otherwise return -remainder.

The value returned from this function will be added to either the player's x or y value.

For example, if the player presses left they want to move along the negative X-axis (vx is multiplied by the player's speed and added to the x value further down in the function), which means they might need to be realigned along the Y-axis. In this instance, the value returned from align() will be added to the player's y value.

if (btn(⬅️)) then
    vx = -1
    y+=align(y, 8)
end
Enter fullscreen mode Exit fullscreen mode

The way I did this differs a bit from how Code Workshop did it so if you are interested you should definitely check out his video.

Here is a gif of my version in Pico-8 to show the player's 16x16 sprite snapping along an 8x8 grid.


Link movement snapping to grid as the player turns

A few months ago I entered a game jam and made a game (featuring that robot buddy in the gif above) where the player moves by pixels within a 16x16 grid but the movement was...well, bad. My character stuck to the corners every time I turned because the size of the player was just about equal to the space the sprite had to move and I wasn't sure at the time how to fix it. I can't wait to go back and update the movement logic with this technique!

References

Discussion (3)

pic
Editor guide
Collapse
darkwiiplayer profile image
DarkWiiPlayer
function align(val, alignTo)
  local remainder = val % alignTo
  local halfway = alignTo/2

  if (remainder > halfway) then
    return alignTo - remainder
  else
    return -remainder
  end
end
Enter fullscreen mode Exit fullscreen mode

This code can be simplified a bit:

  • divide the value by the grid size
  • add one half to the value
  • get floor of the value
  • multiply by the grid size again
  • subtract the value
function align(val, grid)
   return math.floor(val / grid+0.5) * grid - value
end
Enter fullscreen mode Exit fullscreen mode
Collapse
darkwiiplayer profile image
DarkWiiPlayer

dev.to (or rather, whatever it sues for HL) can do syntax highlighting for Lua if you start your code blocks with <3 backticks>lua :D

local function example()
   print("See? This code gets colours!")
end
Enter fullscreen mode Exit fullscreen mode
Collapse
robotspacefish profile image
Jess Author

awesome, thanks for the tip!