Remove pointers for concurrency

main
Sean Hickey 2022-11-20 15:20:20 -08:00
parent e20133d5d0
commit 4882cdedf5
15 changed files with 117 additions and 94 deletions

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}"
}
]
}

View File

@ -1,5 +1,6 @@
game.title = "Project Ely" game.title = "Project Ely"
game.framerate = 60.0 game.framerate = 60.0
log.utcTime = false
log.file = "output.log" log.file = "output.log"
log.writeToFile = false log.writeToFile = false

View File

@ -9,6 +9,7 @@ import (
var defaultConfig gosimpleconf.ConfigMap = gosimpleconf.ConfigMap{ var defaultConfig gosimpleconf.ConfigMap = gosimpleconf.ConfigMap{
"game.title": "Project Ely", "game.title": "Project Ely",
"game.framerate": "60.0", "game.framerate": "60.0",
"log.utcTime": "false",
"log.writeToFile": "false", "log.writeToFile": "false",
} }

View File

@ -9,19 +9,24 @@ import (
type logWriter struct { type logWriter struct {
writeToFile bool writeToFile bool
utcTime bool
logFile *os.File logFile *os.File
} }
func (w *logWriter) Write(bytes []byte) (int, error) { func (w *logWriter) Write(bytes []byte) (int, error) {
t := time.Now().UTC().Format(time.RFC3339) t := time.Now()
return fmt.Fprintf(w.logFile, "%v %v", t, string(bytes)) if w.utcTime {
t = t.UTC()
}
format := t.Format(time.RFC3339)
return fmt.Fprintf(w.logFile, "%v %v", format, string(bytes))
} }
func (w *logWriter) Cleanup() { func (w *logWriter) Cleanup() {
defer w.logFile.Close() defer w.logFile.Close()
} }
func SetupLogging(writeToFile bool, logFilename string) (*logWriter, error) { func SetupLogging(writeToFile bool, utcTime bool, logFilename string) (*logWriter, error) {
var err error var err error
log.SetFlags(0) log.SetFlags(0)
@ -35,6 +40,7 @@ func SetupLogging(writeToFile bool, logFilename string) (*logWriter, error) {
writer := &logWriter{ writer := &logWriter{
writeToFile: writeToFile, writeToFile: writeToFile,
utcTime: utcTime,
logFile: logFile, logFile: logFile,
} }
log.SetOutput(writer) log.SetOutput(writer)

View File

@ -6,7 +6,7 @@ import "github.com/veandco/go-sdl2/sdl"
// Since this is only a 2D game with SDL, the projection is relatively simple: window + camera = world. // Since this is only a 2D game with SDL, the projection is relatively simple: window + camera = world.
// https://gamedev.stackexchange.com/a/123844 // https://gamedev.stackexchange.com/a/123844
type camera struct { type camera struct {
pos *sdl.Point pos sdl.Point
} }
func NewCamera() *camera { func NewCamera() *camera {

View File

@ -5,7 +5,7 @@ import (
) )
type SpriteAnimation interface { type SpriteAnimation interface {
Draw(frame int, 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) // An entityAnimation will take a SpriteAnimation and may manipulate it somehow (e.g. flipped for walking the other direction)
@ -15,7 +15,7 @@ type entityAnimation struct {
speed int speed int
length int length int
angle float64 angle float64
center *sdl.Point center sdl.Point
flip sdl.RendererFlip flip sdl.RendererFlip
} }
@ -24,11 +24,11 @@ func NewEntityAnimation(
speed int, speed int,
length int, length int,
angle float64, angle float64,
center *sdl.Point, center sdl.Point,
flip sdl.RendererFlip, flip sdl.RendererFlip,
) *entityAnimation { ) entityAnimation {
e := entityAnimation{ return entityAnimation{
spriteAnimation: spriteAnimation, spriteAnimation: spriteAnimation,
speed: speed, speed: speed,
length: length, length: length,
@ -36,18 +36,23 @@ func NewEntityAnimation(
center: center, center: center,
flip: flip, flip: flip,
} }
return &e
} }
func (e *entityAnimation) Draw(frame int, 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) return e.spriteAnimation.Draw(frame, windowPosition, e.angle, e.center, e.flip)
} }
func (e *entityAnimation) GetSpeed() int { func (e entityAnimation) GetSpeed() int {
return e.speed return e.speed
} }
func DefineAnimations() { func DefineAnimations() {
DefinePenguinAnimations() DefinePenguinAnimations()
} }
func getCenter(dimensions sdl.Point) sdl.Point {
x := dimensions.X / 2
y := dimensions.Y / 2
return sdl.Point{X: x, Y: y}
}

View File

@ -5,7 +5,7 @@ import (
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
var PenguinAnimations map[int]*entityAnimation var PenguinAnimations map[int]entityAnimation
const ( const (
PENGUIN_WALK_RIGHT int = iota PENGUIN_WALK_RIGHT int = iota
@ -29,21 +29,21 @@ func DefinePenguinAnimations() {
var ( var (
dimensions sdl.Point dimensions sdl.Point
offset sdl.Point offset sdl.Point
center *sdl.Point center sdl.Point
length int length int
speed int speed int
border int border int
) )
dimensions = sdl.Point{X: 13, Y: 17} dimensions = sdl.Point{X: 13, Y: 17}
PenguinAnimations = make(map[int]*entityAnimation) PenguinAnimations = make(map[int]entityAnimation)
// Walking Right is in the spritesheet. // Walking Right is in the spritesheet.
speed = 5 speed = 5
offset = sdl.Point{X: 0, Y: 1} offset = sdl.Point{X: 0, Y: 1}
length = 5 length = 5
border = 1 // optional border around each sprite border = 1 // optional border around each sprite
center = nil // center is for rotation, nil will default to w/2 h/2 center = getCenter(dimensions) // center is for rotation, nil will default to w/2 h/2
walkRight := sprite.NewAnimation(filename, dimensions, offset, length, border) walkRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
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)

View File

@ -6,7 +6,7 @@ import (
) )
type EntityAnimation interface { type EntityAnimation interface {
Draw(step int, windowPosition *sdl.Point) error Draw(step int, windowPosition sdl.Point) error
GetSpeed() int GetSpeed() int
} }
@ -21,12 +21,12 @@ const (
// The following are axis direction vectors based on world coordinates. // The following are axis direction vectors based on world coordinates.
// UP/DOWN is intentionally UP = positive (which is different from window coordinates) // UP/DOWN is intentionally UP = positive (which is different from window coordinates)
var ( var (
VEC_LEFT = &vector.Vec2F{X: -1, Y: 0} VEC_LEFT = vector.Vec2F{X: -1, Y: 0}
VEC_RIGHT = &vector.Vec2F{X: 1, Y: 0} VEC_RIGHT = vector.Vec2F{X: 1, Y: 0}
VEC_UP = &vector.Vec2F{X: 0, Y: 1} VEC_UP = vector.Vec2F{X: 0, Y: 1}
VEC_DOWN = &vector.Vec2F{X: 0, Y: -1} VEC_DOWN = vector.Vec2F{X: 0, Y: -1}
VEC_DIRECTIONS = []*vector.Vec2F{ VEC_DIRECTIONS = []vector.Vec2F{
// Prefer left/right animations by checking them first in the list // Prefer left/right animations by checking them first in the list
VEC_LEFT, VEC_LEFT,
VEC_RIGHT, VEC_RIGHT,
@ -35,7 +35,7 @@ var (
} }
) )
func determineClosestDirection(velocity *vector.Vec2F) int { func determineClosestDirection(velocity vector.Vec2F) int {
closest := DIR_RIGHT closest := DIR_RIGHT
max := 0.0 max := 0.0
buffer := 0.5 // This buffer lets us stick to the left/right animations for diagonal movement buffer := 0.5 // This buffer lets us stick to the left/right animations for diagonal movement

View File

@ -20,27 +20,25 @@ type penguin struct {
updateAnimation bool // if false, don't change the animation updateAnimation bool // if false, don't change the animation
// Physical parameters // Physical parameters
worldPosition *vector.Vec2F // where is the center of this object worldPosition vector.Vec2F // where is the center of this object
velocity *vector.Vec2F // movement direction to be applied each tick velocity vector.Vec2F // movement direction to be applied each tick
speed float64 // movement magnitude to multiply with the velocity speed float64 // movement magnitude to multiply with the velocity
} }
func NewPenguin(renderer *sdl.Renderer) *penguin { func NewPenguin(renderer *sdl.Renderer) penguin {
position := vector.Vec2F{} position := vector.Vec2F{}
velocity := vector.Vec2F{} velocity := vector.Vec2F{}
speed := 1.0 speed := 1.0
p := penguin{ return penguin{
currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT], currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT],
animationStep: 0, animationStep: 0,
direction: DIR_RIGHT, direction: DIR_RIGHT,
worldPosition: &position, worldPosition: position,
speed: speed, speed: speed,
velocity: &velocity, velocity: velocity,
} }
return &p
} }
func (p *penguin) Draw() error { func (p *penguin) Draw() error {
@ -48,7 +46,7 @@ func (p *penguin) Draw() error {
// TODO // TODO
//windowPosition := worldPosToWindowPos() //windowPosition := worldPosToWindowPos()
windowPosition := &sdl.Point{ windowPosition := sdl.Point{
X: int32(math.Round(p.worldPosition.X)), X: int32(math.Round(p.worldPosition.X)),
Y: int32(math.Round(-1 * p.worldPosition.Y)), Y: int32(math.Round(-1 * p.worldPosition.Y)),
} }
@ -63,7 +61,7 @@ func (p *penguin) Draw() error {
return nil return nil
} }
func (p *penguin) SetPosition(vec *vector.Vec2F) { func (p *penguin) SetPosition(vec vector.Vec2F) {
p.worldPosition = vec p.worldPosition = vec
} }
@ -91,7 +89,7 @@ func (p *penguin) MoveX(x float64) {
defer p.mx.Unlock() defer p.mx.Unlock()
p.velocity.X = x p.velocity.X = x
p.velocity.Normalize() p.velocity = p.velocity.Normalized()
p.updateAnimation = true p.updateAnimation = true
} }
@ -101,7 +99,7 @@ func (p *penguin) MoveY(y float64) {
// (0,0) is the top left, so negative y moves up // (0,0) is the top left, so negative y moves up
p.velocity.Y = y p.velocity.Y = y
p.velocity.Normalize() p.velocity.Normalized()
p.updateAnimation = true p.updateAnimation = true
} }

View File

@ -78,7 +78,6 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
err = fmt.Errorf("failed in InitSpriteCache: %w", err) err = fmt.Errorf("failed in InitSpriteCache: %w", err)
return err return err
} }
defer sprite.CleanupSpriteCache()
animation.DefineAnimations() animation.DefineAnimations()
@ -93,7 +92,7 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
// Let them control a penguin to start with // Let them control a penguin to start with
player1 := player.NewPlayer(ctx, inputHandler) player1 := player.NewPlayer(ctx, inputHandler)
penguinEntity := types.NewPenguin(renderer) penguinEntity := types.NewPenguin(renderer)
penguinCmdHandler := command.NewCommandHandler(ctx, penguinEntity) penguinCmdHandler := command.NewCommandHandler(ctx, &penguinEntity)
penguinEntity.SetSpeed(2.0) penguinEntity.SetSpeed(2.0)
entityList = append(entityList, penguinCmdHandler) entityList = append(entityList, penguinCmdHandler)
player1.SetEntityChan(penguinCmdHandler.CommandRequest()) player1.SetEntityChan(penguinCmdHandler.CommandRequest())
@ -105,9 +104,9 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
entity := types.NewPenguin(renderer) entity := types.NewPenguin(renderer)
entityCmd := command.NewCommandHandler(ctx, entity) 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.SetPosition(randomPos)
entity.SetAnimation(rand.Intn(animation.PENGUIN_NUM_ANIMS)) entity.SetAnimation(rand.Intn(animation.PENGUIN_NUM_ANIMS))
entityList = append(entityList, entityCmd) entityList = append(entityList, entityCmd)
@ -204,6 +203,8 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
e.CloseRequests() e.CloseRequests()
} }
sprite.CleanupSpriteCache()
wg.Wait() wg.Wait()
return nil return nil

View File

@ -22,26 +22,24 @@ func NewAnimation(
offset sdl.Point, offset sdl.Point,
length int, length int,
border int, border int,
) *spriteAnimation { ) spriteAnimation {
spritesheet := GetSpritesheet(filename) spritesheet := GetSpritesheet(filename)
a := spriteAnimation{ return spriteAnimation{
spritesheet: spritesheet, spritesheet: spritesheet,
dimensions: dimensions, dimensions: dimensions,
offset: offset, offset: offset,
length: length, length: length,
border: border, border: border,
} }
return &a
} }
func (a *spriteAnimation) Draw( func (a spriteAnimation) Draw(
frame int, frame int,
windowPosition *sdl.Point, windowPosition sdl.Point,
angle float64, angle float64,
center *sdl.Point, center sdl.Point,
flip sdl.RendererFlip, flip sdl.RendererFlip,
) error { ) error {
@ -64,7 +62,7 @@ func (a *spriteAnimation) Draw(
H: height, H: height,
} }
err := a.checkBounds(&section) err := a.checkBounds(section)
if err != nil { if err != nil {
return err return err
} }
@ -76,7 +74,7 @@ func (a *spriteAnimation) Draw(
H: height * 2, H: height * 2,
} }
err = a.spritesheet.Draw(&section, &placement, angle, center, flip) err = a.spritesheet.Draw(section, placement, angle, center, flip)
if err != nil { if err != nil {
return err return err
} }
@ -84,7 +82,7 @@ func (a *spriteAnimation) Draw(
return nil return nil
} }
func (a *spriteAnimation) checkBounds(section *sdl.Rect) error { func (a spriteAnimation) checkBounds(section sdl.Rect) error {
width := a.spritesheet.surface.W width := a.spritesheet.surface.W
height := a.spritesheet.surface.H height := a.spritesheet.surface.H
@ -103,7 +101,7 @@ func (a *spriteAnimation) checkBounds(section *sdl.Rect) error {
} }
if outOfBounds { if outOfBounds {
return fmt.Errorf("draw section was out of bounds - section: %v, image: %v", *section, a.spritesheet.surface.Bounds()) return fmt.Errorf("draw section was out of bounds - section: %v, image: %v", section, a.spritesheet.surface.Bounds())
} }
return nil return nil

View File

@ -49,39 +49,33 @@ func NewSprite(renderer *sdl.Renderer, filename string) (*spritesheet, error) {
} }
func (s *spritesheet) Cleanup() { func (s *spritesheet) Cleanup() {
// Clean up image sdl.Do(func() {
defer func() { // Clean up spritesheet
if s.surface != nil {
sdl.Do(func() {
s.surface.Free()
})
}
}()
// Clean up spritesheet
defer func() {
if s.texture != nil { if s.texture != nil {
sdl.Do(func() { err := s.texture.Destroy()
err := s.texture.Destroy() if err != nil {
if err != nil { log.Printf("error destroying spritesheet %v: %v\n", s.filename, err)
log.Printf("error destroying spritesheet %v: %v\n", s.filename, err) }
}
})
} }
}()
// Clean up image
if s.surface != nil {
s.surface.Free()
}
})
} }
func (s *spritesheet) Draw( func (s *spritesheet) Draw(
section *sdl.Rect, section sdl.Rect,
placement *sdl.Rect, placement sdl.Rect,
angle float64, angle float64,
center *sdl.Point, center sdl.Point,
flip sdl.RendererFlip, flip sdl.RendererFlip,
) error { ) error {
var err error var err error
sdl.Do(func() { sdl.Do(func() {
err = s.renderer.CopyEx(s.texture, section, placement, angle, center, flip) err = s.renderer.CopyEx(s.texture, &section, &placement, angle, &center, flip)
}) })
if err != nil { if err != nil {
return err return err
@ -90,10 +84,9 @@ func (s *spritesheet) Draw(
return nil return nil
} }
func (s *spritesheet) Bounds() *sdl.Point { func (s *spritesheet) Bounds() sdl.Point {
p := sdl.Point{ return sdl.Point{
X: s.surface.W, X: s.surface.W,
Y: s.surface.H, Y: s.surface.H,
} }
return &p
} }

View File

@ -28,20 +28,19 @@ var (
) )
func InitSpriteCache(renderer *sdl.Renderer) error { func InitSpriteCache(renderer *sdl.Renderer) error {
var err error
defaultSprite, err = createDefaultSprite(renderer)
if err != nil {
log.Printf("failed to create DefaultSprite: %v", err)
return err
}
spriteCache = make(map[string]*spritesheet) spriteCache = make(map[string]*spritesheet)
for _, filename := range fileList { for _, filename := range fileList {
s, err := NewSprite(renderer, filename) s, err := NewSprite(renderer, filename)
if err != nil { if err != nil {
log.Printf("error creating sprite %v, using DefaultSprite: %v", filename, err) log.Printf("error creating sprite %v, using DefaultSprite: %v", filename, err)
defaultSprite, err = createDefaultSprite(renderer)
if err != nil {
log.Printf("failed to create DefaultSprite: %v", err)
return err
}
spriteCache[filename] = defaultSprite spriteCache[filename] = defaultSprite
} else { } else {
spriteCache[filename] = s spriteCache[filename] = s
@ -82,6 +81,7 @@ func createDefaultSprite(renderer *sdl.Renderer) (*spritesheet, error) {
} }
s := spritesheet{ s := spritesheet{
filename: "DEFAULT_SPRITE",
renderer: renderer, renderer: renderer,
texture: texture, texture: texture,
surface: surface, surface: surface,

View File

@ -11,24 +11,28 @@ type Vec2F struct {
Y float64 Y float64
} }
func (v *Vec2F) Zero() bool { func (v Vec2F) Zero() bool {
return v.X == 0.0 && v.Y == 0.0 return v.X == 0.0 && v.Y == 0.0
} }
func (v *Vec2F) LengthSquared() float64 { func (v Vec2F) LengthSquared() float64 {
return v.X*v.X + v.Y*v.Y return v.X*v.X + v.Y*v.Y
} }
func (v *Vec2F) Normalize() { func (v Vec2F) Normalized() Vec2F {
output := Vec2F{X: v.X, Y: v.Y}
length := math.Hypot(v.X, v.Y) length := math.Hypot(v.X, v.Y)
if length == 0 { if length == 0 {
return return output
} }
v.X = v.X / length output.X = v.X / length
v.Y = v.Y / length output.Y = v.Y / length
return output
} }
func (v *Vec2F) Dot(other *Vec2F) float64 { func (v Vec2F) Dot(other Vec2F) float64 {
return v.X*other.X + v.Y*other.Y return v.X*other.X + v.Y*other.Y
} }

View File

@ -58,8 +58,9 @@ func run(ctx context.Context) error {
// Setup logging // Setup logging
writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"]) writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"])
utcTime := gosimpleconf.Bool(configMap["log.utcTime"])
logFilename := configMap["log.file"] logFilename := configMap["log.file"]
logWriter, err := config.SetupLogging(writeToFile, logFilename) logWriter, err := config.SetupLogging(writeToFile, utcTime, logFilename)
if err != nil { if err != nil {
log.Fatalf("error setting up logging: %v\n", err) log.Fatalf("error setting up logging: %v\n", err)
} }