Graphuary день 6. Наблюдение
С опозданием, но наверное лучше позже, чем никогда)
Покажу и я наших шерстяных друзей в ответ на этот пост
Эти товарищи вполне самодостаточные, но тоже требует внимания. Вот как они проводят субботний день: выясняют отношения за все обиды скопившиеся за неделю друг другу.
Работы за вчерашний день
Ну и моё
После прошлого поста я что-то так проникся происходящим, что решил воссоздать ещё одну любовь детства. Но как говорил Маркус Персон: «Если вы не можете написать свой движок, то гавно вы, а не разработчики». Там, скорее всего, было как-о иначе, но суть такая. И да, с этой мыслью я в корне не согласен, но написать свой движок – задача, как минимум, интересная. Я решил, что аркадная игра для этого подходит, как ничто другое. По сути, некий аналог движка уже был реализован в тетрисе, но здесь нужна будет и какая-никакая физическая модель, и интеллект врагов и рендер всего этого безобразия в несколько слоёв и с ФПС побольше, чем 2 :)
Так что, приступим. Что нам нужно:
Для одного поста такая задачка звучит жирновато, поэтому, видимо, будет серия.
Я решил разделить рисования окружения (статичных объектов), врагов и игроков (динамических объектов) и UI на три разных канваса, которые просто повещены один поверх другого. Там можно будет проще и меньше перерисовывать.
Начинается наш код с проверки, готова ли страница к явлению миру нашего движка.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
Если готова, то давай же скорее всё проинициализируем
Тут всё просто:
function init() {
console.log('Battle City Remake - Initializing...');
// Инициализация рендерера
initRenderer();
// Очищаем все слои
clearBackground();
clearForeground();
clearUI();
// Рисуем границу игрового поля и отладочную сетку
renderGameFieldBorder();
renderDebugGrid();
console.log('Initialization complete. Starting game loop...');
// Запускаем game loop
requestAnimationFrame(gameLoop);
}
Но понятное дело, что кода тут мало, потому что всё вынесено в отдельные методы.
Есть ли среди них хоть что-то интересное?
Ну, renderDebugGrid и renderGameFieldBorder одним названием уже говорят, что там происходит.
Логично, что самое интересное происходит где-то тут requestAnimationFrame(gameLoop);
Но давайте сначала заглянем в инициализацию рендера
export function initRenderer() {
// Получаем canvas элементы
backgroundCanvas = document.getElementById('background-canvas');
foregroundCanvas = document.getElementById('foreground-canvas');
uiCanvas = document.getElementById('ui-canvas');
// Получаем контексты
backgroundCtx = backgroundCanvas.getContext('2d');
foregroundCtx = foregroundCanvas.getContext('2d');
uiCtx = uiCanvas.getContext('2d');
// Определяем DPR для HiDPI экранов
dpr = window.devicePixelRatio || 1;
// Настраиваем все три canvas
setupCanvas(backgroundCanvas, backgroundCtx);
setupCanvas(foregroundCanvas, foregroundCtx);
setupCanvas(uiCanvas, uiCtx);
console.log(`Renderer initialized (DPR: ${dpr})`);
}
function setupCanvas(canvas, ctx) {
// Физический размер canvas
canvas.width = CANVAS_WIDTH * dpr;
canvas.height = CANVAS_HEIGHT * dpr;
// Масштабируем контекст для компенсации DPR
ctx.scale(dpr, dpr);
// Отключаем сглаживание для пиксельной графики
ctx.imageSmoothingEnabled = false;
console.log(`Canvas ${canvas.id} setup: ${canvas.width}×${canvas.height} (logical: ${CANVAS_WIDTH}×${CANVAS_HEIGHT})`);
}
CANVAS_WIDTH и CANVAS_HEIGHT — это константы, они у меня в отдельном файлике лежат, там просто 800 на 600.
В целом, страничка готова к тому, чтоб рисовать всякое
Дёргаем requestAnimationFrame и отдаём её наш gameLoop
function gameLoop(currentTime) {
// Вычисляем deltaTime
const deltaTime = currentTime - lastTime;
lastTime = currentTime;
// Накапливаем время
accumulator += deltaTime;
// Fixed timestep - обновляем логику с фиксированным шагом
while (accumulator >= FRAME_TIME) {
update(FRAME_TIME);
accumulator -= FRAME_TIME;
}
// Рендерим независимо от update
render();
// Продолжаем loop
requestAnimationFrame(gameLoop);
}
Я решил фиксировать ФПС на 30
export const FPS = 30; // Фиксированный FPS
export const FRAME_TIME = 1000 / FPS; // ~33.33ms на кадр
Метод update содержит (ну, будет содержать) всю нашу игровую логику. Пока он только считает фпс и выводит его на экран для отладки.
function update(dt) {
// Пока пусто - здесь будет логика игры
// Подсчёт FPS для отладки
frameCount++;
fpsTimer += dt;
if (fpsTimer >= 1000) {
fps = frameCount;
frameCount = 0;
fpsTimer = 0;
// Обновляем FPS на странице
const fpsElement = document.getElementById('fps');
if (fpsElement) {
fpsElement.textContent = fps;
}
}
}
Ну и когда всё закончено, отрисовываем
function render() {
// Background canvas - перерисовывается только при изменении карты
// (пока только отладочная сетка, нарисованная в init)
// Foreground canvas - очищаем и рисуем сущности каждый кадр
clearForeground();
// TODO: Здесь будет рендер танков, пуль, эффектов
// UI canvas - очищаем и рисуем UI каждый кадр
clearUI();
renderUI();
}
То есть, по сути, ничего интересного, просто рисую полосочки и квадратики для UI
Результат выглядит вот так
Следующий пост будет посвящён второму этапу
И да, я упоролся и расписал себе полноценный план реализации на 13 этапов. Без ДизДока последнее время вообще не представляю как работать. А раз уж я в команде один, то сам себе и ТЗ пишу... Но лучше так, чем лепить полную отсебятину.
Вопросы замечания предложения в комментариях жду с нетерпением)
А пойду пилить обновление для Лиспублики)
Кошки сложили жопку
А Фанька подглядывает!
Котовка сидит на самой верхотуре
И кажется чем-то недовольна
Привет, лисятки!
А у меня теперь тоже если лисичка, хотя может быть даже писец. :) Спасибо за милейший подарочек!
И еще очень захотелось порисовать. Слово "наблюдение" с ассоциировалось с боевиком.
Поэтому беру лисий блокнот, набрасываю набросок...
И раскрашиваю...
А всем спасибо за прочтение!
@talk.about@Hippopofox@SergPrg@Linda_M@moortnelis@Aid314@Tiamin@Nataalika@Moonshine@Alenari@SergeyRY@Pepels@BespiriL@Cubinec@Palebody@DoctorDoom@rammdarkfunny@AlNiKo@kimpokom@Brainy@etoshtrudel@jewell...