asebiten

package module
v0.6.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2023 License: MIT Imports: 11 Imported by: 0

README

asebiten

Go Report Card GitHub

Load exported Aseprite animations and use them in Ebitengine games.

Inspired by ebiten-aseprite, with a simplified interface.

Tested with Aesprite v1.3-rc1.

Usage:

  • Export a SpriteSheet from Aseprite, using the 'Array' option from the dropdown, not 'Hash'.

  • Use as in the following example

package main

import (
	"embed"
	"log"
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/Frabjous-Studios/asebiten"
    "github.com/Frabjous-Studios/asebiten/models/asepritev3"
	_ "image/png"
)


// example requires 'img.json' and 'img.png' from Aesprite output in the imgs directory
//go:embed imgs
var embedded embed.FS

var anim *asebiten.Animation

func init() {
	var err error
	// image is loaded relative to the JSON file; according to the path specified in the json output.
	anim, err = asepritev3.LoadAnimation(embedded, "imgs/img.json") 
	if err != nil {
		log.Fatal(err)
	}
}

const (
	width = 320
	height = 240
)

type Game struct {}

func (g *Game) Draw(screen *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(width/2, height/2)
	anim.DrawTo(screen, op)
}

func (g *Game) Update() error {
	asebiten.Update() // call Update() once in each frame prior to calling Update() on any animations.
	anim.Update()     // call Update() once on each animation to update it based on the current frame.
	return nil
}

func (g *Game) Layout(ow, oh int) (int, int) {
	return width, height
}

func main() {
	ebiten.SetWindowSize(width, height)
	ebiten.SetWindowTitle("Aesbiten Demo")
	if err := ebiten.RunGame(&Game{}); err != nil {
		log.Fatal(err)
	}
}
Loading SpriteSheets

SpriteSheets can be loaded by referencing the JSON file output by Aseprite. The corresponding image is loaded relative to the location of this JSON file.

anim, err = asepritev3.LoadAnimation(embedded, "imgs/img.json") 
if err != nil {
	panic(err)
}

To use the loaded animation, update the Update() and Draw() funcs in your game as follows.

func (g *Game) Update() error {
  asebiten.Update() // call once per update, prior to updating individual animations
  
  anim.Update() // call once per animation to make progress
  
  // ...
}

func (g *Game) Draw(screen *ebiten.Image) {
	opts := &ebiten.DrawImageOptions{}
	anim.DrawTo(screen, opts)
}
Tags

All animation tags set in Aseprite are loaded and made available for use in the loaded Animation. SetTag sets the currently running animation to the provided tag and restarts the animation from the first frame.

anim.SetTag("running")
Callbacks

All animations loop by default. If this is undesirable, each animation can be modified via callbacks which are run at the end of the provided tag. This can be used to pause a running animation, change the current tag, play a sound, or do anything else.

anim.SetCallback("falling", func(a *Animation) {
	a.SetTag("idle")
	game.PlaySound("oof.mp3")
})

Take care not to put too much logic in callbacks as it can become hard to manage.

Cloning Animations

To reuse the images loaded as part of the original sprite sheet, just Clone() an existing animation as follows. Each cloned animation tracks its state separately.

ball, err = asepritev3.LoadAnimation(embedded, "imgs/ball.json") 
if err != nil {
	panic(err)
}
ball1 := ball.Clone() 
ball2 := ball.Clone()
ball3 := ball.Clone()

All callbacks which are set on the source animation will be copied as well. To create a lightweight animation which doesn't receive a copy of all its callbacks, use NewFlyweightAnimation, which only inherits the SpriteSheet of its argument.

ball, err = asepritev3.LoadAnimation(embedded, "imgs/ball.json")
if err != nil {
	panic(err)
}
balls := make([]Animation, 100)
for i := 0; i < 100; i++ {
	balls[i] = NewFlyweightAnimation(ball) // lightweight copy; no callbacks transferred
}

Comparison

