summaryrefslogtreecommitdiffstats
path: root/pongo.go
diff options
context:
space:
mode:
Diffstat (limited to 'pongo.go')
-rw-r--r--pongo.go240
1 files changed, 149 insertions, 91 deletions
diff --git a/pongo.go b/pongo.go
index f4dc038..b632626 100644
--- a/pongo.go
+++ b/pongo.go
@@ -2,79 +2,76 @@ package main
import (
_ "embed"
- "github.com/gen2brain/raylib-go/raylib"
+ "fmt"
+ "github.com/veandco/go-sdl2/mix"
+ "github.com/veandco/go-sdl2/sdl"
+ "gitlab.com/rhogenson/pongo/numbers"
"math"
- "strconv"
+ "os"
+ "time"
)
const (
screenWidth = 1920
screenHeight = 1080
- ballSpeed = 5
-
+ ballSpeed = 5
paddleSpeed = 5
)
-func printCenter(text string, size int32, color rl.Color) {
- rl.DrawText(text, screenWidth/2-rl.MeasureText(text, size), screenHeight/2, size, color)
-}
-
-type rect rl.Rectangle
-
-func (r rect) bottom() float32 {
- return r.Y + r.Height
+func rectRight(r *sdl.Rect) int32 {
+ return r.X + r.W
}
-func (r rect) right() float32 {
- return r.X + r.Width
+func rectBottom(r *sdl.Rect) int32 {
+ return r.Y + r.H
}
-func (r rect) collides(s rect) bool {
- return rl.CheckCollisionRecs(rl.Rectangle(r), rl.Rectangle(s))
+type vector2 struct {
+ x, y float64
}
-func (r rect) draw(color rl.Color) {
- rl.DrawRectangleRec(rl.Rectangle(r), color)
+func (v vector2) add(w vector2) vector2 {
+ return vector2{v.x + w.x, v.y + w.y}
}
type ball struct {
- pos rl.Vector2
- velocity rl.Vector2
+ pos vector2
+ velocity vector2
}
-func (b *ball) bounds() rect {
+func (b *ball) bounds() *sdl.Rect {
const ballSize = 30
- return rect{
- X: b.pos.X,
- Y: b.pos.Y,
- Width: ballSize,
- Height: ballSize,
+ return &sdl.Rect{
+ X: int32(b.pos.x - ballSize/2),
+ Y: int32(b.pos.y - ballSize/2),
+ W: ballSize,
+ H: ballSize,
}
}
-func (b *ball) predictIntersection(targetX float32) (float32, bool) {
- if b.velocity.X <= 0 {
+func (b *ball) predictIntersection(targetX float64) (float64, bool) {
+ if b.velocity.x <= 0 {
return 0, false
}
- x := b.pos.X
- y := b.pos.Y
+ x := b.pos.x
+ y := b.pos.y
velocity := b.velocity
dist := targetX - x
- prediction := y + velocity.Y*dist/velocity.X
+ prediction := y + velocity.y*dist/velocity.x
if prediction < 0 {
bounces := int(-prediction / screenHeight)
if bounces%2 == 0 {
- return -prediction - float32(screenHeight*bounces), true
+ return -prediction - float64(screenHeight*bounces), true
}
- return prediction + float32(screenHeight*(bounces+1)), true
+ return prediction + float64(screenHeight*(bounces+1)), true
}
if prediction > screenHeight {
bounces := int(prediction / screenHeight)
if bounces%2 == 0 {
- return prediction - float32(screenHeight*bounces), true
+ return prediction - float64(screenHeight*bounces), true
}
- return -prediction + float32(screenHeight*(bounces+1)), true
+ return -prediction + float64(screenHeight*(bounces+1)), true
}
return prediction, true
}
@@ -87,29 +84,29 @@ const (
)
func (b *ball) reset(side side) {
- b.pos = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2}
- b.velocity = rl.Vector2{X: ballSpeed}
+ b.pos = vector2{screenWidth / 2, screenHeight / 2}
+ b.velocity = vector2{x: ballSpeed}
if side == left {
- b.velocity.X = -b.velocity.X
+ b.velocity.x = -b.velocity.x
}
}
type paddle struct {
- pos float32
+ pos float64
side side
}
-func (p *paddle) bounds() rect {
+func (p *paddle) bounds() *sdl.Rect {
const (
paddleHeight = 200
thickness = 10
padding = 100
)
- r := rect{
- X: padding - thickness/2,
- Y: p.pos - paddleHeight/2,
- Width: thickness,
- Height: paddleHeight,
+ r := &sdl.Rect{
+ X: padding - thickness/2,
+ Y: int32(p.pos - paddleHeight/2),
+ W: thickness,
+ H: paddleHeight,
}
if p.side == right {
r.X = screenWidth - padding - thickness/2
@@ -117,21 +114,35 @@ func (p *paddle) bounds() rect {
return r
}
-//go:embed ping.mp3
-var pingMP3 []byte
+//go:embed ping.wav
+var pingWAV []byte
-func main() {
- rl.InitWindow(screenWidth, screenHeight, "pongo")
- defer rl.CloseWindow()
- rl.SetTargetFPS(120)
+func pong() error {
+ const fps = 60
- rl.InitAudioDevice()
- defer rl.CloseAudioDevice()
+ if err := sdl.Init(sdl.INIT_AUDIO & sdl.INIT_VIDEO & sdl.INIT_EVENTS); err != nil {
+ return err
+ }
+ defer sdl.Quit()
+ if err := mix.OpenAudio(44100, mix.DEFAULT_FORMAT, 2, 4096); err != nil {
+ return err
+ }
+ defer mix.CloseAudio()
- pingWave := rl.LoadWaveFromMemory(".mp3", pingMP3, int32(len(pingMP3)))
- defer rl.UnloadWave(pingWave)
- ping := rl.LoadSoundFromWave(pingWave)
- defer rl.UnloadSound(ping)
+ ping, err := mix.QuickLoadWAV(pingWAV)
+ if err != nil {
+ return err
+ }
+ defer ping.Free()
+
+ window, err := sdl.CreateWindow("pongo", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, screenWidth, screenHeight, sdl.WINDOW_SHOWN)
+ if err != nil {
+ return err
+ }
+ defer window.Destroy()
+
+ upPressed := false
+ downPressed := false
playerScore := 0
aiScore := 0
@@ -142,74 +153,121 @@ func main() {
paddle1 := paddle{pos: screenHeight / 2, side: left}
paddle2 := paddle{pos: screenHeight / 2, side: right}
- for !rl.WindowShouldClose() {
+ frameTargetTime := time.Now().Add(time.Second / fps)
+
+ for {
+ for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
+ switch t := event.(type) {
+ case *sdl.QuitEvent:
+ return nil
+ case *sdl.KeyboardEvent:
+ switch t.Keysym.Sym {
+ case sdl.K_UP:
+ upPressed = t.State == sdl.PRESSED
+ case sdl.K_DOWN:
+ downPressed = t.State == sdl.PRESSED
+ }
+ }
+ }
+
switch {
- case rl.IsKeyDown(rl.KeyUp) && paddle1.bounds().Y >= 0:
+ case upPressed && paddle1.bounds().Y >= 0:
paddle1.pos -= paddleSpeed
- case rl.IsKeyDown(rl.KeyDown) && paddle1.bounds().bottom() <= screenHeight:
+ case downPressed && rectBottom(paddle1.bounds()) <= screenHeight:
paddle1.pos += paddleSpeed
}
- if prediction, ok := ball.predictIntersection(paddle2.bounds().X); ok {
+ if prediction, ok := ball.predictIntersection(float64(paddle2.bounds().X)); ok {
switch {
- case paddle2.pos < prediction && paddle2.bounds().bottom() <= screenHeight:
+ case paddle2.pos < prediction && rectBottom(paddle2.bounds()) <= screenHeight:
paddle2.pos += paddleSpeed
case paddle2.pos > prediction && paddle2.bounds().Y >= 0:
paddle2.pos -= paddleSpeed
}
}
- ball.pos = rl.Vector2Add(ball.pos, ball.velocity)
+ ball.pos = ball.pos.add(ball.velocity)
switch {
- case ball.bounds().collides(paddle1.bounds()):
- rl.PlaySound(ping)
- d := ball.pos.Y - paddle1.pos
+ case ball.bounds().HasIntersection(paddle1.bounds()):
+ ping.Play(-1, 0)
+ d := ball.pos.y - paddle1.pos
if d < 0 {
d = -d
}
- angle := d / (paddle1.bounds().Height/2 + ball.bounds().Height) * (75 * 2 * math.Pi / 360)
- if ball.pos.Y < paddle1.pos {
+ 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/(paddle1.bounds().Height/2+ball.bounds().Height))
- ball.velocity = rl.Vector2{X: speed * float32(math.Cos(float64(angle))), Y: speed * float32(math.Sin(float64(angle)))}
- case ball.bounds().collides(paddle2.bounds()):
- rl.PlaySound(ping)
- d := ball.pos.Y - paddle2.pos
+ 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
}
- angle := d / (paddle2.bounds().Height/2 + ball.bounds().Height) * (75 * 2 * math.Pi / 360)
- if ball.pos.Y < paddle2.pos {
+ 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/(paddle2.bounds().Height/2+ball.bounds().Height))
- ball.velocity = rl.Vector2{X: speed * float32(math.Cos(float64(angle))), Y: speed * float32(math.Sin(float64(angle)))}
- case ball.bounds().right() <= paddle1.bounds().X:
+ 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 || ball.bounds().bottom() >= screenHeight:
- ball.velocity.Y = -ball.velocity.Y
- rl.PlaySound(ping)
+ case ball.bounds().Y <= 0 || rectBottom(ball.bounds()) >= screenHeight:
+ ping.Play(-1, 0)
+ ball.velocity.y = -ball.velocity.y
}
- rl.BeginDrawing()
+ surface, err := window.GetSurface()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ continue
+ }
- rl.ClearBackground(rl.Black)
- rl.DrawLine(screenWidth/2, 0, screenWidth/2, screenHeight, rl.Gray)
+ gray := sdl.MapRGB(surface.Format, 130, 130, 130)
+ offWhite := sdl.MapRGB(surface.Format, 245, 245, 245)
- paddle1.bounds().draw(rl.RayWhite)
- paddle2.bounds().draw(rl.RayWhite)
- ball.bounds().draw(rl.RayWhite)
+ if err := surface.FillRect(nil, 0); 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 {
+ fmt.Fprintln(os.Stderr, err)
+ }
- const fontSize = 120
- rl.DrawText(strconv.Itoa(playerScore), int32(paddle1.bounds().X)+20, 30, fontSize, rl.RayWhite)
- rl.DrawText(strconv.Itoa(aiScore), int32(paddle2.bounds().X)-rl.MeasureText("9999", fontSize), 30, fontSize, rl.RayWhite)
+ if err := surface.FillRect(paddle1.bounds(), offWhite); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ if err := surface.FillRect(paddle2.bounds(), offWhite); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ if err := surface.FillRect(ball.bounds(), offWhite); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
- rl.EndDrawing()
+ if err := numbers.Draw(surface, playerScore, paddle1.bounds().X+20, 30); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ if err := numbers.Draw(surface, aiScore, paddle2.bounds().X-500, 30); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+
+ if err := window.UpdateSurface(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+
+ time.Sleep(time.Until(frameTargetTime))
+ frameTargetTime = frameTargetTime.Add(time.Second / fps)
+ }
+}
+
+func main() {
+ if err := pong(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
}
}