рд▓рд░реНрдирд┐рдВрдЧ рд╕реНрдХрд╛рд▓рд╛: рднрд╛рдЧ 1 - рдж рд╕реНрдиреЗрдХ рдЧреЗрдо


рд╣рд╛рдп рд╣рдорд░! рдЬрдм рдореИрдВ рдПрдХ рдирдИ рднрд╛рд╖рд╛ рд╕реАрдЦрддрд╛ рд╣реВрдВ, рддреЛ рдореИрдВ рдЖрдорддреМрд░ рдкрд░ рдЙрд╕ рдкрд░ рдПрдХ рд╕рд╛рдВрдк рдмрдирд╛рддрд╛ рд╣реВрдВред рд╢рд╛рдпрдж рдХреБрдЫ рдиреМрд╕рд┐рдЦрд┐рдпрд╛ рдЬреЛ рд╕реНрдХрд╛рд▓рд╛ рдХрд╛ рдЕрдзреНрдпрдпрди рднреА рдХрд░рддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдЗрд╕ рдкреАрдПрд▓ рдореЗрдВ рдПрдХ рдФрд░ рдиреМрд╕рд┐рдЦрд┐рдпрд╛ рдХреЗ рдХреЛрдб рдореЗрдВ рджрд┐рд▓рдЪрд╕реНрдкреА рд╣реЛрдЧреАред рдЕрдиреБрднрд╡реА рд░реЙрдХрд░реНрд╕ рдХреЗ рд▓рд┐рдП, рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рд╕реНрдХрд╛рд▓рд╛ рдХреЛрдб рджреБрдЦреА рд╣реЛрдиреЗ рдХреА рд╕рдВрднрд╛рд╡рдирд╛ рд╣реИред рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред

рд╕рд╛рдордЧреНрд░реА


  • рд▓рд░реНрдирд┐рдВрдЧ рд╕реНрдХрд╛рд▓рд╛: рднрд╛рдЧ 1 - рдж рд╕реНрдиреЗрдХ рдЧреЗрдо

рд╕рдВрджрд░реНрдн


рд╕реЛрд░реНрд╕ рдХреЛрдб

рдЦреЗрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ


рдЧреНрд░рд╛рдлрд┐рдХреНрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, LWJGL рдмреИрдХрдПрдВрдб рдкрд░ libGdx рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдкреНрд░рдмрдВрдзрди рдХреАрдмреЛрд░реНрдб рдкрд░ рддреАрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╣реЛрддрд╛ рд╣реИред

рдбреЛрдореЗрди


рдХреЗрд╕ рдХреНрд▓рд╛рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдбреНрд░рд╛рдпрдиреЗрд╕ рдХреЛ рдЕрдиреБрдХрд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдХреНрдпреЛрдВрдХрд┐ рд╡реЗ рдкрд░рд╕реНрдкрд░ рдирд╣реАрдВ рд╣реИрдВ, рдореВрд▓реНрдп рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╣реИрдВ рдФрд░ рдЖрдорддреМрд░ рдкрд░ рд╣рд╛рд╕реНрдХреЗрд▓ рд╕реЗ рд░рд┐рдХреЙрд░реНрдб рдХреЗ рд╕рдорд╛рди рд╣реИрдВред

2 рдбреА рдЕрдВрддрд░рд┐рдХреНрд╖ рдореЗрдВ рдмрд┐рдВрджреБ:

case class Point(x: Int, y: Int)

рдпрд╛рддреНрд░рд╛ рдХреА рджрд┐рд╢рд╛ред рдорд┐рд▓рд╛рди рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдЗрди рд╡рд░реНрдЧреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ enum рдХреЗ рд░реВрдк рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:


    //         
    //enum Direction
    //{
    //    Up,
    //    Down,
    //    Right,
    //    Left
    //}
sealed abstract class Direction

case object Up extends Direction

case object Down extends Direction

case object Right extends Direction

case object Left extends Direction

рд╡рд╣ рдврд╛рдБрдЪрд╛ рдЬрд┐рд╕рдХреЗ рднреАрддрд░ рд╕рд╛рдБрдк рдЪрд▓рддрд╛ рд╣реИ

case class Frame(min: Point, max: Point) {
  def points = {
    for (i <- min.x until max.x + 1;
         j <- min.y until max.y + 1
         if i == min.x ||
           i == max.x ||
           j == min.y ||
           j == max.y)
      yield Point(i, j)
  }
}

рд╕рд╛рдВрдк рдХреЗ рд▓рд┐рдП рднреЛрдЬрди:

case class Food(body: Point, random: Random) {
  def moveRandomIn(frame: Frame): Food = {
    val x = random.between(frame.min.x + 1, frame.max.x)
    val y = random.between(frame.min.y + 1, frame.max.y)
    copy(body = Point(x, y))
  }
}

рд╕рд╛рдБрдк:

