Pimp floppy example

This commit is contained in:
Milan Nikolic 2017-02-03 00:26:27 +01:00
parent ea0628725d
commit ea2748fd8c

View file

@ -2,148 +2,305 @@ package main
import ( import (
"fmt" "fmt"
"os"
"runtime"
"unsafe"
"github.com/gen2brain/raylib-go/raylib" "github.com/gen2brain/raylib-go/raylib"
) )
const ( const (
maxTubes = 100 // Maximum number of pipes
floppyRadius = 24.0 maxPipes = 100
tubesWidth = 80 // Pipes width
pipesWidth = 60
// Sprite size
spriteSize = 48
) )
// Floppy
type Floppy struct { type Floppy struct {
Position raylib.Vector2 Position raylib.Vector2
Radius float32
Color raylib.Color
} }
type Tubes struct { // Pipe
type Pipe struct {
Rec raylib.Rectangle Rec raylib.Rectangle
Color raylib.Color Color raylib.Color
Active bool Active bool
} }
// Game
type Game struct { type Game struct {
ScreenWidth int32 ScreenWidth int32
ScreenHeight int32 ScreenHeight int32
FramesCounter int32 FxFlap raylib.Sound
GameOver bool FxSlap raylib.Sound
Pause bool FxPoint raylib.Sound
Score int FxClick raylib.Sound
HiScore int
Floppy Floppy Texture raylib.Texture2D
Tubes []Tubes FrameRec raylib.Rectangle
TubesPos []raylib.Vector2
TubesSpeedX int32 GameOver bool
SuperFX bool Dead bool
Pause bool
SuperFX bool
Score int
HiScore int
FramesCounter int32
WindowShouldClose bool
Floppy Floppy
Pipes []Pipe
PipesPos []raylib.Vector2
PipesSpeedX int32
} }
// New Game
func NewGame() (g Game) {
g.Init()
return
}
// On Android this sets callback function to be used for android_main
func init() {
raylib.SetCallbackFunc(run)
}
// Main function, not used on Android, used on desktop platform
func main() { func main() {
game := Game{} run(nil)
game.Init() }
raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "sample game: floppy") // Callback function on Android, not needed on desktop platform
func run(app unsafe.Pointer) {
// Initialize game
game := NewGame()
// Initialize window
if runtime.GOOS != "android" {
raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "Floppy Gopher")
} else {
raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, app)
}
// Initialize audio
raylib.InitAudioDevice()
// NOTE: Textures and Sounds MUST be loaded after Window/Audio initialization
game.Load()
// Limit FPS
raylib.SetTargetFPS(60) raylib.SetTargetFPS(60)
for !raylib.WindowShouldClose() { // Main loop
for !game.WindowShouldClose {
// Update game
game.Update() game.Update()
// Draw game
game.Draw() game.Draw()
} }
// Free resources
game.Unload()
// Close audio
raylib.CloseAudioDevice()
// Close window
raylib.CloseWindow() raylib.CloseWindow()
// Exit
os.Exit(0)
} }
// Initialize game // Initialize game
func (g *Game) Init() { func (g *Game) Init() {
g.ScreenWidth = 800 // Window resolution
g.ScreenHeight = 450 g.ScreenWidth = 504
g.ScreenHeight = 896
g.Floppy = Floppy{} g.Floppy = Floppy{}
g.Floppy.Radius = floppyRadius g.Floppy.Position = raylib.NewVector2(80, float32(g.ScreenHeight)/2-spriteSize/2)
g.Floppy.Position = raylib.NewVector2(80, float32(g.ScreenHeight)/2-g.Floppy.Radius) g.PipesSpeedX = 2
g.TubesSpeedX = 2
g.TubesPos = make([]raylib.Vector2, maxTubes) // Sprite rectangle
g.FrameRec = raylib.NewRectangle(0, 0, spriteSize, spriteSize)
for i := 0; i < maxTubes; i++ { // Pipes positions
g.TubesPos[i].X = float32(400 + 280*i) g.PipesPos = make([]raylib.Vector2, maxPipes)
g.TubesPos[i].Y = -float32(raylib.GetRandomValue(0, 120)) for i := 0; i < maxPipes; i++ {
g.PipesPos[i].X = float32(480 + 360*i)
g.PipesPos[i].Y = -float32(raylib.GetRandomValue(0, 240))
} }
g.Tubes = make([]Tubes, maxTubes*2) // Pipes colors
colors := []raylib.Color{
raylib.Orange, raylib.Red, raylib.Gold, raylib.Lime,
raylib.Violet, raylib.Brown, raylib.LightGray, raylib.Blue,
raylib.Yellow, raylib.Green, raylib.Purple, raylib.Beige,
}
for i := 0; i < maxTubes*2; i += 2 { // Pipes
g.Tubes[i].Rec.X = int32(g.TubesPos[i/2].X) g.Pipes = make([]Pipe, maxPipes*2)
g.Tubes[i].Rec.Y = int32(g.TubesPos[i/2].Y) for i := 0; i < maxPipes*2; i += 2 {
g.Tubes[i].Rec.Width = tubesWidth g.Pipes[i].Rec.X = int32(g.PipesPos[i/2].X)
g.Tubes[i].Rec.Height = 255 g.Pipes[i].Rec.Y = int32(g.PipesPos[i/2].Y)
g.Pipes[i].Rec.Width = pipesWidth
g.Pipes[i].Rec.Height = 550
g.Pipes[i].Color = colors[raylib.GetRandomValue(0, int32(len(colors)-1))]
g.Tubes[i+1].Rec.X = int32(g.TubesPos[i/2].X) g.Pipes[i+1].Rec.X = int32(g.PipesPos[i/2].X)
g.Tubes[i+1].Rec.Y = int32(600 + g.TubesPos[i/2].Y - 255) g.Pipes[i+1].Rec.Y = int32(1200 + g.PipesPos[i/2].Y - 550)
g.Tubes[i+1].Rec.Width = tubesWidth g.Pipes[i+1].Rec.Width = pipesWidth
g.Tubes[i+1].Rec.Height = 255 g.Pipes[i+1].Rec.Height = 550
g.Tubes[i/2].Active = true g.Pipes[i/2].Active = true
} }
g.Score = 0 g.Score = 0
g.FramesCounter = 0 g.FramesCounter = 0
g.WindowShouldClose = false
g.GameOver = false g.GameOver = false
g.Dead = false
g.SuperFX = false g.SuperFX = false
g.Pause = false g.Pause = false
} }
// Load resources
func (g *Game) Load() {
g.FxFlap = raylib.LoadSound("sounds/flap.wav")
g.FxSlap = raylib.LoadSound("sounds/slap.wav")
g.FxPoint = raylib.LoadSound("sounds/point.wav")
g.FxClick = raylib.LoadSound("sounds/click.wav")
g.Texture = raylib.LoadTexture("images/sprite.png")
}
// Unload resources
func (g *Game) Unload() {
raylib.UnloadSound(g.FxFlap)
raylib.UnloadSound(g.FxSlap)
raylib.UnloadSound(g.FxPoint)
raylib.UnloadSound(g.FxClick)
raylib.UnloadTexture(g.Texture)
}
// Update game // Update game
func (g *Game) Update() { func (g *Game) Update() {
if raylib.WindowShouldClose() {
g.WindowShouldClose = true
}
if !g.GameOver { if !g.GameOver {
if raylib.IsKeyPressed(raylib.KeyP) { if raylib.IsKeyPressed(raylib.KeyP) || raylib.IsKeyPressed(raylib.KeyBack) {
raylib.PlaySound(g.FxClick)
if runtime.GOOS == "android" && g.Pause {
g.WindowShouldClose = true
}
g.Pause = !g.Pause g.Pause = !g.Pause
} }
if !g.Pause { if !g.Pause {
for i := 0; i < maxTubes; i++ { if !g.Dead {
g.TubesPos[i].X -= float32(g.TubesSpeedX) // Scroll X
for i := 0; i < maxPipes; i++ {
g.PipesPos[i].X -= float32(g.PipesSpeedX)
}
for i := 0; i < maxPipes*2; i += 2 {
g.Pipes[i].Rec.X = int32(g.PipesPos[i/2].X)
g.Pipes[i+1].Rec.X = int32(g.PipesPos[i/2].X)
}
// Movement/Controls
if raylib.IsKeyDown(raylib.KeySpace) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) && !g.GameOver {
raylib.PlaySound(g.FxFlap)
// Switch flap sprites every 8 frames
g.FramesCounter++
if g.FramesCounter >= 8 {
g.FramesCounter = 0
g.FrameRec.X = spriteSize * 3
} else {
g.FrameRec.X = spriteSize * 2
}
// Floppy go up
g.Floppy.Position.Y -= 3
} else {
// Default sprite
//g.FrameRec.X = spriteSize
// Switch flap sprites every 8 frames
g.FramesCounter++
if g.FramesCounter >= 8 {
g.FramesCounter = 0
g.FrameRec.X = spriteSize
} else {
g.FrameRec.X = 0
}
// Floppy fall down
g.Floppy.Position.Y += 1
}
} }
for i := 0; i < maxTubes*2; i += 2 { if !g.Dead {
g.Tubes[i].Rec.X = int32(g.TubesPos[i/2].X) // Check Collisions
g.Tubes[i+1].Rec.X = int32(g.TubesPos[i/2].X) for i := 0; i < maxPipes*2; i++ {
} if raylib.CheckCollisionRecs(raylib.NewRectangle(int32(g.Floppy.Position.X), int32(g.Floppy.Position.Y), spriteSize, spriteSize), g.Pipes[i].Rec) {
// OMG You killed Gopher you bastard!
g.Dead = true
if raylib.IsKeyDown(raylib.KeySpace) && !g.GameOver { raylib.PlaySound(g.FxSlap)
g.Floppy.Position.Y -= 3 } else if (g.PipesPos[i/2].X < g.Floppy.Position.X-spriteSize) && g.Pipes[i/2].Active && !g.GameOver {
} else { // Score point
g.Floppy.Position.Y += 1 g.Score += 1
} g.Pipes[i/2].Active = false
// Check Collisions // Flash screen
for i := 0; i < maxTubes*2; i++ { g.SuperFX = true
if raylib.CheckCollisionCircleRec(g.Floppy.Position, g.Floppy.Radius, g.Tubes[i].Rec) {
g.GameOver = true
g.Pause = false
} else if (g.TubesPos[i/2].X < g.Floppy.Position.X) && g.Tubes[i/2].Active && !g.GameOver {
g.Score += 100
g.Tubes[i/2].Active = false
g.SuperFX = true // Update HiScore
if g.Score > g.HiScore {
g.HiScore = g.Score
}
if g.Score > g.HiScore { raylib.PlaySound(g.FxPoint)
g.HiScore = g.Score
} }
} }
} else {
// Wait 60 frames before GameOver
g.FramesCounter++
if g.FramesCounter >= 60 {
g.GameOver = true
}
g.FrameRec.X = spriteSize * 4
}
} else {
if raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
g.Pause = !g.Pause
} }
} }
} else { } else {
if raylib.IsKeyPressed(raylib.KeyEnter) { if raylib.IsKeyPressed(raylib.KeyEnter) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
raylib.PlaySound(g.FxClick)
// Return of the Gopher!
g.Init() g.Init()
g.GameOver = false } else if runtime.GOOS == "android" && raylib.IsKeyDown(raylib.KeyBack) {
g.WindowShouldClose = true
} }
} }
} }
@ -151,31 +308,43 @@ func (g *Game) Update() {
func (g *Game) Draw() { func (g *Game) Draw() {
raylib.BeginDrawing() raylib.BeginDrawing()
raylib.ClearBackground(raylib.RayWhite) raylib.ClearBackground(raylib.SkyBlue)
if !g.GameOver { if !g.GameOver {
raylib.DrawCircle(int32(g.Floppy.Position.X), int32(g.Floppy.Position.Y), g.Floppy.Radius, raylib.DarkGray) // Draw Gopher
raylib.DrawTextureRec(g.Texture, g.FrameRec, g.Floppy.Position, raylib.RayWhite) // Draw part of the texture
// Draw tubes // Draw pipes
for i := 0; i < maxTubes; i++ { for i := 0; i < maxPipes; i++ {
raylib.DrawRectangle(g.Tubes[i*2].Rec.X, g.Tubes[i*2].Rec.Y, g.Tubes[i*2].Rec.Width, g.Tubes[i*2].Rec.Height, raylib.Gray) raylib.DrawRectangle(g.Pipes[i*2].Rec.X, g.Pipes[i*2].Rec.Y, g.Pipes[i*2].Rec.Width, g.Pipes[i*2].Rec.Height, g.Pipes[i*2].Color)
raylib.DrawRectangle(g.Tubes[i*2+1].Rec.X, g.Tubes[i*2+1].Rec.Y, g.Tubes[i*2+1].Rec.Width, g.Tubes[i*2+1].Rec.Height, raylib.Gray) raylib.DrawRectangle(g.Pipes[i*2+1].Rec.X, g.Pipes[i*2+1].Rec.Y, g.Pipes[i*2+1].Rec.Width, g.Pipes[i*2+1].Rec.Height, g.Pipes[i*2].Color)
// Draw borders
raylib.DrawRectangleLines(g.Pipes[i*2].Rec.X, g.Pipes[i*2].Rec.Y, g.Pipes[i*2].Rec.Width, g.Pipes[i*2].Rec.Height, raylib.Black)
raylib.DrawRectangleLines(g.Pipes[i*2+1].Rec.X, g.Pipes[i*2+1].Rec.Y, g.Pipes[i*2+1].Rec.Width, g.Pipes[i*2+1].Rec.Height, raylib.Black)
} }
// Draw flashing fx (one frame only) // Draw Super Flashing FX (one frame only)
if g.SuperFX { if g.SuperFX {
raylib.DrawRectangle(0, 0, g.ScreenWidth, g.ScreenHeight, raylib.White) raylib.DrawRectangle(0, 0, g.ScreenWidth, g.ScreenHeight, raylib.White)
g.SuperFX = false g.SuperFX = false
} }
raylib.DrawText(fmt.Sprintf("%04d", g.Score), 20, 20, 40, raylib.Gray) // Draw HI-SCORE
raylib.DrawText(fmt.Sprintf("HI-SCORE: %04d", g.HiScore), 20, 70, 20, raylib.LightGray) raylib.DrawText(fmt.Sprintf("%02d", g.Score), 20, 20, 32, raylib.RayWhite)
raylib.DrawText(fmt.Sprintf("HI-SCORE: %02d", g.HiScore), 20, 64, 20, raylib.RayWhite)
if g.Pause { if g.Pause {
raylib.DrawText("GAME PAUSED", g.ScreenWidth/2-raylib.MeasureText("GAME PAUSED", 40)/2, g.ScreenHeight/2-40, 40, raylib.Gray) // Draw PAUSED text
raylib.DrawText("PAUSED", g.ScreenWidth/2-raylib.MeasureText("PAUSED", 24)/2, g.ScreenHeight/2-50, 20, raylib.RayWhite)
} }
} else { } else {
raylib.DrawText("PRESS [ENTER] TO PLAY AGAIN", raylib.GetScreenWidth()/2-raylib.MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Gray) // Draw PLAY AGAIN text
if runtime.GOOS == "android" {
raylib.DrawText("[TAP] TO PLAY AGAIN", raylib.GetScreenWidth()/2-raylib.MeasureText("[TAP] TO PLAY AGAIN", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.RayWhite)
} else {
raylib.DrawText("[ENTER] TO PLAY AGAIN", raylib.GetScreenWidth()/2-raylib.MeasureText("[ENTER] TO PLAY AGAIN", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.RayWhite)
}
} }
raylib.EndDrawing() raylib.EndDrawing()