2020-07-31 19:00:45 +02:00
|
|
|
// Copyright 2020 The Ebiten Authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image/color"
|
|
|
|
"log"
|
|
|
|
"math/rand"
|
|
|
|
"time"
|
|
|
|
|
2020-10-03 19:35:13 +02:00
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
2022-10-21 08:23:09 +02:00
|
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
2020-07-31 19:00:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-07-12 18:55:03 +02:00
|
|
|
screenWidth = 640
|
|
|
|
screenHeight = 480
|
|
|
|
gridSize = 10
|
|
|
|
xGridCountInScreen = screenWidth / gridSize
|
|
|
|
yGridCountInScreen = screenHeight / gridSize
|
2020-07-31 19:00:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
dirNone = iota
|
|
|
|
dirLeft
|
|
|
|
dirRight
|
|
|
|
dirDown
|
|
|
|
dirUp
|
|
|
|
)
|
|
|
|
|
|
|
|
type Position struct {
|
|
|
|
X int
|
|
|
|
Y int
|
|
|
|
}
|
|
|
|
|
|
|
|
type Game struct {
|
|
|
|
moveDirection int
|
|
|
|
snakeBody []Position
|
|
|
|
apple Position
|
|
|
|
timer int
|
|
|
|
moveTime int
|
|
|
|
score int
|
|
|
|
bestScore int
|
|
|
|
level int
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) collidesWithApple() bool {
|
|
|
|
return g.snakeBody[0].X == g.apple.X &&
|
|
|
|
g.snakeBody[0].Y == g.apple.Y
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) collidesWithSelf() bool {
|
|
|
|
for _, v := range g.snakeBody[1:] {
|
|
|
|
if g.snakeBody[0].X == v.X &&
|
|
|
|
g.snakeBody[0].Y == v.Y {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) collidesWithWall() bool {
|
|
|
|
return g.snakeBody[0].X < 0 ||
|
|
|
|
g.snakeBody[0].Y < 0 ||
|
2022-07-12 18:55:03 +02:00
|
|
|
g.snakeBody[0].X >= xGridCountInScreen ||
|
|
|
|
g.snakeBody[0].Y >= yGridCountInScreen
|
2020-07-31 19:00:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) needsToMoveSnake() bool {
|
|
|
|
return g.timer%g.moveTime == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) reset() {
|
|
|
|
g.apple.X = 3 * gridSize
|
|
|
|
g.apple.Y = 3 * gridSize
|
|
|
|
g.moveTime = 4
|
|
|
|
g.snakeBody = g.snakeBody[:1]
|
2022-07-12 18:55:03 +02:00
|
|
|
g.snakeBody[0].X = xGridCountInScreen / 2
|
|
|
|
g.snakeBody[0].Y = yGridCountInScreen / 2
|
2020-07-31 19:00:45 +02:00
|
|
|
g.score = 0
|
|
|
|
g.level = 1
|
|
|
|
g.moveDirection = dirNone
|
|
|
|
}
|
|
|
|
|
2020-10-04 10:42:54 +02:00
|
|
|
func (g *Game) Update() error {
|
2021-04-17 18:13:17 +02:00
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) || inpututil.IsKeyJustPressed(ebiten.KeyA) {
|
2020-07-31 19:00:45 +02:00
|
|
|
if g.moveDirection != dirRight {
|
|
|
|
g.moveDirection = dirLeft
|
|
|
|
}
|
2021-04-17 18:13:17 +02:00
|
|
|
} else if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) || inpututil.IsKeyJustPressed(ebiten.KeyD) {
|
2020-07-31 19:00:45 +02:00
|
|
|
if g.moveDirection != dirLeft {
|
|
|
|
g.moveDirection = dirRight
|
|
|
|
}
|
2021-04-17 18:13:17 +02:00
|
|
|
} else if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) {
|
2020-07-31 19:00:45 +02:00
|
|
|
if g.moveDirection != dirUp {
|
|
|
|
g.moveDirection = dirDown
|
|
|
|
}
|
2021-04-17 18:13:17 +02:00
|
|
|
} else if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) {
|
2020-07-31 19:00:45 +02:00
|
|
|
if g.moveDirection != dirDown {
|
|
|
|
g.moveDirection = dirUp
|
|
|
|
}
|
|
|
|
} else if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
|
|
|
|
g.reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.needsToMoveSnake() {
|
|
|
|
if g.collidesWithWall() || g.collidesWithSelf() {
|
|
|
|
g.reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.collidesWithApple() {
|
2022-07-12 18:55:03 +02:00
|
|
|
g.apple.X = rand.Intn(xGridCountInScreen - 1)
|
|
|
|
g.apple.Y = rand.Intn(yGridCountInScreen - 1)
|
2020-07-31 19:00:45 +02:00
|
|
|
g.snakeBody = append(g.snakeBody, Position{
|
|
|
|
X: g.snakeBody[len(g.snakeBody)-1].X,
|
|
|
|
Y: g.snakeBody[len(g.snakeBody)-1].Y,
|
|
|
|
})
|
|
|
|
if len(g.snakeBody) > 10 && len(g.snakeBody) < 20 {
|
|
|
|
g.level = 2
|
|
|
|
g.moveTime = 3
|
|
|
|
} else if len(g.snakeBody) > 20 {
|
|
|
|
g.level = 3
|
|
|
|
g.moveTime = 2
|
|
|
|
} else {
|
|
|
|
g.level = 1
|
|
|
|
}
|
|
|
|
g.score++
|
|
|
|
if g.bestScore < g.score {
|
|
|
|
g.bestScore = g.score
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := int64(len(g.snakeBody)) - 1; i > 0; i-- {
|
|
|
|
g.snakeBody[i].X = g.snakeBody[i-1].X
|
|
|
|
g.snakeBody[i].Y = g.snakeBody[i-1].Y
|
|
|
|
}
|
|
|
|
switch g.moveDirection {
|
|
|
|
case dirLeft:
|
|
|
|
g.snakeBody[0].X--
|
|
|
|
case dirRight:
|
|
|
|
g.snakeBody[0].X++
|
|
|
|
case dirDown:
|
|
|
|
g.snakeBody[0].Y++
|
|
|
|
case dirUp:
|
|
|
|
g.snakeBody[0].Y--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g.timer++
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) Draw(screen *ebiten.Image) {
|
|
|
|
for _, v := range g.snakeBody {
|
2022-10-22 13:29:17 +02:00
|
|
|
vector.DrawFilledRect(screen, float32(v.X*gridSize), float32(v.Y*gridSize), gridSize, gridSize, color.RGBA{0x80, 0xa0, 0xc0, 0xff})
|
2020-07-31 19:00:45 +02:00
|
|
|
}
|
2022-10-22 13:29:17 +02:00
|
|
|
vector.DrawFilledRect(screen, float32(g.apple.X*gridSize), float32(g.apple.Y*gridSize), gridSize, gridSize, color.RGBA{0xFF, 0x00, 0x00, 0xff})
|
2020-07-31 19:00:45 +02:00
|
|
|
|
|
|
|
if g.moveDirection == dirNone {
|
|
|
|
ebitenutil.DebugPrint(screen, fmt.Sprintf("Press up/down/left/right to start"))
|
|
|
|
} else {
|
2022-07-17 04:25:45 +02:00
|
|
|
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f Level: %d Score: %d Best Score: %d", ebiten.ActualFPS(), g.level, g.score, g.bestScore))
|
2020-07-31 19:00:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
|
|
|
return screenWidth, screenHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
func newGame() *Game {
|
|
|
|
g := &Game{
|
|
|
|
apple: Position{X: 3 * gridSize, Y: 3 * gridSize},
|
|
|
|
moveTime: 4,
|
|
|
|
snakeBody: make([]Position, 1),
|
|
|
|
}
|
2022-07-12 18:55:03 +02:00
|
|
|
g.snakeBody[0].X = xGridCountInScreen / 2
|
|
|
|
g.snakeBody[0].Y = yGridCountInScreen / 2
|
2020-07-31 19:00:45 +02:00
|
|
|
return g
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
ebiten.SetWindowSize(screenWidth, screenHeight)
|
2022-08-29 04:16:39 +02:00
|
|
|
ebiten.SetWindowTitle("Snake (Ebitengine Demo)")
|
2020-07-31 19:00:45 +02:00
|
|
|
if err := ebiten.RunGame(newGame()); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|