diff options
| -rw-r--r-- | numbers/numbers.go | 94 | ||||
| -rw-r--r-- | pongo.go | 145 |
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 @@ -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)) } } |
