package main /******************************************************************************************* * * raylib - sample game: arkanoid * * Sample game Marc Palau and Ramon Santamaria * * This game has been created using raylib v1.3 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * * Copyright (c) 2015 Ramon Santamaria (@raysan5) * * Ported to raylib-go by Nehpe (@nehpe), July 2019 * ********************************************************************************************/ import ( "math" rl "github.com/gen2brain/raylib-go/raylib" ) const ( PLAYER_MAX_LIFE = 5 LINES_OF_BRICKS = 5 BRICKS_PER_LINE = 20 ) type Player struct { position rl.Vector2 size rl.Vector2 life int } type Ball struct { position rl.Vector2 speed rl.Vector2 radius float32 active bool } type Brick struct { position rl.Vector2 active bool } const ( screenWidth = 800 screenHeight = 450 ) type Game struct { gameOver bool pause bool player Player ball Ball brick [LINES_OF_BRICKS][BRICKS_PER_LINE]Brick brickSize rl.Vector2 } func main() { rl.InitWindow(screenWidth, screenHeight, "sample game: arkanoid") // Init game game := NewGame() game.gameOver = true rl.SetTargetFPS(60) for !rl.WindowShouldClose() { game.Update() game.Draw() } rl.CloseWindow() } // For android func init() { rl.SetCallbackFunc(main) } // NewGame - Create a new game instance func NewGame() (g Game) { g.Init() return } // Init - initialize game func (g *Game) Init() { g.brickSize = rl.Vector2{float32(rl.GetScreenWidth() / BRICKS_PER_LINE), 40} // Initialize player g.player.position = rl.Vector2{float32(screenWidth / 2), float32(screenHeight * 7 / 8)} g.player.size = rl.Vector2{float32(screenWidth / 10), 20} g.player.life = PLAYER_MAX_LIFE // Initialize ball g.ball.position = rl.Vector2{float32(screenWidth / 2), float32(screenHeight*7/8 - 30)} g.ball.speed = rl.Vector2{0, 0} g.ball.radius = 7 g.ball.active = false initialDownPosition := int(50) for i := 0; i < LINES_OF_BRICKS; i++ { for j := 0; j < BRICKS_PER_LINE; j++ { g.brick[i][j].position = rl.Vector2{float32(j)*g.brickSize.X + g.brickSize.X/2, float32(i)*g.brickSize.Y + float32(initialDownPosition)} g.brick[i][j].active = true } } } // Update - update game state func (g *Game) Update() { if !g.gameOver { if rl.IsKeyPressed(rl.KeyP) { g.pause = !g.pause } if !g.pause { if rl.IsKeyDown(rl.KeyLeft) || rl.IsKeyDown(rl.KeyA) { g.player.position.X -= 5 } if (g.player.position.X - g.player.size.X/2) <= 0 { g.player.position.X = g.player.size.X / 2 } if rl.IsKeyDown(rl.KeyRight) || rl.IsKeyDown(rl.KeyD) { g.player.position.X += 5 } if (g.player.position.X + g.player.size.X/2) >= screenWidth { g.player.position.X = screenWidth - g.player.size.X/2 } if !g.ball.active { if rl.IsKeyPressed(rl.KeySpace) { g.ball.active = true g.ball.speed = rl.Vector2{0, -5} } } if g.ball.active { g.ball.position.X += g.ball.speed.X g.ball.position.Y += g.ball.speed.Y } else { g.ball.position = rl.Vector2{g.player.position.X, screenHeight*7/8 - 30} } // Collision logic: ball vs walls if ((g.ball.position.X + g.ball.radius) >= screenWidth) || ((g.ball.position.X - g.ball.radius) <= 0) { g.ball.speed.X *= -1 } if (g.ball.position.Y - g.ball.radius) <= 0 { g.ball.speed.Y *= -1 } if (g.ball.position.Y + g.ball.radius) >= screenHeight { g.ball.speed = rl.Vector2{0, 0} g.ball.active = false g.player.life-- } if (rl.CheckCollisionCircleRec(g.ball.position, g.ball.radius, rl.Rectangle{g.player.position.X - g.player.size.X/2, g.player.position.Y - g.player.size.Y/2, g.player.size.X, g.player.size.Y})) { if g.ball.speed.Y > 0 { g.ball.speed.Y *= -1 g.ball.speed.X = (g.ball.position.X - g.player.position.X) / (g.player.size.X / 2) * 5 } } // Collision logic: ball vs bricks for i := 0; i < LINES_OF_BRICKS; i++ { for j := 0; j < BRICKS_PER_LINE; j++ { if g.brick[i][j].active { if ((g.ball.position.Y - g.ball.radius) <= (g.brick[i][j].position.Y + g.brickSize.Y/2)) && ((g.ball.position.Y - g.ball.radius) > (g.brick[i][j].position.Y + g.brickSize.Y/2 + g.ball.speed.Y)) && ((float32(math.Abs(float64(g.ball.position.X - g.brick[i][j].position.X)))) < (g.brickSize.X/2 + g.ball.radius*2/3)) && (g.ball.speed.Y < 0) { // Hit below g.brick[i][j].active = false g.ball.speed.Y *= -1 } else if ((g.ball.position.Y + g.ball.radius) >= (g.brick[i][j].position.Y - g.brickSize.Y/2)) && ((g.ball.position.Y + g.ball.radius) < (g.brick[i][j].position.Y - g.brickSize.Y/2 + g.ball.speed.Y)) && ((float32(math.Abs(float64(g.ball.position.X - g.brick[i][j].position.X)))) < (g.brickSize.X/2 + g.ball.radius*2/3)) && (g.ball.speed.Y > 0) { // Hit above g.brick[i][j].active = false g.ball.speed.Y *= -1 } else if ((g.ball.position.X + g.ball.radius) >= (g.brick[i][j].position.X - g.brickSize.X/2)) && ((g.ball.position.X + g.ball.radius) < (g.brick[i][j].position.X - g.brickSize.X/2 + g.ball.speed.X)) && ((float32(math.Abs(float64(g.ball.position.Y - g.brick[i][j].position.Y)))) < (g.brickSize.Y/2 + g.ball.radius*2/3)) && (g.ball.speed.X > 0) { // Hit left g.brick[i][j].active = false g.ball.speed.X *= -1 } else if ((g.ball.position.X - g.ball.radius) <= (g.brick[i][j].position.X + g.brickSize.X/2)) && ((g.ball.position.X - g.ball.radius) > (g.brick[i][j].position.X + g.brickSize.X/2 + g.ball.speed.X)) && ((float32(math.Abs(float64(g.ball.position.Y - g.brick[i][j].position.Y)))) < (g.brickSize.Y/2 + g.ball.radius*2/3)) && (g.ball.speed.X < 0) { // Hit right g.brick[i][j].active = false g.ball.speed.X *= -1 } } } } } // Game over logic if g.player.life <= 0 { g.gameOver = true } else { g.gameOver = true for i := 0; i < LINES_OF_BRICKS; i++ { for j := 0; j < BRICKS_PER_LINE; j++ { if g.brick[i][j].active { g.gameOver = false } } } } } else { if rl.IsKeyPressed(rl.KeyEnter) { g.Init() g.gameOver = false } } } // Draw - draw game func (g *Game) Draw() { rl.BeginDrawing() rl.ClearBackground(rl.White) if !g.gameOver { // Draw player bar rl.DrawRectangle(int32(g.player.position.X-g.player.size.X/2), int32(g.player.position.Y-g.player.size.Y/2), int32(g.player.size.X), int32(g.player.size.Y), rl.Black) // Draw player lives for i := 0; i < g.player.life; i++ { rl.DrawRectangle(int32(20+40*i), screenHeight-30, 35, 10, rl.LightGray) } // Draw Ball rl.DrawCircleV(g.ball.position, g.ball.radius, rl.Maroon) for i := 0; i < LINES_OF_BRICKS; i++ { for j := 0; j < BRICKS_PER_LINE; j++ { if g.brick[i][j].active { if (i+j)%2 == 0 { rl.DrawRectangle(int32(g.brick[i][j].position.X-g.brickSize.X/2), int32(g.brick[i][j].position.Y-g.brickSize.Y/2), int32(g.brickSize.X), int32(g.brickSize.Y), rl.Gray) } else { rl.DrawRectangle(int32(g.brick[i][j].position.X-g.brickSize.X/2), int32(g.brick[i][j].position.Y-g.brickSize.Y/2), int32(g.brickSize.X), int32(g.brickSize.Y), rl.DarkGray) } } } } if g.pause { rl.DrawText("GAME PAUSED", screenWidth/2-rl.MeasureText("GAME PAUSED", 40)/2, screenHeight/2+screenHeight/4-40, 40, rl.Gray) } } else { str := "PRESS [ENTER] TO PLAY AGAIN" x := int(rl.GetScreenWidth()/2) - int(rl.MeasureText(str, 20)/2) y := rl.GetScreenHeight()/2 - 50 rl.DrawText(str, int32(x), int32(y), 20, rl.Gray) } rl.EndDrawing() }