package main import ( "bytes" _ "embed" "image" _ "image/png" "log" "math/rand" "github.com/hajimehoshi/ebiten/v2" ) type CharacterType uint const ( WhiteCharacter CharacterType = 0 PinkCharacter CharacterType = 1 BlueCharacter CharacterType = 2 GreenCharacter CharacterType = 3 BlackCharacter CharacterType = 4 OrangeCharacter CharacterType = 5 RedCharacter CharacterType = 6 ) func (c CharacterType) IsValid() bool { result := false switch c { case WhiteCharacter, PinkCharacter, BlueCharacter, GreenCharacter, BlackCharacter, OrangeCharacter, RedCharacter: result = true } return result } const ( CHARACTER_NUMTYPES = 7 CHARACTER_WIDTH = 50 CHARACTER_HEIGHT = 50 CHARACTER_ANIMATION_CYCLES = 6 CHARACTER_WINIMATION_CYCLES = 8 ) var ( //go:embed resources/images/idle.png assets_png []byte assetsImage *ebiten.Image //go:embed resources/images/win.png winnerAssets_png []byte assetsWinner *ebiten.Image ) type CharacterPosition struct { X float64 Y float64 } type Character struct { Sprite *ebiten.Image //our principle sprite representing the character chartype CharacterType //character type, determines offset position to use from asset image cycle int //current animation cycle for the character targetcycles int //specified animation cycle pos CharacterPosition //character coordinates } func (c *Character) RandomizeCycleStart() { c.cycle = rand.Int() % c.targetcycles } func NewCharacter(t CharacterType) *Character { c := &Character{ Sprite: ebiten.NewImage(CHARACTER_WIDTH, CHARACTER_HEIGHT), pos: CharacterPosition{X: 0, Y: 0}, targetcycles: CHARACTER_ANIMATION_CYCLES, } if t.IsValid() { c.chartype = t } else { c.chartype = WhiteCharacter } return c } func (c *Character) SetTargetCycles(cycles int) { c.targetcycles = cycles } func (c *Character) CycleUpdate() { c.cycle = (c.cycle + 1) % c.targetcycles } func (c *Character) Animate() { c.AnimateFromTarget(assetsImage) } func (c *Character) AnimateWinner() { c.AnimateFromTarget(assetsWinner) } func (c *Character) AnimateFromTarget(source *ebiten.Image) { //compute start and end location of asset to use for animation frame cycle sx := CHARACTER_WIDTH * c.cycle sy := CHARACTER_HEIGHT * int(c.chartype) ex := CHARACTER_WIDTH * (c.cycle + 1) ey := CHARACTER_HEIGHT * (int(c.chartype) + 1) c.Sprite.Clear() c.Sprite.DrawImage(source.SubImage(image.Rect(sx, sy, ex, ey)).(*ebiten.Image), nil) c.CycleUpdate() } func (c *Character) SetPosition(p CharacterPosition) { c.pos = p } func (c *Character) Position() CharacterPosition { return c.pos } func (c *Character) GetWidth() int { return c.Sprite.Bounds().Dx() } func (c *Character) GetHeight() int { return c.Sprite.Bounds().Dy() } func (c *Character) GetType() CharacterType { return c.chartype } func init() { img, _, err := image.Decode(bytes.NewReader(assets_png)) if err != nil { log.Fatal(err) } assetsImage = ebiten.NewImageFromImage(img) img, _, err = image.Decode(bytes.NewReader(winnerAssets_png)) if err != nil { log.Fatal(err) } assetsWinner = ebiten.NewImageFromImage(img) }