logo
LIS PUBLICA
☰
  • Новое
  • Горячее
  • Сокровищница
  • Лучшее
  • Сообщества
  • Видео
  • Обсуждаемое

VariusSoft
VariusSoft Серия: Пишем танчики на JavaScript Сообщество: GameDev Опубликовано 4 дня назад
  • [моё]
  • GameDev
  • Программирование
  • Длиннопост
  • Battle City

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

Стрельба | Снаряды

В прошлой серии вы видели: стены более непротицаемы.

В этой серии мы научим наш так шмалять белыми квадратами по сторонам! И будем убеждать себя, что это снаряды.

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

    export class Bullet {
    constructor(x, y, direction, owner, power = 0) {
        this.x = x;
        this.y = y;
        this.direction = direction;
        this.speed = Speed.BULLET;
        this.power = power;
        this.owner = owner;

        // Состояние
        this.active = true;
    }
}

Сущность пули помнит, кто её владелец, знает свою скорость и мощность. Мощность нам понадобится на следующих этапах. Например, стандартная пуля может только «брить» кирпичи, более мощная сможет уничтожать уже и бетонные/стальные блоки. В каких-то версиях пиратских танков была возможность даже сбривать кусты.

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

    update(gameMap) {
        if (!this.active) return;

        // Вычисляем смещение
        let dx = 0;
        let dy = 0;

        switch (this.direction) {
            case Direction.UP:
                dy = -this.speed;
                break;
            case Direction.DOWN:
                dy = this.speed;
                break;
            case Direction.LEFT:
                dx = -this.speed;
                break;
            case Direction.RIGHT:
                dx = this.speed;
                break;
        }

        // Новая позиция
        const newX = this.x + dx;
        const newY = this.y + dy;

        // Проверка границ карты
        if (newX < 0 || newX + BULLET_SIZE > LOGICAL_FIELD_SIZE ||
            newY < 0 || newY + BULLET_SIZE > LOGICAL_FIELD_SIZE) {
            this.destroy();
            return;
        }

        // Проверка коллизий с тайлами
        if (gameMap && this.checkTileCollision(newX, newY, gameMap)) {
            return; // Пуля уничтожена в checkTileCollision
        }

        this.x = newX;
        this.y = newY;
    }

    checkTileCollision(newX, newY, gameMap) {
        const tiles = getTilesUnderEntity(newX, newY, BULLET_SIZE, BULLET_SIZE);

        for (const {tx, ty} of tiles) {
            const tileId = gameMap.getTile(tx, ty);
            const tileDef = TILE_DEFS[tileId];

            if (!tileDef) continue;

            // Пуля блокируется этим тайлом?
            if (tileDef.blocksBullet) {
                this.destroy();
                // TODO: Разрушение тайла (на следующем этапе)
                return true;
            }
        }

        return false;
    }

    destroy() {
        if (!this.active) return;
        this.active = false;

        if (this.owner) {
            this.owner.activeBullets = Math.max(0, this.owner.activeBullets - 1);
        }

        // TODO: Создание эффекта взрыва (Не скоро, ещё этапа через три)
    }

    render() {
        if (!this.active) return;

        const ctx = foregroundCtx;

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

        // Рисуем пулю как белый квадрат
        ctx.fillStyle = Colors.BULLET;
        ctx.fillRect(px, py, size, size);
    }

    getBounds() {
        return {
            x: this.x,
            y: this.y,
            width: BULLET_SIZE,
            height: BULLET_SIZE
        };
    }

В самом танке обновляем метод стрельбы. Раньше там не было нифига, теперь вот:

    shoot() {
        if (this.activeBullets >= this.bulletCount) {
            return null;
        }
        //return null; - так было

        // так стало: 
        // Вычисляем позицию пули (центр передней части танка)
        let bulletX, bulletY;
        const centerOffset = (TANK_SIZE - BULLET_SIZE) / 2;

        switch (this.direction) {
            case Direction.UP:
                bulletX = this.x + centerOffset;
                bulletY = this.y - BULLET_SIZE;
                break;
            case Direction.DOWN:
                bulletX = this.x + centerOffset;
                bulletY = this.y + TANK_SIZE;
                break;
            case Direction.LEFT:
                bulletX = this.x - BULLET_SIZE;
                bulletY = this.y + centerOffset;
                break;
            case Direction.RIGHT:
                bulletX = this.x + TANK_SIZE;
                bulletY = this.y + centerOffset;
                break;
        }

        // Создаём пулю
        const bullet = new Bullet(bulletX, bulletY, this.direction, this, this.bulletLevel);
        this.activeBullets++;

        return bullet;
    }

Изменилась обработка пользовательского ввода

    function processInput(player, gameMap = null, bullets = []) {
    if (!player || player.destroyed) return;

    // Обрабатываем клавиатуру
    processKeyboardInput(player, gameMap, bullets);

    // Обрабатываем геймпад
    processGamepadInput(player, gameMap, bullets);
}

И вот тут я прям серьёзно задумался: «А не хуйню ли я делаю?». Как будто передавать карту и массив снарядов в обработку инпутов – гавно идея...

К следующему посту поправлю.

А пока вот так:

    if (keys['Space'] || keys['Enter']) {
        if (player.canShoot()) {
            const bullet = player.shoot();
            if (bullet) {
                bullets.push(bullet);
            }
        }
    }

В основном методе update теперь вызываем и апдейт всех пуль

    for (const bullet of bullets) {
    bullet.update(gameMap);
  }

И в основном рендере их рисуем

    // Рендер пуль
  for (const bullet of bullets) {
    bullet.render();
  }

Если есть идеи по улучшению кода, рад буду почитать

Результат

План на следующий этап

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

Читать дальше...
10
+10 / -0
2
37
ТГ ВК
zmmx
zmmx Опубликовано 4 дня назад

newX < 0 || newX + BULLET_SIZE > LOGICAL_FIELD_SIZE

Если координаты изменяются от 0 включительно до LOGICAL_FIELD_SIZE включительно, то реальный размер поля LOGICAL_FIELD_SIZE+1. Короче в проверках ошибка. Кстати, типичная для программистов, т.к. есть элемент с индексом 0

0
+1 / -1
[ Свернуть ]
VariusSoft
VariusSoft Опубликовано 4 дня назад
Ответ на Комментарий от zmmx

newX &lt; 0 || newX + BULLET_SIZE &gt; LOGICAL_FIELD_SIZE

Если координаты изменяются от 0 включительно до LOGICAL_FIELD_SIZE включительно, то реальный размер поля LOGICAL_FIELD_SIZE+1. Короче в проверк...

Ошибка типичная, но здесь ошибки нет. Здесь чистая матаматика. Размер поля 208, то есть от 0 до 207. Размер патрона 4. Координата новой позиции пляшет от верхнего левого угла. Если координата, скажем, 204, то 204+4 = 208 – это и есть размер поля. Смещаемся ещё хоть на один пиксель, выходим за края поля.

1
+1 / -0
Войти

Вход

Регистрация

Я не помню пароль

Войти через Google
Порог горячего 13
  • Kukabara
    Kukabara

    понимание))

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

    потому я ваще щас не жалею, что вафли себе не заберу

    если б хотела - стащила б:...

    +1
  • Kukabara
    Kukabara

    толька давай не так прям груснааа!)

    а то звучит как суицид:D

    +1
  • Brainy
    Brainy

    Штото на богатом...

    +1
Правила сайта
Пользовательское соглашение
О ПД
Принципы самоуправления
Нашёл ошибку?
©2026 Varius Soft