Hopefuly fix the issue with continuous walking

main
Sean Hickey 2022-10-23 21:28:38 -07:00
parent b3836dc411
commit 0368193061
8 changed files with 136 additions and 122 deletions

View File

@ -9,13 +9,13 @@ const (
SET_ANIMATION
)
type EntityCommand struct {
type Command struct {
key int
value float64
}
func NewEntityCommand(key int, value float64) EntityCommand {
return EntityCommand{
func NewCommand(key int, value float64) Command {
return Command{
key: key,
value: value,
}

View File

@ -21,7 +21,7 @@ type CommandHandler struct {
ctx context.Context
timeout time.Duration
entity Entity
commandChan chan EntityCommand
commandChan chan Command
drawRequestChan chan bool
drawResponseChan chan bool
updateRequestChan chan bool
@ -29,7 +29,7 @@ type CommandHandler struct {
}
func NewCommandHandler(ctx context.Context, entity Entity) *CommandHandler {
commandChan := make(chan EntityCommand, 10)
commandChan := make(chan Command, 10)
drawRequestChan := make(chan bool)
drawResponseChan := make(chan bool)
updateRequestChan := make(chan bool)
@ -59,7 +59,7 @@ func (c *CommandHandler) CloseRequests() {
close(c.commandChan)
}
func (c *CommandHandler) CommandRequest() chan EntityCommand {
func (c *CommandHandler) CommandRequest() chan Command {
return c.commandChan
}
@ -103,7 +103,7 @@ func (c *CommandHandler) Run() {
}()
case cmd := <-c.commandChan:
wg.Add(1)
go func(cmd EntityCommand) {
go func(cmd Command) {
defer wg.Done()
c.HandleWithTimeout(cmd)
}(cmd)
@ -127,7 +127,7 @@ func (c *CommandHandler) Run() {
}
}
func (c *CommandHandler) HandleWithTimeout(cmd EntityCommand) {
func (c *CommandHandler) HandleWithTimeout(cmd Command) {
err := channels.RunWithTimeout(c.timeout, func() error {
return c.Handle(cmd)
})
@ -136,7 +136,7 @@ func (c *CommandHandler) HandleWithTimeout(cmd EntityCommand) {
}
}
func (c *CommandHandler) Handle(cmd EntityCommand) error {
func (c *CommandHandler) Handle(cmd Command) error {
switch cmd.key {
case MOVE_X:
c.entity.MoveX(cmd.value)

View File

@ -13,6 +13,7 @@ import (
"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/game/window"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/vector"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
"github.com/veandco/go-sdl2/sdl"
@ -21,7 +22,7 @@ import (
type EntityCmdHandler interface {
Run()
CloseRequests()
CommandRequest() chan command.EntityCommand
CommandRequest() chan command.Command
DrawRequest() chan bool
DrawResponse() chan bool
UpdateRequest() chan bool
@ -42,13 +43,13 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
}
framerateDelay := uint32(1000 / framerate)
err = SdlInit()
err = window.SdlInit()
if err != nil {
return err
}
defer SdlQuit()
defer window.SdlQuit()
gameWindow, err := NewWindow(configMap["game.title"])
gameWindow, err := window.NewWindow(configMap["game.title"])
if err != nil {
err = fmt.Errorf("failed creating GameWindow: %w", err)
return err
@ -81,6 +82,8 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
animation.DefineAnimations()
inputHandler := window.NewInputHandler(ctx)
// Done with main setup, now moving on to creating specific entities
entityList := make([]EntityCmdHandler, 0)
@ -88,7 +91,7 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
// Setup Player 1
// Let them control a penguin to start with
player1 := player.NewPlayer(ctx)
player1 := player.NewPlayer(ctx, inputHandler)
penguinEntity := types.NewPenguin(renderer)
penguinCmdHandler := command.NewCommandHandler(ctx, penguinEntity)
penguinEntity.SetSpeed(2.0)
@ -115,13 +118,14 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
}()
}
// And now starting the main loop
// Start the inputHandler
wg.Add(1)
go func() {
defer wg.Done()
player1.Run()
inputHandler.Run()
}()
// And now starting the main loop
running := true
for running {
// Poll for SDL events
@ -139,8 +143,8 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
log.Println("Esc quitting")
cancel()
} else {
// Publish the event so other components can do whatever they need with it
player1.GetSdlEventsChan() <- *e
// Publish the event to the inputHandler
inputHandler.KeyboardChan() <- *e
}
}
@ -159,6 +163,9 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
break
}
// Players
player1.Update()
// Background
sdl.Do(func() {
err = renderer.SetDrawColor(0, 120, 0, 255)
@ -191,7 +198,7 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
})
}
player1.Cleanup()
inputHandler.CloseChannels()
for _, e := range entityList {
e.CloseRequests()

View File

@ -2,138 +2,67 @@ 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/command"
"github.com/veandco/go-sdl2/sdl"
)
// player represents a collection of stuff controlled by the user's input.
type InputHandler interface {
Keystate(keycode sdl.Keycode) bool
}
// A player represents a collection of stuff controlled by the user's input.
// It contains the camera used to view the world,
// 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 {
mx sync.RWMutex
ctx context.Context
timeout time.Duration
sdlKeyboardEventsChan chan sdl.KeyboardEvent
ctx context.Context
timeout time.Duration
inputHandler InputHandler
entityChan chan command.EntityCommand
keystates map[sdl.Keycode]bool
entityChan chan command.Command
}
func NewPlayer(ctx context.Context) *player {
sdlKeyboardEventsChan := make(chan sdl.KeyboardEvent, 10)
func NewPlayer(ctx context.Context, inputHandler InputHandler) *player {
defaultTimeout := time.Second
keystates := make(map[sdl.Keycode]bool)
p := player{
ctx: ctx,
timeout: defaultTimeout,
sdlKeyboardEventsChan: sdlKeyboardEventsChan,
keystates: keystates,
ctx: ctx,
timeout: defaultTimeout,
inputHandler: inputHandler,
}
return &p
}
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()
func (p *player) SetEntityChan(e chan command.Command) {
p.entityChan = e
}
func (p *player) GetSdlEventsChan() chan sdl.KeyboardEvent {
p.mx.RLock()
defer p.mx.RUnlock()
return p.sdlKeyboardEventsChan
}
func (p *player) Run() {
running := true
for running {
select {
case e := <-p.sdlKeyboardEventsChan:
go func(event sdl.KeyboardEvent) {
p.HandleWithTimeout(event)
}(e)
case <-p.ctx.Done():
// Graceful shutdown
running = false
// Finish up anything in the queue
for e := range p.sdlKeyboardEventsChan {
p.HandleWithTimeout(e)
}
}
}
}
func (p *player) HandleWithTimeout(event sdl.KeyboardEvent) {
err := channels.RunWithTimeout(p.timeout, func() error {
return p.Handle(event)
})
if err != nil {
log.Printf("%v\n", err)
}
}
func (p *player) Handle(e sdl.KeyboardEvent) error {
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 {
if !p.keystates[e.Keysym.Sym] {
keystateChanged = true
}
p.keystates[e.Keysym.Sym] = true
} else if e.Type == sdl.KEYUP {
if p.keystates[e.Keysym.Sym] {
keystateChanged = true
}
p.keystates[e.Keysym.Sym] = false
}
// Only send events to the entity if something actually changed
if !keystateChanged {
return nil
}
func (p *player) Update() error {
// Speed
if p.keystates[sdl.K_LSHIFT] {
p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 4)
if p.inputHandler.Keystate(sdl.K_LSHIFT) {
p.entityChan <- command.NewCommand(command.SET_SPEED, 4)
} else {
p.entityChan <- command.NewEntityCommand(command.SET_SPEED, 2)
p.entityChan <- command.NewCommand(command.SET_SPEED, 2)
}
// Move X
if p.keystates[sdl.K_d] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, 1.0)
} else if p.keystates[sdl.K_a] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, -1.0)
} else if !p.keystates[sdl.K_d] && !p.keystates[sdl.K_a] {
p.entityChan <- command.NewEntityCommand(command.MOVE_X, 0.0)
if p.inputHandler.Keystate(sdl.K_d) {
p.entityChan <- command.NewCommand(command.MOVE_X, 1.0)
} else if p.inputHandler.Keystate(sdl.K_a) {
p.entityChan <- command.NewCommand(command.MOVE_X, -1.0)
} else if !p.inputHandler.Keystate(sdl.K_d) && !p.inputHandler.Keystate(sdl.K_a) {
p.entityChan <- command.NewCommand(command.MOVE_X, 0.0)
}
// Move Y
if p.keystates[sdl.K_w] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, -1.0)
} else if p.keystates[sdl.K_s] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 1.0)
} else if !p.keystates[sdl.K_w] && !p.keystates[sdl.K_s] {
p.entityChan <- command.NewEntityCommand(command.MOVE_Y, 0.0)
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_w) && !p.inputHandler.Keystate(sdl.K_s) {
p.entityChan <- command.NewCommand(command.MOVE_Y, 0.0)
}
return nil
}

View File

@ -0,0 +1,75 @@
package window
import (
"context"
"sync"
"github.com/veandco/go-sdl2/sdl"
)
type inputHandler struct {
ctx context.Context
keyboardChan chan sdl.KeyboardEvent
// The actual internal state
mxKeyboard sync.RWMutex
keystates map[sdl.Keycode]bool
// TODO joystick/gamepad
// mxJoystick sync.RWMutex
}
func NewInputHandler(ctx context.Context) *inputHandler {
keyboardChan := make(chan sdl.KeyboardEvent)
keystates := make(map[sdl.Keycode]bool)
i := inputHandler{
ctx: ctx,
keystates: keystates,
keyboardChan: keyboardChan,
}
return &i
}
func (i *inputHandler) CloseChannels() {
close(i.keyboardChan)
}
func (i *inputHandler) KeyboardChan() chan sdl.KeyboardEvent {
return i.keyboardChan
}
func (i *inputHandler) Run() {
running := true
for running {
select {
case event := <-i.keyboardChan:
i.UpdateKeyboard(event)
case <-i.ctx.Done():
running = false
}
}
// Finish up anything in the queues
for e := range i.keyboardChan {
i.UpdateKeyboard(e)
}
}
func (i *inputHandler) UpdateKeyboard(e sdl.KeyboardEvent) {
i.mxKeyboard.Lock()
defer i.mxKeyboard.Unlock()
// Key states (just set a boolean whether the key is actively being pressed)
if e.Type == sdl.KEYDOWN {
i.keystates[e.Keysym.Sym] = true
} else if e.Type == sdl.KEYUP {
i.keystates[e.Keysym.Sym] = false
}
}
func (i *inputHandler) Keystate(keycode sdl.Keycode) bool {
i.mxKeyboard.RLock()
defer i.mxKeyboard.RUnlock()
return i.keystates[keycode]
}

View File

@ -1,4 +1,4 @@
package game
package window
import (
"fmt"

View File

@ -1,4 +1,4 @@
package game
package window
import (
"log"

View File

@ -65,6 +65,9 @@ func run(ctx context.Context) error {
}
defer logWriter.Cleanup()
// Setup Random Number Generator seed
config.InitRNG()
// Start everything with the SDL goroutine context
log.Printf("=== Starting %v ===", configMap["game.title"])
sdl.Main(func() {