etxt is a package for vectorial1 text rendering in Golang designed to be used with Ebitengine, the 2D game engine made by Hajime Hoshi.
While Ebitengine already includes the ebiten/v2/text/v2 package, etxt has some advantages over it:
font.Face for good.What etxt doesn't do:
If you are unfamiliar with typography terms and concepts, I highly recommend reading the first chapters of FreeType Glyph Conventions; one the best references on the topic you can find on the internet.
Less talk and more code!
package main
import ( "math" ; "image/color" )
import "github.com/hajimehoshi/ebiten/v2"
import "github.com/tinne26/etxt"
import "github.com/tinne26/fonts/liberation/lbrtserif"
const WordsPerSec = 2.71828
var Words = []string {
"solitude", "joy", "ride", "whisper", "leaves", "cookie",
"hearts", "disdain", "simple", "death", "sea", "shallow",
"self", "rhyme", "childish", "sky", "tic", "tac", "boom",
}
// ---- Ebitengine's Game interface implementation ----
type Game struct { text *etxt.Renderer ; wordIndex float64 }
func (self *Game) Layout(winWidth int, winHeight int) (int, int) {
scale := ebiten.DeviceScaleFactor() // *
// * ebiten.Monitor().DeviceScaleFactor() in >=v2.7.0
self.text.SetScale(scale) // relevant for HiDPI
canvasWidth := int(math.Ceil(float64(winWidth)*scale))
canvasHeight := int(math.Ceil(float64(winHeight)*scale))
return canvasWidth, canvasHeight
}
func (self *Game) Update() error {
newIndex := (self.wordIndex + WordsPerSec/60.0)
self.wordIndex = math.Mod(newIndex, float64(len(Words)))
return nil
}
func (self *Game) Draw(canvas *ebiten.Image) {
// background color
canvas.Fill(color.RGBA{229, 255, 222, 255})
// get screen center position
bounds := canvas.Bounds() // assumes origin (0, 0)
x, y := bounds.Dx()/2, bounds.Dy()/2
// draw text
word := Words[int(self.wordIndex)]
self.text.Draw(canvas, word, x, y)
}
// ---- main function ----
func main() {
// create text renderer, set the font and cache
renderer := etxt.NewRenderer()
renderer.SetFont(lbrtserif.Font())
renderer.Utils().SetCache8MiB()
// adjust main text style properties
renderer.SetColor(color.RGBA{239, 91, 91, 255})
renderer.SetAlign(etxt.Center)
renderer.SetSize(72)
// set up Ebitengine and start the game
ebiten.SetWindowTitle("etxt/examples/ebiten/words")
err := ebiten.RunGame(&Game{ text: renderer })
if err != nil { panic(err) }
}You can try running this yourself with2:
go run github.com/tinne26/etxt/examples/ebiten/words@latest
Alternatively, you can go to https://tinne26.github.io/etxt-examples/ and click on the first example for the web version.
This is a very simple and self-contained example. If you want to learn more, make sure to take a look at etxt/examples!
Yeah, you can compile it with -tags gtxt. Notice that gtxt will make text drawing happen on the CPU, so don't try to use it for real-time applications. In particular, be careful to not accidentally use gtxt with Ebitengine (they are compatible in many cases, but performance will die).
etxt/test.If you are using pixel-art-like vectorial fonts, read these tips. ↩
You will need Golang >=1.18, and if you have never used Ebitengine before, you may need to install some dependencies (typically only on Linux or FreeBSD). ↩