From b3836dc411e80055be0ada302fd032fd1f17484c Mon Sep 17 00:00:00 2001 From: Sean Hickey Date: Sun, 23 Oct 2022 00:57:23 -0700 Subject: [PATCH] Actually update and draw things asynchronously too, but wait for responses. --- internal/channels/blocking.go | 27 +++ internal/channels/timeout.go | 19 +- .../{ => animation}/entity_animation.go | 6 +- .../{ => animation}/penguin_animations.go | 14 +- .../game/entity/{ => command}/commands.go | 12 +- internal/game/entity/command/handler.go | 170 ++++++++++++++++++ internal/game/entity/types/common.go | 8 + internal/game/entity/{ => types}/penguin.go | 104 +++-------- internal/game/game.go | 100 ++++++----- internal/game/{entity => }/player/player.go | 64 ++++--- 10 files changed, 343 insertions(+), 181 deletions(-) create mode 100644 internal/channels/blocking.go rename internal/game/entity/{ => animation}/entity_animation.go (93%) rename internal/game/entity/{ => animation}/penguin_animations.go (75%) rename internal/game/entity/{ => command}/commands.go (68%) create mode 100644 internal/game/entity/command/handler.go create mode 100644 internal/game/entity/types/common.go rename internal/game/entity/{ => types}/penguin.go (53%) rename internal/game/{entity => }/player/player.go (61%) diff --git a/internal/channels/blocking.go b/internal/channels/blocking.go new file mode 100644 index 0000000..9d28d41 --- /dev/null +++ b/internal/channels/blocking.go @@ -0,0 +1,27 @@ +package channels + +import ( + "context" +) + +// BlockingFunction will wait for the function to be completed +// or exit if the context is done. +func BlockingFunction(ctx context.Context, function func() error) error { + var err error + + finished := make(chan bool) + go func() { + err = function() + finished <- true + }() + + select { + case <-finished: + // Completed + case <-ctx.Done(): + // Context closed (e.g. timed out or ctrl+c) + return ctx.Err() + } + + return err +} diff --git a/internal/channels/timeout.go b/internal/channels/timeout.go index ac432db..1580dbf 100644 --- a/internal/channels/timeout.go +++ b/internal/channels/timeout.go @@ -5,25 +5,12 @@ import ( "time" ) +// RunWithTimeout will run a function in a goroutine with a new timeout context. +// If the context times out, then we exit the goroutine the return the context error. func RunWithTimeout(timeout time.Duration, function func() error) error { - var err error ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - // Launch a goroutine with a channel - finished := make(chan bool) - go func() { - err = function() - finished <- true - }() - - // Then wait for completion or timeout - select { - case <-ctx.Done(): - // Timed out - return ctx.Err() - case <-finished: - // Completed - } + err := BlockingFunction(ctx, function) return err } diff --git a/internal/game/entity/entity_animation.go b/internal/game/entity/animation/entity_animation.go similarity index 93% rename from internal/game/entity/entity_animation.go rename to internal/game/entity/animation/entity_animation.go index 8c9516b..1b39bd0 100644 --- a/internal/game/entity/entity_animation.go +++ b/internal/game/entity/animation/entity_animation.go @@ -1,4 +1,4 @@ -package entity +package animation import ( "github.com/veandco/go-sdl2/sdl" @@ -44,6 +44,10 @@ func (e *entityAnimation) Draw(frame int32, windowPosition *sdl.Point) error { return e.spriteAnimation.Draw(frame, windowPosition, e.angle, e.center, e.flip) } +func (e *entityAnimation) GetSpeed() int32 { + return e.speed +} + func DefineAnimations() { DefinePenguinAnimations() } diff --git a/internal/game/entity/penguin_animations.go b/internal/game/entity/animation/penguin_animations.go similarity index 75% rename from internal/game/entity/penguin_animations.go rename to internal/game/entity/animation/penguin_animations.go index 53571fb..e201928 100644 --- a/internal/game/entity/penguin_animations.go +++ b/internal/game/entity/animation/penguin_animations.go @@ -1,11 +1,11 @@ -package entity +package animation import ( "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite" "github.com/veandco/go-sdl2/sdl" ) -var penguinAnimations map[int]*entityAnimation +var PenguinAnimations map[int]*entityAnimation const ( PENGUIN_WALK_RIGHT int = iota @@ -28,7 +28,7 @@ func DefinePenguinAnimations() { ) dimensions = sdl.Point{X: 32, Y: 32} - penguinAnimations = make(map[int]*entityAnimation) + PenguinAnimations = make(map[int]*entityAnimation) // Walking Right is in the spritesheet. speed = 5 @@ -36,14 +36,14 @@ func DefinePenguinAnimations() { length = 4 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) + 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) + 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) + 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/entity/commands.go b/internal/game/entity/command/commands.go similarity index 68% rename from internal/game/entity/commands.go rename to internal/game/entity/command/commands.go index c89ec65..ae3c950 100644 --- a/internal/game/entity/commands.go +++ b/internal/game/entity/command/commands.go @@ -1,12 +1,12 @@ -package entity +package command // List of keys supported by all entities const ( - COMMAND_DRAW int = iota - COMMAND_UPDATE - COMMAND_MOVE_X - COMMAND_MOVE_Y - COMMAND_SET_SPEED + MOVE_X int = iota + MOVE_Y + SET_SPEED + SET_POSITION + SET_ANIMATION ) type EntityCommand struct { diff --git a/internal/game/entity/command/handler.go b/internal/game/entity/command/handler.go new file mode 100644 index 0000000..ed84bdf --- /dev/null +++ b/internal/game/entity/command/handler.go @@ -0,0 +1,170 @@ +package command + +import ( + "context" + "log" + "sync" + "time" + + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/channels" +) + +type Entity interface { + Draw() error + Update() error + MoveX(x float64) + MoveY(y float64) + SetSpeed(s float64) +} + +type CommandHandler struct { + ctx context.Context + timeout time.Duration + entity Entity + commandChan chan EntityCommand + drawRequestChan chan bool + drawResponseChan chan bool + updateRequestChan chan bool + updateResponseChan chan bool +} + +func NewCommandHandler(ctx context.Context, entity Entity) *CommandHandler { + commandChan := make(chan EntityCommand, 10) + drawRequestChan := make(chan bool) + drawResponseChan := make(chan bool) + updateRequestChan := make(chan bool) + updateResponseChan := make(chan bool) + + defaultTimeout := time.Second + return &CommandHandler{ + ctx: ctx, + timeout: defaultTimeout, + entity: entity, + commandChan: commandChan, + drawRequestChan: drawRequestChan, + drawResponseChan: drawResponseChan, + updateRequestChan: updateRequestChan, + updateResponseChan: updateResponseChan, + } +} + +func (c *CommandHandler) closeResponses() { + close(c.drawResponseChan) + close(c.updateResponseChan) +} + +func (c *CommandHandler) CloseRequests() { + close(c.drawRequestChan) + close(c.updateRequestChan) + close(c.commandChan) +} + +func (c *CommandHandler) CommandRequest() chan EntityCommand { + return c.commandChan +} + +func (c *CommandHandler) DrawRequest() chan bool { + return c.drawRequestChan +} + +func (c *CommandHandler) DrawResponse() chan bool { + return c.drawResponseChan +} + +func (c *CommandHandler) UpdateRequest() chan bool { + return c.updateRequestChan +} + +func (c *CommandHandler) UpdateResponse() chan bool { + return c.updateResponseChan +} + +func (c *CommandHandler) Run() { + defer c.closeResponses() + + wg := sync.WaitGroup{} + + running := true + for running { + select { + case <-c.drawRequestChan: + wg.Add(1) + go func() { + defer wg.Done() + c.DrawWithTimeout() + c.drawResponseChan <- true + }() + case <-c.updateRequestChan: + wg.Add(1) + go func() { + defer wg.Done() + c.UpdateWithTimeout() + c.updateResponseChan <- true + }() + case cmd := <-c.commandChan: + wg.Add(1) + go func(cmd EntityCommand) { + defer wg.Done() + c.HandleWithTimeout(cmd) + }(cmd) + case <-c.ctx.Done(): + // Graceful shutdown + running = false + } + } + + wg.Wait() + + // Finish up anything in the queues + for cmd := range c.commandChan { + c.HandleWithTimeout(cmd) + } + for range c.drawRequestChan { + c.UpdateWithTimeout() + } + for range c.drawRequestChan { + c.DrawWithTimeout() + } +} + +func (c *CommandHandler) HandleWithTimeout(cmd EntityCommand) { + err := channels.RunWithTimeout(c.timeout, func() error { + return c.Handle(cmd) + }) + if err != nil { + log.Printf("handle with timeout error: %v\n", err) + } +} + +func (c *CommandHandler) Handle(cmd EntityCommand) error { + switch cmd.key { + case MOVE_X: + c.entity.MoveX(cmd.value) + case MOVE_Y: + c.entity.MoveY(cmd.value) + case SET_SPEED: + c.entity.SetSpeed(cmd.value) + default: + log.Printf("unknown entity command: %v", cmd.key) + } + + return nil +} + +func (c *CommandHandler) DrawWithTimeout() { + err := channels.RunWithTimeout(c.timeout, func() error { + return c.entity.Draw() + }) + if err != nil { + log.Printf("draw with timeout error: %v\n", err) + } +} + +func (c *CommandHandler) UpdateWithTimeout() { + err := channels.RunWithTimeout(c.timeout, func() error { + return c.entity.Update() + }) + if err != nil { + log.Printf("update with timeout error: %v\n", err) + } +} diff --git a/internal/game/entity/types/common.go b/internal/game/entity/types/common.go new file mode 100644 index 0000000..dd22085 --- /dev/null +++ b/internal/game/entity/types/common.go @@ -0,0 +1,8 @@ +package types + +import "github.com/veandco/go-sdl2/sdl" + +type EntityAnimation interface { + Draw(step int32, windowPosition *sdl.Point) error + GetSpeed() int32 +} diff --git a/internal/game/entity/penguin.go b/internal/game/entity/types/penguin.go similarity index 53% rename from internal/game/entity/penguin.go rename to internal/game/entity/types/penguin.go index caa1bda..d14f957 100644 --- a/internal/game/entity/penguin.go +++ b/internal/game/entity/types/penguin.go @@ -1,23 +1,20 @@ -package entity +package types import ( - "context" "log" "math" - "time" + "sync" - "gitea.wisellama.rocks/Project-Ely/project-ely/internal/channels" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/animation" "gitea.wisellama.rocks/Project-Ely/project-ely/internal/vector" "github.com/veandco/go-sdl2/sdl" ) type penguin struct { - ctx context.Context - timeout time.Duration - commandChan chan EntityCommand + mx sync.RWMutex // Animation parameters - currentAnimation *entityAnimation + currentAnimation EntityAnimation animationStep int32 facingRight bool updateAnimation bool @@ -28,20 +25,13 @@ type penguin struct { speed float64 } -func NewPenguin(ctx context.Context, renderer *sdl.Renderer) *penguin { - commandChan := make(chan EntityCommand, 10) - defaultTimeout := time.Second - +func NewPenguin(renderer *sdl.Renderer) *penguin { position := vector.Vec2F{} direction := vector.Vec2F{} speed := 1.0 p := penguin{ - ctx: ctx, - commandChan: commandChan, - timeout: defaultTimeout, - - currentAnimation: penguinAnimations[PENGUIN_DEFAULT], + currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT], animationStep: 0, facingRight: true, @@ -54,8 +44,9 @@ func NewPenguin(ctx context.Context, renderer *sdl.Renderer) *penguin { } func (p *penguin) Draw() error { - step := p.animationStep / p.currentAnimation.speed + step := p.animationStep / p.currentAnimation.GetSpeed() + // TODO //windowPosition := worldPosToWindowPos() windowPosition := &sdl.Point{ X: int32(math.Round(p.worldPosition.X)), @@ -77,11 +68,11 @@ func (p *penguin) SetPosition(vec *vector.Vec2F) { } func (p *penguin) SetAnimation(id int) { - a, exists := penguinAnimations[id] + a, exists := animation.PenguinAnimations[id] if !exists { log.Printf("animation does not exist: %v", id) - a = penguinAnimations[PENGUIN_DEFAULT] + a = animation.PenguinAnimations[animation.PENGUIN_DEFAULT] } if a != p.currentAnimation { @@ -92,12 +83,18 @@ func (p *penguin) SetAnimation(id int) { } func (p *penguin) MoveX(x float64) { + p.mx.Lock() + defer p.mx.Unlock() + p.direction.X = x p.direction.Normalize() p.updateAnimation = true } func (p *penguin) MoveY(y float64) { + p.mx.Lock() + defer p.mx.Unlock() + // (0,0) is the top left, so negative y moves up p.direction.Y = y p.direction.Normalize() @@ -119,9 +116,9 @@ func (p *penguin) SetMoveAnimation() { if x == 0 && y == 0 { // Stationary if p.facingRight { - p.SetAnimation(PENGUIN_STATIONARY_RIGHT) + p.SetAnimation(animation.PENGUIN_STATIONARY_RIGHT) } else { - p.SetAnimation(PENGUIN_STATIONARY_LEFT) + p.SetAnimation(animation.PENGUIN_STATIONARY_LEFT) } } else { // Moving @@ -136,9 +133,9 @@ func (p *penguin) SetMoveAnimation() { // if x == 0, stay facing whatever direction we were previously facing if p.facingRight { - p.SetAnimation(PENGUIN_WALK_RIGHT) + p.SetAnimation(animation.PENGUIN_WALK_RIGHT) } else { - p.SetAnimation(PENGUIN_WALK_LEFT) + p.SetAnimation(animation.PENGUIN_WALK_LEFT) } } } @@ -157,62 +154,3 @@ func (p *penguin) Update() error { return nil } - -func (p *penguin) GetCommandChan() chan EntityCommand { - return p.commandChan -} - -func (p *penguin) Run() { - running := true - for running { - select { - case c := <-p.commandChan: - go func(cmd EntityCommand) { - p.HandleWithTimeout(cmd) - }(c) - case <-p.ctx.Done(): - // Graceful shutdown - running = false - // Finish up anything in the queue - for c := range p.commandChan { - p.HandleWithTimeout(c) - } - log.Printf("entity shutdown\n") - } - } -} - -func (p *penguin) HandleWithTimeout(c EntityCommand) { - err := channels.RunWithTimeout(p.timeout, func() error { - return p.Handle(c) - }) - if err != nil { - log.Printf("%v\n", err) - } -} - -func (p *penguin) Handle(c EntityCommand) error { - var err error - switch c.key { - case COMMAND_DRAW: - err = p.Draw() - if err != nil { - log.Printf("error drawing entity: %v", err) - } - case COMMAND_UPDATE: - err = p.Update() - if err != nil { - log.Printf("error updating entity: %v", err) - } - case COMMAND_MOVE_X: - p.MoveX(c.value) - case COMMAND_MOVE_Y: - p.MoveY(c.value) - case COMMAND_SET_SPEED: - p.SetSpeed(c.value) - default: - log.Printf("unknown entity command: %v", c.key) - } - - return nil -} diff --git a/internal/game/game.go b/internal/game/game.go index 88027fe..8165e5a 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -4,22 +4,28 @@ import ( "context" "fmt" "log" + "math/rand" "strconv" "sync" - "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity" - "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/player" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/animation" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/command" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/types" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/player" "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite" "gitea.wisellama.rocks/Project-Ely/project-ely/internal/vector" "gitea.wisellama.rocks/Wisellama/gosimpleconf" "github.com/veandco/go-sdl2/sdl" ) -type Entity interface { +type EntityCmdHandler interface { Run() - GetCommandChan() chan entity.EntityCommand - Draw() error - Update() error + CloseRequests() + CommandRequest() chan command.EntityCommand + DrawRequest() chan bool + DrawResponse() chan bool + UpdateRequest() chan bool + UpdateResponse() chan bool } // Run is the main function to start the game. @@ -73,35 +79,41 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error { } defer sprite.CleanupSpriteCache() - entity.DefinePenguinAnimations() + animation.DefineAnimations() // Done with main setup, now moving on to creating specific entities - entityList := make([]Entity, 0) + entityList := make([]EntityCmdHandler, 0) wg := sync.WaitGroup{} // Setup Player 1 // Let them control a penguin to start with player1 := player.NewPlayer(ctx) - penguin := entity.NewPenguin(ctx, renderer) - penguin.SetSpeed(2.0) - entityList = append(entityList, penguin) - player1.SetEntityChan(penguin.GetCommandChan()) + penguinEntity := types.NewPenguin(renderer) + penguinCmdHandler := command.NewCommandHandler(ctx, penguinEntity) + penguinEntity.SetSpeed(2.0) + entityList = append(entityList, penguinCmdHandler) + player1.SetEntityChan(penguinCmdHandler.CommandRequest()) wg.Add(1) go func() { defer wg.Done() - penguin.Run() + penguinCmdHandler.Run() }() - p2 := entity.NewPenguin(ctx, renderer) - p2.SetPosition(&vector.Vec2F{X: 100, Y: 100}) - p2.SetAnimation(entity.PENGUIN_WALK_LEFT) - entityList = append(entityList, p2) - wg.Add(1) - go func() { - defer wg.Done() - p2.Run() - }() + 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} + entity.SetPosition(&randomPos) + entity.SetAnimation(animation.PENGUIN_WALK_LEFT) + entityList = append(entityList, entityCmd) + + wg.Add(1) + go func() { + defer wg.Done() + entityCmd.Run() + }() + } // And now starting the main loop wg.Add(1) @@ -112,15 +124,6 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error { running := true for running { - - // Allow us to exit gracefully if the context is done - select { - case <-ctx.Done(): - running = false - default: - // Keep running - } - // Poll for SDL events var event sdl.Event sdl.Do(func() { @@ -145,6 +148,17 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error { } }) + // Allow us to exit early if the context is done + select { + case <-ctx.Done(): + running = false + default: + // Keep running + } + if !running { + break + } + // Background sdl.Do(func() { err = renderer.SetDrawColor(0, 120, 0, 255) @@ -158,19 +172,16 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error { } }) - // Everything else + // Tell everything to Update and Draw for _, e := range entityList { - //e.GetCommandChan() <- entity.NewEntityCommand(entity.COMMAND_UPDATE, 0) - //e.GetCommandChan() <- entity.NewEntityCommand(entity.COMMAND_DRAW, 0) - err = e.Update() - if err != nil { - log.Printf("error updating: %v", err) - } + e.UpdateRequest() <- true + e.DrawRequest() <- true + } - err = e.Draw() - if err != nil { - log.Printf("error drawing: %v", err) - } + // Wait for each entity to finish their Draw and Update commands before proceeding + for _, e := range entityList { + <-e.UpdateResponse() + <-e.DrawResponse() } // Draw @@ -180,9 +191,10 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error { }) } - close(player1.GetSdlEventsChan()) + player1.Cleanup() + for _, e := range entityList { - close(e.GetCommandChan()) + e.CloseRequests() } wg.Wait() diff --git a/internal/game/entity/player/player.go b/internal/game/player/player.go similarity index 61% rename from internal/game/entity/player/player.go rename to internal/game/player/player.go index 68d20b9..c633ab1 100644 --- a/internal/game/entity/player/player.go +++ b/internal/game/player/player.go @@ -3,10 +3,11 @@ package player import ( "context" "log" + "sync" "time" "gitea.wisellama.rocks/Project-Ely/project-ely/internal/channels" - "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity" + "gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/command" "github.com/veandco/go-sdl2/sdl" ) @@ -15,41 +16,55 @@ import ( // the viewport for the part of the screen to draw to (splitscreen support), // as well as the entity the player is currently controlling. type player struct { - ctx context.Context - timeout time.Duration - sdlEventsChan chan sdl.KeyboardEvent + mx sync.RWMutex + ctx context.Context + timeout time.Duration + sdlKeyboardEventsChan chan sdl.KeyboardEvent - entityChan chan entity.EntityCommand + entityChan chan command.EntityCommand keystates map[sdl.Keycode]bool } func NewPlayer(ctx context.Context) *player { - sdlEventsChan := make(chan sdl.KeyboardEvent, 10) + sdlKeyboardEventsChan := make(chan sdl.KeyboardEvent, 10) defaultTimeout := time.Second keystates := make(map[sdl.Keycode]bool) p := player{ - ctx: ctx, - timeout: defaultTimeout, - sdlEventsChan: sdlEventsChan, - keystates: keystates, + ctx: ctx, + timeout: defaultTimeout, + sdlKeyboardEventsChan: sdlKeyboardEventsChan, + keystates: keystates, } return &p } -func (p *player) SetEntityChan(e chan entity.EntityCommand) { +func (p *player) Cleanup() { + p.mx.Lock() + defer p.mx.Unlock() + + close(p.sdlKeyboardEventsChan) +} + +func (p *player) SetEntityChan(e chan command.EntityCommand) { + p.mx.Lock() + defer p.mx.Unlock() + p.entityChan = e } func (p *player) GetSdlEventsChan() chan sdl.KeyboardEvent { - return p.sdlEventsChan + p.mx.RLock() + defer p.mx.RUnlock() + + return p.sdlKeyboardEventsChan } func (p *player) Run() { running := true for running { select { - case e := <-p.sdlEventsChan: + case e := <-p.sdlKeyboardEventsChan: go func(event sdl.KeyboardEvent) { p.HandleWithTimeout(event) }(e) @@ -57,10 +72,9 @@ func (p *player) Run() { // Graceful shutdown running = false // Finish up anything in the queue - for e := range p.sdlEventsChan { + for e := range p.sdlKeyboardEventsChan { p.HandleWithTimeout(e) } - log.Printf("player shutdown\n") } } } @@ -75,7 +89,9 @@ func (p *player) HandleWithTimeout(event sdl.KeyboardEvent) { } func (p *player) Handle(e sdl.KeyboardEvent) error { - log.Printf("%v", e) + p.mx.Lock() + defer p.mx.Unlock() + // Key states (just set a boolean whether the key is actively being pressed) keystateChanged := false if e.Type == sdl.KEYDOWN { @@ -97,27 +113,27 @@ func (p *player) Handle(e sdl.KeyboardEvent) error { // Speed if p.keystates[sdl.K_LSHIFT] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_SET_SPEED, 4) + p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 4) } else { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_SET_SPEED, 2) + p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 2) } // Move X if p.keystates[sdl.K_d] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_X, 1.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_X, 1.0) } else if p.keystates[sdl.K_a] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_X, -1.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_X, -1.0) } else if !p.keystates[sdl.K_d] && !p.keystates[sdl.K_a] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_X, 0.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_X, 0.0) } // Move Y if p.keystates[sdl.K_w] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_Y, -1.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_Y, -1.0) } else if p.keystates[sdl.K_s] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_Y, 1.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 1.0) } else if !p.keystates[sdl.K_w] && !p.keystates[sdl.K_s] { - p.entityChan <- entity.NewEntityCommand(entity.COMMAND_MOVE_Y, 0.0) + p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 0.0) } return nil }