#[моё]

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

Разные виды врагов и бонусы

39
Читалка

Игра почти готова, но не хватает ещё парочки нюансов)

Сразу смотрим результат, а я пока распишу, что же тут происходит

1. Я добавил разные типы врагов. Как и в оригинале, это базовые, быстроходные и бронированные. Последние плюсом ко всему имеют бронебойные патроны.

2. Я добавил новый вид бонуса: «Строитель». Поднимая мастерок игрок может построить несколько бетонных и несколько кирпичных блоков. Именно для баланса этого бонуса и введены вражеские танки, которые могут уничтожать в том числе и бетонные стены.

Давайте смотреть

export class Bonus {
    /**
     * По всему коду натыкал как можно больше комментов, чтоб понятно было куда собака зарыта
     *
     * @param {number} x - Логическая координата X
     * @param {number} y - Логическая координата Y
     * @param {string} type - Тип бонуса (BonusType.STAR, BonusType.GRENADE, etc.)
     */
    constructor(x, y, type) {
        this.x = x;
        this.y = y;
        this.type = type;

        // Время жизни бонуса
        this.lifetime = Duration.BONUS_LIFETIME;
        this.blinkTime = Duration.BONUS_BLINK_TIME;

        // Состояние
        this.collected = false;
        this.expired = false;

        // Анимация
        this.animationFrame = 0;
        this.animationTimer = 0;
        this.animationSpeed = 6; // Кадров игры между сменой кадра анимации

        // Мигание перед исчезновением
        this.blinkTimer = 0;
        this.blinkSpeed = 4; // Быстрое мигание
        this.visible = true;
    }

    /**
     * Обновление состояния бонуса
     */
    update() {
        if (this.collected || this.expired) return;

        // Уменьшаем время жизни
        this.lifetime--;

        // Проверка истечения времени
        if (this.lifetime <= 0) {
            this.expired = true;
            return;
        }

        // Анимация спрайта
        this.animationTimer++;
        if (this.animationTimer >= this.animationSpeed) {
            this.animationTimer = 0;
            this.animationFrame = (this.animationFrame + 1) % BONUS_SPRITE.frameCount;
        }

        // Мигание перед исчезновением
        if (this.lifetime <= (Duration.BONUS_LIFETIME - this.blinkTime)) {
            this.blinkTimer++;
            if (this.blinkTimer >= this.blinkSpeed) {
                this.blinkTimer = 0;
                this.visible = !this.visible;
            }
        }
    }

    /**
     * Сбор бонуса игроком
     */
    collect() {
        this.collected = true;
    }

    /**
     * Проверка, истёк ли бонус
     */
    isExpired() {
        return this.expired || this.collected;
    }

    /**
     * Получить bounding box в логических координатах
     */
    getBounds() {
        return {
            x: this.x,
            y: this.y,
            width: BONUS_SIZE,
            height: BONUS_SIZE
        };
    }

    /**
     * Отрисовка бонуса
     */
    render() {
        if (this.collected || this.expired) return;
        if (!this.visible) return; // Мигание

        const ctx = foregroundCtx;

        // Конвертация логических координат в физические
        const px = GAME_FIELD_X + this.x * GAME_SCALE;
        const py = GAME_FIELD_Y + this.y * GAME_SCALE;
        const size = BONUS_SIZE * GAME_SCALE;

        const sprite = getBonusSprite(this.type);

        if (sprite) {
            const frameX = this.animationFrame * BONUS_SPRITE.frameWidth;

            ctx.drawImage(
                sprite,
                frameX, 0,
                BONUS_SPRITE.frameWidth, BONUS_SPRITE.frameHeight,
                px, py,
                size, size
            );
        } else {
            // Fallback: цветной квадрат
            this.renderFallback(ctx, px, py, size);
        }
    }

