I am learning as well.
I have been looking at this problem and working on a better solution. I want to understand Lua and Corona SDK better.
My previous solution is not great. It adjusts every time a line is added and looks kind of off.
I have a much better solution.
Each letter is placed in as a newText object into a fixed size dialog. The letters are spaced evenly.
You are going to want to use a mono spaced font so that it looks better but in this code I use what the simulator defaults to.
Here is a link to Legend of Zelda fonts you might want to use.
This code limits the message to 3 lines of 32 characters by truncating extra letters and lines. In your game it would be better to just ensure that the message fits the dialog rather than cutting it off.
I am very new to Lua and Corona so the code can most likely be improved on. But it looks way better than my previous example.
----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- -- Your code here local function split\_lines(input\_string) local \_lines = {} for line in string.gmatch(input\_string, "([^\r\n]+)") do table.insert(\_lines, line) end return \_lines end local function split\_words(input\_string) local \_words={} for word in string.gmatch(input\_string, "([^%s]+)") do table.insert(\_words, word) end return \_words end local function get\_truncated\_text(text) -- dialog holds 32 chars nicely so we clip the rest if string.len(text) \> 32 then text = string.sub(text,1, 32) end return text end local function get\_truncated\_array(array) -- return only the first three items of the array while #array \> 3 do table.remove(array) end return array end --============================================== -- dialog class local Dialog = {} Dialog.\_\_index = Dialog function Dialog.close\_dialog(event) Dialog.\_spit\_timer = nil Dialog.rect:removeSelf() Dialog.rect = nil for i,v in ipairs(Dialog.\_l) do Dialog.\_l[i]:removeSelf() Dialog.\_l[i] = nil end Dialog.\_l = nil end function Dialog.new(message) Dialog.rect = display.newRect(0,0, 300, 100) -- this is the black background dialog box Dialog.rect.x = display.contentCenterX Dialog.rect.y = display.contentCenterY / 2 Dialog.rect:setFillColor(0,0,0) Dialog.rect.isVisible = false Dialog.rect:addEventListener("close", Dialog.close\_dialog) Dialog.\_text = {letters=message or "", index = 1, letter\_count = 0} -- this holds the message, char index and number of letters Dialog.\_l={} -- table containing the newText objects for each letter Dialog.\_x = Dialog.rect.contentBounds.xMin + 3 -- \_x and \_y are the char pixel locations within the dialog box (rect) Dialog.\_y = Dialog.rect.contentBounds.yMin + 3 Dialog.\_line\_index = 1 -- the dialog allows 3 lines and this tells us what line we are working on Dialog.\_char\_index = 1 -- the current character being displayed -- this routine splits the message up into lines based on the \n's found in the message -- each line is truncated to 32 chars max -- anything beyond the 3rd line is also truncated local \_lines = split\_lines(message) for i,line in ipairs(\_lines) do --truncate the line \_lines[i] = get\_truncated\_text(line) Dialog.\_text.letter\_count = Dialog.\_text.letter\_count + #\_lines[i] --get the words local \_words = split\_words(\_lines[i]) end Dialog.\_lines = \_lines return setmetatable({}, Dialog) end function Dialog.\_spit() -- this function creates a newText object for each letter and displays that text local m = display.newText(string.sub(Dialog.\_lines[Dialog.\_line\_index], Dialog.\_char\_index, Dialog.\_char\_index), Dialog.\_x, Dialog.\_y) m.anchorY = 0 m.anchorX = 0 table.insert(Dialog.\_l, m) -- store the display object for later release Dialog.\_x = Dialog.\_x + 15 Dialog.\_char\_index = Dialog.\_char\_index + 1 if Dialog.\_char\_index \> #Dialog.\_lines[Dialog.\_line\_index] then -- do the next line if there is one Dialog.\_line\_index = Dialog.\_line\_index + 1 if Dialog.\_line\_index \> #Dialog.\_lines then -- thats all there is for letters so activate the dialog for a tap event timer.cancel(Dialog.\_spit\_timer) -- probably don't need to do this Dialog.rect:addEventListener("tap", Dialog.close\_dialog) return end -- there is another line Dialog.\_x, Dialog.\_y, Dialog.\_char\_index = Dialog.rect.contentBounds.xMin + 3, Dialog.\_y + 15, 1 end m = nil end function Dialog.show() Dialog.rect.isVisible = true Dialog.\_spit\_timer = timer.performWithDelay(50, Dialog.\_spit, Dialog.\_text.letter\_count) end setmetatable(Dialog, { \_\_call = function(\_, ...) return Dialog.new(...) end }) -- end dialog class --=========================================== display.setDefault("background", .7,.7,.7) local d = Dialog("You need to find \nthe Gilded Sword,\nso get to it!") d.show()
After the message is printed you can click on the dialog to close it.