# Creating a seamlessly looping 2D multiplayer level?

A discussion came up recently about how to create a 2D side-scrolling multiplayer game that can have an looping level design (Think of Starbound and how their worlds are looping).

I thought the simplest way would be to have a rectangular map with trigger zones which could teleport players from one side to another. However the obvious issue with this approach is the case of having multiple players at edge of the map at once. You don't want to just teleport players in front of each other and you would need a way to transport players without having other players disappear.

To append this idea and fix the issue I came up with the following: have a trigger zone (red square in image) where players will be able to see a "clone zone" (green square). In this green square, objects from the opposite side of the trigger zone would be copied into its corresponding clone zone (can be seen with A & B shapes). When a player gets to the starting edge of the "clone zone" they are teleported to the other side of the map.

In this example Player 2 would think they are seeing Player 1, however they would actually be seeing his clone and vice versa.

This seemed a bit extreme and complex for the problem at hand. My question now is to know if this solution is a good approach to tackling the issue, or is there a simpler way to solve this problem?

• Are players allowed to move backwards to a previous area? – XiaoChuan Yu Jul 17 '14 at 7:15
• yeah, back and forth so it gives kind of a "walking around the world" effect. Similar to how a world in starbound is – KenQueso Jul 17 '14 at 7:22
• Have you considered just making the world a big circle? or treating the level as a big circle and translating it to a flat 2D stage? – Nzall Jul 17 '14 at 9:36
• can't you always align camera position with the player being controlled? – Ali1S232 Jul 17 '14 at 15:06

This system with all these triggers sounds a bit too complicated and error prone.

You could wrap the position of the player using modulo with something like playerPositionX = playerPositionX % mapWidth

This way when your player reaches playerPosition == mapWidth the playerPosition will reset back to 0.

This solution could be extended with the whole rendering system.

• wouldn't this have the issue of close by players seeing players whose position reset teleport away? – KenQueso Jul 17 '14 at 7:24
• How would you extend this to the rendering system? – Mikael Högström Jul 17 '14 at 7:29
• You could have a player always at the center of the camera and have the map wrap around. Like a map in civilization on earth mode. One other approach could be to render the visible portion of a player to both sides of the map. – Exaila Jul 17 '14 at 7:52
• @MikaelHögström Just render as usual, but things close to the right edge have to be rendered a second time at the left (i.e. at pos - map_width). – Mario Jul 17 '14 at 8:15
• Anywhere in your code that you're looking for 'what object is at this coordinate' or 'what are this object's coordinates', you'd make it xcoord%mapWidth. It's hard to say without your code, but that would likely make it render correctly. – Tin Man Jul 17 '14 at 17:16

The canonical solution is to use portals. In your example, there is only one level, except there is a portal connecting the left and right ends.

Anything moving across that portal will have its coordinates translated to the other end of the portal, so that if something is moving left through the portal, it will reappear on the right side of the level and vice versa.

Your camera needs to support the portals too; if the portal is inside the camera, then it must render parts of the level on either side of the portal. If you're familiar with image editors for seamless tile graphics, it's the same deal here.

The tedious part is that everything dealing with distance or pathing will also need to support portals. This includes AI, line-of-sight algos, sound attenuation and so on.

The nice thing about portals though is that it's very powerful. The build engine used it to simulate multi-story levels, despite it not being a "true" 3d engine. Some modern engines use portals too to create non-Euclidean spaces; Portal and Antichamber are notable examples in 3D.

• If you listen to the commentary of the portal game, part of the way portals work is implemented by cloning whats visible through the hole. (but for physics reasons rather than rendering) – Mooing Duck Jul 17 '14 at 17:36

Remember that what you display on screen, and what's in memory are two totally different things. Imagine you have a window that you need to fill with data about the world. You fill the window from left to right. While you're parsing your data to fill the world, if you reach the end of the world, simply loop back around to the beginning of your data. Using a modulo operation is ideal. Remember you need to do this for everything. Projectiles, rays, players, physics; they all need to have their positions wrapped when crossing the world bounds.

Each player shares data, but has their own perspective of the data. Their windows are populated differently depending on where they're standing in the world.

This means there's no need to create clones, or teleport anyone. Essentially you are creating clones, just by rendering characters to each other's screens.

Disconnect the rendering from the world and you can do wraparound and correct rendering without resorting to any cloning or teleporting artifacts.

First, in your world you have a fixed size world, from 0 to Width. Anytime an object goes below 0 you wrap it to the end, and anytime an object is over Width wrap it to the start. This means that all logical objects in your world are always within the range 0...Width.

Second, for rendering you'll do modulo on the position. So the left side of the screen is "Base" and the right side is "Base + Size". So you look through your world for anything within that range. You'll actually search for the modulo range, which maps it back to 0...Width.

The trick while searching is to return the object's position relative to the Base on the left side. This converts to local coordinates of the screen so the renderer itself doesn't have to worry about the modulo, only the lookup does.

You don't need to clone anything since each renderer only deals with the object in one location.

If your world is produced in segments, or using 3D structures, you'll have to segment it. Thus it isn't one consitunous block, but can be moved to accomodate this rendering. You don't need many blocks, at a minimum 2.

I think the only reasonable approach would be to implement your wrapped world in an underlying data-structure completely transparent to the game and the user. So on some Low-Level you have a function mapCoordinate() which wrap your actual coordinates to your underlying map-resource...

So if your actual World is only 10units wide, the player and the game will not know it. For the player the world is infinite - and if the game asks what is at position 15 - the underlying function will translate this request, modulo10 and will give pack the item at position 5.

So for the whole game logic and everything else it is just like you have an infinite big world, where there just happen to be copies of everything.

It's not quite the same, but I implemented something similar at a game jam. The game had players moving on a small circular level, wrapped around when the player reached an 'x' position of pi. Rendering was easy because we just rendered everything and then rotated an offset camera to track what was going on. You could implement something similar, as has been suggested above:

• When drawing, check the camera position and determine what needs to be drawn, taking into account the camera position and its vision range.
• In cases where the camera sees past the 'edge' of the map, select an appropriate amount of content from the other side of the world to draw over that edge, usually just by adding or subtracting the width of the level to their position.
• Game logic needs to be aware of this seam and adjust for it as mentioned in other answers. Particular cases to be aware of are collisions where one object is on one side, but is colliding with an object on the other side.