Skip to content

Commit

Permalink
Merge pull request #1 from svera/separate-bounded-animation
Browse files Browse the repository at this point in the history
Separate bounded animation
  • Loading branch information
svera authored Sep 7, 2020
2 parents 9b12a6c + 2313463 commit 9d0606c
Show file tree
Hide file tree
Showing 27 changed files with 703 additions and 691 deletions.
80 changes: 59 additions & 21 deletions animation/animation.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
package animation

import (
"encoding/json"
"fmt"
"io"

"github.com/faiface/pixel"
"github.com/svera/quarter"
)

// Returned errors
const (
ErrorVersionNotSupported = "Version \"%s\" not supported"
ErrorNoAnims = "File must have at least one animation declared, none found"
)

// AnimFile defines the structure of a disk file containing information about animations
type AnimFile struct {
Version string
Sheet string
Anims []struct {
AnimData
Anims map[string]struct {
Frames int
Cycle string
Duration float64
YOffset float64 `json:"y_offset"`
Width float64
Height float64
}
}

type AnimData struct {
Frames int
Cycle string
Duration float64
YOffset float64 `json:"y_offset"`
Width float64
Height float64
}

// Animation types of cycles
const (
SingleReverse = iota - 2
Expand All @@ -34,7 +39,7 @@ const (

// Returned errors
const (
ErrorAnimationDoesNotExist = "Animation %d does not exist"
ErrorAnimationDoesNotExist = "Animation '%s' does not exist"
)

// To convert string values used in sprite definition file to integer values used internally
Expand All @@ -48,8 +53,8 @@ type sequence struct {

// Animation implements an animated sprite
type Animation struct {
anims []*sequence
currentAnimID int
anims map[string]*sequence
currentAnimID string
currentFrameNumber int
elapsed float64
Position pixel.Vec
Expand All @@ -59,16 +64,45 @@ type Animation struct {
// NewAnimation returns a new Sprite instance to be drawn at position x, y
func NewAnimation(pos pixel.Vec, numberAnims int) *Animation {
return &Animation{
anims: make([]*sequence, numberAnims),
anims: make(map[string]*sequence, numberAnims),
Position: pos,
Dir: 1,
}
}

func Deserialize(r io.Reader, pos pixel.Vec) (*Animation, error) {
data := &AnimFile{}
err := json.NewDecoder(r).Decode(data)

if err != nil {
return nil, err
}

anim := NewAnimation(pos, len(data.Anims))

if data.Version != "1" {
return anim, fmt.Errorf(ErrorVersionNotSupported, data.Version)
}

pic, err := quarter.LoadPicture(data.Sheet)
if err != nil {
return anim, err
}

if len(data.Anims) == 0 {
return nil, fmt.Errorf(ErrorNoAnims)
}

for i, an := range data.Anims {
anim.AddAnim(i, pic, an.YOffset, an.Width, an.Height, an.Frames, an.Duration, an.Cycle)
}
return anim, nil
}

// AddAnim adds a new animation to the Sprite, identified with ID,
// whose frames are taken from pic from left to right, starting from X = 0
// duration defines how many seconds should it take for the animation to complete a cycle
func (a *Animation) AddAnim(idx int, pic pixel.Picture, yOffset, width, height float64, numberFrames int, duration float64, cycle string) {
func (a *Animation) AddAnim(idx string, pic pixel.Picture, yOffset, width, height float64, numberFrames int, duration float64, cycle string) {
a.anims[idx] = &sequence{
timePerFrame: duration / float64(numberFrames),
}
Expand All @@ -81,8 +115,8 @@ func (a *Animation) AddAnim(idx int, pic pixel.Picture, yOffset, width, height f
}

// SetCurrentAnim defines which animation to play
func (a *Animation) SetCurrentAnim(ID int) error {
if ID < 0 || ID > len(a.anims)-1 {
func (a *Animation) SetCurrentAnim(ID string) error {
if _, ok := a.anims[ID]; !ok {
return fmt.Errorf(ErrorAnimationDoesNotExist, ID)
}
if ID != a.currentAnimID {
Expand All @@ -97,7 +131,7 @@ func (a *Animation) SetCurrentAnim(ID int) error {
}

// CurrentAnim returns the current animation index
func (a *Animation) CurrentAnim() int {
func (a *Animation) CurrentAnim() string {
return a.currentAnimID
}

Expand All @@ -106,13 +140,13 @@ func (a *Animation) Draw(target pixel.Target, dt float64) {
m := pixel.IM.ScaledXY(pixel.ZV, pixel.V(a.Dir, 1)).Moved(a.Position)
a.anims[a.currentAnimID].frames[a.currentFrameNumber].Draw(target, m)
a.elapsed += dt
if idx := a.nextFrameIndex(dt); idx != a.currentFrameNumber {
if idx := a.nextFrameIndex(); idx != a.currentFrameNumber {
a.elapsed = 0
a.currentFrameNumber = idx
}
}

func (a *Animation) nextFrameIndex(dt float64) int {
func (a *Animation) nextFrameIndex() int {
if a.elapsed <= a.anims[a.currentAnimID].timePerFrame {
return a.currentFrameNumber
}
Expand Down Expand Up @@ -146,3 +180,7 @@ func (a *Animation) lastFrame() int {
func (a *Animation) isLastFrame(number int) bool {
return len(a.anims[a.currentAnimID].frames)-1 == number
}

func (a *Animation) CurrentFrameNumber() int {
return a.currentFrameNumber
}
4 changes: 2 additions & 2 deletions animation/animation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
func TestSetCurrentAnim(t *testing.T) {
var testValues = []struct {
testName string
ID int
ID string
expectedError string
}{
{"Animation ID below 0 does not exist", -1, fmt.Sprintf(animation.ErrorAnimationDoesNotExist, -1)},
{"Inexistent Animation ID throws an error", "NonExistentID", fmt.Sprintf(animation.ErrorAnimationDoesNotExist, "NonExistentID")},
}
for _, tt := range testValues {
t.Run(tt.testName, func(t *testing.T) {
Expand Down
110 changes: 0 additions & 110 deletions animation/bounded_animation.go

This file was deleted.

33 changes: 0 additions & 33 deletions animation/bounded_animation_test.go

This file was deleted.

Loading

0 comments on commit 9d0606c

Please sign in to comment.