diff --git a/internal/game/entity/entity_animation.go b/internal/game/entity/entity_animation.go new file mode 100644 index 0000000..4e71b8c --- /dev/null +++ b/internal/game/entity/entity_animation.go @@ -0,0 +1,43 @@ +package entity + +import "github.com/veandco/go-sdl2/sdl" + +type SpriteAnimation interface { + Draw(frame int32, worldPosition *sdl.Point, angle float64, center *sdl.Point, flip sdl.RendererFlip) error +} + +// An entityAnimation will take a SpriteAnimation and may manipulate it somehow (e.g. flipped for walking the other direction) +// This way you may cut down the actual number of pictures needed to define all the different animations. +type entityAnimation struct { + spriteAnimation SpriteAnimation + speed int32 + length int32 + angle float64 + center *sdl.Point + flip sdl.RendererFlip +} + +func NewEntityAnimation( + spriteAnimation SpriteAnimation, + speed int32, + length int32, + angle float64, + center *sdl.Point, + flip sdl.RendererFlip, +) *entityAnimation { + + e := entityAnimation{ + spriteAnimation: spriteAnimation, + speed: speed, + length: length, + angle: angle, + center: center, + flip: flip, + } + + return &e +} + +func (e *entityAnimation) Draw(frame int32, worldPosition *sdl.Point) error { + return e.spriteAnimation.Draw(frame, worldPosition, e.angle, e.center, e.flip) +} diff --git a/internal/game/entity/penguin.go b/internal/game/entity/penguin.go index 30a58bc..259b69d 100644 --- a/internal/game/entity/penguin.go +++ b/internal/game/entity/penguin.go @@ -1,33 +1,40 @@ package entity import ( + "log" + "github.com/veandco/go-sdl2/sdl" ) type penguin struct { - worldPosition *sdl.Point - animationStep int32 + worldPosition *sdl.Point + currentAnimation *entityAnimation + animationStep int32 + facingRight bool } func NewPenguin(renderer *sdl.Renderer) *penguin { position := sdl.Point{} p := penguin{ - worldPosition: &position, + worldPosition: &position, + currentAnimation: penguinAnimations[PENGUIN_DEFAULT], + animationStep: 0, + facingRight: true, } return &p } func (p *penguin) Draw() error { - a := penguinAnimations[PENGUIN_WALK_RIGHT] - step := p.animationStep / 10 - err := a.Draw(step, p.worldPosition) + step := p.animationStep / p.currentAnimation.speed + + err := p.currentAnimation.Draw(step, p.worldPosition) if err != nil { return err } - p.animationStep += 1 + p.animationStep = 1 + p.animationStep return nil } @@ -36,12 +43,31 @@ func (p *penguin) SetPosition(point *sdl.Point) { p.worldPosition = point } +func (p *penguin) SetAnimation(name string) { + a, exists := penguinAnimations[name] + + if !exists { + log.Printf("animation does not exist: %v", name) + a = penguinAnimations[PENGUIN_DEFAULT] + } + + if a != p.currentAnimation { + p.animationStep = 0 + } + + p.currentAnimation = a +} + func (p *penguin) MoveRight() { p.worldPosition.X += 1 + p.SetAnimation(PENGUIN_WALK_RIGHT) + p.facingRight = true } func (p *penguin) MoveLeft() { p.worldPosition.X -= 1 + p.SetAnimation(PENGUIN_WALK_LEFT) + p.facingRight = false } func (p *penguin) MoveUp() { @@ -53,3 +79,11 @@ func (p *penguin) MoveDown() { // positive y moves down p.worldPosition.Y += 1 } + +func (p *penguin) StopMove() { + if p.facingRight { + p.SetAnimation(PENGUIN_STATIONARY_RIGHT) + } else { + p.SetAnimation(PENGUIN_STATIONARY_LEFT) + } +} diff --git a/internal/game/entity/penguin_animations.go b/internal/game/entity/penguin_animations.go index 20fdec9..042e573 100644 --- a/internal/game/entity/penguin_animations.go +++ b/internal/game/entity/penguin_animations.go @@ -5,14 +5,15 @@ import ( "github.com/veandco/go-sdl2/sdl" ) -type Animation interface { - Draw(frame int32, worldPosition *sdl.Point) error -} - -var penguinAnimations map[string]Animation +var penguinAnimations map[string]*entityAnimation const ( - PENGUIN_WALK_RIGHT = "walk-right" + PENGUIN_WALK_RIGHT = "walk-right" + PENGUIN_WALK_LEFT = "walk-left" + PENGUIN_STATIONARY_RIGHT = "stationary-right" + PENGUIN_STATIONARY_LEFT = "stationary-left" + + PENGUIN_DEFAULT = PENGUIN_STATIONARY_RIGHT ) func DefinePenguinAnimations() { @@ -21,13 +22,28 @@ func DefinePenguinAnimations() { var ( dimensions sdl.Point offset sdl.Point + center *sdl.Point length int32 + speed int32 ) dimensions = sdl.Point{X: 32, Y: 32} - penguinAnimations = make(map[string]Animation) + penguinAnimations = make(map[string]*entityAnimation) + // Walking Right is in the spritesheet. + speed = 10 offset = sdl.Point{X: 0, Y: 2} length = 4 - penguinAnimations[PENGUIN_WALK_RIGHT] = sprite.NewAnimation(filename, dimensions, offset, length) + center = nil // center is for rotation, nil will default to w/2 h/2 + walkRight := sprite.NewAnimation(filename, dimensions, offset, length) + penguinAnimations[PENGUIN_WALK_RIGHT] = NewEntityAnimation(walkRight, speed, length, 0, center, sdl.FLIP_NONE) + + // Walking Left is just that flipped. + penguinAnimations[PENGUIN_WALK_LEFT] = NewEntityAnimation(walkRight, speed, length, 0, center, sdl.FLIP_HORIZONTAL) + + // Stationary is just the first frame. + length = 1 + stationaryRight := sprite.NewAnimation(filename, dimensions, offset, length) + penguinAnimations[PENGUIN_STATIONARY_RIGHT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, sdl.FLIP_NONE) + penguinAnimations[PENGUIN_STATIONARY_LEFT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, sdl.FLIP_HORIZONTAL) } diff --git a/internal/game/sprite/animation.go b/internal/game/sprite/animation.go index 713bc64..59b44a4 100644 --- a/internal/game/sprite/animation.go +++ b/internal/game/sprite/animation.go @@ -6,9 +6,9 @@ import ( "github.com/veandco/go-sdl2/sdl" ) -// animation defines a specific for an entity that references a sequence of sections of a sprite sheet. -// For example, walking to the left could be defined by 4 subsections of a sprite sheet. -type animation struct { +// spriteAnimation specifies which subsections of a spritesheet define this animation. +// For example, walking to the right could be defined by 4 subsections of a sprite sheet. +type spriteAnimation struct { spritesheet *spritesheet dimensions sdl.Point offset sdl.Point @@ -20,10 +20,10 @@ func NewAnimation( dimensions sdl.Point, offset sdl.Point, length int32, -) *animation { +) *spriteAnimation { spritesheet := GetSpritesheet(filename) - a := animation{ + a := spriteAnimation{ spritesheet: spritesheet, dimensions: dimensions, offset: offset, @@ -33,7 +33,14 @@ func NewAnimation( return &a } -func (a *animation) Draw(frame int32, worldPosition *sdl.Point) error { +func (a *spriteAnimation) Draw( + frame int32, + worldPosition *sdl.Point, + angle float64, + center *sdl.Point, + flip sdl.RendererFlip, +) error { + width := a.dimensions.X height := a.dimensions.Y @@ -64,7 +71,7 @@ func (a *animation) Draw(frame int32, worldPosition *sdl.Point) error { H: height, } - err = a.spritesheet.Draw(§ion, &placement) + err = a.spritesheet.Draw(§ion, &placement, angle, center, flip) if err != nil { return err } @@ -72,7 +79,7 @@ func (a *animation) Draw(frame int32, worldPosition *sdl.Point) error { return nil } -func (a *animation) checkBounds(section *sdl.Rect) error { +func (a *spriteAnimation) checkBounds(section *sdl.Rect) error { width := a.spritesheet.surface.W height := a.spritesheet.surface.H diff --git a/internal/game/sprite/spritesheet.go b/internal/game/sprite/spritesheet.go index b4b8f3b..4d1591a 100644 --- a/internal/game/sprite/spritesheet.go +++ b/internal/game/sprite/spritesheet.go @@ -71,10 +71,17 @@ func (s *spritesheet) Cleanup() { }() } -func (s *spritesheet) Draw(section *sdl.Rect, placement *sdl.Rect) error { +func (s *spritesheet) Draw( + section *sdl.Rect, + placement *sdl.Rect, + angle float64, + center *sdl.Point, + flip sdl.RendererFlip, +) error { + var err error sdl.Do(func() { - err = s.renderer.Copy(s.texture, section, placement) + err = s.renderer.CopyEx(s.texture, section, placement, angle, center, flip) }) if err != nil { return err diff --git a/main.go b/main.go index 757fb04..0efbf05 100644 --- a/main.go +++ b/main.go @@ -91,6 +91,7 @@ func run(configMap gosimpleconf.ConfigMap) error { penguin := entity.NewPenguin(renderer) p2 := entity.NewPenguin(renderer) p2.SetPosition(&sdl.Point{X: 100, Y: 100}) + p2.SetAnimation(entity.PENGUIN_WALK_LEFT) keystates := make(map[sdl.Keycode]bool) @@ -123,19 +124,22 @@ func run(configMap gosimpleconf.ConfigMap) error { }) } - if keystates[sdl.K_a] { - penguin.MoveLeft() - } if keystates[sdl.K_d] { penguin.MoveRight() + } else if keystates[sdl.K_a] { + penguin.MoveLeft() } + if keystates[sdl.K_w] { penguin.MoveUp() - } - if keystates[sdl.K_s] { + } else if keystates[sdl.K_s] { penguin.MoveDown() } + if !keystates[sdl.K_a] && !keystates[sdl.K_d] && !keystates[sdl.K_w] && !keystates[sdl.K_s] { + penguin.StopMove() + } + // Background sdl.Do(func() { err = renderer.SetDrawColor(0, 120, 0, 255)