Бессонный кодер
5.18K subscribers
226 photos
42 videos
4 files
87 links
Канал разработчика и поисковика ДПСО LizaAlert

Связь: @sleeplessfeedbackbot
По рекламе: @dpp_ads
Download Telegram
А вы знали, что долгое время во время разработки Элли вообще не должна была участвовать в боях?
Она оставалась в тени как неигровой персонаж, даже Эшли (актриса, сыгравшая Элли) была разочарованна этому. И с процессом разработки это стало расхождением геймплея и историю, которую хотели рассказать.
Тогда Нил Дакман решил что Элли должна быть таким же соучастником как Джоэл.

Когда Naughty Dog впервые ввели Элли в геймплей, всё выглядело логично.
Она была ребёнком рядом с Джоэлом, значит, должна была:
повторять действия игрока
получать ранения
просить лечиться
мешать в перестрелке — иначе не реалистично

Но на практике это стало кошмаром: Элли раздражала. Мешала. Была грузом.
Игрок ненавидел её, несмотря на то что сюжет говорил обратное.

Разработчики стояли перед дилеммой:
реализм — или привязанность?

И приняли нестандартное решение:
Элли стала «призраком».
Она незаметна для врагов. Не мешает. Почти бессмертна.

С точки зрения геймдизайна это — компромисс.
С точки зрения психологии — гениальный ход:
Игрок перестаёт злиться, а значит может эмоционально привязаться.
И тогда, в нужный момент, доверит ей жизнь — и винтовку.

Так была создана легендарная снайперская сцена, где Элли становится настоящим напарником.
Никто не раздражён. Все — вовлечены.

Идеальный союз механики и драмы.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Порой меня спрашивают, какие баги в разработке максимально бывают мемные и глупые. Именно с таким багом я столкнулся и решил вам рассказать.

Для того чтобы отвечать вам за 30 миллисекунд, Имперский стражник кеширует некоторые данные в своей памяти, среди таких: базовые данные о пользователях и настройки чатов. И вот именно с кешированием чатов началась проблема. В последние пару дней вы могли заметить что после того как поменяли настройку чата, она применялась далеко не сразу.

Я начал искать почему это происходит. Для этого надо окунуться в логику настроек:
/**
* Обновляет значение параметра
*/
async updateSetting(ctx, param, value) {
const chat = await Database.instance.getChat(ctx.scene.state.chat);
const transformedValue = param.transform(value);

await Database.instance.updateChat(ctx.scene.state.chat, {
[param.id]: transformedValue
});
await this.saveAudit(ctx, param.id, transformedValue);
}

Вроде всё верно... А что в базе?
async updateChat(chat, data) {
const client = await this._pool.connect();
/** логика SQL запроса */
client.release();
await this._chatsCache.delete(chat);
}

И тут всё верно, в базе обновляется значение, после чего удаляется из кеша.
Я провёл эксперимент, обновил параметр и посмотрел в базу, значение новое и верное.

Убедившись в том что эта часть кода верна, я решил проверить генерацию сообщений настроек, вдруг там была ошибка, но нет, и там всё было верно, на этот момент баг был в процессе поиска уже второй день.

Я решил изучить кэши, обновил параметр, запросил чат из кэша и... А чат не обновился в кэше других инстансов.
Тут надо пояснить что за инстансы такие. Пока вы видите одного цельного Стражника, на самом деле он состоит из различных сервисов которые занимаются разными задачами, некоторые обрабатывают входящие события, некоторые отвечают для вас в вебаппах, некоторые проверяют стикеры. Так вот, тот сервис который обработал изменение настроек обновил чат у себя в кэше, но другие сервисы этого оркестра обновление не получили...

А кто в Стражнике отвечает за кэш? Правильно, я, вернее мой модуль rgcache. Давайте изучать проблему...
В библиотеке на текущий момент есть 3 вида кэша.
MemoryCache - хранит данные в оперативной памяти, работает полностью локально.
RedisCache - хранит данные в Redis, локально ничего не хранит и при каждом запросе делает запрос в Redis.
И их комбинация RedisSyncedMemoryCache - хранит данные в оперативной памяти, но если идёт инвалидация значения (оно обновилось), то через Redis посылает всем другим рабочим кешам задачу удалить у себя это значение.

Для хранения чатов как раз использовался RedisSyncedMemoryCache
Давайте посмотрим как он удаляет значения (для вас расписал построчно комментарии):
async delete(key) {
let entry = this._data.get(key); // Ищем значение локально
if (entry) { // Если значение найдено, то...
await this._options.preDestroy(key, entry.value); // Вызываем хук на удаление значения
this._data.delete(key); // Удаляем его
}
await this._pub.publish(this._name + ":invalidate", key); // Отправляет через Redis команду для всех остальных кешей инвалидировать это значение.
}

Хорошо, а как идёт инвалидация?
async _handleInvalidation(key) {
let entry = this._data.get(key); // Ищем значение локально
if (entry) { // Если значение найдено, то...
await this._options.preDestroy(key, entry.value); // Вызываем хук на удаление значения
this._data.delete(key); // Удаляем его
}
}

Вроде всё правильно, ошибок нет... А ПОТОМ СПУСТЯ 5 МИНУТ ПОДУМАТЬ Я ОСОЗНАЛ.