There are other ways to import Aesprite files and use them in Go. This library aims to provide an opinionated and flexible option for those using Ebitengine. You might be interested in these projects:

  • SolarLune/goaseprite: low-dependency library for making sense of Aseprite export formats. It doesn't integrate with Ebitengine, so if all you want to do is read Aseprite output formats, this could be useful.
  • tducasse/ebiten-aesprite: easy-to-use but hardcoded for 60 Hz, and doesn't support arbitrary matrix transformations.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var TPS int

Functions

func SetTPS added in v0.6.0

func SetTPS()

SetTPS is automatically called before the first animation is drawn to screen. It must be explicitly called again anytime that TPS is changed.

Types

type AniFrame

type AniFrame struct {
	// FrameIdx is the original index of this frame from Aseprite.
	FrameIdx int
	// Image represents an image to use. For efficiency, it's recommended to use subimage for each frame.
	Image image.Image
	// DurationMillis represents the number of milliseconds this frame should be shown.
	DurationMillis int64
	// SourceRect is the source rectangle in the sprite sheet. Primarily used for packed sprites.
	SourceRect image.Rectangle
}

AniFrame denotes a single frame of this animation.

type Animation

type Animation struct {

	// FramesByTagName lists all frames, keyed by their tag. Take care when editing the images associated with this map,
	// as Asebiten uses subimages for each tag, even when that's redundant.
	FramesByTagName map[string][]AniFrame

	// Source is a struct representing the raw JSON read from the Aesprite SpriteSheet on import. Cast to the correct
	// version's SpriteSheet model to use.
	Source SpriteSheet
	// contains filtered or unexported fields
}

Animation is a collection of animations, keyed by a name called a 'tag'. Each tagged animation starts from its first frame and runs until its last frame before looping back to the beginning. Use Callback to take action at the end of a frame. Animation is not thread-safe, but all Callbacks are run synchronously.

Every Animation has an empty tag which loops through every frame in the Sprite Sheet in order. This is the default animation which will be played.

func LoadAnimation

func LoadAnimation(fs fs.FS, jsonPath string) (*Animation, error)

LoadAnimation loads a sprite from the provided filesystem, based on the provided json path. The image paths are assumed to be found in the directory relative to the path passed in. All images in the animation are loaded onto the CPU

func LoadCPUAnimation added in v0.5.2

func LoadCPUAnimation(fs fs.FS, jsonPath string) (*Animation, error)

LoadCPUAnimation loads an animation which is drawn from the CPU each frame. This is good for larger animations such as cinematics done in Aseprite.

func NewAnimation

func NewAnimation(anim map[string][]AniFrame) *Animation

NewAnimation creates a new Animation using the provided map from tag names to a list of frames to run. If a nil map is passed in this func also returns nil.

func NewFlyweightAnimation

func NewFlyweightAnimation(source *Animation) Animation

NewFlyweightAnimation creates a new animation which uses the SpriteSheet already loaded up in the provided animation.

func (*Animation) Bounds

func (a *Animation) Bounds() image.Rectangle

Bounds retrieves the bounds of the current frame.

func (*Animation) Clone

func (a *Animation) Clone() Animation

Clone creates a shallow clone of this animation which uses the same SpriteSheet as the original, but gets its own callbacks and state. The tag, frame, and callbacks set on the source animation are copied for convenience. All timing information is reset at the time the Animation is cloned.

func (*Animation) DrawPackedTo

func (a *Animation) DrawPackedTo(screen *ebiten.Image, optFunc func(options *ebiten.DrawImageOptions))

DrawPackedTo draws a packed animation to the proveded screen. A func to manage any draw options is provided -- the translations needed to unpack frames from packed sprite sheets have already been performed.

func (*Animation) DrawTo

func (a *Animation) DrawTo(screen *ebiten.Image, options *ebiten.DrawImageOptions)

DrawTo draws an animation from to the provided screen using the provided options. Does not automatically perform translation for packed sprite sheets, since doing so requires modifying GeoM prior to other transformations. See DrawPackedTo for that functionality.

func (*Animation) Frame

func (a *Animation) Frame() AniFrame

Frame retrieves the current frame for the provided animation.

func (*Animation) FrameIdx

func (a *Animation) FrameIdx() int

FrameIdx retrieves the index of the current frame.

