As the title suggests, I'd like to have my pixel-art game perform pixel perfect rendering. The common way to do this is to render everything at a low resolution and upscale it using nearest-neighbour.
What's the best way to do this in löve?
How to do pixel-perfect rendering in löve?
Re: How to do pixel-perfect rendering in löve?
https://forum.yoyogames.com/index.php?t ... -game.995/
It's for GameMaker studio, but explanation on how it cand be done, can be used in other cases, i guess
If you know game Pixel Dungeon (or Shattered Pixel Dungeon), game uses something like that:
There options menu which allow you to switch in-beetwen integer x2, x3, x4, etc scaling
Maybe the easest solution will be use options menu, so user can choose more preferable option and, maybe, add "Auto" mode where that factor will be calculated automaticaly depending on window width and height, something like:
Code: Select all
scale = math.ceil(windowWidth / 1000)
Re: How to do pixel-perfect rendering in löve?
If by "pixel perfect" you mean each pixel is a square with sharp borders, people usually use something like Push for this. https://love2d.org/forums/viewtopic.php?f=5&t=80738
The idea behind Push is to render to a canvas of a smaller resolution, then render that canvas to the screen appropriately scaled.
The idea behind Push is to render to a canvas of a smaller resolution, then render that canvas to the screen appropriately scaled.
- Gunroar:Cannon()
- Party member
- Posts: 1143
- Joined: Thu Dec 10, 2020 1:57 am
Re: How to do pixel-perfect rendering in löve?
Very well made game
Re: How to do pixel-perfect rendering in löve?
Meh, maybe
- Gunroar:Cannon()
- Party member
- Posts: 1143
- Joined: Thu Dec 10, 2020 1:57 am
Re: How to do pixel-perfect rendering in löve?
I agree that the might have been an over exaggeration... But what do you mean by maybe (and the "meh" ) I want to see your view.
(I say it's well made, you know, for one guy to make a game that's complete and has a nice ui and clean code, if you look at the code that is. )
PS: I'm talking about the original.
Re: How to do pixel-perfect rendering in löve?
TLDR: Original game fells unfair, unfinished and annoing, better play forks like Shattered, YAPD and maybe RemixedGunroar:Cannon() wrote: ↑Sat Aug 14, 2021 9:15 pmI agree that the might have been an over exaggeration... But what do you mean by maybe (and the "meh" ) I want to see your view.
(I say it's well made, you know, for one guy to make a game that's complete and has a nice ui and clean code, if you look at the code that is. )
PS: I'm talking about the original.
Original game i don't really like because:
1 Random at combat - like, random at level generation and other events it's okay, it makes game fells new every time you play it, but in combat...
It's anoying me, like: you strong, expirienced warriror, you can easly pick up and use heavy (18 strg) weapons, use magic wands...
...but you cant hit rat because random, it really annoy me
Sometimes i lost run because of that
2 There is like 4 quest in game:
kill stink rat and you win some random useless leather armour +0 or shortsword+1 when you have something better
kill fire elemental and win useless sheep wand
mine 15 ore and win free +1 upgrade
kill 8 monks and win yet another wand charger ring when you play purely melee
3 There is not much content at all - there 5 level of deep, but they fells more like copy-paste, level gen not that interesting;most of potions not really helpful, most weapons also not that useful or interesting to play with since they just do little more damage and that it; to win there like 1 valid strat - don't use upgrade scroll until you found some roundom tier 4 weapon at sewers and use all of them in it; there is no optional levels;
4 - swarm of flies; monks; scorpions; i hate them
Like, that okay game, but after you play it more and more, it start fells annoing, repetative and unfair
If you want better Pixel Dungeon expirience, then original game is not that good
- Gunroar:Cannon()
- Party member
- Posts: 1143
- Joined: Thu Dec 10, 2020 1:57 am
Re: How to do pixel-perfect rendering in löve?
Okay, I see your points...
Hmm...I guess it's true. But I think the boss battles are nice and varied. And there's some hidden stuff (like use scroll of mapping in level 5 , goo boss, and find a door where theres loot(not a lot though) and a rat king. Just a fun thing to find. Have you seen it? )
I guess all games get repetitive eventually. Some just get there faster than others
unfair, I guess. Unfinished...It kind of lacks bugs and has some polish to it. YAPD is...better I guess (I actually managed to finish that one one easy mode ). Remixed adds a lot of more content but is a little too hard where getting killed in floor one by a rat is an easy possibility. But it added saves(save scumming ) and I guess they know it's harder. Original to me just seems...simply complete. Like "hey, dude, make a semi-decent roguelike on android"(yes, I'm talking about android version. Comparing it to PC roguelikes is a no-go) and then he makes pixel dungeon. Which is like the first good roguelike on Android with a nice retro vibe.
Hey! That's balanced. In brogue and others you can wield a heavy item but because it's too heavy for you your character keeps missing.Original game i don't really like because:
1 Random at combat - like, random at level generation and other events it's okay, it makes game fells new every time you play it, but in combat...
It's anoying me, like: you strong, expirienced warriror, you can easly pick up and use heavy (18 strg) weapons, use magic wands...
...but you cant hit rat because random, it really annoy me
The quests do give pretty weak stuff . But the sheep wand has saved me countless times. Helps to make an escape2 There is like 4 quest in game:
kill stink rat and you win some random useless leather armour +0 or shortsword+1 when you have something better
kill fire elemental and win useless sheep wand
mine 15 ore and win free +1 upgrade
kill 8 monks and win yet another wand charger ring when you play purely melee
(You mean 25 levels. But I guess you meant like levels (separating on boss fights) and not floors )3 There is not much content at all - there 5 level of deep, but they fells more like copy-paste, level gen not that interesting;most of potions not really helpful, most weapons also not that useful or interesting to play with since they just do little more damage and that it; to win there like 1 valid strat - don't use upgrade scroll until you found some roundom tier 4 weapon at sewers and use all of them in it; there is no optional levels;
Hmm...I guess it's true. But I think the boss battles are nice and varied. And there's some hidden stuff (like use scroll of mapping in level 5 , goo boss, and find a door where theres loot(not a lot though) and a rat king. Just a fun thing to find. Have you seen it? )
Swarm of flies are the beeeeeessssst(best)! Go to a corridor and start killing them. Whatever potion they drop is sure to be a healing potion. And there's like a 60% chance they drop one.4 - swarm of flies; monks; scorpions; i hate them
Like, that okay game, but after you play it more and more, it start fells annoing, repetative and unfair
If you want better Pixel Dungeon expirience, then original game is not that good
I guess all games get repetitive eventually. Some just get there faster than others
Re: How to do pixel-perfect rendering in löve?
Instead of pre-deciding on a fixed resolution with black bars around, I adapt to any screen size and orientation. Scaling only by integer multiples (for square pixels), but drawing the upscaled canvas such that the edgemost 1 pixel on all sides "bleeds" outside the screen. That is, the view fills the whole screen, always, but the edgemost pixels are cut off.
You set the minimum view size you want, on the shorter axis of the screen. The resolution is scaled up as much as possible, but not beyond your minimum size. The calculations then give you the actual view size you get.
This code conceptually splits your screen into two parts: A square that fills your screen - this is your "main view" or "safe drawing area" that is the same for any screen orientation; And the rectangle that is left over, which can be used for a GUI (but isn't guaranteed to have any minimum size). The main view square is centered and the side view rectangle split into two on very wide screens.
The key input variables here are:
For example, if I set my target_view_s to 480, and my screen is 1920x1080, I'll get an effective resolution of 2x, with a main view of 1080x1080, and a side panel of 840x1080, which is split into two 420x1080 panels around the centered main square.
If the screen is taller than wide (portrait orientation, i.e. probably smart phone), then the side panel is horizontal and below the main square (for good thumb access).
You set the minimum view size you want, on the shorter axis of the screen. The resolution is scaled up as much as possible, but not beyond your minimum size. The calculations then give you the actual view size you get.
This code conceptually splits your screen into two parts: A square that fills your screen - this is your "main view" or "safe drawing area" that is the same for any screen orientation; And the rectangle that is left over, which can be used for a GUI (but isn't guaranteed to have any minimum size). The main view square is centered and the side view rectangle split into two on very wide screens.
Code: Select all
function love.draw()
-- DIMENSIONAL MATH:
screen_x, screen_y, screen_w, screen_h = love.window.getSafeArea()
short_s = math.min(screen_w, screen_h)
if force_resolution >= 1 then
target_resolution = force_resolution
elseif target_view_s then
target_resolution = math.max(1, math.ceil(short_s / (target_view_s * 2 -1)))
else
target_resolution = 1
end
if resolution ~= target_resolution then
resolution = target_resolution
view_w = math.ceil(screen_w / resolution)
view_h = math.ceil(screen_h / resolution)
view = love.graphics.newCanvas(view_w, view_h)
view:setFilter("nearest", "nearest")
end
short_s = math.ceil(short_s / resolution)
main_x, main_y, main_w, main_h = 0, 0, short_s, short_s
if view_w > view_h then -- wide
side_x, side_y = main_x + main_w, 0
side_w, side_h = view_w - main_w, view_h
else -- tall
side_x, side_y = 0, main_y + main_h
side_w, side_h = view_w, view_h - main_h
end
split = false
if force_split == true or (force_split ~= false and math.min(side_w, side_h) > (short_s / 2)) then
split = true
end
if split then
if view_w > view_h then
-- wide
side_w = math.floor(side_w / 2)
main_x = main_x + side_w
side_x = side_x + side_w
-- fix odd
if (side_w * 2 + main_w) < view_w then
main_w = main_w + 1
side_x = side_x + 1
end
else
-- tall
side_h = math.floor(side_h / 2)
main_y = main_y + side_h
side_y = side_y + side_h
-- fix odd
if (side_h * 2 + main_h) < view_h then
main_h = main_h + 1
side_y = side_y + 1
end
end
end
view_offs_x = math.floor((screen_w-(view_w*resolution))/2)
view_offs_y = math.floor((screen_h-(view_h*resolution))/2)
mouse_x, mouse_y = love.mouse.getPosition()
mouse_x = (mouse_x - view_offs_x) / resolution
mouse_y = (mouse_y - view_offs_y) / resolution
-- GAME DRAWING:
-- note: the edgemost 1 pixel border on all sides may be partially outside visible screen.
-- game
love.graphics.setCanvas(view)
love.graphics.origin()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setBlendMode("alpha")
love.graphics.setShader()
draw_game()
-- blit
love.graphics.setCanvas()
love.graphics.origin()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setBlendMode("alpha")
love.graphics.setShader()
love.graphics.draw(view, view_offs_x,view_offs_y, 0, resolution, resolution)
end
function love.resize(w, h)
resolution = 0 -- force canvas reset
end
- target_view_s - This is your minimum view size in pixels on the shorter axis of the screen.
- force_resolution - Integer multiple pixel size. If not nil, overrides adaptive resolution. E.g. set to 1 for actual pixels. Can be changed over time to create dynamic pixelation effects.
- force_split - If true or false, forces the side view to either split or not split, respectively.
- view, view_w, view_h - The canvas you draw to and its size. At least target_view_s in both dimensions. May be smaller than actual screen because of scaling.
- main_x, main_y, main_w, main_h - Position and size of main view square on the canvas. This is where you draw your game world.
- side_x, side_y, side_w, side_h - Position and size of side view rectangle on the canvas. Draw GUI here.
- split - Whether the side view is wide enough to be split into two.
- mouse_x, mouse_y - Mouse coordinates on the canvas.
- resolution - Integer multiple pixel size. That is, this is how big your pixels are. The scale of the view canvas.
- draw_game() - The function you write that does the actual drawing.
For example, if I set my target_view_s to 480, and my screen is 1920x1080, I'll get an effective resolution of 2x, with a main view of 1080x1080, and a side panel of 840x1080, which is split into two 420x1080 panels around the centered main square.
If the screen is taller than wide (portrait orientation, i.e. probably smart phone), then the side panel is horizontal and below the main square (for good thumb access).
Re: How to do pixel-perfect rendering in löve?
I mean I want what is usually referred to as pixel perfect rendering, where the pixels of one texture will always align with the pixels of any other, even though those pixels may not actually be a 1x1 pixel size. Basically the opposite of how most 'pixel' games look, where sprites may be offset by less than a pixel.pgimeno wrote: ↑Sat Aug 14, 2021 1:38 pm If by "pixel perfect" you mean each pixel is a square with sharp borders, people usually use something like Push for this. https://love2d.org/forums/viewtopic.php?f=5&t=80738
The idea behind Push is to render to a canvas of a smaller resolution, then render that canvas to the screen appropriately scaled.
Who is online
Users browsing this forum: No registered users and 5 guests