Python Forum
Infinitely repeating 2D map
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Infinitely repeating 2D map
#1
I'm creating a 2D game with a top-down camera.  I think something that would make the game a lot better is if I could have the map infinitely repeat itself (there isn't any particular reason for there to be edges to the map).  In other words, I would like it so if you were to just keep walking in a straight line and saw 3 trees, you'll eventually see those same 3 trees again.

Normally, the concept of this isn't that difficult, where it could be something as simple as "if self.x>world.x: self.x=0" but there are a couple caveats:
1. The camera is constantly centered to the player, so as soon as you're wrapped to the beginning of the map, you'd notice the scene change.  You'd also still see the edge of the map.
2. There are computer players, where the AI is programmed to follow or evade other players based on their direction and relative position.  When a player gets wrapped to the other side, the coordinates of either the AI or the targeted player are completely different.


I don't really know of an effective way to handle wrapping.
For problem #1, I could just have it so objects are drawn twice that are a "world's width/height" apart from each other, but I wonder if there's a more efficient way of doing it, or, if whatever the solution to #2 may be could conflict with this idea.
For problem #2, I can't think of anything that wouldn't involve me having to re-write the AI's decision making, which is already a bit complex as-is.
Reply
#2
I'm not sure I understand problem #1. If the edge of the map is x distance away, and they player can see y distance where y > x, just show what would be y - x distance in that direction from the opposite border.

For the AI, it would need to keep track of multiple distances and directions. That is, keep track of the distance/direction to the player on the AI's map, on the map on the north of the AI's, map, on the map to the east of the AI's map, and so on. So if the AI is to the far west and the player is to the far east, the AI uses the distance across the western border. It's all really one map, but calculate distances across borders as well as within borders.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#3
Well, you could be clever about it, and only draw exactly what you need to, but here that sounds like it would probably be overkill.
I think you should start with the naive implementation and then improve if it seems too inefficient.   Just tile the screensize with the map offset by player's position or something.  As for keeping track of actor positions, keep them as world coordinates just like the player.  Probably consider forgetting them if the offset between player and actor coord gets too large.

In fact you could probably use rect.clipto see how much of the current map the player is in so you can quickly find how much you need to wrap.

This is a different idea than infinite scrolling (paralax scrolling) but maybe you can get some ideas:
https://github.com/Mekire/Rotation-Thruster-Movement

Especially from:
https://github.com/Mekire/Rotation-Thrus...ols.py#L49

If you can link to your current program, even if scrolling is not implemented yet, we could better assist.
Github repo if possible.
Reply
#4
Just as a quick additional note - I'm using pyglet, not pygame. I'm not sure that matters, but I do know the Y axis is inverted (so, a Y value of 0 is the bottom of the screen). Anyway...

@ichabod801:
Yeah, what you described about drawing things off the edge is kind of what I was referring to at the end of my post, but it feels a little sloppy. What's nice is it's not all that difficult to attempt, but as I stated before, I'd rather hold that off until I get problem 2 figured out, since that may impact the way #1 is solved.

I sort of get the idea of the AI checking both the "local" distance/direction as well as one north and west, but why not south and east? For example let's say I'm heading south with a bot closely following me and I reach the bottom of the map (a Y value of 0), I'll be wrapped to the top of the map (a Y value of 4096). If the bot is checking local and northern coordinates, that means it would be checking my Y axis relative to it's Y axis (which at this point I am now very far away), meanwhile my "northern" axis is supposed to count from 4096 toward 8192, which is even farther away. If I had a "southern" axis, that would count from 0 toward -4096. So, my southern axis would remain the closest.

Don't forget - diagonals are possible too, so that means I'd also maybe need to check for (and render) NW, NE, SW, and SE, but I'm not 100% sure of that.

That doesn't sound all too difficult on paper, but it seems seriously inefficient. That means for each player, a set of coordinates for each direction (and "local") must be calculated. That's 8 more calculations than what I'm currently doing. There could be as many as 20 players on the map at a time. Note that the AI players have to calculate the coordinates of each other player too, not just the human player (the AI can decide who to follow or run away from) so those 8 extra calculations add up real fast.

Considering there are games from the early 90s (running on Pentium 1s) that accomplished exactly what I want to do, I find it hard to believe this is the approach they took. But, perhaps I misunderstood your suggestion?


@Mekire
Since the camera is centered to the player, and, since the other edge of the map will never be visible, I'm sure

Parallax scrolling won't work for my need. The ground is what the players are standing on.
Reply
#5
MAX = (500, 400)
# world wrap coordinate
def distance_to_player(enemy, player):
    distance = abs(enemy[0] - player[0]), abs(enemy[1] - player[1])
    real_distance = [0,0]
    direction = [0,0]
    if(MAX[0] / 2 < distance[0]):
        real_distance[0] = MAX[0] - distance[0]
        direction[0] = -1
    else:
        real_distance[0] = distance[0]
        direction[0] = 1

    if(MAX[1] / 2 < distance[1]):
        real_distance[1] = MAX[1] - distance[1]
        direction[1] = -1
    else:
        real_distance[1] = distance[1]
        direction[1] = 1

    return real_distance, direction

def calculation():
    player = (5, 100)
    enemy = (400, 58)

    distance, direction = distance_to_player(enemy, player)
    print(distance)
    print(direction) # W, N

calculation()
99 percent of computer problems exists between chair and keyboard.
Reply
#6
@Windspar
From what I can tell, that seems to do what I want, and in a pretty efficient way. But just to clarify, is "MAX" supposed to be the [width,height] of the map, or is it something else?
Reply
#7
MAX is width and height.
99 percent of computer problems exists between chair and keyboard.
Reply
#8
Ok thanks a lot, I'll go look into this more later.
Reply
#9
Here another you might find more readable.
class Point2D:
	def __init__(self, pyint_x, pyint_y):
		self.x = pyint_x
		self.y = pyint_y
		
	def __abs__(self):
		return Point2D(abs(self.x), abs(self.y))
		
	def __repr__(self):
		return "Point2D({}, {})".format(self.x, self.y)
		
	def __sub__(self, lhs):
		return Point2D(self.x - lhs.x, self.y - lhs.y)
		

WORLD_SIZE = Point2D(500, 400) # width, height
# world wrap coordinate
def distance_to_player(enemy, player):
	distance = abs(enemy - player)
	real_distance = Point2D(0, 0)
	direction = Point2D(0, 0)
	if(WORLD_SIZE.x / 2 < distance.x):
		real_distance.x = WORLD_SIZE.x - distance.x
		direction.x = -1
	else:
		real_distance.x = distance.x
		direction.x = 1
		
	if(WORLD_SIZE.y / 2 < distance.y):
		real_distance.y = WORLD_SIZE.y - distance.y
		direction.y = -1
	else:
		real_distance.y = distance.y
		direction.y = 1
		
	return real_distance, direction
		
def calculation():
	player = Point2D(5, 100)
	enemy = Point2D(400, 58)
	
	distance, direction = distance_to_player(enemy, player)
	print(distance)
	print(direction) # W, N
	
calculation()
99 percent of computer problems exists between chair and keyboard.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020