Useful tips and snippets for Ebitengine.
If you try to make a sub image from another sub image, you may run into unusual behavior due to the fact that absolute coordinates are used for each crop, instead you can use this function so as not to constantly remember this nuance.
func RelativeCrop(source *ebiten.Image, r image.Rectangle) *ebiten.Image {
rx, ry := source.Bounds().Min.X+r.Min.X, source.Bounds().Min.Y+r.Min.Y
return source.SubImage(image.Rect(rx, ry, rx+r.Max.X, ry+r.Max.Y)).(*ebiten.Image)
}
Text bounds behave differently than image bounds. To calculate center point you will need to subtract the minimum point and add half the size.
func DrawCenteredText(screen *ebiten.Image, font font.Face, s string, cx, cy int) {
bounds := text.BoundString(font, s)
x, y := cx-bounds.Min.X-bounds.Dx()/2, cy-bounds.Min.Y-bounds.Dy()/2
text.Draw(screen, s, font, x, y, colornames.Red)
}
Scale the image in the opposite direction by its size, and then move it into place.
func FlipHorizontal(source *ebiten.Image) *ebiten.Image {
result := ebiten.NewImage(source.Size())
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(-1, 1)
op.GeoM.Translate(float64(img.Bounds().Dx()), 0)
return result.DrawImage(source, op)
}
func FlipVertical(source *ebiten.Image) *ebiten.Image {
result := ebiten.NewImage(source.Size())
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(1, -1)
op.GeoM.Translate(0, float64(img.Bounds().Dy()))
return result.DrawImage(source, op)
}
Minimal game, vsync and resize are usually always helpful when debugging.
func main() {
ebiten.SetVsyncEnabled(true)
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
if err := ebiten.RunGame(NewGame()); err != nil {
log.Fatal(err)
}
}
type Game struct{}
func NewGame() *Game {
return &Game{}
}
func (g *Game) Update() error {
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(colornames.White)
}
func (g *Game) Layout(w, h int) (int, int) {
return w, h
}