2D Worlds that wrap around

Monkey Forums/Monkey Programming/2D Worlds that wrap around

Raz(Posted 2012) [#1]
I'm trying to get my head around a 2d game where the level wraps around on itself, and I can't figure out how it's possible.

As far as I see it, there are two issues here, the first being a rendering issue, the second being a logic issue.



The light blue area is the level, the pink area is the screen position, as you can see, screen 1 is fine as it doesn't go over the borders of the level, however screen 2 does, how would you go about this?

Game logic wise, this blows my mind even more!



The bad guy is programmed to walk towards the good guy. Logically, the X position of the good guy is less than that of the bad guy, so the bad guy should walk left. However, realistically, the bad guy should walk right, because it is closer.

I know this isn't a particularly special thing to have in a game, but I really can't get my head round it!

Ta and Merry Christmas!


Belimoth(Posted 2012) [#2]
Collisions are even worse, best solution I've had is to have "phantom" entities that only exist when an entity is near an edge.


Midimaster(Posted 2012) [#3]
the actor on the left has two distances to the actor on the right:

distance1= Abs(X1-X2)

distance2= Abs(X1+PlaygroundWidth-X2)

for you example:

X1=1
X2=9
PlaygroundWidth=10

Distance1=Abs(1-9)=8
Direction1=Sgn(1-9)=-1 = go to the left

Distance2=Abs(1+10-9)=2
Direction2=Sgn(1+10+2)=+1 = go to the right



Distance2 is nearer, Player 2 should go to the right


Jesse(Posted 2012) [#4]
pseudocode(untested):

for the screen logic
draw the image at image.position
if image.position < startOfScreen and image.position + image.width > startOfScreen
    draw the image at (image.position +screenwidth- image.position)
elseif image.position < endOfScreen and (image.position+image.width) > endOfScreen
  draw the image at (image.position - endOfScreen)



for the movement logic

' process dab guy movement
distance 1
vx1 = goodguy.x - badguy.x
vy1 = goodguy.y  - badguy.x

if goodguy.x < badguy.x 

    distance 2
     vx2 = (goodguy.x + screen.width) - badguy.x
     vy2 = goddguy.y - badguy.y

    if (vx1*vx1+vy1+vy1) < (vx2*vx2+vy2*vy2)
         distance 1 is closer  
    else
         distance 2 is closer
    endif
elseif goodguy.x > badguy.x
   distance 2
    vx2 = (goodguy.x - screenWidth) - badguy.x
    vy2 = goodguy.y - badguy.y
    if (vx1*vx1+vy1*vy1) < (vx2*vx2+vy2*vy2)
       distance 1 is closer
    else
      distance 2 is closer
    endif
else
 distance 1 is closer
endif



Kauffy(Posted 2012) [#5]
I think it's actually very, very simple:

If the difference between the bad guy's X and the player's X is greater than 50% of the playfield width, the bad guy should go in the "wrap" direction of whichever border he's closer to. In other words, if the distance between them is greater than 50% of the width of the playfield, the bad guy should move away from the player.

Field width is 100.
Player is at 90
Badguy is at 10.
Abs(90-10) = 80.
80>50% of 100(field width) --> badguy should move "in the opposite direction" from the player.

Field width is 100.
Player is at 60.
Badguy is at 80.
Abs(60-80) = 20
20<50% of 100 --> badguy should move toward player directly.


Gerry Quinn(Posted 2012) [#6]
For rendering, the simplest thing is simply to 'draw it twice' (in practice it gets scissored). E.g. if you imagine two screens side by side, then if the viewing area runs from 60% - 110% of the basic screen, you are drawing a portion of this two-screen area.

You can also work backwards from this to the logic if you know which screen an object is being drawn from. E.g. the creatures from 100-110% are walking left to get to places in 60%-100%.


Samah(Posted 2012) [#7]
If you're using the Diddy tile engine, it supports wrapping maps in both directions.


Paul - Taiphoz(Posted 2012) [#8]
I would also say that simply drawing the actor twice when its width + its x is either over or under one of the screen sides.

When it comes to collisions, I would test both drawn actors , for example if the actor is half in and half out of the sides, then test both actor positions for collisions.

In terms of AI, I would use a simple system of checking distance, when the Actor is being drawn twice because its half in and half out of a side then test distance to both locations, if the AI is closer to the actor heading off screen it should chase the actor OFF SCREEN, instead of turning around to go back over the level.

What you might do is have a flag or variable to state , ok player has gone off the right side and come in the left, any enemy chasing him should also go off the right side and then once in the left, resume the chase.

loads of ways you can do this really and the best solution for you will all depend on the game your making and the structure of the levels your making, so pick the solutions that fit best with what you want and have a crack at it.

hope this helps.


Dima(Posted 2012) [#9]
This is sort of related but only to wrapping coordinates within range; using bitwise AND operator to drop all bits beyond the given range automatically wraps power of two integers within that range in both positive and negative direction. For example, to wrap an integer in the range between 0 and 255 (8 bit precision) simply combine the integer bits with 255 using AND. There are limitations as well; only works with integers that are power of two, but with some trickery can be used to represent other numbers. This is why textures power of two are easier to map when they can repeat many times.
' $FF is hex for 255
Print (255 & $FF)	' result is 255 because number is already in range
Print (256 & $FF)	' result is 0 because number is 1 outside the range
Print (-2 & $FF)	' result is 254 because number is 2 less than range
Print (-23456789 & $FF)	' result is 235 because number is WAY out of range

' instead of using hex, create any precision (up to 30 bit)
Local bits%, mask%

bits = 3		' 3 bits range between 0 and 7
mask = Pow(2,bits)-1	' 3 bit mask = 7
Print (10 & mask)	' result is 2 (wrap goes 7, 0, 1, 2)

bits = 24
mask = Pow(2,bits)-1	' 24 bit mask = 16777215
Print (-1 & mask)	' result is 16777215

' wrapping floating numbers like angle is also possible but with precision loss
' basically convert to pow2 integer, wrap, then convert back to float
' or everything can be kept as fixed integer range until rendering
' also try messing with precision, angle snapping can probably be made with low precision and some rounding.
bits = 30		' 30 is max, need 1 bit to drop and 1 bit for sign.
mask = Pow(2,bits)-1	' 30 bit mask = 1073741824

Local scalar# = 360.0 / mask
Local angle# = 365.0	' we want to wrap this between 0 and 360 (exclusive)
Local fixed% = angle / scalar
Local wrap% = fixed & mask
angle = wrap * scalar	' back to float 
Print (angle)	' result is 4.9933... which is close to 5



Raz(Posted 2012) [#10]
Thanks everyone :) Especially those who took the time to post code and mock code :D


EdzUp(Posted 2012) [#11]
Rendering something like that I would simply do a check to see if the rendering square (if its tile based) X and Y is less than 0 or greater than the width or height of the play area.

If its less than 0 add the width if its X or height if its Y to the check and see whats there. If its greater than width take the play area width from X, if its greater than the height take the play area height from Y and check.

This way no matter what you do it will always look on the other areas and render it so even if you go into a corner it will render perfectly.