TLfres
Contents
About
TLfres aims to make it easy to make any game run at any screen resolution. Using it doesn't even require you to rewrite your graphics code!
It's under the ZLIB license.
Setup
Everything in the module is contained in the TLfres namespace, so don't worry about it messing with your global variables. Assuming your game was built to use the Love-default resolution of 800×600:
- Put tlfres.lua in your game's folder
- At the top of main.lua, add the line
local TLfres = require "tlfres"
- At the beginning of love.draw(), add the line
TLfres.beginRendering(800, 600)
- At the bottom of love.draw() add the line
TLfres.endRendering()
- Whenever you need to know the current scaling factor, call
TLfres.getScale(800, 600)
- Whenever you would call love.mouse.getPosition, instead call
TLfres.getMousePosition(800, 600)
- Whenever you would call love.graphics.setPointSize(x), instead call
love.graphics.setPointSize(x * TLfres.getScale())
(Love doesn't scale point sizes automatically)
FAQ
- Q) Why should I use this instead of love.graphics.scale?
- A) Because even stretching the screen to a given res requires tricky math, which TLfres can do for you. Furthermore, most people hate stretched graphics - that's why you should use TLfres's letterboxing option. Instead of stretching the screen, it resizes it in the correct ratio and draws black boxes at the top and bottom if needed.
- Q) What resolutions / aspect ratios does it support?
- A) Any.
- Q) I made my game to use a non-default resolution. Will I have to change everything to make TLfres work with it?
- A) Nope! Just call TLfres.beginRendering() with the resolution you built your game for (see below).
Functions
TLfres.beginRendering
TLfres.beginRendering(width, height, centered)
Call this at the very beginning of love.draw(), before drawing anything. This scales and translates the screen coordinates as needed by the current screen resolution. If you want to draw something at absolute screen coordinates, do it before this function (or after endRendering()).
number width
- Width of the target canvas.
number height
- Width of the target canvas.
boolean centered (false)
- If true, (0, 0) is the middle of the canvas, otherwise it is the top-left corner.
TLfres.endRendering
TLfres.endRendering(letterboxColor)
Call this at the end of love.draw(), after drawing everything. This draws letterboxes to trim any graphics which should be out of the screen. It defaults to black letterboxes.
table letterboxColor ({0,0,0, 255})
- The color of the letterboxes, given in RGBA (just like love.graphics.setColor). Defaults to black.
TLfres.getScale
TLfres.getScale(width, height)
number width
- The canvas width expected by your code.
number height
- The canvas height expected by your code.
TLfres.getMousePosition
TLfres.getMousePosition(width, height)
Use this any time you would normally call love.mouse.getPosition. The returned position is scaled to the given dimensions.
number width
- The canvas width expected by your input code.
number height
- The canvas height expected by your input code.
tlfres.lua
local lwGetMode = _G.love.window.getMode
local lgPush = _G.love.graphics.push
local lgPop = _G.love.graphics.pop
local lgTranslate = _G.love.graphics.translate
local lgScale = _G.love.graphics.scale
local lgRectangle = _G.love.graphics.rectangle
local lgSetColor = _G.love.graphics.setColor
local lmGetPosition = _G.love.mouse.getPosition
local min = math.min
local TLfres = {}
local lastMouseX, lastMouseY = 0, 0
local currentlyRendering
-- Internal helper function
local function _getRawMousePosition(width, height)
local x, y = lmGetPosition()
local w, h = lwGetMode()
local scale = min(w/width, h/height)
return (x - (w - width * scale) * 0.5)/scale, (y - (h - height * scale) * 0.5)/scale
end
-- Use this any time you would normally call love.mouse.getPosition.
-- The returned position is scaled to the given dimensions.
function TLfres.getMousePosition(width, height)
local x, y = _getRawMousePosition(width, height)
if x >= 0 and x <= width and y >= 0 and y <= height then
lastMouseX, lastMouseY = _getRawMousePosition(width, height)
end
return lastMouseX, lastMouseY
end
-- Calculate the current scale based on the desired dimensions and current ones
-- If called within a rendering block, width and height are optional.
function TLfres.getScale(width, height)
if currentlyRendering then
width = width or currentlyRendering[1]
height = height or currentlyRendering[2]
end
local w, h = lwGetMode()
return min(w/width, h/height)
end
-- Zooms and centers to fit width×height into the current window.
-- 0,0 is at the top-left of the canvas, or the middle if centered is true.
-- Use love.graphics.push before this and love.graphics.pop after done rendering
function TLfres.beginRendering(width, height, centered)
if currentlyRendering then
error("Must call tlfres.endRendering before calling beginRendering.")
return
end
currentlyRendering = {width, height}
lgPush()
local w, h = lwGetMode()
local scale = min(w/width, h/height)
lgTranslate((w - width * scale) * 0.5, (h - height * scale) * 0.5)
lgScale(scale)
if centered then
lgTranslate(0.5 * width, 0.5 * height)
end
return scale
end
local _black = {0, 0, 0, 255}
-- Pops out of the transform; if letterboxColor is true, draws black letterbox
-- bars. letterboxColor can also be any {r, g, b, a} table.
function TLfres.endRendering(letterboxColor)
if not currentlyRendering then
error("Must call tlfres.beginRendering before calling endRendering.")
return
end
local width, height = currentlyRendering[1], currentlyRendering[2]
currentlyRendering = nil
lgPop()
local w, h = lwGetMode()
local scale = min(w/width, h/height)
width, height = width * scale, height * scale
lgSetColor(letterboxColor or _black)
lgRectangle("fill", 0, 0, w, 0.5 * (h - height)) -- top
lgRectangle("fill", 0, h, w, -0.5 * (h - height)) -- bottom
lgRectangle("fill", 0, 0, 0.5 * (w - width), h) -- left
lgRectangle("fill", w, 0, -0.5 * (w - width), h) -- right
end
return TLfres
Example
local TLfres = require "tlfres"
local CANVAS_WIDTH = 800
local CANVAS_HEIGHT = 600
local POINT_SIZE = 1
function love.mouse.getPosition() -- Override the standard function with our helper function
return TLfres.getMousePosition(CANVAS_WIDTH, CANVAS_HEIGHT)
end
function love.draw()
tlfres.beginRendering(CANVAS_WIDTH, CANVAS_HEIGHT)
love.graphics.setPointSize(tlfres.getScale()*POINT_SIZE) -- Point size doesn't scale automatically, so multiply it manually.
love.graphics.points(400, 300) -- Will draw at the center of the canvas no matter how the screen is resized.
tlfres.endRendering() -- Draw black letterbox
end