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
FxSlap raylib.Sound
FxPoint raylib.Sound
FxClick raylib.Sound
Texture raylib.Texture2D
FrameRec raylib.Rectangle
GameOver bool GameOver bool
Dead bool
Pause bool Pause bool
SuperFX bool
Score int Score int
HiScore int HiScore int
FramesCounter int32
WindowShouldClose bool
Floppy Floppy Floppy Floppy
Tubes []Tubes
TubesPos []raylib.Vector2 Pipes []Pipe
TubesSpeedX int32 PipesPos []raylib.Vector2
SuperFX bool 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 < maxTubes*2; i += 2 { for i := 0; i < maxPipes*2; i += 2 {
g.Tubes[i].Rec.X = int32(g.TubesPos[i/2].X) g.Pipes[i].Rec.X = int32(g.PipesPos[i/2].X)
g.Tubes[i+1].Rec.X = int32(g.TubesPos[i/2].X) g.Pipes[i+1].Rec.X = int32(g.PipesPos[i/2].X)
} }
if raylib.IsKeyDown(raylib.KeySpace) && !g.GameOver { // 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 g.Floppy.Position.Y -= 3
} else { } else {
g.Floppy.Position.Y += 1 // 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
} }
// Check Collisions // Floppy fall down
for i := 0; i < maxTubes*2; i++ { g.Floppy.Position.Y += 1
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
if !g.Dead {
// Check Collisions
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
raylib.PlaySound(g.FxSlap)
} else if (g.PipesPos[i/2].X < g.Floppy.Position.X-spriteSize) && g.Pipes[i/2].Active && !g.GameOver {
// Score point
g.Score += 1
g.Pipes[i/2].Active = false
// Flash screen
g.SuperFX = true g.SuperFX = true
// Update HiScore
if g.Score > g.HiScore { if g.Score > g.HiScore {
g.HiScore = g.Score g.HiScore = g.Score
} }
}
raylib.PlaySound(g.FxPoint)
} }
} }
} else { } else {
if raylib.IsKeyPressed(raylib.KeyEnter) { // Wait 60 frames before GameOver
g.Init() g.FramesCounter++
g.GameOver = false if g.FramesCounter >= 60 {
g.GameOver = true
} }
g.FrameRec.X = spriteSize * 4
}
} else {
if raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
g.Pause = !g.Pause
}
}
} else {
if raylib.IsKeyPressed(raylib.KeyEnter) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
raylib.PlaySound(g.FxClick)
// Return of the Gopher!
g.Init()
} 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()