func (*Animation) OnEnd

func (a *Animation) OnEnd(tag string, callback Callback)

OnEnd registers the provided Callback to run on the same frame that the final frame of the animation is crossed. Each Callback is called only once every time the animation ends, even if the animation ends multiple times during a single frame. Callbacks for a given tag can be disabled by calling OnEnd(tag, nil).

Note: for "reverse" or "pingpong" animations, the end of the animation is defined as the end of the sequence of frames stored by asebiten.

func (*Animation) Pause

func (a *Animation) Pause()

Pause pauses a currently running animation. Animations are running by default.

func (*Animation) Restart

func (a *Animation) Restart()

Restart restarts the currently running animation from the beginning.

func (*Animation) Resume

func (a *Animation) Resume()

Resume resumes a previously paused animation. Animations are running by default.

func (*Animation) SetFrame

func (a *Animation) SetFrame(idx int) error

func (*Animation) SetTag

func (a *Animation) SetTag(tag string)

SetTag sets the currently running tag to the provided tag name. If the tag name is different from the currently running tag, this func also sets the frame number to 0.

func (*Animation) Toggle

func (a *Animation) Toggle()

Toggle toggles the running state of this animation; if running it pauses, if paused, it resumes.

func (*Animation) Update

func (a *Animation) Update()

Update should be called once on every running animation each frame, only after calling asebiten.Update(). Calling Update() on a paused animation immediately returns.

type Callback

type Callback func(*Animation)

Callback is used for animation callbacks, which are triggered whenever an animation runs out of frames. All callbacks are run synchronously on the same thread where Animation.Update() is called.

type Frame

type Frame struct {
	Frame            Rect `json:"frame"`
	Rotated          bool `json:"rotated"`
	Trimmed          bool `json:"trimmed"`
	SpriteSourceSize Rect `json:"spriteSourceSize"`
	SourceSize       Size `json:"sourceSize"`
	Duration         int  `json:"duration"`
}

type FrameTag

type FrameTag struct {
	Name      string `json:"name"`
	From      int    `json:"from"`
	To        int    `json:"to"`
	Direction string `json:"direction"`
	Color     string `json:"color"`
}

type Layer

type Layer struct {
	Name      string `json:"name"`
	Opacity   byte   `json:"opacity"`
	BlendMode string `json:"blendMode"`
}

type Meta

type Meta struct {
	App       string     `json:"app"`
	Version   string     `json:"version"`
	Image     string     `json:"image"`
	Format    string     `json:"format"`
	Size      Size       `json:"size"`
	Scale     string     `json:"scale"`
	FrameTags []FrameTag `json:"frameTags"`
	Layers    []Layer    `json:"layers"`
	Slices    []Slice    `json:"slices"`
}

type Rect

type Rect struct {
	X int `json:"x"`
	Y int `json:"y"`
	Size
}

func (Rect) ImageRect

func (r Rect) ImageRect() image.Rectangle

type Size

type Size struct {
	W int `json:"w"`
	H int `json:"h"`
}

type Slice

type Slice struct {
	Name string     `json:"name"`
	Keys []SliceKey `json:"keys"`
}

type SliceKey

type SliceKey struct {
	Frame  int  `json:"frame"`
	Bounds Rect `json:"bounds"`
}

type SpriteSheet

type SpriteSheet struct {
	Frames []*Frame `json:"frames"`
	Meta   Meta     `json:"meta"`

	// Image is the image referred to by the SpriteSheet.
	Image image.Image

	// Animations stores anmiations ready to go; keyed by frameTag. If no frametags are used the
	// entire sprite sheet is available under a single animation keyed by the empty string.
	Animations map[string]Animation
}

SpriteSheet represents the json export format for an Aesprite sprite sheet, which has been exported with frames in an *Array*.

func LoadSpriteSheet

func LoadSpriteSheet(fs fs.FS, jsonPath string) (SpriteSheet, error)

LoadSpriteSheet only loads sprite sheet metadata for use in whatever manner the caller would prefer. If you want an asebiten.Animation, you should probably use LoadAnimation instead.

type SubImager added in v0.5.2

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL