A character based programmable text module for LÖVE. Simplifies text operations by providing a way for manipulating how each character in a string behaves and is drawn.
The module and the utf8-l files should be dropped on your project and required like so:
Text = require 'Text'
An object is returned and from that you can create multiple text objects.
Creates a text object and then updates and draws it:
function love.load()
text = Text(10, 10, 'Test text')
end
function love.update(dt)
text:update(dt)
end
function love.draw()
text:draw(10, 10) -- if x, y are omitted here then it will use the x, y passed in the Text object creation step
end
When creating a text object, a table can be passed as the second argument (after the text string) to specify settings for this text. One of those settings allows to change the text's font:
text = Text(10, 10, 'Popo popO', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
})
You can use multiple fonts by adding a new font to the configuration table like this:
text = Text(10, 10, '[Popo](bold) [popO](italic)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
bold = love.graphics.newFont('DJB Almost Perfect Bold.ttf', 72),
italic = love.graphics.newFont('DJB Almost Perfect Italic.ttf', 72),
})
You can also create functions that will change the text in some way:
text = Text(10, 10, '[Popo popO](randomColor)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
randomColor = function(dt, c)
love.graphics.setColor(math.random(32, 222), math.random(32, 222), math.random(32, 222))
end
})
And that should do this:
All functions defined in this way receive two arguments: dt
and c
. The first is just the normal dt
you see in update functions, the second is the character table, which contains general information about the current character. For instance, if we want to make the character move randomly based on how big its position is in the text string (meaning characters more to the right will move more):
text = Text(10, 10, '[Popo popO](move)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
move = function(dt, c)
c.x = c.x + c.position*math.random(-1, 1)/5
c.y = c.y + c.position*math.random(-1, 1)/5
end
})
Multiple functions can operate on a single piece of text and multiple pieces of text be created:
text = Text(10, 10, '[Popo](move) [popO](move; rotateScale)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
move = function(dt, c)
c.x = c.x + c.position*math.random(-1, 1)/5
c.y = c.y + c.position*math.random(-1, 1)/5
end,
rotateScale = function(dt, c)
c.r = math.random(-1, 1)/10
c.sx = math.random(10, 20)/10
c.sy = math.random(10, 20)/10
end
})
Functions act every update and the character table holds information about each character. If you want to set some state that can be updated or used on the update functions you'll need to use Init
functions. They're just like normal functions except they have Init
after their name and they only receive the character table as an argument. So, for instance:
text = Text(10, 10, '[Popo popO](shake)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
shakeInit = function(c)
c.anchor_x = c.x
c.anchor_y = c.y
end,
shake = function(dt, c)
c.x = c.anchor_x + c.position*math.random(-1, 1)/2
c.y = c.anchor_y + c.position*math.random(-1, 1)/2
end
})
In this example the shakeInit
function gets called as the text object gets created, which means that for every character in the string, the anchor_x, anchor_y
attributes are set to the character's initial positions. Then, the shake
function gets called every update and uses those values to shake the characters. In the next example, the Init
function is used to set some state that will then be changed in the update function:
text = Text(10, 10, '[Popo popO](textbox)', {
font = love.graphics.newFont('DJB Almost Perfect.ttf', 72),
textboxInit = function(c)
c.t = 0
end,
textbox = function(dt, c)
c.t = c.t + dt
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(r, g, b, 0)
if c.t > c.position*0.2 then
love.graphics.setColor(r, g, b, 255)
end
end
})
So in this case a textbox effect can be created by setting a time variable for each character and then only drawing that character (alpha = 255)
if this time is over a certain value based on its string position.
Values can also be passed to functions that are defined to receive them:
text = Text(10, 10, '[Popo popO](color: 222, 222, 222)', {
font = love.graphics.setFont('DJB Almost Perfect.ttf', 72),
color = function(dt, c, r, g, b)
local n_characters = #c.str_text
local i = n_characters - c.position
love.graphics.setColor(32 + i*16*r/255, 32 + i*16*g/255, 32 + i*16*b/255)
end
})
And changing the parameters to color: 222, 111, 222
:
Currently values that can be passed are numbers
, strings
and booleans
. I haven't gotten around to implementing tables yet.
In case you want to have even more control over how each character is drawn you can also specify a custom draw function:
text = Text(10, 10, 'Popo popO', {
customDraw = function(x, y, c)
love.graphics.print(c.character, (x or c.text.x) + c.x, (y or c.text.y) + c.y, c.r or 0, c.sx or 1, c.sy or 1, 0, 0)
end,
})
This function should be named customDraw
and it should receive the x, y position as well as the character being drawn. The function above is the default draw call that Popo uses for each character.
[]:
brackets are used to envelop a piece of text so that functions can be applied to it
():
parenthesis envelop the functions which specify how the text behaves, they must come right after the brackets
(function):
functions with no arguments simply need their name specified
(function1; function2):
multiple functions applied to the same brackets are separated by ;
(function: arg1, arg2, ..., argn):
multiple arguments are separated by ,
and start after a :
(function1: arg1, arg2; function2: arg1):
multiple functions with arguments just follow the previous definitions
@:
the @
character is used to escape special characters in text, for instance:
text = Text('@[Popo popO@]')
Will produce [Popo popO]
. To escape @
itself use @@
. It's also used to break into a new line via @n
.
The text object has a few variables that can be specified on its configuration table:
font:
sets the font to be used by default. You can also set other types of fonts directly (bold, bold_italic, italic, light, etc) and they will work like a function inside a tag:
text = Text(10, 10, '[Light text](light) [bold + italic text](bold_italic) normal text', {
font = love.graphics.setFont('DJB Almost Perfect.ttf', 72),
light = love.graphics.setFont('DJB Almost Perfect Light.ttf', 72),
bold_italic = love.graphics.setFont('DJB Almost Perfect Bold Italic.ttf', 72),
})
line_height:
the actual line height drawn in pixels is the multiplication of this number by the font height, so, for instance, if your font is of size 20 and you want the line height to be a bit bigger than that, like, let's say 28, you want to set line_height
to 20*x = 28 -> x = 28/20 = 1.4
wrap_width:
maximum width in pixels that this text can go, after that it will wrap to the next line
align_right:
if wrap_width
is set, will align text to the right if set to true
align_center:
if wrap_width
is set, will align text to the center if set to true
justify:
if wrap_width
is set, will align text to be perfectly aligned to both left and right if set to true
Here's an example of some of those settings being used:
text = Text(10, 10, 'Popo popO', {
font = love.graphics.setFont('DJB Almost Perfect.ttf', 72),
wrap_width = 250,
justify = true,
line_height = 2,
})
The text object also has a few read-only variables:
config:
reference to the configuration table passed on this text object's creation
str_text:
the text string as it will be printed on the screen
n_lines:
the number of lines this text has
new_line_positions:
an array containing all new line positions in the text string, so, for instance, if on the first line of this text the character 24
breaks into a new line because wrap_width
is set, then the number 24
will be the first value in this array
The character table has a few variables that might be useful:
x, y:
the x, y position of the character
r:
this character's rotation, isn't set to anything initially
sx, sy:
this character's x and y scales, aren't set to anything initially
character:
the character string
position:
this character's position in relation to the entire string, starts at 1
text:
reference to the text object
str_text:
the string representation of the text this character belongs to
line:
the line number this character belongs to if the text has more than one line
You can do whatever you want with this. See the LICENSE.