Sprite Sheet/Custom Font for Guis

Pretty much what the title says. I’ve looked for tutorials on this subject just can’t find one. I am trying to figure out how to do it, how exactly would I make custom fonts for Guis using the Sprite Sheets?

6 Likes

Although this is not a tutorial MORGOTH made this custom font manager which is very helpful: https://devforum.roblox.com/t/custom-font-generator-manager/14831

It is a very easy way to create custom fonts and there is a tutorial for it as well.

Note: this could be outdated have not used it in a while.

3 Likes

Thanks, I am trying to create them myself though, trying to learn,

1 Like

You can check this out!

All the instructions are inside the readme

1 Like

I have never used spritesheets before, but I can immediately tell you that you would need to play with the ImageRectOffset and ImageRectSize properties of image labels, and you would need to keep the value of each property for each letter in a table:

local Letters = {
	-- letter = {position, size}
	["a"] = { Vector2.new(0, 0), Vector2.new(10, 15) },
	["b"] = { ... },
	["c"] = { ... },
	...,
	["z"] = { ... },
	["A"] = { ... },
	["B"] = { ... },
	...,
	["."] = { ... },
	["!"] = { ... },
	...,
	[" "] = { ... }, -- don't forget the space!
}

Then, for each of these letters in your string, you can simply index it in the table, generate an ImageLabel for it, and size it properly:

local function CreateLetter(ltr: string): ImageLabel?
	local ltrArray = Letters[ltr]
	if not ltrArray then
		return nil
	end

	local pos, siz = ltrArray[1], ltrArray[2]
	local letter = Instance.new("ImageLabel")
	letter.Image = -- sprite sheet here
	letter.ImageRectOffset = pos
	letter.ImageRectSize = siz
	letter.Size = UDim2.fromOffset(siz.X, siz.Y) -- this is pixel-for-pixel, which might be bad...
	letter.BackgroundTransparency = 1

	return letter
end

As for indexing the string itself, there is a method for it, string.sub, and you can interpret entire strings with a numeric for loop:

local function CreatePhrase(str: string): { ImageLabel? }
	local phrase = {}
	for i = 1, str:len() do
		local ltr = str:sub(i, i)
		local letter = CreateLetter(ltr)
		if letter then
			letter.LayoutOrder = i -- for the next step
			phrase[i] = letter
		end
	end

	return phrase
end

If you want to put these letters beside each other, the best way to do it is probably with a UIListLayout instance, with the letters listed horizontally and sorting by LayoutOrder.
If you want to put these letters in a frame, the hierarchy in the explorer would probably look like this:

Frame
 - UIListLayout
 - ImageLabel
 - ImageLabel
 - ImageLabel
 - ...

That would be my implementation of it, and I thought this out, but I’m still not entirely sure if it would work on grounds of how easy it would be to create and use this custom font and the performance issues it might have. This is just a concept, and I just don’t have the time or motive to test this.
I hope this helps…

Edit: I don’t know how you would be finding the position and size of each letter. Most likely, you could use a photo-editing software to box out the letter or something like that. If that can’t be done, you could just experiment with the spritesheet in studio to see what works.

Edit-edit: UIListLayouts work for singular lines of text, but not for multi-line text. This could be solved using a double-layered UIListLayout, one for the frames housing the string, and one for the string itself. A UITableLayout will not work because it only sorts the rows of frames by LayoutOrder, not the phrase contained inside. A UIGridLayout will not work because it restricts size as well as position, which is coincidentially very useful for monospace fonts.
The hierarchy in the explorer would look like this:

MasterFrame
 - UIListLayout
 - Frame
    - UIListLayout
    - ImageLabel
    - ImageLabel
    - ...
 - Frame
    - ...
 - ...

This evidently means you will need to use a word-wrapping function to make sure each letter does not trail off to the end of the screen. I think my function (below) is complex, but it does its job properly as long as I didn’t make any logical errors:

local function WrapPhrase(str: string, sizeY: UDim, parent: GuiObject): { Frame }
	local block = {}
	local phrase = CreatePhrase(str)
	local spaces = { [0] = true }
	local strLength = str:len()
	for i = 1, strLength do
		if str:sub(i, i) == " " then
			spaces[i] = true
		end
	end

	local i = 1
	while i <= strLength do
		-- Create a new frame to house a line
		local line = Instance.new("Frame")
		line.Size = UDim2.new(UDim.new(1, 0), sizeY)

		local listLayout = Instance.new("UIListLayout")
		listLayout.FillDIrection = Enum.FillDirection.Horizontal
		listLayout.SortOrder = Enum.SortOrder.LayoutOrder
		listLayout.Parent = line

		line.Parent = parent -- required to evaluate line.AbsoluteSize
		table.insert(block, line)

		-- Start generating the phrase
		while listLayout.AbsoluteContentSize.X <= line.AbsoluteSize.X and i <= strLength do
			local ImageLetter = phrase[i]
			if ImageLetter then
				ImageLetter.Parent = line
				-- listLayout:ApplyLayout() -- just in case
			end
			i += 1
		end

		-- Go back until the last space character is reached
		if i <= strLength then -- OR the end of the string has been reached
			repeat
				i -= 1
			until spaces[i - 1]
		end
	end
	return block
end
22 Likes

Alright, I’ll make sure to try this out! Thanks so much!

1 Like

Personally I use after effects to make whatever sprite sheet I want. You can customize it a lot more and get the exact effect you are looking for.

I went and tried it out and got a working version, thanks so much! https://gyazo.com/a3a9564dd985c2cb3dae99924c5972de

2 Likes