summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-08-25 20:41:15 -0700
committerRose Hogenson <rosehogenson@posteo.net>2024-08-25 20:41:15 -0700
commit4860f772af83e3bf8557bb0e3b1ebb44d2298537 (patch)
treed1906b6523ee1f6f3a1d9c0b5684e416074aa07f
parentebefdf78442648e86d9d305ba17cc0004dfb444b (diff)
downloadpongo-4860f772af83e3bf8557bb0e3b1ebb44d2298537.tar.zst
Use GPU rendering and fix FPS limiter.
-rw-r--r--numbers/numbers.go94
-rw-r--r--pongo.go145
2 files changed, 113 insertions, 126 deletions
diff --git a/numbers/numbers.go b/numbers/numbers.go
index 21e5cb5..7a2194a 100644
--- a/numbers/numbers.go
+++ b/numbers/numbers.go
@@ -6,80 +6,60 @@ import (
const width = 60
-func drawDigit(surface *sdl.Surface, d int, x, y int32) error {
+var digitSegments = [][]int{
+ {0, 1, 2, 4, 5, 6},
+ {2, 5},
+ {0, 2, 3, 4, 6},
+ {0, 2, 3, 5, 6},
+ {1, 2, 3, 5},
+ {0, 1, 3, 5, 6},
+ {0, 1, 3, 4, 5, 6},
+ {0, 2, 5},
+ {0, 1, 2, 3, 4, 5, 6},
+ {0, 1, 2, 3, 5, 6},
+}
+
+func drawDigit(renderer *sdl.Renderer, d int, x, y int32) error {
const (
height = 100
thickness = 10
)
- white := sdl.MapRGB(surface.Format, 245, 245, 245)
- switch d {
- case 0:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 1:
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- case 2:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 3:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 4:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- case 5:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y + height/2, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 6:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y + height/2, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 7:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- case 8:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- case 9:
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height / 2}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness}, white)
- surface.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness}, white)
- default:
- panic("invalid digit")
+ for _, segment := range digitSegments[d] {
+ switch segment {
+ case 0:
+ renderer.FillRect(&sdl.Rect{X: x, Y: y, W: width, H: thickness})
+ case 1:
+ renderer.FillRect(&sdl.Rect{X: x, Y: y, W: thickness, H: height / 2})
+ case 2:
+ renderer.FillRect(&sdl.Rect{X: x + width - thickness, Y: y, W: thickness, H: height / 2})
+ case 3:
+ renderer.FillRect(&sdl.Rect{X: x, Y: y + height/2 - thickness/2, W: width, H: thickness})
+ case 4:
+ renderer.FillRect(&sdl.Rect{X: x, Y: y + height/2, W: thickness, H: height / 2})
+ case 5:
+ renderer.FillRect(&sdl.Rect{X: x + width - thickness, Y: y + height/2, W: thickness, H: height / 2})
+ case 6:
+ renderer.FillRect(&sdl.Rect{X: x, Y: y + height - thickness, W: width, H: thickness})
+ }
}
return nil
}
-func Draw(surface *sdl.Surface, n int, x, y int32) error {
+func Draw(renderer *sdl.Renderer, n int, x, y int32) error {
const padding = 20
+ if err := renderer.SetDrawColor(245, 245, 245, 255); err != nil {
+ return err
+ }
if n == 0 {
- return drawDigit(surface, 0, x, y)
+ return drawDigit(renderer, 0, x, y)
}
digits := make([]int, 0, 19)
for ; n > 0; n /= 10 {
digits = append(digits, n%10)
}
for i := len(digits) - 1; i >= 0; i-- {
- if err := drawDigit(surface, digits[i], x, y); err != nil {
+ if err := drawDigit(renderer, digits[i], x, y); err != nil {
return err
}
x += width + padding
diff --git a/pongo.go b/pongo.go
index 44f79a2..cb0ebe3 100644
--- a/pongo.go
+++ b/pongo.go
@@ -118,8 +118,6 @@ func (p *paddle) bounds() *sdl.Rect {
var pingWAV []byte
func pong() error {
- const fps = 60
-
if err := sdl.Init(sdl.INIT_AUDIO & sdl.INIT_VIDEO & sdl.INIT_EVENTS); err != nil {
return err
}
@@ -135,11 +133,12 @@ func pong() error {
}
defer ping.Free()
- window, err := sdl.CreateWindow("pongo", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, screenWidth, screenHeight, sdl.WINDOW_SHOWN)
+ window, renderer, err := sdl.CreateWindowAndRenderer(screenWidth, screenHeight, sdl.WINDOW_OPENGL&sdl.WINDOW_BORDERLESS)
if err != nil {
return err
}
defer window.Destroy()
+ defer renderer.Destroy()
upPressed := false
downPressed := false
@@ -153,7 +152,8 @@ func pong() error {
paddle1 := paddle{pos: screenHeight / 2, side: left}
paddle2 := paddle{pos: screenHeight / 2, side: right}
- frameTargetTime := time.Now().Add(time.Second / fps)
+ lastFrameTime := time.Now()
+ accumulator := time.Duration(0)
for {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
@@ -170,99 +170,106 @@ func pong() error {
}
}
- switch {
- case upPressed && paddle1.bounds().Y >= 0:
- paddle1.pos -= paddleSpeed
- case downPressed && rectBottom(paddle1.bounds()) <= screenHeight:
- paddle1.pos += paddleSpeed
- }
+ frameStart := time.Now()
+ accumulator += frameStart.Sub(lastFrameTime)
+ lastFrameTime = frameStart
- if prediction, ok := ball.predictIntersection(float64(paddle2.bounds().X)); ok {
+ const simulationSpeed = time.Second / 120
+ for ; accumulator >= simulationSpeed; accumulator -= simulationSpeed {
switch {
- case paddle2.pos <= prediction-paddleSpeed && rectBottom(paddle2.bounds()) <= screenHeight:
- paddle2.pos += paddleSpeed
- case paddle2.pos >= prediction+paddleSpeed && paddle2.bounds().Y >= 0:
- paddle2.pos -= paddleSpeed
+ case upPressed && paddle1.bounds().Y >= 0:
+ paddle1.pos -= paddleSpeed
+ case downPressed && rectBottom(paddle1.bounds()) <= screenHeight:
+ paddle1.pos += paddleSpeed
}
- }
- ball.pos = ball.pos.add(ball.velocity)
- switch {
- case ball.bounds().HasIntersection(paddle1.bounds()):
- ping.Play(-1, 0)
- d := ball.pos.y - paddle1.pos
- if d < 0 {
- d = -d
- }
- angle := d / (float64(paddle1.bounds().H)/2 + float64(ball.bounds().H)) * (75 * 2 * math.Pi / 360)
- if ball.pos.y < paddle1.pos {
- angle = -angle
- }
- speed := ballSpeed * (1 + 4*d/(float64(paddle1.bounds().H)/2+float64(ball.bounds().H)))
- ball.velocity = vector2{speed * math.Cos(angle), speed * math.Sin(angle)}
- case ball.bounds().HasIntersection(paddle2.bounds()):
- ping.Play(-1, 0)
- d := ball.pos.y - paddle2.pos
- if d < 0 {
- d = -d
+ if prediction, ok := ball.predictIntersection(float64(paddle2.bounds().X)); ok {
+ switch {
+ case paddle2.pos <= prediction-paddleSpeed && rectBottom(paddle2.bounds()) <= screenHeight:
+ paddle2.pos += paddleSpeed
+ case paddle2.pos >= prediction+paddleSpeed && paddle2.bounds().Y >= 0:
+ paddle2.pos -= paddleSpeed
+ }
}
- angle := d / (float64(paddle2.bounds().H)/2 + float64(ball.bounds().H)) * (75 * 2 * math.Pi / 360)
- if ball.pos.y < paddle2.pos {
- angle = -angle
+
+ oldPos := ball.pos
+ ball.pos = ball.pos.add(ball.velocity)
+ switch {
+ case ball.bounds().HasIntersection(paddle1.bounds()):
+ ping.Play(-1, 0)
+ d := ball.pos.y - paddle1.pos
+ if d < 0 {
+ d = -d
+ }
+ angle := d / (float64(paddle1.bounds().H)/2 + float64(ball.bounds().H)) * (75 * 2 * math.Pi / 360)
+ if ball.pos.y < paddle1.pos {
+ angle = -angle
+ }
+ speed := ballSpeed * (1 + 4*d/(float64(paddle1.bounds().H)/2+float64(ball.bounds().H)))
+ ball.velocity = vector2{speed * math.Cos(angle), speed * math.Sin(angle)}
+ ball.pos = oldPos
+ case ball.bounds().HasIntersection(paddle2.bounds()):
+ ping.Play(-1, 0)
+ d := ball.pos.y - paddle2.pos
+ if d < 0 {
+ d = -d
+ }
+ angle := d / (float64(paddle2.bounds().H)/2 + float64(ball.bounds().H)) * (75 * 2 * math.Pi / 360)
+ if ball.pos.y < paddle2.pos {
+ angle = -angle
+ }
+ angle += math.Pi
+ speed := ballSpeed * (1 + 4*d/(float64(paddle2.bounds().H)/2+float64(ball.bounds().H)))
+ ball.velocity = vector2{speed * math.Cos(angle), speed * math.Sin(angle)}
+ ball.pos = oldPos
+ case rectRight(ball.bounds()) <= 0:
+ aiScore++
+ ball.reset(right)
+ case ball.bounds().X >= screenWidth:
+ playerScore++
+ ball.reset(left)
+ case ball.bounds().Y <= 0 || rectBottom(ball.bounds()) >= screenHeight:
+ ping.Play(-1, 0)
+ ball.pos = oldPos
+ ball.velocity.y = -ball.velocity.y
}
- angle += math.Pi
- speed := ballSpeed * (1 + 4*d/(float64(paddle2.bounds().H)/2+float64(ball.bounds().H)))
- ball.velocity = vector2{speed * math.Cos(angle), speed * math.Sin(angle)}
- case rectRight(ball.bounds()) <= paddle1.bounds().X:
- aiScore++
- ball.reset(right)
- case ball.bounds().X >= paddle2.bounds().X:
- playerScore++
- ball.reset(left)
- case ball.bounds().Y <= 0 || rectBottom(ball.bounds()) >= screenHeight:
- ping.Play(-1, 0)
- ball.velocity.y = -ball.velocity.y
}
- surface, err := window.GetSurface()
- if err != nil {
+ if err := renderer.SetDrawColor(0, 0, 0, 255); err != nil {
fmt.Fprintln(os.Stderr, err)
- continue
}
-
- gray := sdl.MapRGB(surface.Format, 130, 130, 130)
- offWhite := sdl.MapRGB(surface.Format, 245, 245, 245)
-
- if err := surface.FillRect(nil, 0); err != nil {
+ if err := renderer.FillRect(nil); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- if err := surface.FillRect(&sdl.Rect{X: screenWidth / 2, Y: 0, W: 1, H: screenHeight}, gray); err != nil {
+ if err := renderer.SetDrawColor(130, 130, 130, 255); err != nil {
fmt.Fprintln(os.Stderr, err)
}
-
- if err := surface.FillRect(paddle1.bounds(), offWhite); err != nil {
+ if err := renderer.FillRect(&sdl.Rect{X: screenWidth / 2, Y: 0, W: 1, H: screenHeight}); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- if err := surface.FillRect(paddle2.bounds(), offWhite); err != nil {
+
+ if err := renderer.SetDrawColor(245, 245, 245, 255); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- if err := surface.FillRect(ball.bounds(), offWhite); err != nil {
+ if err := renderer.FillRect(paddle1.bounds()); err != nil {
fmt.Fprintln(os.Stderr, err)
}
-
- if err := numbers.Draw(surface, playerScore, paddle1.bounds().X+20, 30); err != nil {
+ if err := renderer.FillRect(paddle2.bounds()); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- if err := numbers.Draw(surface, aiScore, paddle2.bounds().X-500, 30); err != nil {
+ if err := renderer.FillRect(ball.bounds()); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- if err := window.UpdateSurface(); err != nil {
+ if err := numbers.Draw(renderer, playerScore, paddle1.bounds().X+20, 30); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ if err := numbers.Draw(renderer, aiScore, paddle2.bounds().X-500, 30); err != nil {
fmt.Fprintln(os.Stderr, err)
}
- time.Sleep(time.Until(frameTargetTime))
- frameTargetTime = frameTargetTime.Add(time.Second / fps)
+ renderer.Present()
+ time.Sleep(time.Second/60 - time.Since(frameStart))
}
}