Map that wraps - how to wrap the graphics?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Map that wraps - how to wrap the graphics?

Post by Taehl »

I'm making a game where the map wraps from one side to the other - if you keep going right far enough, you'll end up on the left of the map. The camera follows the player. So when approaching the edge, I need some way to show all the graphics from the opposite edge of the map. I've had a few ideas on how to do this:

Idea 1) Do some kind of tricky math on the coordinates of everything I draw
Idea 2) Make my draw function cover specific parts of the screen using specific camera coordinates, so I can call it again with different coordinates for areas which exceed the edge of the map

Any suggestions? Has anyone tackled this problem before?
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: Map that wraps - how to wrap the graphics?

Post by scutheotaku »

EDIT: Oops! I think I misread your problem...when I have more time later, I'll try to write/think of something more useful for your situation. Sorry!

I'll leave the original post below, in case someone comes across this wondering about normal (i.e. Pacman or Asteroids style) image wrapping.

---

I've never tried this before, but...perhaps just check if part of the image is outside of the screen, and if it is then draw the image again on the opposite side of the screen?

For example:
Your screen is 640 pixels wide. Your image is 16 pixels wide.
Your image is sitting at x position 630, meaning that its first 10 pixels on the x axis are being displayed, with the last 6 being cut off.
Calculate how much is being cutoff (e.g. math.abs(screenWidth - (image.x + image.width)), and store that in, let's say, "imageXCutoff".
Now draw the image a second time on the opposite side of the screen, taking into account how much should be shown (to get the x-coordinate, maybe do something like this: "-(image.width - imageXCutoff)").

Ok, that was probably explained horibbly, so here is an example (it takes into account both the x and y axes, both positive wrapping and negative wrapping)...

Code: Select all

--IMAGE WRAPPING
--This is *probably* NOT the best way to do this...in fact, this is written sort of badly...
--But it should work!
--...though I haven't tested it!

local screenWidth = 640
local screenHeight = 480

love.graphics.setMode( screenWidth, screenHeight)

image = { x = 630, y = 472, width = 16, height = 16, img = IMAGE_GOES_HERE }

local imageXCutoff
local imageYCutoff

function love.update()
	UpdateImage()
end

function love.draw()
	DrawImage()
end

function UpdateImage()
	if (image.x > screenWidth) then
		imageXCutoff = math.abs(screenWidth - (image.x + image.width))
	elseif (image.x < 0) then
		imageXCutoff = image.x
	else
		imageXCutoff = 0
	end
	
	if (image.y > screenHeight) then
		imageYCutoff = math.abs(screenHeight - (image.y + image.height))
	elseif (image.y < 0) then
		imageYCutoff = image.y
	else
		imageYCutoff = 0
	end
end

function DrawImage()
	love.graphics.draw(image.img,image.x,image.y)
	
	if ((imageXCutoff ~= 0) or (imageYCutoff ~= 0)) then
		local drawX = image.x
		if (imageXCutoff > 0) then drawX = -(image.width - imageXCutoff)
		elseif (imageXCutoff < 0) drawX = screenWidth + imageXCutoff end
		
		local drawY = image.y
		if (imageYCutoff > 0) then drawY = -(image.height - imageYCutoff)
		elseif (imageYCutoff < 0) drawY = screenheight + imageYCutoff end
		
		love.graphics.draw(image.img,drawX,drawY)
	end
end
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Map that wraps - how to wrap the graphics?

Post by Taehl »

My map is way bigger than one screen. Right now I'm using an extent of 20,000x20,000 pixels. Guess I should have mentioned that, sorry. So a worst-case scenario with my current metrics means that the player is at, for instance, (9999,0), leaving the whole right half of the screen off of the map. So I'd need everything from (-10000,-300) to (-9601,300) to fill it.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
substitute541
Party member
Posts: 484
Joined: Fri Aug 24, 2012 9:04 am
Location: Southern Leyte, Visayas, Philippines
Contact:

Re: Map that wraps - how to wrap the graphics?

Post by substitute541 »

Hmm. I'll copy this function from my "TRMT(the random moving things) Nodes"...

Code: Select all

function setBounds(object, l, r, t, b, oxl, oxr, oyt, oyb)
	if object.x - oxl > r then
		object.x = l - oxr
	elseif object.x + oxr < l then
		object.x = r + oxl
	end
	if object.y - oyb > t then
		object.y = b - oyt
	elseif object.y + oyt < b then
		object.y = t + oyb
	end
end
Those letters there are just the starting letters of "left, right, top, bottom" respectively. oxl, and oxr are the difference between the x coordinate and the left edge, and between the x coordinate and the right edge respectively. oxt and oxb are the diff-- ah you probably understand now.
Currently designing themes for WordPress.

Sometimes lurks around the forum.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Map that wraps - how to wrap the graphics?

Post by Taehl »

Unless I'm failing to understand it, that code seems to move objects around to wrap them within boundaries? I already figured that part out (see code below). What I need to figure out is how to draw things from the opposite side of the map when the camera goes past the boundary.

My solution to wrapping objects when they exceed the boundary:

Code: Select all

function gameUpdate(dt, state, isClient)
	local function wrap(n)
		if n > state.extent then return n - state.extent*2
		elseif n < -state.extent then return n + state.extent*2
		end
		return n
	end

	-- move things according to velocities
	for i=1,#state.ships do
		local s = state.ships[i]
		local accel = 400	-- todo: determined by weight
		s.vx, s.vy = s.vx+(s.inX or 0)*dt*accel, s.vy+(s.inY or 0)*dt*accel
		if math.abs(s.vx)+math.abs(s.vy)<50 then s.vx,s.vy = s.vx*(1-dt), s.vy*(1-dt) end	-- space-brakes!
		s.x, s.y = wrap(s.x + s.vx*dt), wrap(s.y + s.vy*dt)
	end
end
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
substitute541
Party member
Posts: 484
Joined: Fri Aug 24, 2012 9:04 am
Location: Southern Leyte, Visayas, Philippines
Contact:

Re: Map that wraps - how to wrap the graphics?

Post by substitute541 »

Taehl wrote:Unless I'm failing to understand it, that code seems to move objects around to wrap them within boundaries? I already figured that part out (see code below). What I need to figure out is how to draw things from the opposite side of the map when the camera goes past the boundary.

My solution to wrapping objects when they exceed the boundary:

Code: Select all

function gameUpdate(dt, state, isClient)
	local function wrap(n)
		if n > state.extent then return n - state.extent*2
		elseif n < -state.extent then return n + state.extent*2
		end
		return n
	end

	-- move things according to velocities
	for i=1,#state.ships do
		local s = state.ships[i]
		local accel = 400	-- todo: determined by weight
		s.vx, s.vy = s.vx+(s.inX or 0)*dt*accel, s.vy+(s.inY or 0)*dt*accel
		if math.abs(s.vx)+math.abs(s.vy)<50 then s.vx,s.vy = s.vx*(1-dt), s.vy*(1-dt) end	-- space-brakes!
		s.x, s.y = wrap(s.x + s.vx*dt), wrap(s.y + s.vy*dt)
	end
end
EDIT! Understood what you really want.

Alright... Hmm. It depends on what background you have. If it's basically a plain color background, or a background that doesn't move with the camera, you can just use a bit of math to change the coordinates of the player and camera like this :

Code: Select all

if camera.x + camera.view.width/2 >= rightBound then
    player.x = leftBound - camera.view.width/2 --I'm also assuming that the camera follows the player automatically
end
if camera.x - camera.view.width/2 <= leftBound then
    player.x = -rightBound + camera.view.width/2
end
It's the same with the Y axis (replace x with y, width with height, rightBound with bottom, leftBound with top).

Original post:

Oooohhh... I actually have that kind of code, though at low FPS it's not accurate. This snippet here is ripped off from my "Tank Battle I" game.

Code: Select all

for i, tile in ipairs(grassTiles) do
	if tile.x >= stage.width + 64 - 32 then
		tile.x = -64 + 32
	elseif tile.x <= -64 + 32 then
		tile.x = stage.width + 64 - 32
	end
	tile.x = tile.x + world.vx * dt
end
You can edit it to support any object, and support the Y coordinate. I''ll edit this post later when I did that..[/size]
Last edited by substitute541 on Mon Feb 18, 2013 5:38 am, edited 3 times in total.
Currently designing themes for WordPress.

Sometimes lurks around the forum.
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: Map that wraps - how to wrap the graphics?

Post by scutheotaku »

If I'm understanding what you want, this seems like the easiest way to do it (you could probably slim the code down a bit, but I wanted it to be easily readable):

Code: Select all

--setup camera
camera = {x = 0, y = 0, w = 640, h = 480, moveSpeed = 150}

love.graphics.setMode( camera.w, camera.h)

--setup map
map = {x = 0, y = 0, w = 1280, h = 1024}

--setup example box
box = {x = 32, y = 32, drawX = 32, drawY = 32, w = 32, h = 32}

function love.update(dt)
	--update box, including wrapping
	UpdateBox()
	
	--update camera
	UpdateCamera(dt)
end

function love.draw()
	--draw box
	DrawBox()
	
	love.graphics.print("Box X: "..box.x, 4, 4)
	love.graphics.print("Box Y: "..box.y, 4, 16)
	love.graphics.print("Box Draw X: "..box.drawX, 4, 28)
	love.graphics.print("Box Draw Y: "..box.drawY, 4, 40)
	love.graphics.print("Camera X: "..camera.x, 4, 52)
	love.graphics.print("Camera Y: "..camera.y, 4, 64)
end

function UpdateBox()
	--set the box's draw coordinates, relative to the camear
	box.drawX = box.x - camera.x
	box.drawY = box.y - camera.y
	
	--map wrapping
	if ((box.drawX + box.w) > map.w) then box.drawX = box.drawX - map.w
	elseif ((box.drawX + box.w) < map.x) then box.drawX = box.drawX + map.w end
	if ((box.drawY + box.h) > map.h) then box.drawY = box.drawY - map.h
	elseif ((box.drawY + box.h) < map.y) then box.drawY = box.drawY + map.h end
end

function DrawBox()
	love.graphics.rectangle("fill",box.drawX,box.drawY,box.w,box.h)
end

function UpdateCamera(dt)
	if love.keyboard.isDown('right') then camera.x = camera.x + camera.moveSpeed * dt
	elseif love.keyboard.isDown('left') then camera.x = camera.x - camera.moveSpeed * dt end
	if love.keyboard.isDown('down') then camera.y = camera.y + camera.moveSpeed * dt
	elseif love.keyboard.isDown('up') then camera.y = camera.y - camera.moveSpeed * dt end
	
	--correct camera coordinates, if needed
	if (camera.x > map.w) then camera.x = camera.x - map.w
	elseif ((camera.x + camera.w) < map.x) then camera.x = camera.x + map.w end
	if (camera.y > map.h) then camera.y = camera.y - map.h
	elseif ((camera.y + camera.h) < map.y) then camera.y = camera.y + map.h end
end
Is this what you're looking for?

EDIT:
I attached a .love file version. Use the arrow keys to move the camera.

EDIT AGAIN:
Oops! There was an error in my code that caused the camera to only wrap once...both the code above and the attached .love file have been fixed :)
Attachments
MapWrapping1.1.love
EDIT: Bug in this version - see the version attached to my next post. The code snippet in this post is correct though.
(719 Bytes) Downloaded 81 times
Last edited by scutheotaku on Mon Feb 18, 2013 6:16 am, edited 2 times in total.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Map that wraps - how to wrap the graphics?

