Pixel font [how do I tint a mesh?]

How would I go about making a pixel font? What I mean by that is how would I create a class that would have a function to convert the characters of a string into sprites that look like (pixelated) letters? The first letter/sprite would have the original x, y coordinates in the setup function, but the other letters/sprites would use the same y coordinate but the x value would change after each letter/sprite.

For example if I set the coordinates to 20 (x), and 500 (y) the first letter/sprite coordinates would of course be 20 , and 500 (x, y), but the second letter/sprite would be 40, and 500 (x + 20, y), but the third letter/sprite would be 60, and 500 (x + 20 * 2, x), but the fourth would be 80, and 500 (x + 20 * 3, y), and so on. Depending on the width of the letter/sprite I may add more or less to x. I would also add more to x if there was a space.

I hope I made myself clear enough. I tried my best to make that understandable

displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)

function draw()
    background(40, 40, 50)
    fill(255)
    for z=33,127 do
        text(string.char(z),10.5*(z-31),500)
    end
end

Or if you want to do it from a string. Just have to figure out the pixelated part.

displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)

function setup()
    str="abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"    
end

function draw()
    background(40, 40, 50)
    fill(255)
    for z=1,#str do
        text(string.sub(str,z,z),18*z,500)
    end
end

(pixelated) letters

Do you mean, like a retro, 8-bit, pixel-art look? Big chunky pixels?

Here’s an example of setting up your own characters. You can create any shape character you want.

supportedOrientations(LANDSCAPE_ANY)

function setup()
    tab={
    -- 0 to 9
    ["0"]={64,496,280,776,520,520,520,520,776,776,280,240,0,0,0,0},
    ["1"]={0,192,224,240,192,192,192,192,192,192,192,192,0,0,0,0},
    ["2"]={64,496,792,776,768,256,384,192,48,24,24,1020,0,0,0,0},
    ["3"]={0,496,280,776,256,448,384,768,512,776,792,496,0,0,0,0},
    ["4"]={0,384,448,480,416,400,408,396,1020,384,384,384,0,0,0,0},
    ["5"]={0,1008,16,24,88,504,776,512,512,776,280,240,0,0,0,0},
    ["6"]={64,496,792,8,8,504,796,520,520,776,280,496,0,0,0,0},
    ["7"]={0,1016,256,384,128,192,64,96,32,32,48,48,0,0,0,0},
    ["8"]={64,496,280,792,280,496,440,776,520,520,792,496,0,0,0,0},
    ["9"]={0,496,280,776,520,776,920,752,512,776,408,240,0,0,0,0},
    
    -- A to Z
    ["A"]={0,192,448,352,800,816,528,2032,2040,3080,3084,2060,0,0,0,0},
    ["B"]={240,1016,1560,1048,1560,1016,2040,1048,3096,3096,1560,1016,0,0,0,0},
    ["C"]={384,2016,3120,2072,8,8,8,8,6168,2072,3120,2016,0,0,0,0},
    ["D"]={112,2040,3096,2072,2072,6168,6168,6168,2072,3096,1560,1016,0,0,0,0},
    ["E"]={2032,2040,24,24,24,2040,2040,24,24,24,24,4088,0,0,0,0},
    ["F"]={2032,2040,24,24,24,1016,1016,24,24,24,24,24,0,0,0,0},
    ["G"]={896,4064,6192,4120,24,8,7688,7688,4120,4112,7280,4032,0,0,0,0},
    ["H"]={2048,2072,2072,2072,2072,4088,4088,2072,2072,2072,2072,2072,0,0,0,0},
    ["I"]={0,24,24,24,24,24,24,24,24,24,24,24,0,0,0,0},
    ["J"]={0,384,384,384,384,384,384,384,384,396,216,248,0,0,0,0},
    ["K"]={0,1560,792,408,216,248,504,408,792,1560,1048,3096,0,0,0,0},
    ["L"]={0,24,24,24,24,24,24,24,24,24,24,1016,0,0,0,0},
    ["M"]={12304,12344,14392,14392,10360,11352,9304,9432,8856,8856,9112,8472,0,0,0,0},
    ["N"]={2048,2072,2104,2168,2136,2264,2456,2328,2840,3608,3096,3096,0,0,0,0},
    ["O"]={896,4064,6192,6168,4104,12296,12296,12296,4120,6168,3120,2016,0,0,0,0},
    ["P"]={240,2040,3096,3096,3096,3096,2040,24,24,24,24,24,0,0,0,0},
    ["Q"]={896,4064,2096,6168,4104,12296,12296,4104,4120,6936,3632,8160,4096,0,0,0},
    ["R"]={496,4088,3096,2072,2072,3608,1016,792,1560,3096,3096,6168,0,0,0,0},
    ["S"]={448,1008,1560,1048,24,240,1984,1536,3080,3096,1592,1008,0,0,0,0},
    ["T"]={2040,2044,192,192,192,192,192,192,192,192,192,192,0,0,0,0},
    ["U"]={2048,2072,2072,2072,2072,2072,2072,2072,2072,3096,1584,992,0,0,0,0},
    ["V"]={2048,2060,3080,1048,1552,1552,560,800,288,352,448,192,0,0,0,0},
    ["W"]={512,34572,34568,34056,52632,19864,18576,18576,30960,12400,12384,12384,0,0,0,0},
    ["X"]={8,1048,1584,800,480,192,448,480,800,1584,1048,3084,0,0,0,0},
    ["Y"]={2048,3080,1560,560,800,480,192,192,192,192,192,192,0,0,0,0},
    ["Z"]={2040,2040,1536,768,384,192,64,96,48,24,28,2044,0,0,0,0},
    [" "]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    }
    
    str="THIS IS A TEST 123 456 789"
