#[моё]

С-Хоревыходные

Когда есть время поиграть с питомцами

41

Покажу и я наших шерстяных друзей в ответ на этот пост

Эти товарищи вполне самодостаточные, но тоже требует внимания. Вот как они проводят субботний день: выясняют отношения за все обиды скопившиеся за неделю друг другу.

Аврора всё высказала этому большому чурбану Рафаэлю.
Аврора всё высказала этому большому чурбану Рафаэлю.
Тот, конечно же, всё отрицал. И в ход пошли другие аргументы
Тот, конечно же, всё отрицал. И в ход пошли другие аргументы
Теперь ясно, кто в этой семье доминирует
Теперь ясно, кто в этой семье доминирует
Потому что надо соблюдать порядок и чистоту, и делиться едой, а не съедать всё в одно горло.
Потому что надо соблюдать порядок и чистоту, и делиться едой, а не съедать всё в одно горло.
Рафаэль всё понял и извинился.
Рафаэль всё понял и извинился.
Это бонус для тех кто развернул пост - носятая мордаха
Это бонус для тех кто развернул пост - носятая мордаха
Кто просил жопку? А вот и жопка))))
Кто просил жопку? А вот и жопка))))

«Танчики» на html5

Пишем свой игровой движок

36

После прошлого поста я что-то так проникся происходящим, что решил воссоздать ещё одну любовь детства. Но как говорил Маркус Персон: «Если вы не можете написать свой движок, то гавно вы, а не разработчики». Там, скорее всего, было как-о иначе, но суть такая. И да, с этой мыслью я в корне не согласен, но написать свой движок – задача, как минимум, интересная. Я решил, что аркадная игра для этого подходит, как ничто другое. По сути, некий аналог движка уже был реализован в тетрисе, но здесь нужна будет и какая-никакая физическая модель, и интеллект врагов и рендер всего этого безобразия в несколько слоёв и с ФПС побольше, чем 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 этапов. Без ДизДока последнее время вообще не представляю как работать. А раз уж я в команде один, то сам себе и ТЗ пишу... Но лучше так, чем лепить полную отсебятину.

Вопросы замечания предложения в комментариях жду с нетерпением)

А пойду пилить обновление для Лиспублики)


Всем хороших игр!

С-котовыходные

39

Кошки сложили жопку

А Фанька подглядывает!

Котовка сидит на самой верхотуре

И кажется чем-то недовольна

Наблюдение.

42

Привет, лисятки!

А у меня теперь тоже если лисичка, хотя может быть даже писец. :) Спасибо за милейший подарочек!

И еще очень захотелось порисовать. Слово "наблюдение" с ассоциировалось с боевиком.

Поэтому беру лисий блокнот, набрасываю набросок...

в главной роли Мэтта Деймона - благородный олень
в главной роли Мэтта Деймона - благородный олень

И раскрашиваю...

Антуан де Сент-Экзюпери красавчик.)
Антуан де Сент-Экзюпери красавчик.)

А всем спасибо за прочтение!

Осьминожка !

Вторая поделка в садик

44
Решились на вторую попытку
Решились на вторую попытку
Тюнинг от Лиски)
Тюнинг от Лиски)
Вот и сам Осьминожка !
Вот и сам Осьминожка !
Себя тоже надо украсить!)
Себя тоже надо украсить!)
Померили! Всё сошлось
Померили! Всё сошлось
Ноги гнутся) можно и так поставить
Ноги гнутся) можно и так поставить

Graphuary 2к26

День 4: Любовь. День 5: Ровный. День 6: Наблюдение

40

Художник не опаздывает, художник задерживается

День 4. Любовь

Сон, кофе, масочки для лица и спорт. А что для вас любовь к себе?
Сон, кофе, масочки для лица и спорт. А что для вас любовь к себе?

День 5. Ровный

Ситуация жизненная, ноги короткие, штаны длинные
Ситуация жизненная, ноги короткие, штаны длинные

День 6. Наблюдение

Основано тоже на реальных событиях. Не хватает звука цоканья. Птица сидит уже минут 10, почистила крылышки. Продолжаем наблюдение
Основано тоже на реальных событиях. Не хватает звука цоканья. Птица сидит уже минут 10, почистила крылышки. Продолжаем наблюдение