Add scrolling background and particles

This commit is contained in:
Milan Nikolic 2017-02-25 21:54:32 +01:00
parent 37dbc16396
commit d9d26d30f3
4 changed files with 155 additions and 40 deletions

View file

@ -0,0 +1,3 @@
## Floppy Gopher
![screenshot](https://goo.gl/uwRi60)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -10,12 +10,28 @@ import (
) )
const ( const (
// Screen width
screenWidth = 504
// Screen height
screenHeight = 896
// Maximum number of pipes // Maximum number of pipes
maxPipes = 100 maxPipes = 100
// Maximum number of particles
maxParticles = 50
// Pipes width // Pipes width
pipesWidth = 60 pipesWidth = 60
// Sprite size // Sprite size
spriteSize = 48 spriteSize = 48
// Pipes speed
pipesSpeedX = 2.5
// Clouds speed
cloudsSpeedX = 1
// Gravity
gravity = 1.2
) )
// Floppy type // Floppy type
@ -30,17 +46,28 @@ type Pipe struct {
Active bool Active bool
} }
// Particle type
type Particle struct {
Position raylib.Vector2
Color raylib.Color
Alpha float32
Size float32
Rotation float32
Active bool
}
// Game type // Game type
type Game struct { type Game struct {
ScreenWidth int32
ScreenHeight int32
FxFlap raylib.Sound FxFlap raylib.Sound
FxSlap raylib.Sound FxSlap raylib.Sound
FxPoint raylib.Sound FxPoint raylib.Sound
FxClick raylib.Sound FxClick raylib.Sound
Texture raylib.Texture2D TxSprites raylib.Texture2D
TxSmoke raylib.Texture2D
TxClouds raylib.Texture2D
CloudRec raylib.Rectangle
FrameRec raylib.Rectangle FrameRec raylib.Rectangle
GameOver bool GameOver bool
@ -51,13 +78,14 @@ type Game struct {
Score int Score int
HiScore int HiScore int
FramesCounter int32 FramesCounter int32
WindowShouldClose bool WindowShouldClose bool
Floppy Floppy Floppy Floppy
Particles []Particle
Pipes []Pipe Pipes []Pipe
PipesPos []raylib.Vector2 PipesPos []raylib.Vector2
PipesSpeedX int32
} }
// NewGame - Start new game // NewGame - Start new game
@ -80,12 +108,13 @@ func main() {
func run(app unsafe.Pointer) { func run(app unsafe.Pointer) {
// Initialize game // Initialize game
game := NewGame() game := NewGame()
game.GameOver = true
// Initialize window // Initialize window
if runtime.GOOS != "android" { if runtime.GOOS != "android" {
raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "Floppy Gopher") raylib.InitWindow(screenWidth, screenHeight, "Floppy Gopher")
} else { } else {
raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, app) raylib.InitWindow(screenWidth, screenHeight, app)
} }
// Initialize audio // Initialize audio
@ -121,17 +150,27 @@ func run(app unsafe.Pointer) {
// Init - Initialize game // Init - Initialize game
func (g *Game) Init() { func (g *Game) Init() {
// Window resolution
g.ScreenWidth = 504
g.ScreenHeight = 896
g.Floppy = Floppy{} // Gopher
g.Floppy.Position = raylib.NewVector2(80, float32(g.ScreenHeight)/2-spriteSize/2) g.Floppy = Floppy{raylib.NewVector2(80, float32(screenHeight)/2-spriteSize/2)}
g.PipesSpeedX = 2
// Sprite rectangle // Sprite rectangle
g.FrameRec = raylib.NewRectangle(0, 0, spriteSize, spriteSize) g.FrameRec = raylib.NewRectangle(0, 0, spriteSize, spriteSize)
// Cloud rectangle
g.CloudRec = raylib.NewRectangle(0, 0, screenWidth, g.TxClouds.Height)
// Initialize particles
g.Particles = make([]Particle, maxParticles)
for i := 0; i < maxParticles; i++ {
g.Particles[i].Position = raylib.NewVector2(0, 0)
g.Particles[i].Color = raylib.RayWhite
g.Particles[i].Alpha = 1.0
g.Particles[i].Size = float32(raylib.GetRandomValue(1, 30)) / 20.0
g.Particles[i].Rotation = float32(raylib.GetRandomValue(0, 360))
g.Particles[i].Active = false
}
// Pipes positions // Pipes positions
g.PipesPos = make([]raylib.Vector2, maxPipes) g.PipesPos = make([]raylib.Vector2, maxPipes)
for i := 0; i < maxPipes; i++ { for i := 0; i < maxPipes; i++ {
@ -179,7 +218,9 @@ func (g *Game) Load() {
g.FxSlap = raylib.LoadSound("sounds/slap.wav") g.FxSlap = raylib.LoadSound("sounds/slap.wav")
g.FxPoint = raylib.LoadSound("sounds/point.wav") g.FxPoint = raylib.LoadSound("sounds/point.wav")
g.FxClick = raylib.LoadSound("sounds/click.wav") g.FxClick = raylib.LoadSound("sounds/click.wav")
g.Texture = raylib.LoadTexture("images/sprite.png") g.TxSprites = raylib.LoadTexture("images/sprite.png")
g.TxSmoke = raylib.LoadTexture("images/smoke.png")
g.TxClouds = raylib.LoadTexture("images/clouds.png")
} }
// Unload - Unload resources // Unload - Unload resources
@ -188,7 +229,9 @@ func (g *Game) Unload() {
raylib.UnloadSound(g.FxSlap) raylib.UnloadSound(g.FxSlap)
raylib.UnloadSound(g.FxPoint) raylib.UnloadSound(g.FxPoint)
raylib.UnloadSound(g.FxClick) raylib.UnloadSound(g.FxClick)
raylib.UnloadTexture(g.Texture) raylib.UnloadTexture(g.TxSprites)
raylib.UnloadTexture(g.TxSmoke)
raylib.UnloadTexture(g.TxClouds)
} }
// Update - Update game // Update - Update game
@ -210,9 +253,9 @@ func (g *Game) Update() {
if !g.Pause { if !g.Pause {
if !g.Dead { if !g.Dead {
// Scroll X // Scroll pipes
for i := 0; i < maxPipes; i++ { for i := 0; i < maxPipes; i++ {
g.PipesPos[i].X -= float32(g.PipesSpeedX) g.PipesPos[i].X -= float32(pipesSpeedX)
} }
for i := 0; i < maxPipes*2; i += 2 { for i := 0; i < maxPipes*2; i += 2 {
@ -220,10 +263,28 @@ func (g *Game) Update() {
g.Pipes[i+1].Rec.X = int32(g.PipesPos[i/2].X) g.Pipes[i+1].Rec.X = int32(g.PipesPos[i/2].X)
} }
// Scroll clouds
g.CloudRec.X += cloudsSpeedX
if g.CloudRec.X > g.TxClouds.Width {
g.CloudRec.X = 0
}
// Movement/Controls // Movement/Controls
if raylib.IsKeyDown(raylib.KeySpace) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) && !g.GameOver { if raylib.IsKeyDown(raylib.KeySpace) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
raylib.PlaySound(g.FxFlap) raylib.PlaySound(g.FxFlap)
// Activate one particle every frame
for i := 0; i < maxParticles; i++ {
if !g.Particles[i].Active {
g.Particles[i].Active = true
g.Particles[i].Alpha = 1.0
g.Particles[i].Position = g.Floppy.Position
g.Particles[i].Position.X += spriteSize / 2
g.Particles[i].Position.Y += spriteSize / 2
i = maxParticles
}
}
// Switch flap sprites every 8 frames // Switch flap sprites every 8 frames
g.FramesCounter++ g.FramesCounter++
if g.FramesCounter >= 8 { if g.FramesCounter >= 8 {
@ -236,10 +297,7 @@ func (g *Game) Update() {
// Floppy go up // Floppy go up
g.Floppy.Position.Y -= 3 g.Floppy.Position.Y -= 3
} else { } else {
// Default sprite // Switch run sprites every 8 frames
//g.FrameRec.X = spriteSize
// Switch flap sprites every 8 frames
g.FramesCounter++ g.FramesCounter++
if g.FramesCounter >= 8 { if g.FramesCounter >= 8 {
g.FramesCounter = 0 g.FramesCounter = 0
@ -249,11 +307,23 @@ func (g *Game) Update() {
} }
// Floppy fall down // Floppy fall down
g.Floppy.Position.Y += 1 g.Floppy.Position.Y += gravity
}
// Update active particles
for i := 0; i < maxParticles; i++ {
if g.Particles[i].Active {
g.Particles[i].Position.X -= 1.0
g.Particles[i].Alpha -= 0.05
if g.Particles[i].Alpha <= 0.0 {
g.Particles[i].Active = false
}
g.Particles[i].Rotation += 3.0
} }
} }
if !g.Dead {
// Check Collisions // Check Collisions
for i := 0; i < maxPipes*2; i++ { 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) { if raylib.CheckCollisionRecs(raylib.NewRectangle(int32(g.Floppy.Position.X), int32(g.Floppy.Position.Y), spriteSize, spriteSize), g.Pipes[i].Rec) {
@ -284,8 +354,13 @@ func (g *Game) Update() {
g.GameOver = true g.GameOver = true
} }
// Switch dead sprite
if g.FramesCounter >= 8 {
g.FrameRec.X = spriteSize * 5
} else {
g.FrameRec.X = spriteSize * 4 g.FrameRec.X = spriteSize * 4
} }
}
} else { } else {
if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { if raylib.IsMouseButtonDown(raylib.MouseLeftButton) {
g.Pause = !g.Pause g.Pause = !g.Pause
@ -301,6 +376,15 @@ func (g *Game) Update() {
g.WindowShouldClose = true g.WindowShouldClose = true
} }
// Switch flap sprites
g.FramesCounter++
if g.FramesCounter >= 8 {
g.FramesCounter = 0
g.FrameRec.X = spriteSize
} else {
g.FrameRec.X = 0
}
} }
} }
@ -311,8 +395,31 @@ func (g *Game) Draw() {
raylib.ClearBackground(raylib.SkyBlue) raylib.ClearBackground(raylib.SkyBlue)
if !g.GameOver { if !g.GameOver {
// Draw clouds
raylib.DrawTextureRec(g.TxClouds, g.CloudRec, raylib.NewVector2(0, float32(screenHeight-g.TxClouds.Height)), raylib.RayWhite)
// Draw rotated clouds
raylib.DrawTexturePro(g.TxClouds, raylib.NewRectangle(-g.CloudRec.X, 0, g.TxClouds.Width, g.TxClouds.Height),
raylib.NewRectangle(0, 0, g.TxClouds.Width, g.TxClouds.Height), raylib.NewVector2(float32(g.TxClouds.Width), float32(g.TxClouds.Height)), 180, raylib.White)
// Draw Gopher // Draw Gopher
raylib.DrawTextureRec(g.Texture, g.FrameRec, g.Floppy.Position, raylib.RayWhite) // Draw part of the texture raylib.DrawTextureRec(g.TxSprites, g.FrameRec, g.Floppy.Position, raylib.RayWhite)
// Draw active particles
if !g.Dead {
for i := 0; i < maxParticles; i++ {
if g.Particles[i].Active {
raylib.DrawTexturePro(
g.TxSmoke,
raylib.NewRectangle(0, 0, g.TxSmoke.Width, g.TxSmoke.Height),
raylib.NewRectangle(int32(g.Particles[i].Position.X), int32(g.Particles[i].Position.Y), g.TxSmoke.Width*int32(g.Particles[i].Size), g.TxSmoke.Height*int32(g.Particles[i].Size)),
raylib.NewVector2(float32(g.TxSmoke.Width)*g.Particles[i].Size/2, float32(g.TxSmoke.Height)*g.Particles[i].Size/2),
g.Particles[i].Rotation,
raylib.Fade(g.Particles[i].Color, g.Particles[i].Alpha),
)
}
}
}
// Draw pipes // Draw pipes
for i := 0; i < maxPipes; i++ { for i := 0; i < maxPipes; i++ {
@ -326,25 +433,30 @@ func (g *Game) Draw() {
// Draw Super 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, screenWidth, screenHeight, raylib.White)
g.SuperFX = false g.SuperFX = false
} }
// Draw HI-SCORE // Draw HI-SCORE
raylib.DrawText(fmt.Sprintf("%02d", g.Score), 20, 20, 32, raylib.RayWhite) raylib.DrawText(fmt.Sprintf("%02d", g.Score), 20, 20, 32, raylib.Black)
raylib.DrawText(fmt.Sprintf("HI-SCORE: %02d", g.HiScore), 20, 64, 20, raylib.RayWhite) raylib.DrawText(fmt.Sprintf("HI-SCORE: %02d", g.HiScore), 20, 64, 20, raylib.Black)
if g.Pause { if g.Pause {
// Draw PAUSED text // Draw PAUSED text
raylib.DrawText("PAUSED", g.ScreenWidth/2-raylib.MeasureText("PAUSED", 24)/2, g.ScreenHeight/2-50, 20, raylib.RayWhite) raylib.DrawText("PAUSED", screenWidth/2-raylib.MeasureText("PAUSED", 24)/2, screenHeight/2-50, 20, raylib.Black)
} }
} else { } else {
// Draw PLAY AGAIN text // Draw text
raylib.DrawText("Floppy Gopher", raylib.GetScreenWidth()/2-raylib.MeasureText("Floppy Gopher", 40)/2, raylib.GetScreenHeight()/2-150, 40, raylib.RayWhite)
if runtime.GOOS == "android" { 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) raylib.DrawText("[TAP] TO PLAY", raylib.GetScreenWidth()/2-raylib.MeasureText("[TAP] TO PLAY", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Black)
} else { } 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.DrawText("[ENTER] TO PLAY", raylib.GetScreenWidth()/2-raylib.MeasureText("[ENTER] TO PLAY", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Black)
} }
// Draw Gopher
raylib.DrawTextureRec(g.TxSprites, g.FrameRec, raylib.NewVector2(float32(raylib.GetScreenWidth()/2-spriteSize/2), float32(raylib.GetScreenHeight()/2)), raylib.RayWhite)
} }
raylib.EndDrawing() raylib.EndDrawing()