Hopefuly fix the issue with continuous walking
parent
b3836dc411
commit
0368193061
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package game
|
||||
package window
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package game
|
||||
package window
|
||||
|
||||
import (
|
||||
"log"
|
Loading…
Reference in New Issue