Don't Starve style camera?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
clofresh
Citizen
Posts: 87
Joined: Sun Jul 26, 2009 4:21 pm
Contact:

Don't Starve style camera?

Post by clofresh »

Hey folks,

I'm trying to imitate the Don't Starve camera. For those who haven't played it, Don't Starve is a 2d game with a camera perspective very slightly tilted such that entities on the objects of the screen appear slightly larger than objects at the top of the screen. Objects are still 2d orthogonal sprites though, that is, they appear flat without any tilt.

The main thing I want to mimic is that parallax scrolling effect that each object exhibits when you walk left and right. To illustrate, I've taken some screenshots. I've placed three twigs alone the same y position:
dontstarve0.png
dontstarve0.png (327.29 KiB) Viewed 3651 times
Now I move to the right:
dontstarve1.png
dontstarve1.png (310.36 KiB) Viewed 3651 times
Note that twig towards the bottom has moved further to the left than the twig towards the top.

Here is a composite image:
dontstarve2.png
dontstarve2.png (301.72 KiB) Viewed 3651 times
What's the cleanest way to implement something like this? Let's assume I have a single atlas image that I will be rendering as a sprite batch. In addition to doing a global love.graphics.translate() to move the camera with the player, do I have to manually calculate x offsets for each sprite proportional to their y position? Or is there a cleaner way to do it?
----------------------------------------
Sluicer Games
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Don't Starve style camera?

Post by micha »

love.graphics.translate is probably not a big help here, because it translates all objects by the same amount. That means that you have to calculate the screen coordinates of each object yourself. If you want to use a sprite batch, you can still do that. Just keep the sprite id of each object and you can still change the coordinates of each sprite individually.

I suggest you store all object coordinates internally as physical world coordinates (2d standard) and only when you draw objects, you calculate the according screen coordinates of the object.
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Don't Starve style camera?

Post by Jasoco »

The way I would do it since I already have the code to do it is to translate the 2D X and Y coordinates of everything into 3D coordinates with a fixed elevation camera. This is how Don't Starve would do it if it were built in Löve. (I don't know what it's built in but it probably uses an engine already designed for simple 3D.)

It's hard to just post all my code here but I'll post the two relevant functions. Hopefully you can use them:

Code: Select all

function calculateCameraRotation(cam)
	if cam.pty ~= cam.tilty then
		cam.sx = math.sin(toRad(cam.tilty))
		cam.cx = math.cos(toRad(cam.tilty))
	end
	if cam.ptz ~= cam.tiltz then
		cam.sy = math.sin(toRad(cam.tiltz))
		cam.cy = math.cos(toRad(cam.tiltz))
	end
	if cam.ptx ~= cam.tiltx then
		cam.sz = math.sin(toRad(cam.tiltx))
		cam.cz = math.cos(toRad(cam.tiltx))
	end
	cam.ptx = cam.tiltx
	cam.pty = cam.tilty
	cam.ptz = cam.tiltz
end

function point3dto2d(x, y, z, cam, world)
	local dw = tonumber(world.draw_width or lgr.getWidth())
	local dh = tonumber(world.draw_height or lgr.getHeight())

	local wss = app.screenScale

	_dbg.totalPointsCalculated = _dbg.totalPointsCalculated + 1

	--3D Calculations are done here!!!!
	local xy, xz, yz, yx, zx, zy, scale, dist, rx, ry, rz, xo, yo

	rx = -cam.locX + x
	ry = -cam.y + y
	rz = -cam.locZ + z

	xy = cam.cx * ry - cam.sx * rz
	xz = cam.sx * ry + cam.cx * rz

	yz = cam.cy * xz - cam.sy * rx
	yx = cam.sy * xz + cam.cy * rx

	zx = cam.cz * yx - cam.sz * xy
	zy = cam.sz * yx + cam.cz * xy

	xo = dw / 2
	yo = dh / 2
	scale = world.focal / (world.focal + yz) * 2
	dist = yz
	rx = xo + (zx * scale) * wss
	ry = yo + (zy * scale) * wss
	local onScreen = false
	if (rx >= 0 and rx <= dw) then onScreen = true end
	if dist < preferences.min_dist then
		return huge, huge, 0, 0, false
	end

	return rx, ry, dist, scale * wss, onScreen
end
Now of course this code is specific to my 3D engine, but the maths check out. You would need to calculate the camera rotation at least once before calling for any 3D to 2D calculations. Also it's not perfect. Rotating certain camera axes looks wrong but I can't be bothered to fix it. You can tilt it forward but if you try to also turn left or right it won't rotate correctly. So it's not a perfect system. (I can always fix this later if I get the correct math.)

The 3D to 2D function accepts the X, Y and Z of a point as well as the camera object and a world object (You can edit that out if you need to. As long as focal exists. The default for focal is 400.) and returns the new screen X and Y, the distance of the point and the scale of the point for use with scaling stuff like sprites, as well as whether it's on screen or not.

I'd give more but it's very complex and unfinished 3D engine code that would make no sense. But this might help at least with scaling and positioning sprites. The only problem is it won't help with a ground texture. That would be more difficult without actual 3D perspective warping on images. (Some Lövers have done various methods to fake it though.)
Post Reply

Who is online

Users browsing this forum: No registered users and 144 guests