След
Graphuary
Хотела нарисовать весёлый реверсивный след... Верите, искренне старалась, други мои. Ну хоть так.
Всем спокойного выходного вечера!
Хотела нарисовать весёлый реверсивный след... Верите, искренне старалась, други мои. Ну хоть так.
Всем спокойного выходного вечера!
Всех продержавшихся - с преодолением экватора. Мы большие молодцы!
А мои новые маркеры оставляют хороший след, надеюсь их теперь хватит до финиша :)
Работы за вчерашний день:
И моё... Бесконечность
В прошлый раз мы нарисовали наш танк и научили его передвигаться. Теперь необходимо научить его врезаться в стены/воду.
Для этого создаём сущность коллизии и после инициализации уровня храним в памяти все коллизии и в момент попытки двигаться проверяем столкновение коллизий.
Это некая базовая версия физического движка. Нужно понимать логику: то, что игрок видит на экране не имеет никакого значения. Физика просто работает и говорит можно ехать дальше или нет. В какой-то мере это почти тоже самое, что происходит, например, в Unity. Но там система сложнее и она работает скорее подобно тому, как у меня сделано в тетрисе:
Здесь же я просчёт сделал по принципу экстраполяции (С Юнити тоже есть такой вариант у коллизий), когда просчёт пересечений происходит как бы с взглядом в будущее, просчёт потенциального пересечение ещё до того, как оно произошло.
Давайте же посмотрим, как это выглядит.
Коллизия – это не класс как таковой, это просто набор общедоступных методов.
/**
* Получить список субтайлов, покрываемых прямоугольником
*
* @returns {Array<{tx: number, ty: number}>} Массив координат субтайлов
*/
export function getTilesUnderEntity(x, y, width, height) {
const left = Math.floor(x / TILE_SIZE);
const right = Math.floor((x + width - 1) / TILE_SIZE);
const top = Math.floor(y / TILE_SIZE);
const bottom = Math.floor((y + height - 1) / TILE_SIZE);
const tiles = [];
for (let ty = top; ty <= bottom; ty++) {
for (let tx = left; tx <= right; tx++) {
// Проверяем границы сетки
if (tx >= 0 && tx < GRID_SIZE && ty >= 0 && ty < GRID_SIZE) {
tiles.push({ tx, ty });
}
}
}
return tiles;
}
/**
* Проверить, может ли танк войти на данный тайл
*
* @returns {boolean} true если танк может войти
*/
export function canTankEnter(tileDef, tank) {
if (!tileDef) return true;
// Вода: проходима только с бонусом "лодка"
if (tileDef.id === TileType.WATER) {
return tank && tank.hasBoat;
}
return !tileDef.blocksTank;
}
/**
* Проверить коллизию танка с картой при движении в новую позицию
* та самая экстраполяция
*
* @returns {boolean} true если коллизия есть (нельзя двигаться)
*/
export function checkTankMapCollision(tank, newX, newY, gameMap) {
// Получаем субтайлы под новой позицией танка
const tiles = getTilesUnderEntity(newX, newY, TANK_SIZE, TANK_SIZE);
// Проверяем каждый субтайл
for (const { tx, ty } of tiles) {
const tileId = gameMap.getTile(tx, ty);
const tileDef = TILE_DEFS[tileId];
if (!canTankEnter(tileDef, tank)) {
return true; // Есть пересечение
}
}
return false; // Нет пересечений
}
/**
* Проверить коллизию двух AABB (Axis-Aligned Bounding Box)
*
* @returns {boolean} true если боксы пересекаются
*/
export function checkAABBCollision(a, b) {
return (
a.x < b.x + b.width &&
a.x + a.width > b.x &&
a.y < b.y + b.height &&
a.y + a.height > b.y
);
}
/**
* Проверить коллизию двух танков
*
* @returns {boolean} true если есть коллизия с другим танком
*/
export function checkTankTankCollision(tank, newX, newY, tanks) {
const tankBox = {
x: newX,
y: newY,
width: TANK_SIZE,
height: TANK_SIZE
};
for (const other of tanks) {
// Пропускаем себя и уничтоженных
if (other === tank || other.destroyed) continue;
const otherBox = other.getBounds();
if (checkAABBCollision(tankBox, otherBox)) {
return true; // Коллизия с другим танком
}
}
return false;
}
А ещё с прошлого поста был проведён рефакторинг. У меня чесалось нёбо от неудобных некрасивых констант, с которыми были вездесущие сравнения. В целом, не магические числа – уже хорошо, но тем не менее. Енамки JS не поддерживает, поэтому делаем костыль через замороженные объекты.
// Было
export const TILE_EMPTY = 0;
export const TILE_BRICK = 1;
export const TILE_STEEL = 2;
export const TILE_WATER = 3;
export const TILE_FOREST = 4;
export const TILE_ICE = 5;
export const TILE_BASE = 6;
// Стало
export const TileType = Object.freeze({
EMPTY: 0,
BRICK: 1,
STEEL: 2,
WATER: 3,
FOREST: 4,
ICE: 5,
BASE: 6
});
Проверка самих коллизий происходит в методе передвижения танка
newX = Math.max(0, Math.min(newX, LOGICAL_FIELD_SIZE - TANK_SIZE));
newY = Math.max(0, Math.min(newY, LOGICAL_FIELD_SIZE - TANK_SIZE));
if (gameMap && checkTankMapCollision(this, newX, newY, gameMap)) {
return; // не применяем результат передвижения, просто выходим
}
Ну и в сам метод передаётся информация о карте в инпут менеджере
if (gp.buttons[12]?.pressed) player.move(Direction.UP, gameMap);
if (gp.buttons[13]?.pressed) player.move(Direction.DOWN, gameMap);
if (gp.buttons[14]?.pressed) player.move(Direction.LEFT, gameMap);
if (gp.buttons[15]?.pressed) player.move(Direction.RIGHT, gameMap);
А сам gameMap у нас и главного скрипта, а там он формируется как и раньше в
loadLevel(TEST_LEVEL_1);
Результат:
Далее по плану научить танк стрелять и, в идеале, отрабатывать попадания снарядов в стены.
Есть у нас в одной сети пекарен прикольные пирожные-корзиночки, а тут ебнуло мне, что можно ж самой такие попробовать сделать, чтоб не покупать их.
Купила, распробовала, вроде поняла, что должно быть в начинке и накатала себе список недостающего. Ну и как обычно - время 3 часа ночи, а значит, пора ебашить :D
(Ради любопытства полезла в галерею и убедилась, что по чистой случайности у меня многие кухонные пиздопляски происходят примерно в это время)
Корзинки там были с ладоху, у меня таких форм не оказалось, поэтому, сегодня они будут тарталетками :D
Итак...
Для теста:
- яйцо - 1 шт.
- мёд - 50 гр.
- сливочное масло (мягкое) - 60 гр.
- сахар - 40 гр.
- мука - 160 гр.
- разрыхлитель - 1 ч.л.
- ванилин по вкусу
Для творожного крема:
- творог 9% - 180 гр.
- яйцо - 1 шт.
- сгущёнка - 60 гр.
- мука - 20 гр.
- сахар - 20 гр.
- ванилин по вкусу.
В тех самых корзинках был ещё джем на дне (в каждой точке пекарни разный кладут) и ягоды в самом креме (раньше была вишня, сейчас чёрная смородина). У меня дома был черничный джем и чёрная смородина в морозилке. Взяла их.
Процесс лёгкий:
Смешать все ингредиенты для теста
Замесить в комочек
Брать по шарику и размазывать по формочке, чтоб с бортиками было
А потом смешать всё для крема
Сделать ВЖЖЖБЖЖББЖЖЖЖ блендером
Ну и всё, можно собирать одно в другое))
Поставила на 180° и ушла мыть посуду...
Так как в моём случае тарталетки были тонкие, за 15 минут всё уже было готово :)
Сделала вывод, что мне всё-таки нужны алюминиевые формочки для таких штук, ибо если выпекать в них, то корзинки будут более хрустящие, чем после силиконовых. И я бы разрыхлителя поменьше чуть добавила, чёт дохуя его как-то, имхо.
Закажу формочки и буду исправляться)
Поздравляю! Пыщ-пыщ-пыщ (это салют) 🎉
Водонапорная башня на плотинке, по легенде там экскурсии даже проводят, но сколько сыне пытались попасть вечно табличка - буду через 30 минут) я в детстве была разок, я вспомнила, там была какая то ти...
Ну вот, к скелетам и чучелам тоже не ходила ))