Post by Taehl »

I tried that .love... The camera moves, the box never re-appears? Plus, it seems if you move the box out of the screen, the camera's coords become huge and never stop moving???

EDIT)
What I need is this (Please excuse the ASCII art, this netbook doesn't have my normal software).

Code: Select all

Imagine a map with some objects:
ABCDEFGH
IJKLMNOP
QRSTUVWX

And a camera:
 ___
|   |
|___| (it's big enough to show 3x2 of the letters)

And we scroll right...
 ___    ___    ___    ___
|EFG|  |FGH|  |GHA|  |HAB|
|MNO|->|NOP|->|OPI|->|PIJ|

And then down...
 ___    ___    ___
|HAB|  |PIJ|  |XQR|
|PIJ|->|XQR|->|HAB|
Does that make any more sense?
Last edited by Taehl on Mon Feb 18, 2013 6:35 am, edited 2 times in total.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: Map that wraps - how to wrap the graphics?

Post by scutheotaku »

Taehl wrote:I tried that .love... The camera moves, the box never re-appears? Plus, it seems if you move the box out of the screen, the camera's coords become huge and never stop moving???
Hmm, I just tested it again, and it works fine for me...The map width is 1280px and the camera's width is 640px, so if, for example, you hold down the right arrow key then the box will be out of the camera's view for a bit before it pops back in.

EDIT:
Ah, I had a sign wrong in the UpdateCamera() function. That may have caused it not to work right for you, but only if you were moving the camera to the left.

I updated the code in my last post with the fix, and you'll find an updated .love file attached to this post.
Attachments
MapWrapping1.2.love
(720 Bytes) Downloaded 104 times
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: Map that wraps - how to wrap the graphics?

Post by scutheotaku »

Taehl wrote: EDIT)
What I need is this (Please excuse the ASCII art, this netbook doesn't have my normal software).

Code: Select all

Imagine a map with some objects:
ABCDEFGH
IJKLMNOP
QRSTUVWX

And a camera:
 ___
|   |
|___| (it's big enough to show 3x2 of the letters)

And we scroll right...
 ___    ___    ___    ___
|EFG|  |FGH|  |GHA|  |HAB|
|MNO|->|NOP|->|OPI|->|PIJ|

And then down...
 ___    ___    ___
|HAB|  |PIJ|  |XQR|
|PIJ|->|XQR|->|HAB|
Does that make any more sense?
Yep, unless I'm missing something, that's what the examples in my last two posts do. They wrap the camera around the map, and then draw things relative to the camera's position. For example, if the map is 1024 pixels wide, the camera is 640 pixels wide, and the camera's x position is at 512, then the camera will draw 512 to 1024, and then 0 to 127 (for a total of 640, the camera's width). They do the same on the y-axis.

Please try the "MapWrapping1.2.love" file I attached to my last post.

Here is a rough image illustrating what it does:
Image

If you want to see this better, try modifying MapWrapping1.2.love (or my example snippet from before) so that the example box's width and height are 640 and 480 respectively.
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests