Brainy
Опубликовано 2 дня назад
Читать дальше...
Было давно @Pepels сказал надо запилить сову!
Сам я Блендер еще не познал, поэтому чуть погуглив, нашел вполне себе неплохо делающие нейронки. Поэтому...
Цели сделать сразу хорошо не стояло, поэтому отправил я её на печать добавив подставку. Чего и следовало ожидать, зубы и ноги частично отлетели, страшные края у крыльев и всё вот это вот. Нужно ноги с подставкой сделать отдельно, увеличить размер модельки и можно приводить в красивый вид. Но это потом, когда я с Блендером разберусь немного.
Кому надо моделька тут. Архив 30.1 Мб, сама модель 75,5 Мб. У нее овермного вершин, но упрощается это легко.
Композитное изображение — комбинация данных нескольких телескопов. Данные в видимом диапазоне — от телескопа «Хаббл», а инфракрасные данные — от телескопа «Джеймс Уэбб» (жёлтые, серые и золотистые тона). Плюс данные в рентгеновском диапазоне — от телескопа «Чандра» (синий цвет) и радиоволны — от телескопа VLA (розовый).
Я тут вспомнил, что в прошлом посте забыл добавить планы на будущее. Планами было: добавить врагов.
Соответственно: точки спавна, их перемещение по карте, стрельба.
Давайте посмотрим на результат.
Появился новый класс, отвечающий за поведение врагов
export class EnemyAI {
constructor(tank) {
this.tank = tank;
// Таймер смены направления
this.changeDirectionTimer = 0;
this.changeDirectionInterval = 60; // ~2 сек при 30 FPS
// Cooldown стрельбы
this.shootCooldown = 0;
this.shootInterval = 30; // ~1 сек при 30 FPS
// Флаг застревания (для смены направления при коллизии)
this.wasBlocked = false;
}
randomDirection() {
const directions = [Direction.UP, Direction.RIGHT, Direction.DOWN, Direction.LEFT];
return directions[Math.floor(Math.random() * 4)];
}
update(allTanks, gameMap, bullets) {
const tank = this.tank;
if (tank.destroyed) return;
// === 1. Логика смены направления ===
this.changeDirectionTimer++;
// Смена направления по таймеру
if (this.changeDirectionTimer >= this.changeDirectionInterval) {
tank.direction = this.randomDirection();
this.changeDirectionTimer = 0;
// Рандомизируем интервал (40-80 кадров)
this.changeDirectionInterval = 40 + Math.floor(Math.random() * 40);
}
// === 2. Попытка движения ===
const prevX = tank.x;
const prevY = tank.y;
tank.move(tank.direction);
// Проверка: застрял ли танк (не сдвинулся)
const stuck = (tank.x === prevX && tank.y === prevY && !tank.moving);
if (stuck && !this.wasBlocked) {
// Танк только что застрял — меняем направление
tank.direction = this.randomDirection();
this.wasBlocked = true;
this.changeDirectionTimer = 0;
} else if (!stuck) {
this.wasBlocked = false;
}
// === 3. Автоматическая стрельба ===
this.shootCooldown--;
if (this.shootCooldown <= 0 && tank.canShoot()) {
const bullet = tank.shoot();
if (bullet) {
bullets.push(bullet);
// Рандомизируем cooldown (20-50 кадров)
this.shootCooldown = 20 + Math.floor(Math.random() * 30);
}
}
}
}
Он простой как три рубля, так что, думаю, можно обойтись без объяснений (тем более что комментариев в коде и так предостаточно).
К файле main появились коллекции врагов и их "мозгов"
/** @type {Array<Tank>} */
let enemies = [];
/** @type {Array<EnemyAI>} */
let enemyAIs = [];
Спавн врагов при старте игры
spawnEnemy(0, 0); // Левый угол
spawnEnemy(12 * 8, 0); // Центр (96px)
spawnEnemy(24 * 8 - 16, 0); // Правый угол (176px, учитываем размер танка)
// Пока спавню сразу все три, а не по очереди, как в оригинале
function spawnEnemy(x, y) {
const enemy = new Tank(x, y, TankType.ENEMY_BASIC);
enemy.direction = Direction.DOWN; // Враги всегда смотрят вниз
enemy.setMap(gameMap);
// Создаём AI для врага
const ai = new EnemyAI(enemy);
enemies.push(enemy);
enemyAIs.push(ai);
console.log(`Enemy spawned at (${x}, ${y})`);
}
И рисуем это
for (const enemy of enemies) {
if (!enemy.destroyed) {
enemy.render();
}
}
Там ещё есть обновление ссылок чтоб чистить из памяти уничтоженные танки, но в целом ничего больше интересного.
Оптимистичный план на следующий этап вот такой:
Жили-были. И всё у них было. И блины пеклись, и масло шкворчало, и Масленица во дворе плясала.
Да вот беда приключилася. Наша хлопотунья Лиса, главная по блинам да по праздникам, поскользнулась на маслице — и бух! Прямиком в тёмный подвал, где солёные огурцы в банках дозревают да картошка прошлогодняя дремлет.
Сидит Лиса, бедная, под провалом в подвал, лапой утирается, тонко подвывает:
— Ой, пропала я, касатики! Не видать мне больше белого света, не печь мне блинов румяных, не встречать весну красну! Век мне теперь в подвале куковать...
Услыхал тот плач первый блинодел. Прибежал, сковал ПЕРВОЕ ЗВЕНО, спустил цепь — тянет-потянет, а вытянуть не может. Да и что сказать? Не цепь, а всего одно звено!
Кликнул он соседа — второе звено сковали. Тянут-потянут — всё одно не достаёт. Кликнули бывалого блинопёка — третье звено приковали. Тянут-потянут — нет, не вытянуть!
И пошло-поехало! Кого только ни кликали: мастеров с печатями, песенников-скоморохов с блинами «жирненькими», стряпух с блинами безлактозными, заварными да разными, гостей заморских привечали, а еще того самого, кто сначала на печи лежал, бока отлёживал, а потом как подскочит - «Ща ворвусь»!
Много звеньев сковали — а цепи всё не хватает. Лиса ухватится, ее тянут, а звенья только растягиваются.
Кликнули Кирюху со стены. Но Кирюха сам маленький, кого он вытянет? Разве что для храбрости приклеился покрепче.
И тут поняли они: одной цепи мало. Нужна СЕТЬ! Чтобы не одной ниточкой тянуть, а всем миром сразу, со всех сторон!
И пошла плестись СЕТЬ СПАСЕНИЯ!
И стали они кликать всех-всех-всех: и тех, кто блины печет, и тех, кто только смотрит, и тех, кто в комментариях шутит, и тех, кто в пяти цепочках сразу бежит!
И когда цепь эта стала длинною в бесконечность, когда связала она всех воедино — от первого блинодела до последнего молчуна в комментариях, — дружно они крикнули:
— РАЗ! ДВА! ТРИ! ТЯНИ-И-И-И!!!
И — АЙ ДА МОЛОДЦЫ! АЙ ДА СПАСАТЕЛИ!
ВЫТЯНУЛИ ЛИСУ!
Вылетела она из подвала пулей, прижимая к груди банку со сгущенкой (стратегический запас, как-никак), отклеенного Кирюху, да Кошь за пазухой. Глаза сияют, хвост трубой, на морде — счастье пополам с блином.
— Спасибо вам, добрые люди! — раскланялась Лиса на все стороны. — Век вашей доброты не забуду! А теперь — ВСЕМ ЗА СТОЛ! БЛИНЫ ЕСТЬ! МАСЛЕНИЦУ ВСТРЕЧАТЬ!
И был пир на весь мир. И блины были — с икрой, со сметаной, с медом, с вареньем, с мясом, с творогом, с селедкой, да со всем, чего душа пожелает. И те, кто пек, и те, кто в цепочках стоял, и те, кто просто мимо проходил — все за одним столом собрались.
А Лиса, главная затейница, всем по блину подкладывала да приговаривала:
— Ешьте, родимые! Ешьте, спасители мои! Без вас бы я там, с огурцами, навеки осталась!
И тут слово молвил главный блинный летописец:
— А кто Лису спасал, кто блины пек да в цепочках стоял — всем носить отныне почётное звание «ВОТ БЛИН ПЕРВОЙ СТЕПЕНИ»! И мастер наш искусный уже за работу принялся — награды ладит, одна к одной, круглые да румяные, прямо как блины, только с душой и на вечную память!
Тут и сказке конец. А кто дочитал да блином закусил — тот и в цепочке нашей навеки остался. С Масленицей вас, люди добрые! 🥞🦊
На самом деле нет. Достаточно устойчивая штука. Тем летом катался на прокатном
Да. Ибо хуеты ебаной и без того достаточно вокруг
Если хорошее не нести - так и сдохнем все уныло
Вам доя какой толщины?