2D Worlds that wrap around
Monkey Forums/Monkey Programming/2D Worlds that wrap around
| ||
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! |
| ||
Collisions are even worse, best solution I've had is to have "phantom" entities that only exist when an entity is near an edge. |
| ||
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 |
| ||
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 |
| ||
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. |
| ||
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%. |
| ||
If you're using the Diddy tile engine, it supports wrapping maps in both directions. |
| ||
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. |
| ||
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 |
| ||
Thanks everyone :) Especially those who took the time to post code and mock code :D |
| ||
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. |