Hello and welcome back to my blog!
This time I’m going to be talking about something related to the game I’m currently working on, which is a little asteroids style MMO: a wrap-around world. This was necessary because the asteroids world I’d made was infinite in every direction, which meant finding other players could quickly get difficult.
The wrap around
What I mean by wrap-around, is that when you fly off one edge of the world, you get warped to the other side, so you can travel infinitely but the world has finite area.
In Figure 1, A travels off the right hand edge of the world and is then wrapped around to the left hand edge.
‘Trivial!’ I hear you cry, just wrap the position of the object as it crosses the boundary, right? Wrong. This will only work if your objects are 0 size, like points. Furthermore, what happens if the player has a view of the world which also intersects the world boundary?
Figure 2 shows just such a predicament. Things are suddenly a lot more complex because the things in the player’s view, such as object A are not actually there at all, since they have been wrapped around into the world bounds.
Toroidal geometry
This kind of wrapping is not actually spherical as you might imagine, but rather toroidal as that supports an entirely rectangular area with no distortions like you get when trying to map a rectangle onto the surface of a sphere.
Image taken from harmonicresolution.com
The process
When dealing with this in a game there are several problems to solve – rendering of wrapped objects and collision detection. Both of these problems require that we can find out what objects are supposed to be in the viewing/collision area when it lies outside the bounds of the world.
In order to satisfy this requirement we must first start with the basics – given an AABB (bounding rectangle) which intersects the world bounds, what are the other AABBs within the world where there might be objects we ought to consider?
Figure 3 shows the two easy cases: the query AABB is simply wrapped off the offending axis. In both these cases there are two AABBs which need checking in total – the original AABB and the new wrapped region AABB (shown in red). Actually, there are two more cases for -x and +y, but they are very similar.
But what happens when an AABB straddles both x and y edges?
Figure 4 shows on the left a candidate AABB straddling both x and y edgse. On the right there are now three new AABB regions, A, B and C generated from the original AABB cut into sub-regions a, b and c.
A and B just represent the two cases we’ve seen earlier but C is different, in that the position in both axis has been wrapped.
There are now four regions in total we must check for objects.
Note that for simplification Figure 4 shows A, B and C as being smaller than the original AABB. In practice this is not necessary – just keep them the same size.
Detecting regions
Here is some code which will mark which axis need checking as a candidate AABB begins to intersect the world bounds:
/** Flags for overlap */ public class OverlapFlags { static public const kMinusX:uint = 1; static public const kMinusY:uint = 2; static public const kPlusX:uint = 4; static public const kPlusY:uint = 8; } /** The overlap check itself */ static public function AabbWithinFlags( candidate:AABB, container:AABB ):uint { var flags:uint = 0; var delta:Vector2 = candidate.m_Centre.Sub( container.m_Centre ); // check -x if ( delta.m_x-candidate.m_HalfExtents.m_x<-container.m_HalfExtents.m_x ) { flags |= OverlapFlags.kMinusX; } // check -y if ( delta.m_y-candidate.m_HalfExtents.m_y<-container.m_HalfExtents.m_y ) { flags |= OverlapFlags.kMinusY; } // check +x if ( delta.m_x+candidate.m_HalfExtents.m_x>container.m_HalfExtents.m_x ) { flags |= OverlapFlags.kPlusX; } // check +y if ( delta.m_y+candidate.m_HalfExtents.m_y>container.m_HalfExtents.m_y ) { flags |= OverlapFlags.kPlusY; } return flags; }
The wrapping
Once we have our regions to check, whenever we find objects in those regions we need to temporarily move them into a common space in order to do comparisons between them (like computing distances for example), or for rendering them.
In this demo you can move the black rectangle around with the mouse, the generated regions are shown as it intersects the world bounds.
Typically, an object is only actually fully wrapped when it is completely outside the world bounds.
Download the source
You can download the source for free here, its provided with no warranty
That’s it!
Until next time, have fun!
Cheers, Paul.