end

function draw()
    background(0)
    fill(255)
    pos=20
    for b=1,#str do
        c=string.sub(str,b,b)
        pos=pos+25
        for z=1,16 do
            for x=0,16 do
                if 2^x&tab[c][z] >0 then
                    rect(pos+x*2,600-z*2,2,2)
                end
            end 
        end       
    end
end

Here is another method, drawing a character into a small image, then creating a large image (eg 4x larger) and copying each pixel of the small image into the large image as a block of pixels to give a chunky effect.

The advantage is you can create a whole set of pixelated characters, in any font you like, with no need to hand draw any of them. The best would be to use this program to create a single sprite sheet with all the characters in a table format, then you just have to look up the one you need each time.

Note, you will need to play with the font choice, and the size of the larger image, to get the effect you want.

function setup()
    chars={}
    for i=0,9 do
        chars[i]=MakePixelChar(i)
    end
end

function draw()
    background(200)
    for i=0,9 do
        sprite(chars[i],100+i*60,HEIGHT/2)
    end
end

--draws a char in a small image, then copies it to a larger image
function MakePixelChar(a)
    --s is size of initial image
    --x is the size of the enlarged image, as a multiple of x, eg 3 or 5
    --fs is the font size of the text in the initial image
    local s,x,fs=20,8,16
    local img=image(s,s)
    setContext(img)
    fontSize(fs)
    text(a,s/2,s/2)
    setContext()
    local img2=image(s*x,s*x)
    for i=1,s do
        local ik=(i-1)*x+1
        for j=1,s do
            local jk=(j-1)*x+1
            local r,g,b,a=img:get(i,j)
            if a>0 then
                for i2=ik,ik+x do
                    for j2=jk,jk+x do
                        img2:set(i2,j2,color(r,g,b))
                    end
                end
            end
        end
    end
    return img2
end

^ These are pretty cool. How to install a font to let it be in Codea? Not working for me.

@TokOut search the forum for “install font”. You’ll need to use a third party app to install a font system-wide on your iPad.

Don’t forget lots of fonts are built into Codea - see the font command

Can’t wait to try them all and see what works best for me.

Do you mean, like a retro, 8-bit, pixel-art look? Big chunky pixels?

Yes @yojimbo2000 , but not too big. Small enough to be the size of regular text (maybe a bit bigger). So I could have a dialogue box on the bottom of the screen that wouldn’t be to big to interfere with the game (that I’m making). For some reason I like the look of retro pixel, plus it is the easiest to make and import into Codea for me. I think it’s because I like games like Mincraft and Terraria.

@Ignatz there is no need to make a copy of the image, just Sprite or mesh it big with noSmooth

@GameCoder the key to making pixel art in Codea is the noSmooth() command. This stops the graphics system from trying to smooth out your blow-up texture, giving you the blocky hard-edged look you need. Here is a pixel vision class, it can be used for anything, not just text


--# Main
-- PixelVision
displayMode(FULLSCREEN)

function setup()
    pixelVision = PixelVision()
    message = [[
It is a dark time for the Rebellion.
Although the Death Star has been destroyed,
Imperial troops have driven the Rebel forces
from their hidden base and pursued them
across the galaxy.
Evading the dreaded Imperial Starfleet, 
a group of freedom fighters led by 
Luke Skywalker has established a new secret
base on the remote ice world of Hoth.
The evil lord Darth Vader, obsessed with
finding young Skywalker, has dispatched
thousands of remote probes into the far
reaches of space....
]]
    font("Inconsolata")
    fill(255, 247, 0, 255)
    textAlign(CENTER)
