Змея игра MVC (Vanilla JS) — Помогите разделить проблемы

Я делаю змейку для отработки паттерна MVC, а также выхожу из зоны комфорта (я занимаюсь обработкой данных / наукой о данных, в основном процедурным кодированием)

Игра доступна в https://nabla-f.github.io/

Это классическая игра в змейку, но с препятствиями на доске (в коде они называются блоками).

Я впервые использую MVC, поэтому все комментарии приветствуются. Кроме того, у меня возникли проблемы с реализацией следующих вещей, потому что я не знаю, где разместить эти функции (модель, представление или контроллер):

  • Класс уровня (чтобы иметь возможность создавать разные этапы)
  • Сообщения пользовательского интерфейса не связаны с игрой («лучше работает в ландшафтном режиме», «добро пожаловать в игру» и т. Д.)
  • Все, что связано с будущим рабочим процессом, похожим на приложение (экраны загрузки, экран авторизации, функции, управляющие этим и т. Д.)

Я опускаю детали реализации, потому что мой вопрос касается дизайна SW, а не реализации каждой функции (например, я знаю, что использование холста лучше, чем отображение элементов html в каждом цикле и т. Д.). Вот код:

models.js

const DIRECTIONS = {
    // an object representing directions (left, right, etc)
}

class Snake {
    constructor() {
        this.body = // Stores coordinates (an array) that represent snake position (i.e. an array of arrays)
        this.len = this.body.length 
        this.head = this.body[0]
        this.last = this.body[this.len-1]
        this.direction = // Direction in which snake moves
        this.toDigest = [] // Stores coordinates that snake should add in the following cycle
    }

    updateDirection(direction) {
        this.direction = direction
    }

    updatePosition() {
        // Updates snakes body to a new position according to his direction            
    }

    updateData() {
        this.len = this.body.length
        this.head = this.body[0]
        this.last = this.body[this.len-1]
    }

    eatFruit(fruitCoord) {
        // Eats a fruit
    }

    checkToDigest() {
        // 
    }

    grow() {
        this.body.push(Array.from(this.toDigest[0]))
        this.toDigest.shift()
    }
}


class Board {
    constructor(width, height) {
        this.width = width
        this.height = height
        this.fruits = new Fruits()
        this.coords = // Stores coordinates of board. Each coordinate is an array
        this.blocks = // Stores coordinates of blocking elements
    }

    generateFruits(forbiddenCoords) {
        this.fruits.generateFruits()
    }
    
    removeFruits() {
        this.fruits.removeFruits()
    }
}


class Fruits {
    constructor() {
        this.fruits = [] // Stores a list of fruits
    }

    generateFruits(maxWidth, maxHeight, forbiddenCoords) {
        // Generate a fruit coordinante that:
        // - Isn't the same as a block coordinate
        // - Isn't the same as a snake body coordinate
    }

    checkFruits() {
        if (this.fruits) {return true}
        else {return false}
    }

    removeFruits() {
        // Remove a fruit for the list of fruits
    }
}


export { Snake, Board, DIRECTIONS }

views.js


class BoardDrawer {
    constructor(HTMLelem, snake, board) {
        this.main = HTMLelem
        this.board = board
        this.snake = snake
    }

    drawCells() {
        // draw cells of the board
    }

    drawSnake() {
        // draws snake in board
    }

    drawBlocks() {
        // draws blocks in board
    }

    drawFruits() {
        // draws fruits in board
    }

    eraseSnake() {
        // erase the snake in board
    }

    eraseFruits() {
        // erase the fruits in board
    }
}


export { BoardDrawer }

controllers.js

import { DIRECTIONS } from './models.js'


class InputHandler {
    constructor(deniedKey) {
        this.deniedKey = deniedKey // last key pressed
    }
    
    userInput = (key) => {
       // translates an arrow key into direction coordinate
    }
}


class ObstaclesChecker {
    constructor(snake, board) {
        this.snake = snake.body
        this.boundaries = {
            left: -1,
            right: board.width,
            up: -1,
            down: board.height,
        }
        this.snakeWithoutHead = []
        this.blocks = board.blocks
        this.fruits = board.fruits.fruits
    }

    checkBoundaryCollision(snakeHead) {
        // Checks if snake collides with boundaries
    }

    checkSelfCollision(snakeHead) {
        // Checks if snake collides with itself
    }

    checkBlockCollision(snakeHead) {
        // Checks if snake collides with a block
    }

    checkFruitCollision(snakeHead) {
        // Checks if snake collides with a fruit
    }
}


export { InputHandler, ObstaclesChecker }


app.js

import { Snake, Board } from './js/models.js'
import { BoardDrawer } from './js/views.js'
import { InputHandler, ObstaclesChecker } from './js/controllers.js'
 

// VARIABLE INIT

let boardHTMLelement = document.querySelector('#board');
let snake = new Snake()
let board = new Board(10, 10)
let obstacles = new ObstaclesChecker(snake, board)
let boardDrawer = new BoardDrawer(boardHTMLelement, snake, board)
let inputHandler = new InputHandler('ArrowLeft')


window.onload = () => {    

    // INITIALIZE GAME
    boardDrawer.drawCells()
    boardDrawer.drawBlocks()
    board.generateFruits()
    boardDrawer.drawFruits()
    boardDrawer.drawSnake()
    startGame()
};

/// GAME LOOP

function gameLoop(timeStamp){

    // SNAKE AND FRUITS LOGIC BLOCK
    
    boardDrawer.eraseSnake()
    boardDrawer.eraseFruits()

    // check digestion
    if (snake.checkToDigest()) {
        console.log('The snake has a new block')
        snake.updatePosition()
        snake.grow()
        snake.updateData()
    } else {
        snake.updatePosition()
        snake.updateData()
    }

    // check new fruit
    if (obstacles.checkFruitCollision(snake.head)) {
        console.log('The snake found a fruit')
        snake.eatFruit(snake.head)
        board.removeFruits()
        board.generateFruits([snake.toDigest, board.blocks])
    }

    // COLLISION CHECKING BLOCK
    if (
        obstacles.checkSelfCollision(snake.head) ||
        obstacles.checkBoundaryCollision(snake.head) ||
        obstacles.checkBlockCollision(snake.head)
        )
    {
        console.log('u lose')
        loserFunction()
    }
    
    // DRAWING BLOCK
    boardDrawer.drawFruits()
    boardDrawer.drawSnake()

    // Keep requesting new frames
    setTimeout(window.requestAnimationFrame, 250, gameLoop);    
}




/// Things that I doesn't know where to put in :(


let startGame = (event) => {
    if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
        window.requestAnimationFrame(gameLoop)
        document.removeEventListener('keydown', startGame)
    }
};

let startGameListener = () => {
    document.addEventListener('keydown', startGame)
}


const loserFunction = () => {
    // Delete the screen when user loses, show "you lose" message and so on...
}

Если поможет, вот репо: https://github.com/nabla-f/nabla-f.github.io

Спасибо заранее

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *