Хаббл получил самое чёткое на сегодняшний день изображение туманности «Яйцо» Объект RAFGL 2688 предоставляет редкую возможность проверить теории поздней стадии звёздной эволюции. Два луча от умирающей звезды освещают быстро движущиеся полярные «лопасти», которые пронизывают более медленную, старую серию концентрических дуг. Их форма и движение предполагают гравитационное взаимодействие с одной или несколькими скрытыми звездами-компаньонами, все они погребены глубоко в толстом диске звёздной пыли.
Изображение, полученное телескопом «Хаббл», показывает так называемую Космическую Змею — далёкую галактику с ярко выраженными узлами интенсивного звездообразования. Её изображение искажено эффектом гравитационного линзирования.
На самом деле эта галактика находится за массивным скоплением галактик MACSJ1206.2-0847, но благодаря сильному гравитационному полю скопления её свет искривляется и достигает Земли, образуя гигантскую дугу.
Свет от этой далёкой галактики с высоким красным смещением проходит мимо скопления, и его траектория искривляется под действием гравитации. Вместо того чтобы затруднять наблюдения, такое сильное линзирование увеличивает удалённый объект, улучшая разрешение и глубину изображения. В некоторых случаях оно даже создаёт несколько изображений одного и того же объекта, поскольку свет огибает скопление по разным траекториям.
Меня позвала @capybarystic Продолжим предаваться блинным воспоминаниям.
Стараюсь каждую масленицу напечь побольше блинов(больших, тонких, на дрожжах) и наделать кучу начинок(капуста, фарш, рыба, творог, икра, варенья, мед...). Всю неделю питаемся блинами. В другое время года практически не пеку их, если только внезапно захочется.
С юности умела печь тонкие блины. Другие не так интересны, а в тонкие можно завернуть все, что хочешь! Но для этого нужна простая советская раскаленная сковородка (а лучше 2-3 для скорости). И вот, переехав лет 12 назад на новую квартиру, я столкнулась с проблемой: индукционная плита прекрасна, но она не любит тонких блинов :( - раскаленная сковорода, это " алярм, алярм, я выключаюсь ой все!". И стою я с почти двумя литрами теста и пищащей плитой. Выкрутилась я тогда с помощью духовки. Если вы любите толстые, очень толстые блины - то духовка и силиконовые формы - ваш выбор. Были у меня тогда "блины" в форме котят, зайцев и медведей.
А потом попросила на др электроблинницу и по старой традиции задвоения подарков мне их две подарили! И это прекрасно. Да, блины не такие ажурные/зажаристые, зато большие и почти ровные. Почувствуй себя поваром в "Теремке"! Особенно когда рядом скачет ребенок: "мне Фермерский, мне Фермерский".
Нашла на нижней полке, вымыла как могла от пылищи, и в честь среды принесла вам. Жабку подарили когда-то давно и не из-за жабки, а из-за котика, потому что дело было на выставке кошек )
Изначально я хотел добавить поддержку геймпада только на финальном этапе разработки, но получилось так, что это делается достаточно просто, поэтому засунул сразу.
Пойдём по порядку
function handleKeyDown(e) {
keys[e.code] = true; // аккумулируем все нажатия, чтоб потом обработать
// Предотвращаем прокрутку страницы стрелками и пробелом
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Space'].includes(e.code)) {
e.preventDefault();
}
}
function handleKeyUp(e) {
keys[e.code] = false; // "забываем" отпущенную кнопку
}
function handleGamepadConnected(e) {
console.log(`Gamepad connected: ${e.gamepad.id}`);
gamepadIndex = e.gamepad.index;
}
function handleGamepadDisconnected(e) {
console.log(`Gamepad disconnected: ${e.gamepad.id}`);
if (gamepadIndex === e.gamepad.index) {
gamepadIndex = null;
}
}
В отличии от клавиатуры, которая отдаёт браузеру события о нажатии на кнопка, с геймпадом такой финт ушами не прокатывает и надо опрашивать его состояние самостоятельно (прям как на привычных движках в старые добрые времена).
Дальше заспавним наш "танк" на игровой сцене
const spawnX = 4 * CELL_SIZE; // 4 клетки от левого края = 64 px
const spawnY = 24 * 8 - 32; // Чуть выше базы = 160 px
player = new Tank(spawnX, spawnY, TANK_PLAYER);
Класс танка достаточно большой, он ко конструкторе принимает значения координат спавна и типа танка (танк игрока или тип врага). Ему устанавливается "здоровье" (сколько раз в него надо попасть, чтобы убить), сколько активных патронов есть и т.д.
constructor(x, y, type = TANK_PLAYER) {
this.x = x;
this.y = y;
// Направление (0=вверх, 1=вправо, 2=вниз, 3=влево)
this.direction = DIR_UP;
this.speed = TANK_SPEED_NORMAL; // в пекселях за кадр
// Тип танка
this.type = type;
this.health = 1;
this.destroyed = false;
// Флаг движения в текущем кадре
this.moving = false; // Пока только задаётся, но нигде не используется. Есть мысли на будущее, но если не понадобится, удалю
// Апгрейды (для игрока)
this.bulletLevel = 0; // 0=обычная, 1=быстрая, 2=усиленная
this.bulletCount = 1; // Макс. пуль на экране
this.hasShield = false; // Временная защита
this.hasBoat = false; // Движение по воде
// Активные пули (для подсчёта лимита)
this.activeBullets = 0;
}
Непосредственно движение выгляди таким образом:
move(direction) {
// Сначала поворачиваем, если нужно
if (this.direction !== direction) {
this.turn(direction);
return; // В оригинале, если повернуть, танк не двигается в этом кадре, может уберу, если плейтесты покажут необходимость
}
// Вычисляем смещение по направлению
let dx = 0;
let dy = 0;
switch (direction) {
case DIR_UP: dy = -this.speed; break;
case DIR_DOWN: dy = this.speed; break;
case DIR_LEFT: dx = -this.speed; break;
case DIR_RIGHT: dx = this.speed; break;
}
// Новая позиция
let newX = this.x + dx;
let newY = this.y + dy;
// Ограничение границами игрового поля
newX = Math.max(0, Math.min(newX, LOGICAL_FIELD_SIZE - TANK_SIZE));
newY = Math.max(0, Math.min(newY, LOGICAL_FIELD_SIZE - TANK_SIZE));
// TODO: Проверка коллизий с тайлами (Этап 4)
// TODO: Проверка коллизий с другими танками (Этап 7)
// Применяем новую позицию
this.x = newX;
this.y = newY;
this.moving = true;
}
Ну и рисование танка на игровом поле
render() {
if (this.destroyed) return;
const ctx = foregroundCtx;
// Конвертация логических координат в физические
const px = GAME_FIELD_X + this.x * GAME_SCALE;
const py = GAME_FIELD_Y + this.y * GAME_SCALE;
const size = TANK_SIZE * GAME_SCALE;
// Цвет танка в зависимости от типа, пока всего два, потом будут новые типы и новые цвета
// Основной квадрат танка
ctx.fillStyle = this.type === TANK_PLAYER ? COLOR_TANK_PLAYER : COLOR_TANK_ENEMY;
ctx.fillRect(px, py, size, size);
// Индикатор направления (треугольник)
this.renderDirectionIndicator(ctx, px, py, size); // понял, что просто квадрато недостаточно, поэтому добавил "морду"
}
Рендер танка вызывается в методе основного рендера после очистки всего динамического слоя
// Foreground canvas — очищаем и рисуем сущности каждый кадр
clearForeground();
// Рендер танка игрока
if (player) {
player.render();
}
// TODO: Здесь будет рендер врагов, пуль, эффектов
Результат всего этого безобразия
Танк ездит сквозь стены, потому что "физического" движка ещё нет. Проверка коллизий как раз на очереди.
не, эт просто я всегда хочу всё))
ПРОБЛЕМА НЕ В ТЕБЕ!)
@Pinocet ля чо!
На туе ) ну или кипарисе