case class Snake(body: List[Point], direction: Direction) {
  def move(): Snake = {
    val point = direction match {
      case Up() => body.head.copy(y = body.head.y + 1)
      case Down() => body.head.copy(y = body.head.y - 1)
      case Left() => body.head.copy(x = body.head.x - 1)
      case Right() => body.head.copy(x = body.head.x + 1)
    }
    copy(body = point :: body.filter(p => p != body.last))
  }

  def turn(direction: Direction): Snake = {
    copy(direction = direction)
  }

  def eat(food: Food): Snake = {
    copy(body = food.body :: body)
  }

  def canEat(food: Food): Boolean = {
    food.body == body.head
  }

  def headIsIn(frame: Frame): Boolean = {
    body.head.x < frame.max.x &&
      body.head.y < frame.max.y &&
      body.head.x > frame.min.x &&
      body.head.y > frame.min.y
  }

  def isBitTail() = {
    body.tail.exists(p => p == body.head)
  }
}

рдПрдХ рдЦреЗрд▓:

package domain

case class Game(food: Food, snake: Snake, frame: Frame, elapsedTime: Float, start: Snake) {
  val framePoints = frame.points.toList

  def handle(input: List[Direction]): Game = {
    if (input.isEmpty) {
      this
    } else {
      copy(snake = input.foldLeft(snake)((s, d) => s.turn(d)))
    }
  }

  def update(deltaTime: Float): Game = {
    val elapsed = elapsedTime + deltaTime
    if (elapsed > 0.1) {
      val game = copy(snake = snake.move(), elapsedTime = 0)
      if (!game.snake.headIsIn(frame)) {
        game.reset()
      } else if (game.snake.isBitTail()) {
        game.reset()
      } else if (game.snake.canEat(food)) {
        game.copy(snake = snake.eat(food), food = food.moveRandomIn(frame))
      } else {
        game
      }
    } else {
      copy(elapsedTime = elapsed)
    }
  }

  def reset() = copy(snake = start)

  def points: List[Point] = {
    (food.body :: snake.body) ::: framePoints
  }
}

рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг


рдПрдХ рд╡рд░реНрдЧ рдЬреЛ рджрдмрд╛рдП рдЧрдП рдмрдЯрдиреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдПрдХрддреНрд░ рдХрд░рддрд╛ рд╣реИ

import com.badlogic.gdx.{InputAdapter}

class InputCondensate extends InputAdapter {

  private var keys: List[Int] = Nil

  def list: List[Int] = keys.reverse

  def clear(): Unit = {
    keys = Nil
  }

  override def keyDown(keycode: Int): Boolean = {
    keys = keycode :: keys
    true
  }
}

рдЦреЗрд▓ рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рд╡рд░реНрдЧ:

import com.badlogic.gdx.Input.Keys
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType
import com.badlogic.gdx.graphics.{Color, GL20}
import com.badlogic.gdx.{Game, Gdx}

class SnakeGame(var game: domain.Game, val sizeMultiplayer: Float) extends Game {
  lazy val prs = new InputCondensate
  lazy val shapeRenderer: ShapeRenderer = new ShapeRenderer()

  override def create(): Unit = {
    Gdx.input.setInputProcessor(prs)
  }

  override def render(): Unit = {
    game = game
      .handle(prs.list.map(i => i match {
        case Keys.UP => domain.Up()
        case Keys.DOWN => domain.Down()
        case Keys.LEFT => domain.Left()
        case Keys.RIGHT => domain.Right()
      }))
      .update(Gdx.graphics.getDeltaTime())

    prs.clear()
    Gdx.gl.glClearColor(1, 1, 1, 1)
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
    shapeRenderer.setColor(Color.BLACK)
    shapeRenderer.begin(ShapeType.Filled)
    for (p <- game.points)
      shapeRenderer.circle(p.x * sizeMultiplayer, p.y * sizeMultiplayer, sizeMultiplayer / 2)
    shapeRenderer.end()
  }
}

рдЦреЗрд▓ рдХрд╛ рдореБрдЦреНрдп рдкреНрд░рд╡реЗрд╢ рдмрд┐рдВрджреБ:

import com.badlogic.gdx.backends.lwjgl.{LwjglApplication, LwjglApplicationConfiguration}

import scala.util.Random

object Main extends App {
  val config = new LwjglApplicationConfiguration
  config.title = "Scala Snake Game"
  config.width = 300
  config.height = 300
  val food = domain.Food(domain.Point(4, 4), new Random())
  val frame = domain.Frame(domain.Point(0, 0), domain.Point(30, 30))
  val snake = domain.Snake(domain.Point(5, 5) :: domain.Point(6, 6) :: domain.Point(7, 7) :: Nil, domain.Right())
  val game = domain.Game(food, snake, frame, 0, snake)
  new LwjglApplication(new SnakeGame(game, 10), config)
}

All Articles