Difference between revisions of "TLfres"
m (Added link to forum thread) |
(Rewrite, since you can't even find the original anymore.) |
||
Line 1: | Line 1: | ||
== About == | == About == | ||
− | TLfres aims to make it | + | 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 [http://www.opensource.org/licenses/zlib-license.php ZLIB license]. | It's under the [http://www.opensource.org/licenses/zlib-license.php ZLIB license]. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Setup == | == 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 | + | 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 | + | # Put tlfres.lua in your game's folder |
− | # At the top of main.lua, add the line <code>require"TLfres"</code> | + | # At the top of main.lua, add the line <code>local TLfres = require "TLfres"</code> |
− | # At the beginning of love.draw(), add the line <code>TLfres. | + | # At the beginning of love.draw(), add the line <code>TLfres.beginRendering(800, 600)</code> |
− | # At the bottom of love.draw() add the line <code>TLfres. | + | # At the bottom of love.draw() add the line <code>TLfres.endRendering()</code> |
+ | # Whenever you need to know the current scaling factor, call <code>TLfres.getScale(800, 600)</code> | ||
+ | # Whenever you would call love.mouse.getPosition, instead call <code>TLfres.getMousePosition(800, 600)</code> | ||
+ | # Whenever you would call love.graphics.setPointSize(x), instead call <code>love.graphics.setPointSize(x * TLfres.getScale())</code> (Love doesn't scale point sizes automatically) | ||
== FAQ == | == FAQ == | ||
Line 24: | Line 20: | ||
;Q) What resolutions / aspect ratios does it support? | ;Q) What resolutions / aspect ratios does it support? | ||
− | :A) | + | :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? | ;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. | + | :A) Nope! Just call TLfres.beginRendering() with the resolution you built your game for (see below). |
== Functions == | == Functions == | ||
− | ====TLfres. | + | ====TLfres.beginRendering==== |
− | <source lang="lua">TLfres. | + | <source lang="lua">TLfres.beginRendering(width, height, centered)</source> |
− | Call this | + | 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()). |
− | {{param| | + | {{param|number|width|Width of the target canvas.}} |
− | {{param|number| | + | {{param|number|height|Width of the target canvas.}} |
− | + | {{param|boolean|centered (false)|If true, (0, 0) is the middle of the canvas, otherwise it is the top-left corner.}} | |
− | + | ||
+ | ====TLfres.endRendering==== | ||
+ | <source lang="lua">TLfres.endRendering(letterboxColor)</source> | ||
+ | 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. | ||
+ | {{param|table|letterboxColor ({0,0,0, 255})|The color of the letterboxes, given in RGBA (just like [[love.graphics.setColor#Function 2|love.graphics.setColor]]). Defaults to black.}} | ||
+ | |||
+ | ====TLfres.getScale==== | ||
+ | <source lang="lua">TLfres.getScale(width, height)</source> | ||
+ | |||
+ | {{param|number|width|The canvas width expected by your code.}} | ||
+ | {{param|number|height|The canvas height expected by your code.}} | ||
+ | |||
+ | ====TLfres.getMousePosition==== | ||
+ | <source lang="lua">TLfres.getMousePosition(width, height)</source> | ||
+ | Use this any time you would normally call [[love.mouse.getPosition]]. The returned position is scaled to the given dimensions. | ||
+ | |||
+ | {{param|number|width|The canvas width expected by your input code.}} | ||
+ | {{param|number|height|The canvas height expected by your input code.}} | ||
+ | |||
+ | ==tlfres.lua== | ||
+ | <source lang="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 | ||
+ | </source> | ||
+ | |||
+ | ==Example== | ||
+ | <source lang="lua"> | ||
+ | local TLfres = require "tlfres" | ||
+ | |||
+ | local CANVAS_WIDTH = 800 | ||
+ | local CANVAS_HEIGHT = 600 | ||
+ | local POINT_SIZE = 1 | ||
+ | |||
+ | function love.mouse.getPosition() | ||
+ | return TLfres.getMousePosition(CANVAS_WIDTH, CANVAS_HEIGHT) | ||
+ | end | ||
+ | |||
+ | function love.draw() | ||
+ | local scale = | ||
+ | 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(true) -- Draw black letterbox | |
− | + | end | |
− | + | </source> | |
− | |||
− | |||
− | |||
{{#set:LOVE Version=0.7.0}} | {{#set:LOVE Version=0.7.0}} |
Revision as of 02:22, 28 October 2017
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()
return TLfres.getMousePosition(CANVAS_WIDTH, CANVAS_HEIGHT)
end
function love.draw()
local scale =
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(true) -- Draw black letterbox
end