end

function draw()
    
    pixelVision:startDrawing()
    --do your drawing here, using full screen coordinates (it all gets scaled)
    background(40, 40, 50)
    fontSize(32)
    text(message, WIDTH/2, HEIGHT/2)
    
    pixelVision:stopDrawing()
end


--# PixelVision
PixelVision = class()

function PixelVision:init(size) 
    local size = size or 4 --size of each block
    self.scale = 1/size
    self.image = image(WIDTH//size, HEIGHT//size)
    self.mesh = mesh()
    self.mesh:addRect(WIDTH/2, HEIGHT/2,WIDTH,HEIGHT)
    self.mesh.texture = self.image
end

function PixelVision:startDrawing()
    setContext(self.image)
    pushMatrix()
    scale(self.scale)
end

function PixelVision:stopDrawing()
    popMatrix()
    setContext()
    noSmooth()
    self.mesh:draw()
end

Here’s another way. Change the fontSize(5) and Sprite size (2000) to get different effects.

function setup()
    img=image(WIDTH,300)
    message = [[
    It is a dark time for the Rebellion.
    Although the Death Star has been destroyed,
    Imperial troops have driven the Rebel forces
    from their hidden base and pursued them
    across the galaxy.
    Evading the dreaded Imperial Starfleet, 
    a group of freedom fighters led by 
    Luke Skywalker has established a new secret
    base on the remote ice world of Hoth.
    The evil lord Darth Vader, obsessed with
    finding young Skywalker, has dispatched
    thousands of remote probes into the far
    reaches of space....
    ]]
    pix()
end

function draw()
    background(0)
    sprite(img,WIDTH/2,300,2000)
end

function pix()
    noSmooth()
    fontSize(5)
    setContext(img)
    fill(117, 226, 7, 255)
    text(message,WIDTH/2,150)
    setContext()
end

I got bored and added a retro star field, to demonstrate animated pixel vision:


--# Main
-- PixelVision
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)

function setup()
    size = 4
    pixelVision = PixelVision(size)
    starField = StarField()
    message = [[
It is a dark time for the Rebellion. Although the Death Star has been destroyed, Imperial troops have driven the Rebel forces from their hidden base and pursued them across the galaxy.
    
Evading the dreaded Imperial Starfleet, a group of freedom fighters led by Luke Skywalker has established a new secret base on the remote ice world of Hoth.
    
The evil lord Darth Vader, obsessed with finding young Skywalker, has dispatched thousands of remote probes into the far reaches of space....
    ]]
    
    font("Futura-Medium")
    fontSize(32)
    fill(255, 247, 0, 255)
    textWrapWidth(490)
  --  textAlign(CENTER)
    messageW, messageH = textSize(message)
end

function draw()
    
    pixelVision:startDrawing()
    --do your drawing here, using full screen coordinates (it all gets scaled)
    background(0)
    starField:draw()
    
    perspective()
    camera(0,0,1000 / size, 0,0,0)
    rotate(-70, 1,0,0)
    text(message,0,(-messageH * 1.2) + ElapsedTime * 20) 
  --  text(message, WIDTH/2, -250 + ElapsedTime * 40)
    
    pixelVision:stopDrawing()
end


--# PixelVision
PixelVision = class()

