There are two things going on in that game: the ground is rendered on an inclined plane, and the vertical things are rendered standing straight up. Since he's in Defold, he's rendering things in proper 3D (using the depth buffer to determine which things are in front of which other things). But if you're never going to rotate the camera (he never does), the depth buffer is unnecessary. You can just write a vertex shader to give the perspective squashing to the ground, and then reset to the normal shader and draw the upright images (sorted from farthest away to nearest, which is easy if you're looking at it straight along one axis).
In the ground vertex shader you'll want to transform the corners of the ground tiles from world to screen coordinates. If you're going to do mouse picking or anything you'll also need the inverse transform (screen to world). I did this recently for a game I'm working on. I
think I can extract it fairly easily: I'll take a look tonight and try to post a quick demo.
girng wrote: ↑Sun Sep 16, 2018 9:57 am
I'm pretty sure you can achieve the same effect in love2d without "a lot of work"
Yes, that's why I didn't say "a lot of work".
But in Defold, where the rendering is all 3D by default (though the collision/physics is 2D) it's completely trivial: you probably don't have to write any code at all or use any special shaders or anything that isn't built into the engine. In LOVE you'll have to write or find a custom shader or library to deal with the 3D stuff. Depending on your skill level that may not be a problem, but it is, as I said, "significantly more work" than placing some objects and a built-in camera.
Defold has its issues and quirks, and you may have other reasons for wanting to use (or continue to use Love). If you're happy to learn a bit of math and like to have total control over how things work, then Love is a better choice. If you just want to place a bunch of sprites and have them rendered in 3D, and you don't mind the workflow imposed by a visual editor, then there are other engines which will serve you much better.
zorg wrote: ↑Sun Sep 16, 2018 10:03 am
Löve is a framework; what you accomplish with it is your business; it uses OpenGL, which is able to handle both 2D, 3D, or any Dimensions you want it to, be it 2.5 or 4.
That's like saying "all Turing-complete programming languages are equally powerful." It's
obviously true in the theoretical sense, but in practice it's totally unhelpful, because there can be
major differences in the suitability of different languages for different tasks. Even if you write a custom shader, you'd still (AFAICT) have to rewrite any drawing primitives that you want to use. You can't just magically pass
3D coordinates and angles to love.graphics.draw, love.graphics.circle, etc.
Edit: here you go. This doesn't handle scrolling the ground because that was more work and I couldn't be bothered. But it should get you started.
Code: Select all
local function groundToScreen(x, y)
-- Apply any translation/rotation etc. first.
-- Then do the perspective transform
local z = 1 - cosAngle * y/h
x, y = w/2 + (x - w/2) / z, y * sinAngle / z
return x, y
end
local function screenToGround(x, y)
-- Undo the perspective transform first.
local z = sinAngle / (sinAngle + y/h * cosAngle)
x, y = w/2 + (x - w/2) * z, y / sinAngle * z
-- Then undo any rotation/translation etc.
return x, y
end
function love.load()
angle = 0.2 * math.pi
cosAngle, sinAngle = math.sin(angle), math.cos(angle)
w, h = love.graphics.getDimensions()
groundShader = love.graphics.newShader([[
uniform vec2 size;
uniform float cosAngle, sinAngle;
vec4 position(mat4 m, vec4 p) {
p.z = 1.0 - p.y / size.y * cosAngle;
p.y *= sinAngle / p.z;
p.x = 0.5 * size.x + (p.x - 0.5 * size.x) / p.z;
return m * p;
}
]])
end
local function standingRect(left, bottom, width, height)
local pixelLeft, pixelBottom = groundToScreen(left, bottom)
local pixelRight = groundToScreen(left + width, bottom)
local pixelWidth = pixelRight - pixelLeft
local pixelHeight = height * pixelWidth / width
return pixelLeft, pixelBottom - pixelHeight, pixelWidth, pixelHeight
end
local function pointInRect(x, y, left, top, width, height)
local inHorizontal = x >= left and x < left + width
local inVertical = y >= top and y < top + height
return inHorizontal and inVertical
end
local function mouseInGroundRect(x, y, w, h)
local mx, my = screenToGround(love.mouse.getPosition())
return pointInRect(mx, my, x, y, w, h)
end
function love.draw()
w, h = love.graphics.getDimensions()
cosAngle, sinAngle = math.cos(angle), math.sin(angle)
love.graphics.setShader(groundShader)
groundShader:send("size", {w, h})
groundShader:send("cosAngle", cosAngle)
groundShader:send("sinAngle", sinAngle)
for y = 0, 11 do
for x = 0, 15 do
if (x + y) % 2 == 0 then
if mouseInGroundRect(x * 50, y * 50, 50, 50) then
love.graphics.setColor(0.4, 0.4, 0.3)
else
love.graphics.setColor(0.3, 0.3, 0.25)
end
love.graphics.rectangle('fill', x * 50, y * 50, 50, 50)
end
end
end
love.graphics.setShader()
love.graphics.setColor(0.3, 0.2, 0.5)
love.graphics.rectangle('fill', standingRect(250, 350, 50, 100))
end
--Josh