This is my first post on this forum so I just want to thank Löve and it's community. Thank you all!
I've had this thing that always annoys me when it comes to recreating retro feeling in graphics.
Normally you just scale up some graphic using nearest neighbor interpolation so one pixel in the graphics content is more than an actual pixel on the screen. And that works well. However if you start moving layers/sprites around they will break the immersion if they don't snap to the over sized fake pixels.
In Löve when using the love.graphics.scale function you can solve this by always providing a rounded number of the actual position before drawing the sprite.
The problem I have is with the particle system. Is there a way to get the particles to snap to the fake pixels?
Maybe a better solution would be to run the game in actual low res but then it will be really small in windowed mode.
I haven't read about this anywhere so maybe most people don't care. But I do
How would you guys solve this?
Scaled up retro low res graphics issues.
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
- dreadkillz
- Party member
- Posts: 223
- Joined: Sun Mar 04, 2012 2:04 pm
- Location: USA
Re: Scaled up retro low res graphics issues.
Just store your layer's scale in a variable so you can have stuff snap to it. Then you multiply by (layer_tosnapto_scale/current_layer_scale) to snap to that layer's point.
So...
point(1,1) in layer 1 in screen coordinates: point(2,2)
point(1,1) in layer 2 in screen coordinates: point(3,3)
To snap to point(1,1) in layer 1 just draw at: point(scale1/scale2*x,scale1/scale2*y)
So in layer2, you draw at point(2/3,2/3) ---> screen coordinate: point(2,2) !!!
Code: Select all
scale1 = 2 -- the scale of your layer 1
scale2 = 3 -- the scale of your layer 2
function love.draw()
love.graphics.push()
love.graphics.scale(scale1)
... -- draw your layer 1 stuff
love.graphics.pop()
love.graphics.push()
love.graphics.scale(scale2)
... -- draw your layer 2 stuff
-- if you want to draw something at point(1,1) in layer 1, you would multiply this layer's points by (scale1/scale2) to snap to the other layer's points
love.graphics.pop()
end
point(1,1) in layer 1 in screen coordinates: point(2,2)
point(1,1) in layer 2 in screen coordinates: point(3,3)
To snap to point(1,1) in layer 1 just draw at: point(scale1/scale2*x,scale1/scale2*y)
So in layer2, you draw at point(2/3,2/3) ---> screen coordinate: point(2,2) !!!
Re: Scaled up retro low res graphics issues.
Why go to the effort of enlarging all of the images if you want them to scale like this? Just run the game at a low resolution and scale it up at runtime, that way one 'pixel' (square) in the image represents one actual pixel in the canvas/buffer/screen and not 4 or 16 or 32, whatever.
The particles might also look a little more coherent too this way, though you should consider that particle effects can distort the 'retro effect' and you should design your particles specifically for the visual theme.
The particles might also look a little more coherent too this way, though you should consider that particle effects can distort the 'retro effect' and you should design your particles specifically for the visual theme.
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
Re: Scaled up retro low res graphics issues.
You mean ParticleSystem? Uh, I don't think so. It's not possible to access or modify the position of the particles and it works entirely with floating-point numbers.OuTopos wrote:The problem I have is with the particle system. Is there a way to get the particles to snap to the fake pixels?
I don't know how it would look, but you can try rendering to a Canvas with the small resolution and then draw that (with the appropriate scale and nearest neighbor filtering) to the screen. It means, however, that the graphics driver must support OpenGL 3.0 or the framebuffer extension.
Shallow indentations.
Re: Scaled up retro low res graphics issues.
I understand how to make this with Canvas. But what if Canvas is not supported? And if there are several methods to do this, which one is fastes?Lafolie wrote:Just run the game at a low resolution and scale it up at runtime
Re: Scaled up retro low res graphics issues.
Doesn't Love have some form of scale procedure? And also, is it not possible to simply set the display resolution to match your game's resolution, thus it will be scaled up by the display/driver/gfx card/blah.
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
Re: Scaled up retro low res graphics issues.
I think drivers will not allow me to set resolution like 480x270.
Also i want allow to run my game windowed.
Also i want allow to run my game windowed.
Re: Scaled up retro low res graphics issues.
Try this: love.graphics.scale
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
- Jasoco
- Inner party member
- Posts: 3727
- Joined: Mon Jun 22, 2009 9:35 am
- Location: Pennsylvania, USA
- Contact:
Re: Scaled up retro low res graphics issues.
Here's the code I use nearly-unmodified so you can get an example of how to do it. Sorry there's no comments and you'll have to use your head to figure out some things.
You'll still need to snap all the objects to the grid if you really want to keep it faithful. But using my method below, you won't need to snap if you use Canvas mode, but will need to snap of not using canvas. Personally, as much as I also hate retro games with off-pixel graphics and malformed scaling, I think it's a bit too much work to have to snap every single X and Y position in the code while drawing. Just so much extra work. As I mentioned, if your computer supports canvases (app.canvas below is retrieved by using the love.graphics.isSupported() call.), the snapping will happen automatically. But in non-canvas mode, images will sometimes be partially between pixels like you pointed out above. So it all comes down to whether you want to go to the trouble of snapping the images to the grid or not.
Note: The app and preferences tables are methods I use for organizing this stuff. You will obviously want to modify this to fit your own needs.
Call setupScreen() when you change any settings like fullscreen or window size.
Call screenPreparation() before drawing and screenPresentation() after drawing game related stuff. You can also use more push(), pop(), translate(), scale(), setScissor() calls nested too. As long as you end up closing what you open. i.e. close every push with a pop, every setScissor(x, y, w, h) with an empty setScissor().
Create a function to snap the value:
Or something.
You'll still need to snap all the objects to the grid if you really want to keep it faithful. But using my method below, you won't need to snap if you use Canvas mode, but will need to snap of not using canvas. Personally, as much as I also hate retro games with off-pixel graphics and malformed scaling, I think it's a bit too much work to have to snap every single X and Y position in the code while drawing. Just so much extra work. As I mentioned, if your computer supports canvases (app.canvas below is retrieved by using the love.graphics.isSupported() call.), the snapping will happen automatically. But in non-canvas mode, images will sometimes be partially between pixels like you pointed out above. So it all comes down to whether you want to go to the trouble of snapping the images to the grid or not.
Note: The app and preferences tables are methods I use for organizing this stuff. You will obviously want to modify this to fit your own needs.
Code: Select all
--CHANGE RESOLUTION OR SCALE OR SETUP CANVAS SUPPORT
function setupScreen()
if preferences.fullscreen then
love.graphics.setMode(app.screen_width, app.screen_height, true, preferences.vsync, preferences.fsaa)
else
love.graphics.setMode(app.draw_width * preferences.pixelscale + dbuff, app.draw_height * preferences.pixelscale, false, preferences.vsync, preferences.fsaa)
end
local scale = love.graphics.getHeight() / app.draw_height
if preferences.stretch == false then scale = math.floor(scale) end
if preferences.stretchfill and preferences.stretch then
app.scale_vert = scale
app.scale_horiz = (love.graphics.getWidth()-dbuff) / app.draw_width
else
app.scale_vert = scale
app.scale_horiz = scale
end
app.draw_ox = (love.graphics.getWidth() - (app.draw_width * app.scale_horiz)) / 2
app.draw_oy = (love.graphics.getHeight() - (app.draw_height * app.scale_vert)) / 2
if app.usecanvas then
if love.graphics.isSupported("canvas") then
app.canvas = love.graphics.newCanvas(app.draw_width, app.draw_height)
app.canvas:setFilter("linear", "nearest")
else
app.usecanvas = false
app.canvas = nil
end
else
app.canvas = nil
end
_dbg:updateLineCount()
end
--HANDLE SCREEN PREPARATION
function screenPreparation()
if app.usecanvas == true then
love.graphics.setCanvas(app.canvas)
app.canvas:clear(255,255,255,0)
elseif app.usecanvas == false then
local xx, yy = app.draw_ox, app.draw_oy
if _dbg.on then
xx, yy = app.draw_ox * 2, app.draw_oy * 2
end
love.graphics.push()
love.graphics.translate(xx, yy)
love.graphics.scale(app.scale_horiz, app.scale_vert)
love.graphics.setScissor(xx, yy, app.draw_width * app.scale_horiz, app.draw_height * app.scale_vert)
end
end
--HANDLE SCREEN PRESENTATION
function screenPresentation()
if app.usecanvas == true then
local xx, yy = app.draw_ox, app.draw_oy
if _dbg.on then
xx, yy = app.draw_ox * 2, app.draw_oy * 2
end
love.graphics.setCanvas()
love.graphics.setColor(255,255,255)
love.graphics.draw(app.canvas, xx, yy, 0, app.scale_horiz, app.scale_vert)
elseif app.usecanvas == false then
love.graphics.scale(1)
love.graphics.pop()
love.graphics.setScissor()
end
end
Call screenPreparation() before drawing and screenPresentation() after drawing game related stuff. You can also use more push(), pop(), translate(), scale(), setScissor() calls nested too. As long as you end up closing what you open. i.e. close every push with a pop, every setScissor(x, y, w, h) with an empty setScissor().
Create a function to snap the value:
Code: Select all
function snap(v)
if app.usecanvas then
return v
else
return math.floor(v / scale) * scale
end
end
Re: Scaled up retro low res graphics issues.
Thanks for the suggestions. I will experiment a bit when I have more time.
But for now it seems like I will avoid the particle system all together and just round the sprites positions to whole pixels.
Most of the sprites are already correctly placed and will not move since they are just tiles. It's just the moving objects that cause some problem.
Thanks.
But for now it seems like I will avoid the particle system all together and just round the sprites positions to whole pixels.
Most of the sprites are already correctly placed and will not move since they are just tiles. It's just the moving objects that cause some problem.
Thanks.
OuTopos
Who is online
Users browsing this forum: Google [Bot] and 3 guests