How-To: Code Wrap-Around Grid Coordinates for a 2.5D Game in Swift

This is part of a free course that teaches game design and the Swift programming language. It contains examples from a real game I created called ForeverMaze. The full course includes the source code for the whole game.

This article will show you how to create an “infinite” coordinate system for a 2.5D game.

The video goes into much more depth than the article, so look there for explanations.

Download Source: Coordinate.swift

What is 2.5D?

In a “2.5D” or “isomorphic” game, the world is rotated by 45 degrees to give a sense of depth.

Here’s a screenshot from ForeverMaze:

ForeverMaze Screenshot

It can be tricky to translate between screen space (points) and grid coordinates’ space (the cartesian coordinates of the tiles). It becomes even harder if you want the world to “wrap around,” so that reaching the maximum x/y tiles seamlessly leads back to the 0/0 position.

Defining “Grid Coordinates”

One of the hardest problems in computer programming is naming. I settled on the name “coordinate” because it indicates 2D space and integer x/y values. It is notably different than “point” (which Apple uses, such as in CGPoint, to refer to a pair of x/y float values).

A point is a place on screen.

A coordinate is the location of the tile in the 2D game world.

So a character resides at a coordinate, but is drawn at a point.

One of the wonderful things about Swift is the ability to define operands. For example, if you want to be able to add two coordinates together. That is:  coordinate1 + coordiante2. To allow for this, you can define the “plus” operator as follows:

This makes it really easy to do math with coordinates. You may have noticed that the coordinate class has two different definitions for X and Y coordinates. There are standard x/y variables, which are unsigned integers. This means that they cannot be negative, and form the “canonical” definition of a coordinate. However, there’s also xIndex and yIndex, which are signed variables useful for doing math on a coordinate. This will become important in the next section.

Converting A Coordinate to a Screen Point

Once you have coordinates, the next thing you’ll need to do is be able to determine where to display them on-screen. The 2.5D nature of the game makes this a bit tricky, since “up” (or “north”) is actually towards the top-right corner of the window.

Determining if a Grid Coordinate is On-Screen

One essential thing you’ll need to be able to do is figure out if any given coordinate is currently on the screen. This will allow you to load (and unload) tiles as-needed while the player walks around.

The bufferPx  allows you to add some optional offset from the edge of the screen. For example, if you passed in 100, a tile which was 100 points off-screen would be considered on-screen. This is useful because you generally want to have a “buffer region” around the screen where objects are being loaded and unloaded, thus guaranteeing that by the time they’re actually on-screen they’re actually ready to be shown.

You’ll notice that I use a testCoord  to offset the coordinate. This ensures that we don’t run into any edge-wrapping problems that might otherwise occur (if, say, the input coordinate was at 0x0 ). Speaking of edge-wrapping…

Handling World-Wrapping

When the player steps off the “top” edge of the world, we need to wrap around so that he immediately appears back at the bottom of the world. This is trickier than it sounds because we can’t show a “seam” around the edge of the world.

Let’s say the world is 100x100  tiles large. If I’m standing at myCoordinate = 50x99 , this means the tile directly north of me is at otherCoordinate = 50x0 . If you were to pass these two coordinates into the coordinateToPosition  function above, they’d actually not be next to each other. The second position would be all the way at the bottom of the map!

Really, each coordinate has 4 possible variations of where it could be positioned, based upon world-wrapping. To accommodate this, let’s rewrite our coordinateToPosition  function to add a new input variable called closeToCenter . When true , it enforces that the resultant point should be the one closest to the current center. Note that this function exists on the SKScene  subclass, meaning that self.size  is the size (in points) of the screen.

Phew! It’s a bit more complicated, and there are other ways to go about it, but this gives us the tool we need to allow for a seamless world. When the player is about to take a step, we simply check if the new coordinate is going to wrap around the world. If it will, we redraw the world based upon the new center point before continuing.

More Help

Grid coordinates in a wrap-around world can be tricky. Hopefully this post gave you all the tools you need to implement them in your game. Let me know in the comments if anything was unclear!

If you enjoyed this post, why not enroll in the free course? It contains hours of video content, source code downloads, examples and more. I cover many different intermediate and advanced topics for creating a real-time game in Swift with SpriteKit and Firebase. You can check out the complete game at ForeverMaze.com.

I create video tutorials, articles and other resources for learning skills. Check out my book, The Joy of Craft, and free online courses.

Leave A Comment

You must be logged in to post a comment.

Back to Top