В качестве ключа чата я использую id чата, это отрицательное 64-битное число, число, ЧИСЛО. Так что происходило? Число становилось строкой! Ведь Redis при передаче данных через себя всё кастит в строку. И получалось что другие инстансы получали запрос на удаление строки... храня только числа. Вот так один тайпкаст в библиотеке заставил меня 2 дня перекапывать исходники стражника.

А увидеть код библиотеки и фикс вы можете даже сами, вот так, раз ошибся и ты ошибся.
https://github.com/RedGuys/rgcache/commit/ef610d4e6436a6b492e49b2c61a2cd8222ab2885
Please open Telegram to view this post
VIEW IN TELEGRAM
Визуальный стиль тлоу не просто красивый, но и инженерно продуманный, с кучей решений, которые незаметно управляют вашим вниманием и эмоциями.

☀️ Освещение: свет, который ведёт
Игровые сцены кажутся живыми благодаря технологии глобального освещения (Global Illumination). Это когда свет не просто падает с лампочки или солнца, а отражается от всех поверхностей — стен, пола, мебели — и мягко подсвечивает всё вокруг.

Чтобы сделать это реалистично и при этом не перегрузить консоль, разработчики применили:
Запечённый свет (Baked GI) — заранее рассчитанный и "записанный" свет в статичных частях уровня (например, в стенах). Работает как фоновое освещение.
Light Probes — специальные «точки в воздухе», которые хранят информацию о том, как свет должен выглядеть в этом месте. Персонажи как бы «ощущают» освещение, находясь рядом с этими точками.
Динамическое освещение — например, от фонарика. Этот свет рассчитывается прямо во время игры, он может двигаться, давать тени и взаимодействовать с окружением.

📷 Эффекты камеры, которые мы обычно не замечаем, тоже работают на эмоциональное восприятие:
Размытие движения (Motion Blur): сглаживает рывки, делает движение более плавным.
Глубина резкости (Depth of Field): фокус на главном, фон — в мягком размытии, как в кино. Помогает выделить важное.

Небольшие искажения цвета в катсценах добавляют ощущение реалистичности, будто это снято на старую камеру.

🧱 Материалы в игре ведут себя как настоящие:
Металл блестит по-разному под углами.
Кирпич выглядит шершавым, даже если это просто плоская стена — за счёт специальных «карт» текстуры, которые симулируют объём.

Сейчас многое из этого встроено в движки по типу Unreal Engine, наши 4070 могут легко обработать это всё, но в то время, с теми мощностями это была большая работа, которая выполнялась с совестью
Если бы The Last of Us была не игрой, а выставкой, я бы ходил по ней зал за залом, как по музею дизайна.
В каждом доме — свой характер. В каждой комнате — детали, которые хочется рассматривать.

Не просто «постапокалипсис», а мир, в котором раньше кто-то жил — и жил интересно.
Детские с обоями, книгами, коллекциями игрушек.
Гостинные с фотографиями, каминами, плакатами рок-групп.
Кухни с разбросанными рецептами, банками, неразобранной посудой.

И все они не повторяются. Каждая квартира, каждый дом — уникальны.
Один — богемный и тёплый, другой — строго организованный, третий — старомодный, с вязаными скатертями и радиоприёмником.

Ты словно гуляешь по реальному городу, заглядывая в чужие жизни.
Интерьеры здесь — не фон, а законченное высказывание. Их можно исследовать часами — и каждый раз находить новое.

Вот к примеру квартира многодетной семьи из Питсбурга, у них 3-е детей. Двое детсадовского возраста и один младенец.
Игра пытается создать испытание, дать тебе бой, создать эмоции

Тем временем ты автор канала которому надо сделать пару скриншотов в конце главы:
Live stream started
Виталя затащил меня на вайм. Ловите стрим от моего лица + лучший саундтрек на фоне :D
Live stream finished (2 hours)
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Когда Naughty Dog разрабатывали аудио для The Last of Us, они не просто записывали шаги и скрипы — они искали атмосферу выжженного, заброшенного мира.

Часть команды отправилась в пригород Лос-Анджелеса, где в пыльных, разрушенных домах записывали скрежет по ржавому металлу, шорох обуви по осыпавшимся доскам, скрипы дверей.
Другой дизайнер поехал в трущобы Рио-де-Жанейро, где среди разрушенных зданий жили собаки, куры, крысы.

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

И вот, во время сессии озвучки, актриса Мисти Ли случайно издала глубокий гортанный щелчок.
Так родился звук, ставший символом страха во всей игре.
Его подхватили девять актёров, чтобы дать щелкунам разную интонацию — не просто пугающую, а животную, инстинктивную.

Слышишь щелчок — и уже становится не до шуток.
Элли пытается заговорить о Сэме и Генри. Но Джоэл — молчит.

Это не просто заминка в диалоге или баг в игре. Это запертая дверь. Naughty Dog сознательно убирает драму — оставляя только напряжение. Вместо исповеди — тишина. Вместо сочувствия — смена темы.

Так работает эмоциональная блокада. Так игра передаёт главную идею сцены: горевать — опасно. Это может сломать. Потому лучше не говорить. Лучше забыть.

Но мы-то помним. И Элли — тоже.
Игрушечный робот в её рюкзаке — деталь, которую не замечаешь сразу. А потом она становится уколом. Она — память, которую никто не разрешил проговорить.

И поэтому игрок страдает не вместе с персонажами — а вместо них.