function PixelVision:init(size) 
    local size = size or 4 --size of each block
    self.scale = 1/size
    self.image = image(WIDTH//size, HEIGHT//size)
    self.mesh = mesh()
    self.mesh:addRect(WIDTH/2, HEIGHT/2,WIDTH,HEIGHT)
    self.mesh.texture = self.image
end

function PixelVision:startDrawing()
    setContext(self.image)
    pushMatrix()
    scale(self.scale)
end

function PixelVision:stopDrawing()
    popMatrix()
    setContext()
    ortho()
    viewMatrix(matrix())
    noSmooth()
    self.mesh:draw()
end

--# StarField
StarField = class()

function StarField:init(max)
    self.mesh = mesh()
    self.mesh.texture = readImage("Space Art:Star")
    self.stars = {}
    self.max = max or 300
    for i = 1,self.max do
        self.mesh:addRect(0,0,0,0)
    end
end

function StarField:add(i)
    local x = WIDTH * (math.random()-0.5) * 0.5
    local y = HEIGHT * (math.random()-0.5) * 0.5
    local dx, dy = (vec2(x,y):normalize() * 0.2):unpack()
   -- print(x,y,dx,dy)
    self.stars[i] = {x = x, y = y, dx = dx, dy = dy}   
end

function StarField:draw()
    local len = #self.stars
    if len < self.max then
        len = len + 1
        self:add(len)
    end
    for i = 1,len do
        local star = self.stars[i]
        star.x = star.x + star.dx
        star.y = star.y + star.dy
        star.dx = star.dx * 1.01
        star.dy = star.dy * 1.01
        if math.abs(star.x) > WIDTH * 0.5 or math.abs(star.y) > HEIGHT * 0.5 then
            self:add(i)
        end
        local bright = math.abs(star.dx)
        self.mesh:setRect(i, star.x, star.y, bright * 10, bright * 10, -star.dx)
        self.mesh:setRectColor(i, color(255 * bright))  
    end
    pushMatrix()
    translate(WIDTH/2, HEIGHT/2)
    self.mesh:draw()
    popMatrix()
end

@yojimbo2000, I think it’s time for you to make your own movies with Codea :slight_smile:

@yojimbo2000, very cool!
Just missing the music … But otherwise it’s perfect.

Thank you for the Pixel Vision code, although I will still use Pixel Vision for other things I’ve decided to go with my original idea. I have taken it apon myself to create the class for it, but there’s only one problem.

I can’t rap my head around a way I would create a table within a class. Then add sprites to it in a loop.

please keep in mind that I know the code doesn’t do anything yet

Here’s the code:

-- Pixel Font

PixelFont = class()

function PixelFont:init(x, y, letters)
    -- you can accept and set parameters here
 self.pos = vec2(x,y)
 self.letters = letters

end

function PixelFont:draw()
    -- Codea does not automatically call this method
   
        -- these are all the Characters in the alphabet err... I mean the first three letters in the alphabet
    letters = ""

    for n = 1, 3 do

            if string.find(letters, "a", n) then
            sprite("Project:a", x, y, 40, 40)
         -- code that will add this sprite above into a table
            x = x + n * 28
        elseif string.find(letters, "b", n) then
            sprite("Project:b", x, y, 40, 40)
         -- code that will add this sprite above into a table
            x = x + n * 30
        elseif string.find(letters, "c", n) then
            sprite("Project:c", x, y, 40, 40)
         -- code that will add this sprite above into a table
            x = x + n * 28 -- I don't know how much I should add to x if c is before any letter so for now it's 28
            end                
        n = n + 1
        
    end
-- code that will draw all the sprites in the table that was created up there in the loop

end

-- Main

-- Use this function to perform your initial setup
function setup()
   
letters = PixelFont(500, 450, "abc")
-- the letters should be able to be called in any order for example it could have been "bca" or "cab"
end

-- This function gets called once every frame
function draw()
     -- This sets a dark background color
    background(40, 40, 50)

    -- Do your drawing here
PixelFont:draw()

end

Here’s what it’s supposed to do:

there’s supposed to be a picture of the correct results here but I can’t seem to get the image to paste in

Here’s the code that created the image that was supposed to be above:

-- Test

-- Use this function to perform your initial setup
function setup()
    print("Please Help me Fourm!")
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    
 x = 500
 y = 450
        
    sprite("Project:a", x, y, 40, 50)
    sprite("Project:b", x + 28, y, 40, 50)
    sprite("Project:c", x + 58, y, 44, 50)    
end

I was also going to paste in the sprites I used but I can’t paste them in :frowning:

I just noticed a problem already. On the line with (and ever other line that uses “n” excluding the one in the for loop)

n = n + 1

the variable “n” should be changed to something else. I want the variable that will be replacing “n” in that line to be = 1 in the first run of that loop and then have 1 more be add each time so by the second time it would be 2 then 3.

Edit: I think I should just change the “n” in

for n = 1, 3 do

to “i” and leave the every other “n” alone.

I also can’t figure out how to detect how many individual characters are in a string. I want to do this so I can replace the “3” in

for n = 1, 3 do

with a variable that represents how many characters were in the string.

#myString returns the length of a string (same syntax as an array). This seems like a lot of work just to draw text on the screen. I think you’re reinventing the wheel. You know that you can install custom fonts to the iPad, which Codea can then use? There’s quite a few retro pixel fonts. You’ll need a third party app to install it on the iPad (I use one called FondFont). You won’t be able to see it in the Codea font picker, but you can still use it by just manually putting its name into the font command.