I cannot build an html5 version with release 2024.3713. I have tried several different apps, always get the same error (and a blank screen after the Solar2d logo). Works fine on 3211 and 3212
can you create a new blank project from corona simulator and try to build as HTML5 just to make sure that the error is caused by your code or by simulator itself
I built three of the sample projects, simple ones, and they worked. But none â 0 â of my projects that have worked for years on every build until 3713 work with 3713 html builds, and fail the same way.
None of the examples seem to use composer, all use only main.lua.
I just ran a bunch of programs I have sitting around. I did manage to trigger this on one of them, otherwise okay. Iâm now struggling to pin down the troublesome one again, annoyingly⌠any chance you could send a tiny example that reproduces it?
Weâre merging this stuff, which includes lots of graphics stuff, so WebGL hiccups arenât totally unexpected. (Would also like to know about any non-web weirdness too, of course.)
Build 3712 is everything up to that point, so fine to use.
Sure. I put 3 projects on my server, I will send you the links in a message (I donât need to share all that code with the world). One is a 10-year-old project, one a very simple retro arcade game, one a (programming) simple economics game. All build in 3712 and not in 3713.
None are new, current projects.
I definitely see the issue, and consistently.
I realized one or two of my programs was triggering the error, but the browser stopped nagging after the first incident, thus the âstruggling to pin downâ thing.
The one in question (but not others) actually worked fine in Safari, but not in Opera GX. More strangely, it had some extra scripts (was just a copy-paste to make the project) and only has an issue then; if I strip it down to just a main.lua it was fine.
My theory was that somehow a âlongâ initialization was involved: in my case the slight extra size; in your case maybe that and / or the fonts, which I see have special attention in the web loading code. But some other large ones just work. I saw the error in the Angle project code and it suggested the renderer somehow gets inconsistent, so I had some theory it was tied to the new changes, but havenât seen anything that bears that out.
I was discussing this with @Siu and (with my project) he also found some weirdness with build.settings, but nothing definitive. The debugging techniques are all pretty unfamiliar.
Fonts make some sense â I do load special fonts in all my projects, would be a bit of work to remove them. I removed all references to non-system fonts in one project, made no difference. But they might still be contained in the build.
I tried comparing the build.settings, and nothing obvious I could see, but will try a few more things.
My current project did not work in Safari, Arc, Brave, or Chrome.
I advice you to implement bitemap fonts with that special fonts.
I have some of my HTML5 games using bitmap fonts. Iâve made some improvements to the Ponyfont code because I found a few bugs in it. If you choose this option, I can send you my version of the Ponyfont code.
Additionally, you can use this Bitmap Font Generator software (freeware and open source) to generate your bitmap fonts. You can download it here:
https://www.angelcode.com/products/bmfont/
If you decide to go with this option, I can also share the configuration I use in the software.
PD: Sorry, I made a mistake in my response. That reply was intended for @dmarques42
I will try, but it will take a while, there is a lot to replace in my projects. Last time I tried bitmap fonts they were all fuzzy, and I did not like the result (also slower). Maybe it is better now. The angelcode you recommended is for Windows, I have a mac. But I can try substituting some for one project.
I guess I want to try ponyfont, but it looks like I have to replace every display.newText in my app, is that right? That is a lot of re-coding.
Yes, Iâm afraid youâll have to replace each text in your code. But the replacement consists of:
Never create a text including the âparentâ parameter in the options of display.newText
. Instead, do something like this:
if platform == "html5" then
myGroup:insert(myText.raw)
else
myGroup:insert(myText)
end
Other considerations:
To create your bitmap font, first review your entire code and identify the largest font size you use. Then, in the settings of the bitmap font generator software, set the font size to double the maximum size used in your app.
The configuration I use in bitmapfont64.exe (the version I downloaded from https://www.angelcode.com/products/bmfont/) to export the bitmap font is as follows:
In Font Settings:
- Charset: Unicode
- Match char height: Checked
- Render from TrueType outline: Do NOT check.
- ClearType: Checked (improves rendering quality)
- Font Smoothing: Checked (enhances text appearance)
- Super Sampling: Checked - choose level: 2 (use this when text quality is a priority, such as in HTML5 where texts can appear blurry)
In Export Options:
- Paddings: 2 on each side
- Spacing: 0 for all
- Equalize the cell height: Checked (this ensures the text is properly centered along the âyâ axis).
- Width and Height: Width and height of the bitmap fontâs image sheet
- Bit Depth: Select 32 bits
-
Pack chars in multiple channels: Checked (enabled)
âŚ
Last but not least, Iâve attached the code for ponyfont.lua
below with some changes I implemented to fix several bugs:
-- ponyfont 0.3
-- A bitmap font loader/render for CoronaSDK
local M = {}
local cache = {} -- cache for loaded fonts
-- If you use UTF-8 characters, you will need to add this plugin to your
-- build settings
-- https://docs.coronalabs.com/plugin/utf8/index.html
local utf8 -- or require("lua-utf8") via luarocks
-- Solar2D's utf8 plugin does not have html5 support
if platform == "html5" then
local floor = math.floor
utf8 = {}
function utf8.codes(str)
local pos = 1 -- starting position in the string
str = tostring(str) -- convert to a string (can be a number)
local _len = #str -- string length
return function()
if pos <= _len then
local code
local byte = string.byte(str, pos)
if byte < 0x80 then
code = byte
pos = pos + 1
elseif byte < 0xE0 then
code = (byte % 0x40) * 0x40 + string.byte(str, pos + 1) % 0x80
pos = pos + 2
elseif byte < 0xF0 then
code = (byte % 0x20) * 0x1000 + (string.byte(str, pos + 1) % 0x80) * 0x40 + string.byte(str, pos + 2) % 0x80
pos = pos + 3
else
code = (byte % 0x10) * 0x40000 + (string.byte(str, pos + 1) % 0x80) * 0x1000 + (string.byte(str, pos + 2) % 0x80) * 0x40 + string.byte(str, pos + 3) % 0x80
pos = pos + 4
end
return pos - 1, code
end
end
end -- end of 'utf8.codes()'
utf8.char = function(...)
local function encode(code)
if type(code) ~= "number" then
code = tonumber(code)
if not code then
--error("Expected a number, got " .. type(code) .. " (" .. tostring(code) .. ")")
end
end
if code <= 0x7F then
return string.char(code)
elseif code <= 0x7FF then
return string.char(
0xC0 + floor(code / 0x40),
0x80 + (code % 0x40)
)
elseif code <= 0xFFFF then
return string.char(
0xE0 + floor(code / 0x1000),
0x80 + (floor(code / 0x40) % 0x40),
0x80 + (code % 0x40)
)
elseif code <= 0x10FFFF then
return string.char(
0xF0 + floor(code / 0x40000),
0x80 + (floor(code / 0x1000) % 0x40),
0x80 + (floor(code / 0x40) % 0x40),
0x80 + (code % 0x40)
)
else
--error("Unicode code point out of range: " .. tostring(code))
end
end
local chars = {}
for _, code in ipairs({...}) do
if type(code) ~= "number" then
code = tonumber(code)
if not code then
--error("Expected a number, got " .. type(code) .. " (" .. tostring(code) .. ")")
end
end
table.insert(chars, encode(code))
end
return table.concat(chars)
end
else
utf8 = require("plugin.utf8") -- or require("lua-utf8") via luarocks
end
-- property update events by Jonathan Beebe
-- https://coronalabs.com/blog/2012/05/01/tutorial-property-callbacks/
local function addPropertyUpdate(obj)
local t = {}
t.raw = obj
local mt = {
__index = function(_,k)
if k == "raw" then
return rawget(t, "raw")
end
-- pass method and property requests to the display object
if type(obj[k]) == 'function' then
return function(...) arg[1] = obj; obj[k](unpack(arg)) end
else
return obj[k]
end
end,
__newindex = function(tb,k,v)
-- dispatch event before property update
local event = {
name = "propertyUpdate",
target=tb,
key=k,
value=v
}
obj:dispatchEvent(event)
-- update the property on the display object
obj[k] = v
end
}
setmetatable(t, mt)
return t
end
local function parseFilename(filename)
return string.match(filename,"(.-)([^\\/]-%.?([^%.\\/]*))$")
end
local function extract(s, p)
return string.match(s, p), string.gsub(s, p, '', 1)
end
function M.newText(options)
options = options or {}
-- Modified .fnt loading code from bmf.lua
-- Contacted author and he released under CC0
local function loadFont(name)
-- load the fnt
local path, filename, ext = parseFilename(name)
local font = { info = {}, spritesheets = {}, sprites = {}, chars = {}, kernings = {} }
local contents = io.lines(system.pathForFile(path .. filename, system.ResourceDirectory))
for line in contents do
local t = {}
local tag
tag, line = extract(line, '^%s*([%a_]+)%s*')
while string.len(line) > 0 do
local k, v
k, line = extract(line, '^([%a_]+)=')
if not k then break end
v, line = extract(line, '^"([^"]*)"%s*')
if not v then
v, line = extract(line, '^([^%s]*)%s*')
end
if not v then break end
t[k] = v
end
if tag == 'info' or tag == 'common' then
for k, v in pairs(t) do font.info[k] = v end
elseif tag == 'page' then
font.spritesheets[1 + t.id] = { file = t.file, frames = {} }
elseif tag == 'char' then
t.letter = utf8.char(t.id)
font.chars[t.letter] = {}
for k, v in pairs(t) do
font.chars[t.letter][k] = v
end
if 0 + font.chars[t.letter].width > 0 and 0 + font.chars[t.letter].height > 0 then
font.spritesheets[1 + t.page].frames[#font.spritesheets[1 + t.page].frames + 1] = {
x = 0 + t.x,
y = 0 + t.y,
width = 0 + t.width,
height = 0 + t.height,
}
font.sprites[t.letter] = {
spritesheet = 1 + t.page,
frame = #font.spritesheets[1 + t.page].frames
}
end
elseif tag == 'kerning' then
font.kernings[utf8.char(t.first) .. utf8.char(t.second)] = 0 + t.amount -- old code
end
end
for k, v in pairs(font.spritesheets) do
font.spritesheets[k].sheet = graphics.newImageSheet(path .. v.file, v)
end
for k, v in pairs(font.sprites) do
font.sprites[k].frame = v.frame
end
return font
end -- end of 'loadFont'
-- create displayGroup instance
local instance = display.newGroup()
if options.parent then
options.parent:insert(instance)
end
-- load font if not in cache
local fontFile = options.font or "default"
if not cache[fontFile] then
cache[fontFile] = loadFont(fontFile)
end
instance.bitmapFont = cache[fontFile]
-- Brand-spanking new render code with scaling to fontSize,
-- word wrapping to width and general performance and
-- readability updates
function instance:anchor()
local w,h = self._width or self.width, self.height
local x,y = self.anchorX or 0.5, self.anchorY or 0.5
for i = self.numChildren, 1, -1 do
if self[i]._x and self[i]._y then
self[i].x = self[i]._x
self[i].y = self[i]._y
self[i]:translate(-w * x, -h * y)
end
end
end
function instance:justify(align)
if not self._width then return false end
-- grab first letter
local letter = self[1]
if not letter then return false end
-- default
align = self.align or "left"
local x, last, lastX, w = 0, 1, letter.x, self._width or self.width
local currY
-- push stuff around
local iFinal = self.numChildren
for i = 1, iFinal do -- iterates over all characters in the text
if self[i]._x and self[i]._y then
x = self[i].x + self[i].width
local newLine = false
if i > 1 and ( self[i].y - currY ) > 3 then -- to detect line jump
newLine = true
end
currY = self[i].y
--if ( x < lastX ) or ( i == iFinal ) then -- original code
if newLine or ( i == iFinal ) then -- wrapped
print ("new line")
-- diff is based on assigned width
local diff = w - lastX
if align == "right" then
diff = diff - w*0.5
elseif align == "center" then
diff = diff * 0.5 - w*0.25
else
diff = 0
end
local jFinal = i - ( (i == self.numChildren) and 0 or 1 )
-- positions characters for the same line
for j = last, jFinal do
self[j]:translate(diff,0)
self[i].xScale = self[i]._xScale
self[i].yScale = self[i]._yScale
end
last = i
end
lastX = x - self[i].width*0.5
end
end
end -- end of justify
function instance:render( size )
local text = self.text
local font = self.bitmapFont
local info = font.info
local scale = self.fontSize / info.size
if size then
scale = self.size / info.size
end
if self.size ~= self.fontSize then
scale = self.size / info.size
end
local fill = self.fill or {1}
-- clear previous text
for i = self.numChildren, 1, -1 do
display.remove(self[i])
end
-- locals
local x, y = 0, 0
local last = ''
local lastWord = 0
local function kern(x, pair)
if font.kernings[pair] then
x = x + font.kernings[pair]
end
return x
end
if text then
local prevLetter = nil
for _, codepoint in utf8.codes(text) do
local letter = utf8.char(codepoint)
if letter == '\n' then -- newline
x, y = 0, y + info.lineHeight
elseif font.chars[letter] then
if letter ~= " " and tonumber(font.chars[letter].width) > 0 and tonumber(font.chars[letter].height) > 0 then
local glyph = display.newImage(font.spritesheets[font.sprites[letter].spritesheet].sheet, font.sprites[letter].frame)
x = kern(x, last .. letter)
glyph.anchorX, glyph.anchorY = 0, 0
glyph.xScale = scale
glyph.yScale = scale
glyph.x = scale * (font.chars[letter].xoffset + x)
glyph.y = scale * (font.chars[letter].yoffset + y)
glyph._x = glyph.x -- original offset from self's x
glyph._y = glyph.y -- original offset from self's y
glyph._xScale = glyph.xScale
glyph._yScale = glyph.yScale
glyph.chr = letter
last = letter
lastWord = lastWord + 1
self:insert(glyph)
if fill[2] then
glyph:setFillColor(fill[1], fill[2], fill[3])
else
glyph:setFillColor(fill[1])
end
elseif letter == " " then
lastWord = 0 -- save x of last word
end
x = x + font.chars[letter].xadvance
if self._width and (x * scale) > self._width then
-- jump to a new line
--print ("NEW LINE")
x = 0
y = y + info.lineHeight
local init = self.numChildren - lastWord + 1
for i = init, self.numChildren do
local glyph = self[i]
local wrapped = glyph.chr -- current char
x = kern(x, last .. wrapped)
glyph.x = scale * (font.chars[wrapped].xoffset + x)
glyph.y = scale * (font.chars[wrapped].yoffset + y)
glyph.xScale = scale
glyph.yScale = scale
glyph._x = glyph.x -- orginal offset from self's x
glyph._y = glyph.y -- orginal offset from self's y
x = x + font.chars[wrapped].xadvance
last = wrapped
end
end
end
prevLetter = letter
end
end
self:anchor()
self:justify()
end -- end of render
instance = addPropertyUpdate(instance)
function instance:propertyUpdate(event)
if event.key == "text" then
self.text = event.value
self:render()
elseif event.key == "anchorX" then
self.anchorX = event.value
self:anchor()
elseif event.key == "anchorY" then
self.anchorY = event.value
self:anchor()
elseif event.key == "align" then
self.align = event.value
self:justify()
elseif event.key == "fontSize" then
self.fontSize = event.value
self:render()
elseif event.key == "width" then
self._width = event.value
self:render()
elseif event.key == "x" then
self._x = event.value
self.x = self._x
elseif event.key == "y" then
self._y = event.value
self.y = self._y
elseif event.key == "size" then
self.size = event.value
self:render(true)
elseif event.key == "fill" then
self.fill = event.value
self:render()
end
end
function instance:finalize(event)
self:removeEventListener("propertyUpdate")
end
-- set options
instance.align = options.align or "left"
instance.fontSize = options.fontSize or 24
instance.size = options.size or instance.fontSize
instance.x = options.x or 0
instance.y = options.y or 0
instance._x, instance._y = instance.x, instance.y
instance._width = options.width
instance.text = options.text
instance.fill = {1}
instance:render()
-- add listeners
instance:addEventListener("propertyUpdate")
instance:addEventListener("finalize")
function instance:updateSize(newSize)
if newSize and type(newSize) == "number" then
local scaleFactor = newSize / self.fontSize
for i = 1, self.numChildren do
self[i].xScale = scaleFactor
self[i].yScale = scaleFactor
end
self.fontSize = newSize
end
end
return instance
end -- end of 'M.newText()' function
return M