Ремкомплект
Нашлось на просторах маркетплейсов
Покажу и я наших шерстяных друзей в ответ на этот пост
Эти товарищи вполне самодостаточные, но тоже требует внимания. Вот как они проводят субботний день: выясняют отношения за все обиды скопившиеся за неделю друг другу.
Работы за вчерашний день
Ну и моё
После прошлого поста я что-то так проникся происходящим, что решил воссоздать ещё одну любовь детства. Но как говорил Маркус Персон: «Если вы не можете написать свой движок, то гавно вы, а не разработчики». Там, скорее всего, было как-о иначе, но суть такая. И да, с этой мыслью я в корне не согласен, но написать свой движок – задача, как минимум, интересная. Я решил, что аркадная игра для этого подходит, как ничто другое. По сути, некий аналог движка уже был реализован в тетрисе, но здесь нужна будет и какая-никакая физическая модель, и интеллект врагов и рендер всего этого безобразия в несколько слоёв и с ФПС побольше, чем 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 этапов. Без ДизДока последнее время вообще не представляю как работать. А раз уж я в команде один, то сам себе и ТЗ пишу... Но лучше так, чем лепить полную отсебятину.
Вопросы замечания предложения в комментариях жду с нетерпением)
А пойду пилить обновление для Лиспублики)
Кошки сложили жопку
А Фанька подглядывает!
Котовка сидит на самой верхотуре
И кажется чем-то недовольна
Когда древние викинги впервые увидели переливающееся радужное сияние в ночном небе, они сразу поняли: это блики от мечей валькирий, которые забирают воинов в чертоги Вальхаллы.
На саамском шаманском бубне традиционно рисовали символы северного сияния. В саамском языке есть несколько названий для этого природного явления, одно из них – «гуовссахас» (Guovssahas), что означает «свет, который слышно». Так же саамы считали, что это могут быть весточки живым из мира мертвых, а могут быть искры, которые взметнулись на небеса после взмаха лисьего хвоста (именно благодаря этой легенде возникло финское название природного явления — revontulet, «лисьи огни»).
В Средние века считалось, что полярное сияние предрекает эпидемию и войны.
алюминиевая труха, каустическая сода. Итог водород. Но это не точно
Ахахаха, мода, что ты делаешь, прекрати
Цепная пила
Мультик
Двигателя
Армирование шланчика
Залипалка
Шота со стеклом, но прикольно
Сковородочки
Кросивое
Мячики
Рихтовка моцика
Фитнес и польза
@talk.about@Hippopofox@SergPrg@Linda_M@moortnelis@Aid314@Tiamin@Nataalika@Moonshine@Alenari@SergeyRY@Pepels@BespiriL@Cubinec@Palebody@DoctorDoom@rammdarkfunny@AlNiKo@kimpokom@Brainy@etoshtrudel@jewell...
Это нужно как-то закрепить, зазернить в лисоленте