    /**
     * Fallback отрисовка (цветной квадрат с буквой)
     */
    renderFallback(ctx, px, py, size) {
        // Цвета для разных типов бонусов
        const colors = {
            [BonusType.STAR]: '#FFFF00',
            [BonusType.GRENADE]: '#FF4500',
            [BonusType.TIMER]: '#00BFFF',
            [BonusType.SHOVEL]: '#8B4513',
            [BonusType.TANK]: '#00FF00',
            [BonusType.HELMET]: '#C0C0C0',
            [BonusType.GUN]: '#FF00FF',
            [BonusType.BUILDER]: '#334455'
        };

        const letters = {
            [BonusType.STAR]: 'S',
            [BonusType.GRENADE]: 'G',
            [BonusType.TIMER]: 'T',
            [BonusType.SHOVEL]: 'L',
            [BonusType.TANK]: '+',
            [BonusType.HELMET]: 'H',
            [BonusType.GUN]: 'P',
            [BonusType.BUILDER]: 'B'
        };

        ctx.fillStyle = colors[this.type] || '#FFFFFF';
        ctx.fillRect(px + 2, py + 2, size - 4, size - 4);

        ctx.fillStyle = '#000000';
        ctx.font = `bold ${size * 0.6}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(letters[this.type] || '?', px + size / 2, py + size / 2);
    }
}

Где и как учитываются бонусы?

Во-первый в main.js я сразу создаю бонус для строительства

// DEBUG: Тестовый бонус Builder рядом с игроком
    const testBonus = new Bonus(PLAYER_SPAWN_POINT.x + 32, PLAYER_SPAWN_POINT.y - 32, BonusType.BUILDER);
    bonuses.push(testBonus);

Это сделано для тестов, но, возможно, я даже оставлю в конечном варианте

Дальше в кор геймплее

const collectedBonus = checkTankBonusCollision(player, bonuses);
        if (collectedBonus) {
            applyBonusEffect(collectedBonus);
        }
// И дальше по коду сам метод
function applyBonusEffect(bonus) {
    switch (bonus.type) {
        case BonusType.STAR:
            // Апгрейд оружия (до уровня 3 максимум)
            if (player && player.bulletLevel < 3) {
                player.bulletLevel++;
                // Уровень 1+ даёт 2 пули
                if (player.bulletLevel >= 1) {
                    player.bulletCount = 2;
                }
            }
            break;

        case BonusType.GUN:
            // Мгновенный максимальный апгрейд
            if (player) {
                player.bulletLevel = 3;
                player.bulletCount = 2;
            }
            break;

        case BonusType.TANK:
            // Дополнительная жизнь
            playerLives++;
            break;

        case BonusType.HELMET:
            // Временная неуязвимость
            if (player) {
                player.hasShield = true;
                player.shieldDuration = Duration.EFFECT_SHIELD;
            }
            break;

        case BonusType.TIMER:
            // Заморозка врагов
            freezeTimer = Duration.EFFECT_TIMER;
            break;

        case BonusType.GRENADE:
            // Уничтожение всех врагов на экране
            for (const enemy of enemies) {
                if (!enemy.destroyed) {
                    enemy.destroyed = true;
                    // Создаём взрывы
                    const centerX = enemy.x + TANK_SIZE / 2;
                    const centerY = enemy.y + TANK_SIZE / 2;
                    effects.push(createBigExplosion(centerX, centerY));
                }
            }
            break;

        case BonusType.SHOVEL:
            // Укрепление базы
            shovelTimer = Duration.EFFECT_SHOVEL;
            fortifyBase();
            break;

        case BonusType.BUILDER:
            // Режим строителя: даёт блоки для размещения
            if (player) {
                player.builderBricks += 8;
                player.builderSteel += 4;
            }
            break;
    }

    bonus.collect();
}

Далее все таймеры эффекты глобальные сбрасываются вот тут

function updateGlobalEffects() {
    // Таймер заморозки
    if (freezeTimer > 0) {
        freezeTimer--;
    }

    // Таймер укрепления базы
    if (shovelTimer > 0) {
        shovelTimer--;
        if (shovelTimer <= 0 && baseFortified) {
            restoreBase();
        }
    }
}

Вообще в данном случае было бы логично вынести логику сброса эффекта бонуса в сам бонус. Чтоб он там в фоне считался, а ядру просто отдавал событие или менял внутри себя флаг. Как будто там будет почище. Подумал об этом только сейчас, отрефакторю к следующему выпуску.

Следующий этап уже не про новые фичи, а про полировку, оптимизацию и доработку.

Писал я план ещё в самом начале, так что часть из этих пунктов уже сделана, но всё равно нужно будет пройтись по всем.


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

Топор Тигр

40

Топор советский, 1978 года.
Рисунок делал травлением, потом холодное воронение и сверху оружейное масло.
Длина 55 см. Вес 1.5 кг.
Рукоять из березы, морилка и воск.
Приятного просмотра)

Источник:

Китайские фрукты

44
Читалка

Кто-нибудь, прикатите мне тележку! Я облопалась и теперь могу передвигаться только методом перекатывания. А значит пришло время познакомить и вас с местными фруктами:

.

Яичный фрукт

Возможно, у этой странной фигни есть и другое, более пафосное наименование, но название «Яичный фрукт» как нельзя лучше отражает саму суть этого необычного плода. Цвет и консистенция мякоти - один в один как у сваренного вкрутую желтка.

Вкус тоже похож на желток, но сладкий. Я бы не стала покупать этот фрукт ещё раз, но попробовать его точно стоит — он очень необычный.

.

Танхулу (китайский боярышник)

Размером эта херабора с яблоко. Вкус как у боярышника или шиповника - кисловатый, сладковатый, немного терпкий. Традиционно тут боярышник едят в сахарном сиропе - это главное лакомство в Гуанчжоу. Очень вкусное сочетание!

Кстати, @Ejik, шиповника я не нашла нигде. Но боярышник это же почти шиповник?

А еще тут есть и другой близкий родственник:

Ягоды годжи или тибетский барбарис

Тут эти красные ягодки добавляют вообще везде: в суп, в чай. В мясо, рыбу, мороженое. Продается в сушёном виде в каждом ларьке. На вкус похож на финик, но не на столько сладкий.

Считается суперфруктом с чудодейственными свойствами. Чуть ли не излечивающим рак. Само собой никакой доказательной пользы у ягод нет. Но и вреда тоже. При сладком вкусе у ягод практически нулевой гликемический индекс, т.ч. его можно есть как семечки даже диабетикам.

.

Кивано или рогатая дыня

Пробовали в деревне переспелый огурец? Значит вы пробовали и кивано. На вкус то же самое, только стоит втридорого. Принято есть поливая медом.

Нам не впечатлило и вам не советую.

.

Пепино или сладкий огурец.

Фрукт как фрукт. Умеренно сладкий. Без ярковыроженных нюансов вкуса. Похож на дыню, но маленькую.

.

Рамбутан, Личи и Лонган

Рамбутан волосатый, личи в пупырышку, лонган гладкий (порутчик Ржевский, молчать!). На вкус - вообще ничем не отличаются. Внутри большая твердая косточка. Пробовали личи - считайте, то ели все три фрукта.

Рамбутан и личи
Рамбутан и личи
Лонган
Лонган

.

Мангустин

Не знаю почему, но эта штука тут самая дорогая. Хоть и продаётся на каждом шагу.

Кожура очень твердая. Нож еле прорезает.

Внутренность мягкая - соплеобразная. В каждой дольке по косточке. Вкус похож на Личи, только с кислинкой.

.

Кумкват

Горький мандарин.

Тут его повсеместно едят подсушенным прям с кожурой. Мне не зашло категорически. А мужу понравилось.

.

Карамбола

Очень кислая штука. Немного терпкая.

На вкус похожа на ананас и антоновку. Активно используется в коктейлях

Продается везде. Но мы ее не брали. Наелись до икоты еще на Кубе

.

Зизифус

Внешне - зеленая слива.

Фрукт сочный, хрусткий, сладкий. На вкус - один в один груша сорта Конференс. С закрытыми глазами не отличить

.

Салак/салакка или змеиный фрукт.

Эти фрукты сразу бросаются в глаза на прилавках благодаря своей красивой чешуе.

Фрукт легко чистится. Внутри плод разделен на несколько долек с твердым орешком в центре.

Мякоть очень плотная. По консистенции как чеснок. На вкус - сладковато-ореховая. Я бы даже не назвала это фруктом. Но однозначно рекомендую попробовать.

.

Питахайя или драконфрукт

Это два названия одного и того же растения. Выделяют 4 сорта этого плода кактуса: красный, розовый, белый и желтый. У нас как правило продается только белая питахайя. Красный и розовый сорта имеют более насыщенный вкус. А самая сладкая и сочная - желтая.

У спелого плода мякоть теряет молочную белизну и становится полупрозрачной.

.

Гуава и фейхоа

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

Растения родственные и на вкус похожи. Но фейхоа мельче, его текстура более крупинчатая (как наши груши) и сильнее кислит.

.

Папайя

Тоже ближайший родственник фейхоа и гуавы. Но она большая и вкус совершенно другой. Она немного сладкая, не кислит, не вяжет. Но оставляет неприятное послевкусие ношеных носков. Нямушка

.

Маракуя, гранадилла

Внешне фрукты очень похожи, т.к. являются близкими родственниками. У плодов твердая тонкая корочка на подобии яичной скорлупы. Внутри белый слой несъедобной мякоти как у цитрусовых и соплевидная сердцевина с косточками. У спелых плодов мякоть легко отделяется от скорлупы, как кожура мандарина. У зеленых -тяжело, как у апельсина.

Но пусть внешняя схожесть этих фруктов не вводит вас в заблуждение. У них совершенно разный вкус.

Маракуя кислая. Желтая маракуя просто кислая, а красная - трындец какая кислючая. Вкус как у кислых конфет Toxic wast. Даже от одного воспоминания - сводит скулы.

Красная маракуя
Красная маракуя
Желтая маракуя
Желтая маракуя

Гранадилла при внешней схожести имеет приятный сладкий вкус. Ее косточки очень легко отделяются от слизкой мякоти.

Мне гренадилла зашла. Буду брать еще. Мужу и детям зашла маракуйя. Они любят кислое.

.

Чику или саподилла

На фруктовом развале вы, скорее всего пройдете мимо мешка с картошкой. Но это не картошка, а плоды чику. Мякоть немного крупинчатая, как у груши. Незрелый плод почти безвкусный и водянистый. Тут тоже похож на грушу. А спелая саподилла - сладкая. Похожа по вкусу на шоколадный королёк (тот, который не вяжет).

.

Аннона, гуанабана, черемойя или сметанное яблоко

Сладко-сливочный сочный фрукт. Приятной текстуры. Можно аккуратно резать на дольки или есть ложкой. Внутри много крупный семечек. Бывает желтым и зеленым. Мы пробовали только зеленый.

Из аннона часто делают жареное мороженое. Но фрукт вкусный и сам по себе. Рекомендую к покупке

.

Джекфрут

Ела его много раз в России, а тут нам с ним не повезло. Купили огромный плод, а он оказался зеленым. Какая же это вредная гадость, я вам скажу. Выкинули целиком. При разрезании зеленого плода по линии разреза начал выделяться млечный сок в который я тут же измазалась. Ну сок и сок - подумала я. Отмою. Ага... Щаззз!!! Уже через 3 минуты мои пальцы перестали гнуться и начали безбожно липнуть ко всему. И это не обычное прилепание сладкого сока. Эта жижа начала густеть и превратилась в странную липкую тянучку - внебрачного сына жвачки и клея момента. Ни вода, ни мыло, ни мочалка ее не брала. Она распределялась равномерно между всеми материалами которыми я пробовала эту дрянь стереть. В итоге извозюкав 3 полотенца, 2 мочалки и пару щеток я сдалась и обернула руки в пакет, чтоб больше ничего не заляпать этой заразой. Окончательно эта жижа сформировалась в жвачку часа через два и тогда я смогла ее отодрать.

Вот так должен выглядеть спелый плод - внутри легко отделяемые плотные оранжевые дольки
Вот так должен выглядеть спелый плод - внутри легко отделяемые плотные оранжевые дольки
А вот так выглядит изнутри зеленый плод. Дольки еще не сформированы.
А вот так выглядит изнутри зеленый плод. Дольки еще не сформированы.

Вкус у нормального джекфрукта - мягкий, сладко-сливочный. Текстура плотная, волокнистая.

.

Дуриан

Это вонючий брат Джекфрукта. Тут эта гадость продается вообще на каждом шагу. Места продажи легко найти с закрытыми глазами - по запаху гнилого подмороженного лука. Я так и не решилась купить этот фрукт и ограничилась мороженым с кусочками дуриана (прости, @Kukabara ). Воняет так же, но порция явно меньше. 😅 и даже мороженое мы с мужем не осилили. Половина ушла в мусорку. Вкус - сладко-сливочный. Но не мягкий, как у черемойи, а резкий.


Ну и конечно тут на каждом шагу продаются классические манго, дыня, арбуз, ананас и бананы. И свежевыжатые соки из них. Стоят копейки - 150р за литр, поэтому пьем пока из ушей не потечет. Арбузы, к слову, здесь настолько сладкие, что кажется будто их залили сиропом. Сок нужно разводить водой хотя бы 1к1, чтоб не было так приторно.

Щи простые

OH SHI..! Here we go again! Всем привет, мои маленькие любители вкусно покушать!

46
Читалка

В эфире снова рубрика "Кирюхино жорево" и сегодня у нас опять суп. На этот раз самый русский суп - обычные щи с капустой. Но вкусненькие. Как всегда прилагается кирюхин рецепт и эротишные фоточки в конце. А так же, по многочисленным просьбам, мой способ делать фудпорно-фоточки. Погнали.


Нам потребуется:

  • Куриный суповой набор, либо курица на кости
  • Стебли укропа и петрушки
  • Капуста, лук, моковь, картофель
  • Томатная паста
  • Соль, перец, сметана, свежий укроп.

Начинаем с варки бульона в технологической кастрюле. Бульон солю сразу. Традиционно добавляю при варке стебли укропа и петрушки, по пучку и несколько горошин чёрного перца. Одновременно в соседней кастрюле ставлю вариться нашинкованную капусту, один маленький кочан, или половину большого. Капусту варю параллельно, потому как люблю её в супе совсем мягенькую, разваренную, как раньше в постсоветских столовках! Бульон варю 1,5 часа, капусту минут 40. В капусту при варке добавляю немного соли и сахара, чтобы усилить вкус.

Пока всё варится, подготавливаем остальные ингредиенты:

Лук и картофель режем кубиками. Берём @Nataalika 1шт. и заставляем натереть морковь на крупной тёрке. Ибо это процесс муторный, такими приземлёнными вещами нам заниматься некогда, мы тут кулинарную магию творим:)

Делаем зажарку. Опять же классическим способом. Разогреваем сковороду с растительным маслом, вываливаем морковь

Некоторое время её пассеруем, потом добавляем лук и обжариваем вместе.

Обжариваем ещё немного, сдвигаем к краям, в центр добавляем томатную пасту.

Прогреваем. Перемешиваем. Пассеруем вместе. Еще пару минут. Зажарка готова.

Пока ковырялись, уже и бульон сварился. Процеживаем его обязательно.

Если брали кости с мясом, мясо счищаем и добавим в суп в конце. Если только суповой набор - жмых выкидываем.

В бульон добавляем капустку.

Вновь ставим на огонь, даём закипеть. Добавляем зажарку.

Снова доводим до кипения. Варим минут 5 и добавляем картофель. Варим до готовности картофеля, минут 15.

Нарезаем свежий укропчик.

Когда суп сварился, выключаем огонь. В кастрюлю добавляем добрую щепоть укропа, один-два листика лаврушки и треть столовой ложки 70% уксуса. Уксус добавляю потому как капуста у меня только свежая. Если готовить используя квашенную капусту, либо смесь свежей и квашенной, то уксус можно не добавлять.

Накрываем крышкой, даем постоять 20-30 минут и можно подавать. Подача обязательно с хлебом, сметаной, горчицей и свежей зеленью. Можно с бекончиком или грудинкой! Чеснок вприкуску тоже хорошо. У меня суп на курице, поскольку супруга не любит мясо. Но себе я так же отдельно добавляю в тарелку свиной тушёнки или грудинки. Ибо какие щи без свинины? Пусть даже и бульон неполноценный😁 А вообще, варите сразу на свинине, и не извращайтесь, как я:)

Несмотря на то, что суп простой, он безумно вкусный, особенно на второй день, когда максимально настоится!

Пока не начали кушать, ещё же нужно красиво отснять для лиспубликанцев! Как это делаю я:

Отдельно для @Kukabara - всё совсем несложно, даже в условиях маленькой кухни. Берём фон и закрепляем его на столе прищепками. В моём случае это модельные зажимы для сосков. В качестве фона у мну среднематовая бумага для упаковки полиграфической плёнки. Я её взял просто потому что она у меня есть, это по сути, вообще мусор. Но обычный фотофон метр на два стоит 200 рублей на озонах:)

Расставляем композицию. Берём камеру, внешнюю вспышку и любой объектив с относительно небольшой МДФ.

Штатив я поставил просто для эффектности. Можно и с рук снимать, со вспышкой особой разницы нет.

Я снимаю вообще всегда только в полностью ручном режиме. Не потому что я такой фотосноб (но немножк и поэтому тоже, да😁), а потому как автоматика никогда мне не даёт того, чего я хочу.

Оптимальное фокусное расстояние имхо 60-70 мм. Съёмка лучше в RAW, если сразу в JPEG, то цветовая температура чуть потеплее средней 5000 К.

ISO выставляем 200 или 100, диафрагму зажимаем до 5.6, или еще немного меньше. Это нужно для большей глубины резкости и детализации. Выдержку регулируем по ситуации. Вспышку выставляем на 1/2 или 1/4 мощности. Самый важный момент: голову вспышки направляем в потолок, или в сторону, в стену. Таким образом мы работаем с отражённым, рассеяным и мягким светом (потолки в 90% случаев белые). Ещё вспышку можно установить в стороне с использованием синхронизатора, или пыхать в ассистента, который держит в руках лист белой бумаги. Вариантов масса. Главное не светить прям в лоб, само собой. Конечно, внешняя вспышка есть не у всех. Но и это не проблема. Почти на всех моделях фотокамер есть встроенная вспышка. Можно использовать и её, поставив перед ней обычный белый надувной шарик (приклеив на кусочек скотча, например).

Свет, пройдя сквозь шар, рассеится и даст мягкое и равномерное освещение. Всё это применимо не только при фотографировании еды, а вобще при любой предметной съёмке. Вот как-то так:)
Ну и финальный фотосет с супчиком:

Сметанку размешиваем! Ням!
Сметанку размешиваем! Ням!

Всем кто прочитал - спасибо! А тем кто ужинает - приятного аппетита!❤