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.framerate = 60.0
log.utcTime = false
log.file = "output.log"
log.writeToFile = false

View File

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

View File

@ -9,19 +9,24 @@ import (
type logWriter struct {
writeToFile bool
utcTime bool
logFile *os.File
}
func (w *logWriter) Write(bytes []byte) (int, error) {
t := time.Now().UTC().Format(time.RFC3339)
return fmt.Fprintf(w.logFile, "%v %v", t, string(bytes))
t := time.Now()
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() {
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
log.SetFlags(0)
@ -35,6 +40,7 @@ func SetupLogging(writeToFile bool, logFilename string) (*logWriter, error) {
writer := &logWriter{
writeToFile: writeToFile,
utcTime: utcTime,
logFile: logFile,
}
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.
// https://gamedev.stackexchange.com/a/123844
type camera struct {
pos *sdl.Point
pos sdl.Point
}
func NewCamera() *camera {

View File

@ -5,7 +5,7 @@ import (
)
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)
@ -15,7 +15,7 @@ type entityAnimation struct {
speed int
length int
angle float64
center *sdl.Point
center sdl.Point
flip sdl.RendererFlip
}
@ -24,11 +24,11 @@ func NewEntityAnimation(
speed int,
length int,
angle float64,
center *sdl.Point,
center sdl.Point,
flip sdl.RendererFlip,
) *entityAnimation {
) entityAnimation {
e := entityAnimation{
return entityAnimation{
spriteAnimation: spriteAnimation,
speed: speed,
length: length,
@ -36,18 +36,23 @@ func NewEntityAnimation(
center: center,
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)
}
func (e *entityAnimation) GetSpeed() int {
func (e entityAnimation) GetSpeed() int {
return e.speed
}
func DefineAnimations() {
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"
)
var PenguinAnimations map[int]*entityAnimation
var PenguinAnimations map[int]entityAnimation
const (
PENGUIN_WALK_RIGHT int = iota
@ -29,21 +29,21 @@ func DefinePenguinAnimations() {
var (
dimensions sdl.Point
offset sdl.Point
center *sdl.Point
center sdl.Point
length int
speed int
border int
)
dimensions = sdl.Point{X: 13, Y: 17}
PenguinAnimations = make(map[int]*entityAnimation)
PenguinAnimations = make(map[int]entityAnimation)
// Walking Right is in the spritesheet.
speed = 5
offset = sdl.Point{X: 0, Y: 1}
length = 5
border = 1 // optional border around each sprite
center = nil // center is for rotation, nil will default to w/2 h/2
border = 1 // optional border around each sprite
center = getCenter(dimensions) // center is for rotation, nil will default to w/2 h/2
walkRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
PenguinAnimations[PENGUIN_WALK_RIGHT] = NewEntityAnimation(walkRight, speed, length, 0, center, sdl.FLIP_NONE)

View File

@ -6,7 +6,7 @@ import (
)
type EntityAnimation interface {
Draw(step int, windowPosition *sdl.Point) error
Draw(step int, windowPosition sdl.Point) error
GetSpeed() int
}
@ -21,12 +21,12 @@ const (
// 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_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{
VEC_DIRECTIONS = []vector.Vec2F{
// Prefer left/right animations by checking them first in the list
VEC_LEFT,
VEC_RIGHT,
@ -35,7 +35,7 @@ var (
}
)
func determineClosestDirection(velocity *vector.Vec2F) int {
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

View File

@ -20,27 +20,25 @@ type penguin struct {
updateAnimation bool // if false, don't change the animation
// Physical parameters
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
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 {
func NewPenguin(renderer *sdl.Renderer) penguin {
position := vector.Vec2F{}
velocity := vector.Vec2F{}
speed := 1.0
p := penguin{
return penguin{
currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT],
animationStep: 0,
direction: DIR_RIGHT,
worldPosition: &position,
worldPosition: position,
speed: speed,
velocity: &velocity,
velocity: velocity,
}
return &p
}
func (p *penguin) Draw() error {
@ -48,7 +46,7 @@ func (p *penguin) Draw() error {
// TODO
//windowPosition := worldPosToWindowPos()
windowPosition := &sdl.Point{
windowPosition := sdl.Point{
X: int32(math.Round(p.worldPosition.X)),
Y: int32(math.Round(-1 * p.worldPosition.Y)),
}
@ -63,7 +61,7 @@ func (p *penguin) Draw() error {
return nil
}
func (p *penguin) SetPosition(vec *vector.Vec2F) {
func (p *penguin) SetPosition(vec vector.Vec2F) {
p.worldPosition = vec
}
@ -91,7 +89,7 @@ func (p *penguin) MoveX(x float64) {
defer p.mx.Unlock()
p.velocity.X = x
p.velocity.Normalize()
p.velocity = p.velocity.Normalized()
p.updateAnimation = true
}
@ -101,7 +99,7 @@ func (p *penguin) MoveY(y float64) {
// (0,0) is the top left, so negative y moves up
p.velocity.Y = y
p.velocity.Normalize()
p.velocity.Normalized()
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)
return err
}
defer sprite.CleanupSpriteCache()
animation.DefineAnimations()
@ -93,7 +92,7 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
// Let them control a penguin to start with
player1 := player.NewPlayer(ctx, inputHandler)
penguinEntity := types.NewPenguin(renderer)
penguinCmdHandler := command.NewCommandHandler(ctx, penguinEntity)
penguinCmdHandler := command.NewCommandHandler(ctx, &penguinEntity)
penguinEntity.SetSpeed(2.0)
entityList = append(entityList, penguinCmdHandler)
player1.SetEntityChan(penguinCmdHandler.CommandRequest())
@ -105,9 +104,9 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
for i := 0; i < 10; i++ {
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}
entity.SetPosition(&randomPos)
entity.SetPosition(randomPos)
entity.SetAnimation(rand.Intn(animation.PENGUIN_NUM_ANIMS))
entityList = append(entityList, entityCmd)
@ -204,6 +203,8 @@ func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
e.CloseRequests()
}
sprite.CleanupSpriteCache()
wg.Wait()
return nil

View File

@ -22,26 +22,24 @@ func NewAnimation(
offset sdl.Point,
length int,
border int,
) *spriteAnimation {
) spriteAnimation {
spritesheet := GetSpritesheet(filename)
a := spriteAnimation{
return spriteAnimation{
spritesheet: spritesheet,
dimensions: dimensions,
offset: offset,
length: length,
border: border,
}
return &a
}
func (a *spriteAnimation) Draw(
func (a spriteAnimation) Draw(
frame int,
windowPosition *sdl.Point,
windowPosition sdl.Point,
angle float64,
center *sdl.Point,
center sdl.Point,
flip sdl.RendererFlip,
) error {
@ -64,7 +62,7 @@ func (a *spriteAnimation) Draw(
H: height,
}
err := a.checkBounds(&section)
err := a.checkBounds(section)
if err != nil {
return err
}
@ -76,7 +74,7 @@ func (a *spriteAnimation) Draw(
H: height * 2,
}
err = a.spritesheet.Draw(&section, &placement, angle, center, flip)
err = a.spritesheet.Draw(section, placement, angle, center, flip)
if err != nil {
return err
}
@ -84,7 +82,7 @@ func (a *spriteAnimation) Draw(
return nil
}
func (a *spriteAnimation) checkBounds(section *sdl.Rect) error {
func (a spriteAnimation) checkBounds(section sdl.Rect) error {
width := a.spritesheet.surface.W
height := a.spritesheet.surface.H
@ -103,7 +101,7 @@ func (a *spriteAnimation) checkBounds(section *sdl.Rect) error {
}
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

View File

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

View File

@ -28,20 +28,19 @@ var (
)
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)
for _, filename := range fileList {
s, err := NewSprite(renderer, filename)
if err != nil {
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
} else {
spriteCache[filename] = s
@ -82,6 +81,7 @@ func createDefaultSprite(renderer *sdl.Renderer) (*spritesheet, error) {
}
s := spritesheet{
filename: "DEFAULT_SPRITE",
renderer: renderer,
texture: texture,
surface: surface,

View File

@ -11,24 +11,28 @@ type Vec2F struct {
Y float64
}
func (v *Vec2F) Zero() bool {
func (v Vec2F) Zero() bool {
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
}
func (v *Vec2F) Normalize() {
func (v Vec2F) Normalized() Vec2F {
output := Vec2F{X: v.X, Y: v.Y}
length := math.Hypot(v.X, v.Y)
if length == 0 {
return
return output
}
v.X = v.X / length
v.Y = v.Y / length
output.X = v.X / 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
}

View File

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