Add up/down movement animations and make them work

main project-ely-v0.0.1
Sean Hickey 2022-10-27 01:37:35 -07:00
parent 6871736381
commit 7c89210c85
8 changed files with 135 additions and 68 deletions

View File

@ -5,15 +5,15 @@ import (
)
type SpriteAnimation interface {
Draw(frame int32, worldPosition *sdl.Point, angle float64, center *sdl.Point, flip sdl.RendererFlip) error
Draw(frame int, 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
speed int
length int
angle float64
center *sdl.Point
flip sdl.RendererFlip
@ -21,8 +21,8 @@ type entityAnimation struct {
func NewEntityAnimation(
spriteAnimation SpriteAnimation,
speed int32,
length int32,
speed int,
length int,
angle float64,
center *sdl.Point,
flip sdl.RendererFlip,
@ -40,11 +40,11 @@ func NewEntityAnimation(
return &e
}
func (e *entityAnimation) Draw(frame int32, windowPosition *sdl.Point) error {
func (e *entityAnimation) Draw(frame int, windowPosition *sdl.Point) error {
return e.spriteAnimation.Draw(frame, windowPosition, e.angle, e.center, e.flip)
}
func (e *entityAnimation) GetSpeed() int32 {
func (e *entityAnimation) GetSpeed() int {
return e.speed
}

View File

@ -14,10 +14,12 @@ const (
PENGUIN_WALK_DOWN
PENGUIN_STATIONARY_RIGHT
PENGUIN_STATIONARY_LEFT
PENGUIN_STATIONARY_UP
PENGUIN_STATIONARY_DOWN
)
const (
PENGUIN_NUM_ANIMS = 6
PENGUIN_NUM_ANIMS = 8
PENGUIN_DEFAULT = PENGUIN_STATIONARY_RIGHT
)
@ -28,9 +30,9 @@ func DefinePenguinAnimations() {
dimensions sdl.Point
offset sdl.Point
center *sdl.Point
length int32
speed int32
border int32
length int
speed int
border int
)
dimensions = sdl.Point{X: 13, Y: 17}
@ -48,7 +50,7 @@ func DefinePenguinAnimations() {
// 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.
// Stationary Right/Left is just the first frame.
length = 1
stationaryRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_STATIONARY_RIGHT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, sdl.FLIP_NONE)
@ -60,9 +62,19 @@ func DefinePenguinAnimations() {
walkUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_WALK_UP] = NewEntityAnimation(walkUp, speed, length, 0, center, sdl.FLIP_NONE)
// Stationary Up
length = 1
stationaryUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_STATIONARY_UP] = NewEntityAnimation(stationaryUp, speed, length, 0, center, sdl.FLIP_NONE)
// Walk Down
length = 4
offset = sdl.Point{X: 0, Y: 0}
walkDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_WALK_DOWN] = NewEntityAnimation(walkDown, speed, length, 0, center, sdl.FLIP_NONE)
// Stationary Down
length = 1
stationaryDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_STATIONARY_DOWN] = NewEntityAnimation(stationaryDown, speed, length, 0, center, sdl.FLIP_NONE)
}

View File

@ -1,8 +1,52 @@
package types
import "github.com/veandco/go-sdl2/sdl"
import (
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/vector"
"github.com/veandco/go-sdl2/sdl"
)
type EntityAnimation interface {
Draw(step int32, windowPosition *sdl.Point) error
GetSpeed() int32
Draw(step int, windowPosition *sdl.Point) error
GetSpeed() int
}
const (
// These line up with the VEC_DIRECTIONS slice
DIR_LEFT int = iota
DIR_RIGHT
DIR_UP
DIR_DOWN
)
// The following are axis direction vectors based on world coordinates.
// UP/DOWN is intentionally UP = positive (which is different from window coordinates)
var (
VEC_LEFT = &vector.Vec2F{X: -1, Y: 0}
VEC_RIGHT = &vector.Vec2F{X: 1, Y: 0}
VEC_UP = &vector.Vec2F{X: 0, Y: 1}
VEC_DOWN = &vector.Vec2F{X: 0, Y: -1}
VEC_DIRECTIONS = []*vector.Vec2F{
// Prefer left/right animations by checking them first in the list
VEC_LEFT,
VEC_RIGHT,
VEC_UP,
VEC_DOWN,
}
)
func determineClosestDirection(velocity *vector.Vec2F) int {
closest := DIR_RIGHT
max := 0.0
buffer := 0.5 // This buffer lets us stick to the left/right animations for diagonal movement
for i, dir := range VEC_DIRECTIONS {
dot := velocity.Dot(dir)
if dot > max+buffer {
max = dot
closest = i
}
}
return closest
}

View File

@ -14,30 +14,30 @@ type penguin struct {
mx sync.RWMutex
// Animation parameters
currentAnimation EntityAnimation
animationStep int32
facingRight bool
updateAnimation bool
currentAnimation EntityAnimation // pointer to current animation loop
animationStep int // index of animation loop
direction int // direction facing for sprite animations
updateAnimation bool // if false, don't change the animation
// Physical parameters
worldPosition *vector.Vec2F
direction *vector.Vec2F
speed float64
worldPosition *vector.Vec2F // where is the center of this object
velocity *vector.Vec2F // movement direction to be applied each tick
speed float64 // movement magnitude to multiply with the velocity
}
func NewPenguin(renderer *sdl.Renderer) *penguin {
position := vector.Vec2F{}
direction := vector.Vec2F{}
velocity := vector.Vec2F{}
speed := 1.0
p := penguin{
currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT],
animationStep: 0,
facingRight: true,
direction: DIR_RIGHT,
worldPosition: &position,
speed: speed,
direction: &direction,
velocity: &velocity,
}
return &p
@ -50,7 +50,7 @@ func (p *penguin) Draw() error {
//windowPosition := worldPosToWindowPos()
windowPosition := &sdl.Point{
X: int32(math.Round(p.worldPosition.X)),
Y: int32(math.Round(p.worldPosition.Y)),
Y: int32(math.Round(-1 * p.worldPosition.Y)),
}
err := p.currentAnimation.Draw(step, windowPosition)
@ -67,6 +67,10 @@ func (p *penguin) SetPosition(vec *vector.Vec2F) {
p.worldPosition = vec
}
func (p *penguin) SetDirection(dir int) {
p.direction = dir
}
func (p *penguin) SetAnimation(id int) {
a, exists := animation.PenguinAnimations[id]
@ -86,8 +90,8 @@ func (p *penguin) MoveX(x float64) {
p.mx.Lock()
defer p.mx.Unlock()
p.direction.X = x
p.direction.Normalize()
p.velocity.X = x
p.velocity.Normalize()
p.updateAnimation = true
}
@ -96,8 +100,8 @@ func (p *penguin) MoveY(y float64) {
defer p.mx.Unlock()
// (0,0) is the top left, so negative y moves up
p.direction.Y = y
p.direction.Normalize()
p.velocity.Y = y
p.velocity.Normalize()
p.updateAnimation = true
}
@ -110,32 +114,38 @@ func (p *penguin) GetSpeed() float64 {
}
func (p *penguin) SetMoveAnimation() {
x := p.direction.X
y := p.direction.Y
if x == 0 && y == 0 {
// Stationary
if p.facingRight {
p.SetAnimation(animation.PENGUIN_STATIONARY_RIGHT)
} else {
if p.velocity.Zero() {
// Stay facing whatever direction we were facing
switch p.direction {
case DIR_LEFT:
p.SetAnimation(animation.PENGUIN_STATIONARY_LEFT)
case DIR_RIGHT:
p.SetAnimation(animation.PENGUIN_STATIONARY_RIGHT)
case DIR_UP:
p.SetAnimation(animation.PENGUIN_STATIONARY_UP)
case DIR_DOWN:
p.SetAnimation(animation.PENGUIN_STATIONARY_DOWN)
default:
log.Printf("unknown direction: %v", p.direction)
p.SetAnimation(animation.PENGUIN_DEFAULT)
}
} else {
// Moving
// Figure out which way we are facing now that we're moving
p.direction = determineClosestDirection(p.velocity)
// Update which direction we're facing
if x > 0 {
p.facingRight = true
} else if x < 0 {
p.facingRight = false
}
// if x == 0, stay facing whatever direction we were previously facing
if p.facingRight {
p.SetAnimation(animation.PENGUIN_WALK_RIGHT)
} else {
switch p.direction {
case DIR_LEFT:
p.SetAnimation(animation.PENGUIN_WALK_LEFT)
case DIR_RIGHT:
p.SetAnimation(animation.PENGUIN_WALK_RIGHT)
case DIR_UP:
p.SetAnimation(animation.PENGUIN_WALK_UP)
case DIR_DOWN:
p.SetAnimation(animation.PENGUIN_WALK_DOWN)
default:
log.Printf("unknown direction: %v", p.direction)
p.SetAnimation(animation.PENGUIN_DEFAULT)
}
}
}
@ -146,8 +156,8 @@ func (p *penguin) Update() error {
p.updateAnimation = false
}
x := p.direction.X * p.speed
y := p.direction.Y * p.speed
x := p.velocity.X * p.speed
y := p.velocity.Y * p.speed
p.worldPosition.X += x
p.worldPosition.Y += y

View File

@ -106,7 +106,7 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
for i := 0; i < 10; i++ {
entity := types.NewPenguin(renderer)
entityCmd := command.NewCommandHandler(ctx, entity)
randomPos := vector.Vec2F{X: rand.Float64() * 500, Y: rand.Float64() * 500}
randomPos := vector.Vec2F{X: rand.Float64() * 500, Y: rand.Float64() * -500}
entity.SetPosition(&randomPos)
entity.SetAnimation(rand.Intn(animation.PENGUIN_NUM_ANIMS))
entityList = append(entityList, entityCmd)

View File

@ -58,9 +58,9 @@ func (p *player) Update() error {
// Move Y
if p.inputHandler.Keystate(sdl.K_w) {
p.entityChan <- command.NewCommand(command.MOVE_Y, -1.0)
} else if p.inputHandler.Keystate(sdl.K_s) {
p.entityChan <- command.NewCommand(command.MOVE_Y, 1.0)
} else if p.inputHandler.Keystate(sdl.K_s) {
p.entityChan <- command.NewCommand(command.MOVE_Y, -1.0)
} else if !p.inputHandler.Keystate(sdl.K_w) && !p.inputHandler.Keystate(sdl.K_s) {
p.entityChan <- command.NewCommand(command.MOVE_Y, 0.0)
}

View File

@ -12,16 +12,16 @@ type spriteAnimation struct {
spritesheet *spritesheet
dimensions sdl.Point
offset sdl.Point
length int32
border int32
length int
border int
}
func NewAnimation(
filename string,
dimensions sdl.Point,
offset sdl.Point,
length int32,
border int32,
length int,
border int,
) *spriteAnimation {
spritesheet := GetSpritesheet(filename)
@ -38,7 +38,7 @@ func NewAnimation(
}
func (a *spriteAnimation) Draw(
frame int32,
frame int,
windowPosition *sdl.Point,
angle float64,
center *sdl.Point,
@ -47,17 +47,18 @@ func (a *spriteAnimation) Draw(
width := a.dimensions.X
height := a.dimensions.Y
border := int32(a.border)
base := sdl.Point{
X: (width+a.border)*a.offset.X + a.border,
Y: (height+a.border)*a.offset.Y + a.border,
X: (width+border)*a.offset.X + border,
Y: (height+border)*a.offset.Y + border,
}
// Assuming all frames are horizontal going left to right.
// Potentially with a border offset as well.
f := frame % a.length
f := int32(frame % a.length)
section := sdl.Rect{
X: base.X + f*(width+a.border),
X: base.X + f*(width+border),
Y: base.Y,
W: width,
H: height,
@ -71,7 +72,7 @@ func (a *spriteAnimation) Draw(
placement := sdl.Rect{
X: windowPosition.X,
Y: windowPosition.Y,
W: width * 2, // TODO just testing with x2
W: width * 2, // TODO just testing with x2, eventually scale based on screen size or something
H: height * 2,
}

View File

@ -11,8 +11,8 @@ type Vec2F struct {
Y float64
}
func (v *Vec2F) NonZero() bool {
return v.LengthSquared() > 0
func (v *Vec2F) Zero() bool {
return v.X == 0.0 && v.Y == 0.0
}
func (v *Vec2F) LengthSquared() float64 {