А так же о всякой фигне
В очередной раз проснулся во мне старый игрофил. И решил я запилить змейку на чистейшем Javascript'е. Времени это заняло не долго, примерно 2-2.5 часа, включая перекуры.
Вот сама игра на отдельное странице, а ниже немного подробностей как и что устроено.
В этот раз я решил не страдать хернёй с рендерингом в чистый html, а заюзал canvas. Весь канвас делится на кватраты, которые могут принимать несколько фиксированных значений: ничего, голова змейки, тушка змейки, еда.
const rectStyles = [ { id: 0, type: 'Background', stroke: 'rgb(25, 25, 25)', fill: 'rgb(15, 15, 15)' }, { id: 1, type: 'Snake head', stroke: 'rgb(250, 250, 250)', fill: 'rgb(220, 220, 220)' }, { id: 2, type: 'Snake body', stroke: 'rgb(200, 200, 200)', fill: 'rgb(180, 180, 180)' }, { id: 3, type: 'Food', stroke: 'rgb(250, 250, 0)', fill: 'rgb(230, 230, 0)' } ] function createDisplay(width=15, height=15) { for(let w=0; w<width; w++) { for(let h=0; h<height; h++) { if( !display[ h ] ) display[ h ] = []; display[ h ][ w ] = { h: h, w: w, id: 0, type: 0 }; } } } function clearDisplay() { for(let h=0; h<display.length; h++) for(let w=0; w<display[ 0 ].length; w++) display[ h ][ w ].type = 0; } function render(element) { let ctx = element.getContext('2d'); ctx.canvas.width = ctx.canvas.clientWidth; ctx.canvas.height = ctx.canvas.clientHeight; let rectWidth = parseInt(ctx.canvas.width / display[ 0 ].length - 1); let rectHeight = parseInt(ctx.canvas.height / display.length - 1); for(let h=0; h<display.length; h++) for(let w=0; w<display[ h ].length; w++) { let left = w * rectWidth + w; let top = h * rectHeight + h; ctx.strokeStyle = rectStyles[ display[ h ][ w ].type ].fill; ctx.fillStyle = rectStyles[ display[ h ][ w ].type ].stroke; ctx.fillRect(left, top, rectWidth, rectHeight); ctx.strokeRect(left, top, rectWidth, rectHeight); } }
Интересно получилось с генератором еды. Так как диспей у нас- это двумерный массив, то мы не можем просто взять случайное число и по нему поместить еду на дисплей, клетки могут быть заняты.
По этому я сперва создаю одномерный массив со ссылками на свободные клетки дисплея, уже потом беру случайное число, узнаю координаты клетки, и создаю объект "еда" с новыми координатами.
function createFood() { let emptyRects = []; for(let h=0; h<display.length; h++) for(let w=0; w<display[ 0 ].length; w++) if( display[ h ][ w ].type == 0 ) emptyRects.push(display[ h ][ w ]); let foodRect = emptyRects[ getRandomIntInclusive(0, emptyRects.length-1) ]; return { x: foodRect.w, y: foodRect.h, type: 1 } }
Перерисовка сцены и пересчёт змейки происходит по таймеру, и в таймере же задаётся скорость игры, которая возрастает каждые 25 ходов.
function tick() { if( gameStatus !== 'game' ) return; // game paused renderIndex ++; if( renderIndex > speedUpLimit ) { renderIndex =0; gameSpeed ++; clearInterval(gameTimer); gameTimer = setInterval(tick, 1000 - (gameSpeed * 50)); } clearDisplay(); renderFood(); renderSnake(); render(viewPort); if( onTick ) onTick(); } ... function startGame(onTickHandler) { snake = createSnake(); food = createFood(); gameStatus = 'game'; onTick = onTickHandler; gameTimer = setInterval(tick, 1000 - (gameSpeed * 50)); }
В остальном код очень простой. Полностью в функциональном стиле. Открывайте исходник страницы, смотрите что там и как.
Ну и конечно играйте с удовольствием!
Обзор MSI GS66 10SE-265RU i7 10750 + RTX2060
Так как с rog zephyrus у меня не сложилось, купил я MSI GS66. И судя по всему, это лучшее что можно купить в 2020 году.
Очень простые задачи для очень юного фронтэндера
Вот Вам ещё пачка очень простых задачек по Javascript/HTML+CSS. Для их решения можно практически не знать ничего программирование/вёрстку, идеальные для тех, кто только решил вкатываться.