diff --git a/cmd/doodle/main.go b/cmd/doodle/main.go index a074b92..3162344 100644 --- a/cmd/doodle/main.go +++ b/cmd/doodle/main.go @@ -26,6 +26,10 @@ func init() { if BuildDate == "" { BuildDate = time.Now().Format(time.RFC3339) } + + // Use all the CPU cores for collision detection and other load balanced + // goroutine work in the app. + runtime.GOMAXPROCS(runtime.NumCPU()) } func main() { diff --git a/pkg/collision/collide_level.go b/pkg/collision/collide_level.go index 0f88e88..6e7539f 100644 --- a/pkg/collision/collide_level.go +++ b/pkg/collision/collide_level.go @@ -1,6 +1,8 @@ package collision import ( + "sync" + "git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/level" @@ -202,10 +204,34 @@ func (c *Collide) IsColliding() bool { func (c *Collide) ScanBoundingBox(box render.Rect, grid *level.Chunker) bool { col := GetCollisionBox(box) - c.ScanGridLine(col.Top[0], col.Top[1], grid, Top) - c.ScanGridLine(col.Bottom[0], col.Bottom[1], grid, Bottom) - c.ScanGridLine(col.Left[0], col.Left[1], grid, Left) - c.ScanGridLine(col.Right[0], col.Right[1], grid, Right) + // Check all four edges of the box in parallel on different CPU cores. + type jobSide struct { + p1 render.Point + p2 render.Point + side Side + } + jobs := []jobSide{ + jobSide{col.Top[0], col.Top[1], Top}, + jobSide{col.Bottom[0], col.Bottom[1], Bottom}, + jobSide{col.Left[0], col.Left[1], Left}, + jobSide{col.Right[0], col.Right[1], Right}, + } + var wg sync.WaitGroup + for _, job := range jobs { + wg.Add(1) + go func(job jobSide) { + defer wg.Done() + c.ScanGridLine(job.p1, job.p2, grid, job.side) + }(job) + } + + wg.Wait() + + // TODO: the old synchronous version of the above. + // c.ScanGridLine(col.Top[0], col.Top[1], grid, Top) + // c.ScanGridLine(col.Bottom[0], col.Bottom[1], grid, Bottom) + // c.ScanGridLine(col.Left[0], col.Left[1], grid, Left) + // c.ScanGridLine(col.Right[0], col.Right[1], grid, Right) return c.IsColliding() } diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index 78ac0e1..3558335 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strings" + "sync" "time" "git.kirsle.net/apps/doodle/lib/events" @@ -191,52 +192,59 @@ func (w *Canvas) Loop(ev *events.State) error { // Move any actors. As we iterate over all actors, track their bounding // rectangles so we can later see if any pair of actors intersect each other. boxes := make([]render.Rect, len(w.actors)) + var wg sync.WaitGroup for i, a := range w.actors { - // Advance any animations for this actor. - if a.activeAnimation != nil && a.activeAnimation.nextFrameAt.Before(now) { - if done := a.TickAnimation(a.activeAnimation); done { - // Animation has finished, run the callback script. - if a.animationCallback.IsFunction() { - a.animationCallback.Call(otto.NullValue()) + wg.Add(1) + go func(i int, a *Actor) { + defer wg.Done() + + // Advance any animations for this actor. + if a.activeAnimation != nil && a.activeAnimation.nextFrameAt.Before(now) { + if done := a.TickAnimation(a.activeAnimation); done { + // Animation has finished, run the callback script. + if a.animationCallback.IsFunction() { + a.animationCallback.Call(otto.NullValue()) + } + + // Clean up the animation state. + a.StopAnimation() } - - // Clean up the animation state. - a.StopAnimation() } - } - // Get the actor's velocity to see if it's moving this tick. - v := a.Velocity() - if a.hasGravity { - v.Y += int32(balance.Gravity) - } + // Get the actor's velocity to see if it's moving this tick. + v := a.Velocity() + if a.hasGravity { + v.Y += int32(balance.Gravity) + } - // If not moving, grab the bounding box right now. - if v == render.Origin { + // If not moving, grab the bounding box right now. + if v == render.Origin { + boxes[i] = doodads.GetBoundingRect(a) + return + } + + // Create a delta point from their current location to where they + // want to move to this tick. + delta := a.Position() + delta.Add(v) + + // Check collision with level geometry. + info, ok := collision.CollidesWithGrid(a, w.chunks, delta) + if ok { + // Collision happened with world. + } + delta = info.MoveTo // Move us back where the collision check put us + + // Move the actor's World Position to the new location. + a.MoveTo(delta) + + // Keep the actor from leaving the world borders of bounded maps. + w.loopContainActorsInsideLevel(a) + + // Store this actor's bounding box after they've moved. boxes[i] = doodads.GetBoundingRect(a) - continue - } - - // Create a delta point from their current location to where they - // want to move to this tick. - delta := a.Position() - delta.Add(v) - - // Check collision with level geometry. - info, ok := collision.CollidesWithGrid(a, w.chunks, delta) - if ok { - // Collision happened with world. - } - delta = info.MoveTo // Move us back where the collision check put us - - // Move the actor's World Position to the new location. - a.MoveTo(delta) - - // Keep the actor from leaving the world borders of bounded maps. - w.loopContainActorsInsideLevel(a) - - // Store this actor's bounding box after they've moved. - boxes[i] = doodads.GetBoundingRect(a) + }(i, a) + wg.Wait() } // Check collisions between actors.