







package main
import (
"fmt"
"image/color"
"log"
"math/rand"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/vector"
)
const (
screenWidth = 320
screenHeight = 480
birdSize = 32
gravity = 0.81
jumpSpeed = -10
pipeGapSize = 130
pipeWidth = 60
pipeSpeed = 1.75
dt = 1.0 // Time step for Euler's method
)
type Game struct {
birdY float64
birdVelocity float64
pipeX float64
upperPipeHeight int
score int
gameOver bool // check if the game is over or not
passCheck bool // check if the bird passed the pipe
}
func (g *Game) Update() error {
// Press space bar to restart the game
if g.gameOver {
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
g.reset()
}
return nil
}
// Euler's method for updating bird's position and velocity
g.birdVelocity += gravity * dt
g.birdY += g.birdVelocity * dt
// Press space bar to make the bird to jump
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
g.birdVelocity = jumpSpeed
}
//pipe movement setting
g.pipeX -= pipeSpeed
if g.pipeX < -pipeWidth {
g.pipeX = screenWidth
g.upperPipeHeight = rand.Intn(screenHeight - pipeGapSize)
g.passCheck = false
}
// score goes up by 1 when the bird passes a pipe
if !g.passCheck && g.pipeX < screenWidth/2-birdSize/2 {
g.score++
g.passCheck = true
}
//If the bird crashes with pipe or goes outide the game screen, bird dies
if g.birdY < 0 || g.birdY+birdSize > screenHeight || g.hitPipe() {
g.gameOver = true
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.RGBA{R: 135, G: 206, B: 235, A: 255}) // blue blue sky(background)
// Draw pipe
pipeColor := color.RGBA{R: 0, G: 128, B: 0, A: 255} //green
vector.DrawFilledRect(screen, float32(g.pipeX), float32(float64(0)), float32(float64(pipeWidth)), float32(float64(g.upperPipeHeight)), color.Color(pipeColor), false)
vector.DrawFilledRect(screen, float32(g.pipeX), float32(float64(g.upperPipeHeight+pipeGapSize)), float32(float64(pipeWidth)), float32(screenHeight-float64(g.upperPipeHeight+pipeGapSize)), color.Color(pipeColor), false)
// Draw bird
birdColor := color.RGBA{R: 255, G: 255, B: 0, A: 255} // yellow
vector.DrawFilledRect(screen, float32(float64(screenWidth/2-birdSize/2)), float32(g.birdY), float32(float64(birdSize)), float32(float64(birdSize)), color.Color(birdColor), false)
// display score
scoreText := fmt.Sprintf("Score: %d", g.score)
ebitenutil.DebugPrintAt(screen, scoreText, 10, 10)
//gmaeover message display
if g.gameOver {
ebitenutil.DebugPrint(screen, "Game Over! Press Space to Restart")
}
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return screenWidth, screenHeight
}
func (g *Game) hitPipe() bool {
return (screenWidth/2+birdSize/2 > g.pipeX && screenWidth/2-birdSize/2 < g.pipeX+pipeWidth) && (g.birdY < float64(g.upperPipeHeight) || g.birdY+birdSize > float64(g.upperPipeHeight+pipeGapSize))
} //check whether the bird hit the pipe(go outside the screen) or not
func (g *Game) reset() {
g.birdY = screenHeight / 2
g.birdVelocity = 0
g.pipeX = screenWidth
g.upperPipeHeight = rand.Intn(screenHeight - pipeGapSize)
g.score = 0
g.gameOver = false
g.passCheck = false
}
func main() {
game := &Game{}
game.reset()
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Flappy Bird (made with Go(ebiten))")
if err := ebiten.RunGame(game); err != nil {
log.Fatal(err)
}
}





