Эксперименты. Испарение
Давно, отмечал новый год (вроде-бы ещё 2021), остался стакан недопитого сока. 1 января в лом было убираться, так и простоял до 2-го. Тут я заметил, за полтора суток уровень сока заметно меньше стал, мне стало интересно, сколько же времени надо будет, чтобы полностью испарить целый стакан сока. Вот так и возникла идея этого эксперимента, почему так долго не сделал пост, будет понятно в процессе изложения. Мало того, начался он на одной квартире, а заканчивал уже на другой.
Итак, вот, собственно, этих красавцев будем испарять:
Справа налево:
1 - Вода фильтрованная (осмос)
2 - Дешёвый сок
3 - Сок средней ценовой категории
4 - Дорогой сок
5 - Газировка
6 - Кофе из натуральных зерен, без сахара.
Начинаем эксперимент.
Прошли первые сутки. Уровень начал понижаться
Прошли вторые сутки. Уровень заметно понизился.
Третьи сутки
Пятый день
В стакане с дорогим соком начала образовываться плесень:
Грибную ферму я разводить не собираюсь, буду топить, чтоб объем сока не изменился (эксперимент должен быть честным!)
Восьмой день
Плесень появилась и на дешевом соке, фотки не сохранилось.
Десятый день
Вот и до кофе плесень добралась, честно говоря думал что уж кофе-то точно не заплесневеет:
Четырнадцатый день эксперимента
Вода уже практически испарилась.
Семнадцатый день
Исторический. Именно в этот день началась СВО.
После этого вообще расхотелось что-либо делать дальше, но я решил всё-таки закончить, осталось совсем чуть чуть. Именно в этом главная причина, почему так долго не оформлял пост.
Восемнадцатый день
Воды осталось совсем чуток:
Девятнадцатый день
Вода испарилась полностью:
Но почему так много осадка, ведь вода фильтрованная. Я даже решился попробовать на вкус, ужасно горький!
Двадцатый день
Кофе тоже полностью испарилось
Двадцать второй - двадцать четвертый день:
День 22
День 24
Практически ничего не происходит, эксперимент можно считать завершенным.
Финальная гифка:
Ещё не всё. Не дал мне покоя тот факт, что в стакане с водой слишком много осадка получилось. Поэтому проведем дополнительное (контрольное) испарение:
В этот раз осадка гораздо меньше. На вкус скорее более соленый, горечи практически нет. И тут до меня дошло, это же не дистиллированная вода, хоть и обратным осмосом отфильтрована, всё равно там минералы остаются, а я почему-то решил, что стакан должен остаться стерильно чистым)) Как говориться, век живи, век учись!
Итак, можно подвести итоги эксперимента:
200 грамм воды полностью испаряется за 19 дней (конечно зависит от температуры в помещении, эксперимент проходил в феврале, а у нас, как известно зимой по квартире в трусах можно ходить, где-то 25 градусов было).
От стоимости сока зависит количество питательных веществ в нем, визуально видно, что в дешевом соке остатка меньше, чем в дорогом.
В воде, если долго стоит открытая емкость, из окружающей среды растворяются минералы.
Теперь вы обогащены очередными бесполезными знаниями, всем спасибо, всем пока!
Warhammer memes
Какой ересь он сотворил?!
Делаю игрулю на Playdate на чистом C. Глава 2
Я пишу игру на игровую консоль Playdate на чистом C. Игра в жанре "выживальщик" наподобие Vampire Survivors. Так как в чистом C отсутствуют многие современные объектно-ориентированные удобства мне приходится по-всякому изворачиваться чтобы адаптировать свои идеи в код. В этих заметках ты узнаешь из первых уст как создаётся игруля с нуля от идеи до публикации.
В прошлой главе я описал сеттинг, показал видео с тем что получилось после первой итерации (оно продублировано ниже), а также детально рассказал как я реализовал в коде свой динамический массив с нуля, потому что ничего подобного ни сишка, ни Playdate SDK мне не предоставляют из коробки. Если ты не читал прошлую главу, то лучше начать с неё.
Знаешь, в детективах есть такая особенность: убийца обязательно показывается в первые минуты. Не бывает такого, что убийца впервые появился под конец фильма или серии если это сериал, потому что так будет неинтересно зрителю. И я бы ни за что не поверил, если бы мне кто-то сказал, что в моём уже опубликованном коде есть баг, который я проглядел, и что этот баг в будущем обязательно выйдет мне боком.
Как ты помнишь логику игры я задумал вынести в класс Game. Да, это именно класс, ну по крайней мере в моей системе координат. А компилятор сишки, конечно же, вообще не в курсе что такое класс, но нас это не огорчает. У класса Game есть функции
GameCreate
GameSetup
GameUpdate
GameDestroy
Интерфейс класса Game выглядит вот так:
Содержимое файла Game.h
Обрати внимание на слово typedef на строке 12. Это слово придётся встречать в моём коде часто. Зачем оно тут нужно? Чтобы я мог использовать тип Game как отдельно-живущий полностью самостоятельный тип без постоянного указания слова struct перед ним. То есть, в сишке по-умолчанию если написать так:
struct MyStruct { ... };
а потом попробовать инстанс структуры MyStruct объявить вот так
MyStruct myStruct;
или попробовать MyStruct передать в функцию
void myFunction(const MyStruct *myStruct)
то мы получим ошибку компиляции. А всё потому что надо добавить слово struct перед названием типа:
struct MyStruct myStruct;
и
void myFunction(const struct MyStruct *myStruct)
Ты спросишь "нахуа?". А я отвечу "ну потому что вот так, потому что это сишка, детка". Если в сишке ты объявляешь структуру, то каждый инстанс с типом этой структуры должен иметь приписку что это структура чтобы не дай Боб компилятор не заподозрил что ты имеешь в виду что-то другое.
Полагаю, если ты шарпер или свифтер ты сидишь в афиге от этого, потому что и в C#, и во Свифте просто объявленая структура это самостоятельный тип без всяких приписок. Да, всё так, но C# и Свифт это современные языки, а сишка создавалась примерно за миллион лет до красной революции, а тогда тренды и привычки в разработке были совсем другие, и, в частности, типы данных struct и union были чем-то диковинным, потому при инстанциировании их нужно писать дополнительно слово struct и union соответственно. Время шло, struct и union из равноправных сущностей изменились в статусе: struct стал мегапопулярным (из него появились объекты в ООП), а union остался местечковой заморочкой. Примерно как USB-флэшки и компакт-диски: когда-то и те, и другие были плюс-минус равноправными способами передачи информации, но со временем мы пришли в точку повествования, где современное поколение не в курсе что такое компакт-диск. Да и я сам забыл когда использовал компакт-диски. А нет, вспомнил: когда у стоматолога делал снимок зубов. Я понятия не имею почему стоматологи снимки зубов принципиально скидывают на компакт-диски, а не на USB-флэшки. Но, возможно, чтобы это понять нужно получить медицинское образование вместе со способностью писать от руки непонятным почерком.
Так, со странностью описания структур в сишке понятно, но причём тут слово typedef? А это совершенно другая фича, которая в некотором виде существует во всех современных языках программирования: конструкция typedef existing new; это объявление алиаса примерно как
using new = existing; // в С++
typealias new = existing // в Swift
(как это делать в C# я забыл. Напиши в коментах если знаешь).
Вот только, как ты видишь, в конструкции typedef новое и старое значения переставлены местами. Почему? Потому что сишка, не задавай вопросы!
В итоге, конструкция типа
typedef struct {...} MyStruct;
это указание того, что я буду считать выражение MyStruct алиасом на struct {...} указанную в этой же строке. Иногда можно встретить вот такое:
typedef struct MyStruct {...} MyStruct;
Это то же самое, но с избыточным указанием имени в оригинальной структуре. Первый вариант не имеет имени, и по сути объявляет анонимную структуру (да, в сишке есть анонимные структуры!) и заводит на неё синоним. А второй вариант создаёт именованную структуру, которую можно из коробки использовать с припиской struct, но так как мы тут же объявляем на неё алиас с тем же именем, то struct можно опустить.
Думаешь "ну и дичь!"? А что если я тебе скажу, что в современном C++ эта особенность сохранилась, но только в виде необязательной фичи? Смотри: можно вот прям сегодня открыть твою любимую плюсовую IDE и написать не
std::vector<int> vec;
а
class std::vector<int> vec;
и оно скомпилится! Я когда впервые об этом узнал несколько лет назад моя реакция была примерно такой
Простой программист осознающий, что его жизнь поделилась на "до" и "после"
Что ж, с typedef'ом разобрались, фух! Давай приступим к сути: к функциям. Пойдём сначала прямо внутрь GameCreate.
Реализация функции GameCreate в файле Game.c
Задача GameCreate создать инстанс игры - это билдер-функция, аналог конструктора в C++. Инстанс мы создаём в куче - это позволяет не создавать инстанс на старте приложения, а отложить его создание до получения init-события от операционной системы Playdate. Там, конечно, разница во времени почти нулевая между стартом игры и получением init-события, но чисто технически эта разница существует, потому делаем так.
Единственный аргумент, который нужен для создания инстанса игры это указатель на PlaydateAPI. Без него мы не можем вызывать API у операционной системы Playdate. Указатель на PlaydateAPI это как контекст в Android'е - без него можно писать код конечно, но только сферический код в вакууме, а не реальный код, который нагло и невозбранно взаимодействует с API системы направо и налево.
Так как я имитирую ООП, то некоторые ООП-шные вещи мне следует реализовывать на ручной тяге. В частности, инициализация всех полей структуры. То есть, я дал себе слово, что create-функции у меня должны обязательно инициализовать все поля создаваемой структуры как это сделано в Свифте из коробки, например. Почему это так важно? Потому что если ты создаёшь инстанс чего либо в куче, то гарантию того, что в поезде выделенных байтов не будет мусора, никто тебе не даст. Выделять память в куче это примерно как сесть покушать за стол на фуд-корте в торговом центре. Стол может оказаться кристально чистым, а может быть невероятно грязным словно на нём только что ела семья потомственных свиней.
На строке 16 происходит как раз выделение памяти для инстанса Game, который функция вернёт в конце. Далее мы заполняем все поля ничего не пропуская.
Указатель на PlaydateAPI мы прихранили в игре на строке 17 - это важно. Далее, мы создаём инстанс машины - той самой, на которой катается игрок и расстреливает животных (смотри видео). Кстати, про машину - это отдельный класс, инстанс которого хранится в игре в единственном экземпляре, так как машина только одна. Создаётся инстанс машины тоже в куче потому что в начале я думал, что инстансы всего буду создавать в куче. Спойлер: уже буквально после машины я передумал, так как достаточно хранить всё что нужно в виде значений как есть внутри игры как часть игры. Но с машиной пока так.
Описание класса PlayerVehicle в файле PlayerVehicle.h
Машина имеет совсем мало полей: ей много и не надо. Это:
позиция в формате Vec2f (вектор имеющий поля x и y в формате float) - строка 9
направление angle, один из восьми вариантов направлений машины. Это перечисление (enum), объявление которого я покажу очень скоро - строка 10
булевое значение isMoving, которое равно 0 если машина стоит, и 1 если машина движется. Так как типа bool в сишке нет мы нагло используем int - строка 11
значение ускорения, которое нужно для реализации физики движения в формате float - строка 12
И ещё две функции: конструктор и деструктор (строки 15 и 16 соответственно). Как и API массива эти функции принимают указатель на функцию realloc так как эту функцию нам предоставляет PlaydateAPI.
Теперь покажу как выглядит PlayerVehicleAngle:
Описание перечисления PlayerVehicleAngle
Как ты помнишь из первой главы, в значения перечислений я вставляю имена этих перечислений в качестве префикса чтобы не было пересечений имён так как в сишке все значения перечислений доступны в глобальном пространстве имён. На Свифте я бы всё сделал в разы проще:
Как бы выглядел PlayerVehicleAngle если бы проект писался на Свифте где-то в альтернативной вселенной
Ну или на C#:
Как бы выглядело описание PlayerVehicleAngle если бы проект писался на C# в другой альтернативной вселенной
Как бы выглядело использование PlayerVehicleAngle если бы проект писался на C#
И как же без С++:
Как бы выглядело описание PlayerVehicleAngle если бы проект писался на C++ (где-то упала одна моя скупая мужская слеза) в ещё одной альтернативной вселенной
С направлением всё понятно, полагаю, как и с тем, как я описываю перечисления. Теперь вернёмся к машине, то есть, ко классу PlayerVehicle. Его объявление я тебе показал, давай теперь покажу реализацию.
Реализация класса PlayerVehicle
Как видишь, всё очень просто - создаём инстанс также в куче, присваиваем все без исключения поля и возвращаем что получилось. Это я говорю про конструктор - функцию PlayerVehicleCreate. А деструктор, PlayerVehicleDestroy, просто чистит выделенную под машину память.
Возвращаемся в игру туда, откуда мы ушли: в GameCreate, конструктор игры. На строке 19 я присваиваю cameraOffset - это смещение камеры, которое нужно для отрисовки позиции всего. Об этом подробнее позже. Далее я присваиваю NULL в поля cactusImage и sandImage. Это вот как раз то, о чём я говорил: это важно сделать, потому что иначе эти значения будут иметь мусор, а так как их тип это указатель (LCDBitmap *cactusImage), то без инициализации нулём на старте эти указатели будут указывать чёрт знает куда, и если я вдруг решу обратиться по этому адресу, то моя программа пойдёт по другому известному адресу, то есть, упадёт, крашнется или словит segfault (или 'сегфолт' по-русски если верить словарю Даля). Этого мы не хотим, потому что не для этого американцы создавали эту маленькую жёлтую консоль, а для бесконечного фана.
Далее прошу обратить внимание на три строки: 22, 23 и 24. В этих строках мы инициализируем наши динамические массивы. Первый это кактусы (cactuses), второй - песчаные горочки (просто sands), третий - перекати-поля (tumbleweeds). Если у тебя после прочтения первой главы остался вопрос как инициализировать массив, так скрупулёзно созданный мной байтик за байтиком, то вот это как раз тот самый пример.
Далее строку 25 давай пока пропустим - потом всё объясню. А вот после у нас идёт заполнение массивов кактусов и песков (буду говорить так вместо "песчаных горочек" , российский усатый политик тут ни при чём). Самые главные строчки это 40 и 43. В них мы непосредственно добавляем свежесозданный кактус либо песок в соответствующий массив вызывая уже известную из первой главы функцию ArrayPushBack.
Как именно заполняется карта игры песком и кактусами и что за такие RangeCreate (строки 31 и 33) и RandomIntInRange (строка 37)? Range это невероятно удобный вспомогательный класс, который я создал подглядев идею в стандартной библиотеке Свифта. Range(x, y) это диапазон значений по аналогии как в математике указывается [x, y), что означает диапазон от числа x включая x (об этом свидетельствует квадратная скобка) и до числа y НЕ включая y (об этом свидетельствует круглая скобка).
Содержимое файла Range.h
Содержимое файла Range.c
А ещё я решил прикрутить генератор случайных чисел прям к диапазону: если у тебя есть диапазон, скажем, от нуля включительно до пятидесяти невключительно, то можно вызвать функцию RandomIntInRange и передать Range, а в ответ вернётся случайное число в указанном диапазоне. Мне показалось это в разы удобнее всех тех миллионов функций для генерации случайных чисел, которыми забит сайт StackOverflow.
Теперь давай я расскажу как же я заполняю игровое поле кактусами, песком, и почему в этом коде нет перекати-поле. Самый тупой вариант это нагеренить N объектов (кактусов и песков) со случайными координатами x и y в пределах размера поля (размер поля, кстати, от -1000 до 1000, то есть, 2000). Но так делать не надо потому что с таким подходом чисто теоретически может получиться, что на достаточно большом куске поля (например, размером с экран, то есть 400 на 240) не выпадет ни одного объекта. И в таком случае поле будет выглядеть пустым и будет казаться, что машина едет по белому листу в MS Paint'е. Такое нам не нужно. Вместо этого нужно чтобы игрок регулярно видел реквизит, напоминающий о том, что мы едем именно по пустыне. Можно, конечно, повтыкать через одно и то же расстояние последовательно кактусы и пески как будто их вкопали солдаты квадратно-гнездовым методом, но это будет выглядеть обсосно и без души. Важно найти золотую середину - использовать рандом, но укротить его. Потому алгоритм я выбрал такой: я делю карту на прямоугольные блоки 150 на 100 (строка 27)
const Vec2i blockSize = Vec2iCreate(75 * 2, 50 * 2);
и в каждый блок в случайную точку этого блока ставлю один объект: либо кактус, либо песок. Поставить ли кактус или песок я определяю так же рандомом на строке 37:
const int isCactus = RandomIntInRange(RangeCreate(0, 2));
И если isCactus равна 1, значит ставим кактус, иначе - песок.
Вообще, конечно, можно попробовать ради простого теста сгенерить столько же кактусов и песков с абсолютно рандомной позицией без всяких ограничений в виде блоков. Давай вот прям щас и попробуем это сделать. Смотри: всего кактусов/песков на карте создаётся
ceil(2000 / 150) * (2000 / 100)
ceil это функция округления вверх. В математике она обозначается как квадратные скобки без нижних пип (у квадратных скобок есть верхние и нижние горизонтальные пипы, вот если нижние убрать, то получится округление вверх). Почему я добавил эту функцию сюда - потому что деление 2000 на 150 не даст целое число, а если нам нужно точное значение, то надо решить что делаем с остатком. Исходя из условия нашего цикла на строке 28
for (float xMin = -1000; xMin <= 1000; xMin += blockSize.x + 20) {
остаток следует учитывать. Упс, я только что заметил, что есть ещё +20 на той же строке 28 - это я делаю пробел между блоками для пущей правдоподобности чтобы не было двух кактусов приклеенных друг к другу. Хм, тогда пересчитываем всё. По оси x я делаю отступ 20, по оси y - 15. Значит, всего кактусов/песков на карте создаётся
ceil(2000 / (150 + 20)) * ceil(2000 / (100 + 15)
это упрощается в
12 * 18 = 216
Значит, всего 216 объектов. Теперь давай перепишем генерацию кактусов/песков на рандомную позицию от -1000 до 1000 и посмотрим что получится.
Изменённый алгоритм генерации кактусов и песка
В целом такой вариант тоже выглядит неплохо, однако бывают большие участки вообще без ничего. А один из тысячи вариантов генерации точно сотворит такое, что будет огромная дыра, а такого мне не надо. Так что поигрались и хватит - возвращаем старый алгоритм и идём дальше.
Кстати, возможно логичнее было бы положить создание кактусов в GameSetup, а не GameCreate. Ну да ладно - как сделали так сделали.
На этом с функцией GameCreate мы закончили. Далее идёт GameSetup - небольшая функция, которая стартует игру. Как я говорил, она по логике похожа на GameCreate потому что тоже вызывается единожды и тоже до всех обновлений (тиков) игры, но строго после GameCreate.
Реализация функции GameSetup
Тут кода мало: мы сначала вызываем функцию с забавным названием srand, которая инициализирует генератор случайных чисел. Можно её и не вызывать, но тогда на пятидесятую игру ты начнёшь замечать, что все случайности в игре (например, позиции кактусов) ничуть не случайны. Нам это не нужно. Далее мы вызываем GamePreloadImages для загрузки картинок (помнишь, мы все картинки проинициализировали как NULL в GameCreate?). После этого мы устанавливаем значение в cameraOffset равным половине экрана - так надо. Давай я подробнее расскажу про загрузку картинок - там есть интересные вещи.
Исходный код функции GamePreloadImages
Все картинки у меня лежат в папке images в проекте. Кадры машины, которых 8 штук по одному для каждого направления, имеют гениальные названия: 1.png, 2.png и так далее до восьми. Для загрузки одной картинки мне нужно вызвать функцию loadImageAtPath, которая принимает путь к картинке в пределах проекта (images/1.png, images/2.png и так далее) и необходимый указатель на PlaydateAPI.
Реализация функции loadImageAtPath
(LCDBitmap это тип из Playdate SDK, который означает картинку) Я честно позаимствовал эту функцию в примере игры на сишке у самого Playdate, так что тут ничего особенного нет. А вот то, как я загружаю, это следует понять. Как вообще загрузить 8 картинок с последовательными названиями? Конечно же, можно сделать просто 8 строк типа такого:
Пример того, как делать не стоит
Такой подход в простонародье называют китайский код. Конечно же, я делаю не так, а реализовал цикл. Имя картинки в цикле можно сгенерить зная индекс итерации. И в языках высокого уровня использовалась бы функция форматирования строки. Вот как я бы такое сделал на Свифте:
Потенциальная реализация загрузки картинок на Свифте
В коде на Свифте важно обратить внимание на генерацию полного пути к файлу картинки. Конструкция покрашенная красным создаёт строку вставляя в неё значение переменной-итератора i, которое меняется от 1 до 8 включительно. В сишке, к большому-пребольшому сожалению, нет простого API для подобной генерации динамических строк. Да, есть функция sprintf, которая позволяет вытворять что-то похожее, однако она не заботится о размере строки потому что это наша забота, а такое нам не нужно. Потому этой функцией я тоже не пользуюсь. Вместо этого я в своём сишном коде создал один раз шаблон пути к файлу "images/0.png" (строка 205) и в каждой итерации просто подменяю одну буковку, точнее, циферку перед точкой (строка 207):
imagePath[7] = '0' + i;
Что же тут такого особенного? А то, что в варианте на Свифте (или на любом другом языке высокого уровня в том числе С++ с той же либой fmt) каждую итерацию будет выделяться и чиститься по одной динамической строке, а у нас в сишке выделяется 0 динамических строк, есть только одна статическая (потому что нам заранее известен её размер), а мы аккуратно скальпелем меняем в ней один байтик и вуаля - никаких строк не нужно генерить. Не нужно использовать хитрый форматер, я сам себе форматер!
Зачем я так делаю? Прекрасный вопрос, спасибо. Я это делаю точно не для того чтобы уменьшить нагрузку на процессор консоли потому что это получится экономия на спичках. Я так делаю лишь потому что это сделать очень просто в сишке и ещё потому что в сишке нет нормальной либы для форматирования динамических строк.
Кстати, подробнее про строку в коде imagePath[7] = '0' + i;. Тут происходит магия ascii-символов. Ноль в одинарных кавычках это литерал одиночного символа равный нулю, как строка, но только один её элемент. Если строка это поезд, то ноль в одинарных кавычках это один вагон. Если к нему прибавить целое число, то символ станет другим. Если прибавить один, то символ изменится на один вперёд в таблице ascii. А в таблице ascii цифры стоят последовательно, к счастью. То есть, если к нулю я прибавлю 1, то получу символ единицы. Если бы в таблице ascii цифры были бы в беспорядке, то данный фокус бы не сработал, и задачу пришлось бы решать либо форматтером, либо китайским кодом.
Фух. Как самочувствие? Не устал ещё? Мы прошли хороший путь: разобрали GameCreate, GameSetup, а сейчас я хочу завершить эту главу функцией, которой завершается игра - GameDestroy.
Реализация функции GameDestroy
Тут всё суперпросто: мы уничтожаем объект машины (строка 186) так как он создавался в куче как и массивы, потом уничтожаем все три массива, которые аналогично последовательно создавали внутри GameCreate (кактусы, пески и перекати-поле), а потом уничтожаем саму игру.
Кстати, в самом начале функции я проверяю game на NULL (строка 182), и если это так, то просто выхожу из функции. Я сначала думал, что это логично и позволяет писать безопасный код. В частности, на С++ я так делаю всегда. Однако на сишке в отличие от С++ почти всё передаётся через указатели, и ты просто запаришься проверять всё на NULL (например, представь если везде проверять указатель PlaydateAPI на NULL). Потому это был первый и последний раз когда я осуществил такую проверку - далее я просто решил для себя, что везде где ожидаются ненулевые данные я просто буду верить себе на слово, что там не NULL.
Итого в первый день я создал машину и поле, а во второй - кактусы и песчаные насыпи.
А что же с функцией GameUpdate? GameUpdate это самая важная функция в моём коде на данный момент потому что она в отличие от всех прочих функций класса Game вызывается стабильно каждый тик примерно так же, как сердце гоняет кровь туда и обратно по организму каждую секунду. Без этой функции игры не будет. Но разберём эту функцию мы уже в следующей главе.
UPD:
К вопросу о проклятых тапках...
Котеечки мои вы в очередной раз меня пугаете!
Ну вы чего в самом-то деле то? Мне же вредно вообще!
🫣🙃
Говорят чтобы понять человека нужно просто на некоторое время поставить себя на его место.
На мой без сомнения субъективный взгляд если человек с которым Вы сошлись, начинает неадекватно себя вести, придавая старым вещам ценность сопоставимую с вашим настроением...
... то дело не в тапках или любых иных вещах - цена которым пятьдесят копеек на озоне, они совершенно точно не могут быть важнее человека.
Нет, котеечки мои, поблема в памяти!
Эти самые пыльные тапки,
... замаранный кусочек обоев оставшийся от ремонта и вывалившийся сейчас непонятно откуда,
... или старый, выцветший от времени чек.
Для Вас являются кусочком вполне объективной реальности (то есть ценность мусора)
Для человека же это ключик возвращающий его в прошлое!
Ключики, они бывают разные!
И конечно, быть может в той, ныне уже прошедшей, жизни было много по настоящему плохого!
... но ведь было и хорошее, иначе бы никаких отношений никогда и не было 🙃
И вот он прижимая к груди эти пыльные тапки буквально вынужден переживать куски прошлого ...
... вновь и вновь.
И для Его мозга все это вполне реально, вплоть до того что человека может натурально колотить, ибо организм начинает вырабатывать ряд специфических веществ как при сильнейшем стрессе.
Конечно же тапки (как и любую иную вещь) можно выбросить, но вот память, это не файл в облаке, и не папочка на стареньком HDD, она привязана к головному мозгу.
У здорового человека этот самый мозг прекрасно справляется с хранением огромных массивов информации и ничего не забывает для глубокой старости.
Что по вашему мнению человек должен сделать. Удалять? 🙃
Потому как чтобы что забыть - нужно оказать на него влияние нанеся травму.
Например наркошам раньше в голове делали отверстие и совали туда спицу.
... операция была крайне сомнительной и сейчас подобное вроде бы уже запретили.
Более того как уже писал - отказ от вещи не принесёт никакой пользы, по той же самой причине, это ключик да, но
... эта вещь лишь ОДНА ИЗ многих выдающих к воспоминаниям.
Хотя конечно некоторые выбрасывают все вещи, переезжют в другую квартиру, а то и меняют город,
... не осуждаю подобное, но на мой взгляд это попытка сбежать от собственной тени.
Да и наш мир, он ведь уже давно не состоит из чего-то уникального:
Вот например выбрали вы пальтишко и оно Вам идет просто несказанно😇
А у Вашей половинки отчего-то мордашка ...
... потому как бывшая любовь Вашего человека будучи облаченной в пальто того же покроя или расцветки, ласково и нежно объясняла Вашей любимки что все пересосанные члены его друзей
(или соответственно перелизанные кисоньки её подруг)
... исключительно его вина, потому что он - такой сякой (она такая сякая) слишком много времени уделял работе (сидела с ребенком), а хуёв (кисунь) очень хотелось.
А после таких вот эпизодов насколько бы сильной и совершено не была нервная система человека, осадочек то всё одно останется.
Что делать в подобных ситуациях?
Решать естественно Вам и только Вам, правда в основном здесь отчего-то предлагают в основном два варианта:
... оскандалиться,
... уйти.
Так что я позволю себе побыть оригинальным и предложу нечто иное - вытеснить кусок памяти обнимашками и уже СВОЕЙ любовью!
Потому как любой нормальный и здоровый человек хочет любви и ласки, соответственно он или она запомнят.
Как сжечь жир на животе: фитнес басни о локальном жиросжигании
Всем привет!
На выходных вышел пост «Почему жир на животе не уходит?!» и породил бурю комментариев, в которых я обещал чуть глубже копнуть тему локального жиросжигания (ЛЖС).
Даже если вы не в курсе, что произошло, не проходите мимо – подчерпнете для себя что-нибудь полезное. Например, сможете доминировать над знакомыми зожниками :D
Авторский мем)
Предисловие
ЛЖС – это такая мечта, в которой вы качаете пресс по десять минут три раза в неделю, потом подходите к зеркалу, задираете футболку, а под ней КУБИКИ.
Или присели в зале три по пятнадцать, а под ягодицами уже сечка прорисовалась.
Фитнес с зари своего становления эксплуатирует эти фантазии. До сих пор регулярно вижу сторис типа: «Делай эти упражнения и жир уйдет за три дня».
Главное, о чем нужно сказать – как клиенты, для индустрии мы делимся на категории. И для каждой существует свой подход.
Новички – можно показывать видео и вешать лапшу на уши
Бывалые – уже раскусили развод, можно говорить, что локально жир не горит, а чтобы убрать пузо нужно менять питание. Вы находитесь здесь.
Предлагаю всех переиграть и разобраться, как ведет себя жир в организме в зависимости от подбора упражнений. Заодно узнаете, существует ли локальное жиросжигание, и есть ли от него польза.
Питание и дефицит калорий оставляю за скобками поста, сегодня концентрируемся на другом.
Поможет нам во всем этом исследование «Упражнения на аэробную выносливость брюшного пресса показывают, что существует точечное уменьшение процента жира»
Кстати, после прочтения статьи вы перейдете в секретную категорию, какую – запаситесь терпением)
Обзор исследования
Тут нудно, почувствуете, что клюете носом, переходите сразу в результаты.
Ученые отобрали шестнадцать мужчин старше 30 лет с ИМТ >25 и случайно поделили их на две группы. Все испытуемые на момент исследования не занимались спортом.
ИМТ – это специальный индекс, который показывает в норме ваш вес, или есть проблемы. В значениях от 25 до 30 указывает на лишний вес, но еще не ожирение.
Первая группа была контрольной (КГ) и 4 раза в неделю бегала на дорожке по 45 минут за сессию.
Вторая группа была фокусной (ФГ) и 4 раза в неделю сначала бегала на дорожке 27 минут, а потом выполняла упражнения на пресс.
Протокол мудреный: 32 минуты поворотов корпуса интервалами по 4 минуты в каждую сторону, затем пятиминутный отдых и 4 интервала по 4 минуты скручиваний с активным отдыхом еще по три минуты. В итоге вся тренировочная сессия занимала 84 минуты.
Грубо говоря, первые бегали, а вторые бегали и жарили упражнения на пресс. Причем, не жалкие 20 минут в неделю, как обычно, а жуткие 4 часа.
Важно! Итоговые затраты энергии на тренировках в каждой группе были равны.
Исследование длилось 10 недель, участников попросили не вносить изменений в свое питание и не добавлять тренировочную активность.
Результаты мерили денситометрией (DEXA) – это супер понтовый метод, в том числе показывающий процент жира в разных участках тела.
Результаты
Жир ушел в обоих группах. В контрольной распределение равномерное – похудели руки, ноги и туловище.
А вот в фокусной мало того, что общего жира ушло больше, так еще и конкретно в туловище наблюдалось значительное снижение (в среднем 1.2 кило).
Картиночка от авторов
Плюс, кто только бегал еще и немного мышц потерял, неприятно.
Выводы и ложка дегтя
Прогнал то, что пишут авторы через переводчик:
Аэробные упражнения на выносливость брюшного сегмента тела приводят к увеличению высвобождения и утилизации жировой ткани, накопленной рядом с работающими мышцами, а не к общему высвобождению жира из всего тела.
Простыми словами жир горит рядом с работающими мышцами. Можно сказать: как физическое явление локальное жиросжигание существует.
Круто? Я считаю, более чем.
Если подумать, вопрос же сам собой напрашивается: когда работает нога, зачем ей гнать жир с руки?
Затем авторы пишут, что не поняли почему так происходит – жир с живота расходуется как топливо уже на тренировке, или после нее при восстановлении?
Обсуждают несколько гипотез, но там очень скучно, липолиз триглицеридов, повышенная перфузия, концентрация катехоламинов в плазме – кто захочет, сам прочитает, ссылку я дал.
Конечно, к самому исследованию есть вопросы:
Фокус-группа была в целом жирнее контрольной, могло это повлиять на результаты?
Можно ли перенести итоги на людей с ИМТ <25, без лишнего веса?
В исследовании не было девушек, будут ли у них схожие результаты?
И самый главный: как все это на практике применять?
Практические советы
1. Предположим, у вас есть лишний вес и желание убрать живот. Какой из вариантов выберете:
Легкие тренировки меньше часа
Интенсивные мучения 90 минут
Помните, что процент жира снизится в любом случае, а визуально разница будет незначительна.
Каждый отвечает за себя сам, а я могу только поделиться практикой. Решающим фактором, добьетесь ли вы успеха, станет ваша приверженность выбранному пути.
Чем больше вы на себя взвалите, тем выше вероятность задолбаться и все бросить.
Поэтому – выбирайте низкую интенсивность и не парьтесь. Можно хоть каждый день вставать на дорожку/эллипс/вело и выполнять план, не перегрузитесь.
Понимаю, что хочется быстрее, а вот так не хочется?
Адам Сэндлер, Клик: с пультом по жизни
2. Вспоминайте, я специально не затрагивал вопросы питания. Цель статьи не понять, как лучше худеть, а набраться знаний.
Вот есть люди, которые с порога заявляют: «Буду худеть, питание менять не буду». Что им делать?
Так что никого не осуждайте, не спорьте – все равно не переубедите – а пробуйте разные подходы и делитесь опытом.
Да, такой совет. Моя статья, что хочу, то и пишу ;D
3. Я как-то собрал базу упражнений на пресс, нашлось аж 63 штуки. Можно миллион программ составить.
Но они подойдут только тренированным людям, опытным и выносливым.
Хотите себя проверить, попробуйте хотя бы две минуты скручиваний выдержать, я уж не говорю о протоколе из исследования, почти на час работы.
Так что реальную пользу из феномена локального жиросжигания могут выжать те, кто без него может и обойтись.
Это как вишенка на торте – сделали хорошую форму, и для финальной проработки нужных частей прошли программу. Бодибилдеры, фитнес-бикини и блогеры хлопают в ладоши.
Послесловие
Пришло время раскрыть секретную категорию, в которой вы как клиент фитнеса теперь находитесь. Поздравляю, теперь вы «Опытный».
Вам снова можно рассказывать о похудении с помощью упражнений, показывать исследования и подбирать программы тренировок.
Шучу, конечно, но на самом деле так приобретается любой опыт. В конце мы оказываемся там, с чего начинали, с новым уровнем понимания.
Всем, кто прочитал до конца – респект. Вообще всем – крутой формы.
Особая благодарность @100mean и @Deniskatlt за подписку, без вас я бы хрен чего написал – на свой сайт за 5 месяцев добавил всего 3 статьи, вот такая я ленивая жопа (кому интересно, зайдите в профиль).
Джоконда без улыбки
источник: про мимические мышцы и визуальный резонанс Джоконды...