Главная Юзердоски Каталог Трекер NSFW Настройки

Gamedev

Ответить в тред Ответить в тред
Check this out!
<<
Назад | Вниз | Каталог | Обновить | Автообновление | 132 2 11
статтеринг повержен Аноним 06/11/25 Чтв 19:50:26 1058847 1
menu.png 0Кб, 202x51
202x51
Итак, дорогие друзья. Да-да, поздравьте меня. Я наконец нашёл единственный способ уничтожжить статтеринг.
На самом деле их два. Первый - выпустить игру строго под, например, 60 гц и принудительно переключать режим монитора игрока (и надеяться, что такой режим поддерживается).
И второй, который скорее всего придумали и до меня, но мне за всё это время он не попадался.
Итак, в чём соль. Что же такое етот ваш статтеринг?
Представим, что у вас есть спрайт персонажа, который движется по экрану. Суть в том, что экран поделен на пиксели, и спрайт как бы прыгает от одного пикселя к другому. допустим, персонаж движется 1 пиксель за кадр. Значит на 60гц мониторе персонаж продвинется за секунду на 60 пикселей, а не 120гц мониторе - на 120. Но мы ведь хотим, чтоб на любой частоте персонаж двигался с одинаковой скоростью. Очень распространенный вариант - умножать скорость на дельту (время между кадрами). Но это и вызывает статтеринг. Потому что ваше расстояние может получиться не ровно 1 пиксель, а 0.7, например.
И если вы каждый кадр будете прибавлять 0.7, то увидите следующую картину позиций спрайта:
0, 0.7, 1.4, 2.1, 2.8
при округлении в меньшую сторону получится:
0, 0, 1, 2, 2
видите? в позиции 0 спрайт отрисовывался 2 кадра, в позиции 1 - 1 кадр. потом в позиции 2 опять два кадра. эта неравномерность ведёт к неприятным для глаза рывкам, всё выглядит дёрганым. fixed timestep создан больше для внутренней логики и никак не уничтожает статтеринг.
Если у вас игра гладкая и в ней используется вся сетка пикселей (да еще и subpixel rendering), то эффект не так заметен. если же вы делаете pixel perfect игру с условным виртуальным разрешением 320x180, и отрисовываете персонажа чётко в больших виртуальных пикселях, то рывки будут очень заметны.
Ну так вот, как же я решил проблему. Очень просто - я намутил обычный пропуск кадров. Только его цель - не повышение производительности, а устранение статтеринга.
Тупо берем нашу базовую частоту за 30 гц. Теперь смотрим частоту обновления экрана игрока и находим ближайшее целое значение с нашим шагом. то есть нам доступны такие частоты как 30,60,90,120,150 и т.д.
Если угадали с частотой - замечательно, просто пропускаем столько обновлений логики, сколько шагов нам потребовалось. То есть на экране 30гц мы ничего не пропускаем. на экране 60гц мы пропускаем кадр через раз. на экране 90 гц мы пропускаем 2 кадра через раз.
если же у игрока монитор оказался 75 гц, что не делится ровно не тридцать, то просто берем ближайшее к нему значение. Это либо 60 либо 90. И всё, дальше по той же схеме. Таким образом состояние всегда будут обновляться равномерно. единственный минус - на мониторах вроде 75, 100, 144 гц, игра будет идти немного быстрее или немного медленнее. самый ужасный случай - 75 гц, так как он равномерно отдален от 60 и 90. значит игра будет идти на 25 процентов быстрее/медленнее. но по мере возрастания герцовки эта разница становится всё меньше. например при частоте 135гц у нас снова одинаково далеко и до 120 и до 150, но теперь разница в скорости будет уже не (75/60), а (150/135), что и без калькулятора понятно. Да, этот способ не подходит для мультиплеерных игр или игр, где важна одинаковая скорость. Спидраннеры могут выставлять нужный режим монитора при надобности. Но вот в играх, где скорость в 75% некритична, это топ способ уничтожить статтеринг. Игра выглядит идеально стабильной. Да, не 60 гц, но на тру пиксель арт стиле вы и так бы не сделали большую частоту. подумайте сами, если у вас сетка 320x180 и вы хотите выжать реальные 60 фпс, значит самое медленное движение в 1 пиксель за кадр даст вам прохождение пути в 60 пикселей за секунду. Это треть высоты. Довольно быстро. условные медленно падающие снежинки вы так не сделаете.
30 гц за базу я взял, потому что это хороший делитель для существующих 60, 90, 120 гц, и при этом не слишком маленький как 15 гц. Так что такие дела. Спустя 2 года борьбы с микростаттерингом, я наконец-то победил его. Ура!
В следующих постах расскажу вам про мой топ движок на C + SDL3. Я намутил форт подобную 16 битную виртуальную машину с банковой системой, синтез звука, сжатие lz4 для изображений и байткод версий скриптов. Короче будет весело
Аноним 06/11/25 Чтв 20:00:11 1058848 2
Я не пишу движков, но делал иначе. Минимальный шаг движения - 1 пиксель. Допустим, нам нужно двигать на 1 пиксель в секунду, тогда аккумулируем пройденное время, как только оно достигнет секунду, двигаем на 1 пиксель. Кстати в некоторых старых (80-90-ые) играх движение основано на таймерах-аккумуляторах, а не на умножении на дельту.
Аноним 06/11/25 Чтв 20:03:14 1058849 3
>>1058848
с аккумулятором тот же прикол получается и статтеринг не исчезает. смотри, представь, что ты задал движение 1 пиксель в секунду.
на 60гц у тебя будет капать в аккумулятор по 16,6666. это не 1.0 делить на целое число как например 0.5, 0.25, 0.125. А значит у тебя каждый каждый раз когда аккумулятор переполняется, будет оставаться остаток. И в какой то момент это выльется в то, что на 1 тик ты потратил 60 кадров, а на какой-то 59. И это тоже будет бить в глаза, когда персонаж двигается-двигается, а потом раз и заикнулся на мгновение.
Аноним 06/11/25 Чтв 20:08:26 1058850 4
>>1058849
это происходит при условии, что у тебя фиксированный ФПС

если игра разгоняет карту до 1200 ФПС, то погрешностью можно пренебречь

если ФПС фиксирован и известен, то ставь таймер на кол-во кадров, а не кол-во секунд

(но на самом деле при шаге в пиксель погрешность накопления при 60 кадрах и так незаметна глазу)
Аноним 06/11/25 Чтв 20:17:48 1058852 5
>>1058850
Не совсем понял про какой фиксированный фпс идет речь.
при вызове свапа буферов vsync ждёт сигнала от монитора. Возможно ты говоришь об отрисовке без vsync или о технологиях вроде freesync.
Без vsync будет тиринг и всё равно нужно как-то спать между кадрами, чтобы не греть процессор лишний раз. А спать ОС точно не умеют. у них гранулярность в 15 мс. То есть ты либо юзаешь vsync, либо спишь с возможностью упустить момент, либо спишь немного и остальное время доводишь через busy loop, который тоже такой себе способ.
Возможно зависит от человека, но мне редкое заикание при накоплении аккумулятора даже сильнее заметно, чем постоянное движение рывками. При постоянных рывках глаз хотя бы привыкает к этому как к стилю что ли (вроде постоянно дергающейся графики в baba is you). а в случае, когда у тебя игра идёт плавно, а потом раз в секунду условно заикается - очень заметно.
Аноним 06/11/25 Чтв 20:29:12 1058853 6
Ну что ж. Перед тем как впаривать вам про мою вм, расскажу вам, а нафига ваще нужна вм.
Смтрите, в зависимости от того, что у вас за игра - вы будете использовать самые разные технологии.
В статичных играх типа косынки можно рисовать прям в окно без всяких 3d api вроде opengl/vulkan тупо когда нужно
отобразить изменения на экране, в каких-то хитрых мегапроектах вам будет удобно юзать ECS.
где-то подойдёт сложное комплексное решение, где-то быстрое и самопальное.
Согласитесь, нафига юзать условный unreal engine для простенькой новелки.
Вот и с виртуальной машиной так же. ВМ даёт кучу плюсов, но самый топовый - это динамическая загрузка.
Представьте, что у вас огромная игра с кучей уровней.
Если вы скомпилировали игру и она у вас написана целиком на C, то при запуске бинарник помещается в память целиком.
На самом деле не совсем так, но суть в том, что вы не можете регулировать какой кусок кода сейчас вам нужен,
а какой - нет. Да, вы можете заморочиться с разделяемыми библиотеками, но это люто геморно. они платформозависимы,
требуют всякие хэдеры и прочее. С виртуальной машиной ты просто загружаешь скрипт в память и в любой момент выгружаешь.
Хочешь запустить мини игру в игре? без проблем - загрузил и играй. Выходишь из мини игры - выгрузил. Это супер топ фича.
Конкретно моя вм 16битная. Она использует всего 2 байта для хранения адреса скрипта. То есть каждый раз, когда ты
вызываешь функцию в скрипте или прыгаешь куда-то через if/else, тебе нужно всего 2 байта для указания абсолютно любой
позиции в коде. Ну и сами переменные тоже размером 16бит.

Кстати о переменных. зацените топ. У них нет типов. Все переменные - это тупо uint16. то есть значения от 0 до 65535.
это и обычное число, и индекс переменной, и адрес для прыжка. Ваще огнёрий. никаких кастов и усложнения вм.
А еще зацените топ тему. Вот вам пример моего скрипта:

ENEMY_AMOUNT 2
SPEED 5
speed_x
speed_y

arr enemies ENEMY_AMOUNT allot

: init
\ код для инициализации
;

: update
\ код обновления логики
;

и т.д
Прикол в том, что мне не нужны ключевые слова const или var (int);
это еще один большой плюс своей вм - ты можешь ее намутить ровно под свой проект, где не будет ничего лишнего.
У моего компилятора и вм есть договорённость:
Если у тебя в скрипте есть константы - они должны идти в первую очередь и писаться капсом.
Если у тебя в скрипте есть переменные - то они должны писаться маленькими буквами и идти после констант (если есть).
Если есть массивы, то после переменных
И только потом функции.
Компилятор зная эти правила легко понимает, что есть что. ему не нужно постоянно писать const const var var var
Аноним 06/11/25 Чтв 20:41:33 1058855 7
>>1058847 (OP)
>персонаж движется 1 пиксель за кадр
Игровой мир нихуя не должен знать о пикселях, только о юнитах. Так же он нихуя не должен знать о кадрах и частоте монитора, только о частоте своих обновлений, которая фиксирована. Ты апдейтишь игровой мир, рендеришь фрейм в котором интерполируешь состояние мира, переводишь все юниты в пиксели на экране и все. В таком случае похуй, сколько фреймов между апдейтами(т.е похуй, какая частота монитора), они все будут отображать интерполированное состояние и передвигать персонажа плавно по экрану. А что за шизу ты пытаешься сделать я так и не понял до конца.
Аноним 06/11/25 Чтв 20:42:27 1058856 8
Вооот, и кароч какие фишки есть еще у этой вм. forward declaration - это когда ты можешь вызывать функцию, которая объявлена ниже места откуда ты ее вызываешь.
Так же я упомянул, что вм банковая. Она устроена так. Код с 0 по 32767 байт (примерно) - это код, в который загружается глобальный скрипт. В нём будут глобальные переменные, нужные между сценами и раличные утилиты.
А область с 32768 по 65535 доступна нам для загрузки нашего текущего скрипта. Таким образом часто повторяющуюся логику и данные необходимые между сценами мы храним в core.f, а данные самой сцены - в текущем скрипте. Текущий скрипт по договоренности знает в каких адресах какие функции и переменные лежат, так как это зашито на уровне компилятора. Поэтому мы тупо можем компилить скрипты вбайткод и готово. Никакой линковки в рантайме, никаких хранений символов для резолвинга. Это шикарно. Да, это не луа, где можно комбинировать кучу скриптов как блоки, но для моей игры это идеально подходит. Я хочу сделать максимально разнообразную игру, поэтому часто повторяющегося кода у меня будет немного. Например, вместо того, чтобы создавать мега объект player.f, с кучей состояний, я лучше просто для каждой сцены запилю свой код. Ценой некоторого дублирования, упрощается архитектура.
Вот вам наглядный пример:
Персонаж в доме. Рисуем его без верхней одежды. Ходит быстро.
Персонаж на улице в пургу. Рисуем его в куртке и идет в пургу он медленно. Персонаж в локации с озером умеет плавать и т.д. То есть если взять всю игру целиком, то может показаться, что персонаж - это лютый раздутый объект с кучей состояний, но на самом деле это просто конкретный скрипт сцены содержит необходимый код. Это позволяет мутить ваще любую дичь. Вот прям ваще. Хочешь в каком то уровне сделать не вид сбоку а вид сверху? Без проблем, хочешь где-то головоломку от первого лица с взаимодействием с рычагами? без проблем. И самое топовое, что весь этот код не хранится в памяти. вм тупо в любой момент времени жрет всего лишь 64кб оперативки на хранение кода и данных.
Аноним 06/11/25 Чтв 20:44:47 1058857 9
>>1058852
если у тебя включен vsync, то можешь мерять скорость спрайтов кол-вом кадров, как это сделано в старых дос играх, вот и всё, зачем тебе ещё какой-то источник синхронизации

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

>у них гранулярность в 15 мс.
попробуй написать проигрыватель миди-файлов без статтера
заикание звука для человека заметней, чем заикание движения
Аноним 06/11/25 Чтв 20:45:26 1058858 10
>>1058855
Это всё понятно, но это вызывает статтеринг. Смотри. Вот у тебя есть виртуальная единица. Условный метр/клетка. Она в реале будет занимать сколько-то пикселей на экране, согласен? И суть в том, что если ты не будешь двигаться с 1/0.5/0.25/0.125 пикселей в секунду, то из-за накоплений у тебя будет статтеринг, понимаешь? Да, если при переводе твоих юнито тебе повезло что на определенном мониторе с определенным разрешением у тебя при каждом рендере объект проходит фиксированное количество реальных пикселей - то поздравляю, статтеринга не будет. но и ситуации такой не будет
Аноним 06/11/25 Чтв 20:48:41 1058859 11
>>1058857
>можешь мерять скорость спрайтов кол-вом кадров
И каковы твои дальнейщие действия? чтобы игра шла одинаково, тебе придется замедлять движение тем у кого герцовка выше, соглы? а замедлять ты обязан на такое значение, чтоб это было либо ровно пиксель, либо половина, либо четверть. То есть так замедлить, чтоб ты проходил реальный пиксель за одно и то же количество кадров каждый раз.
Аноним 06/11/25 Чтв 20:52:13 1058861 12
>>1058859
нет, игроку придётся переключиться на режим 60 гц если хочет помедленнее

т.к. мы по умолчанию делаем какой-то пиксельный кал для ностальгирующих 40-летних нердов, то он и так и так не окупится
Аноним 06/11/25 Чтв 20:56:11 1058862 13
>>1058861
>игроку придётся переключиться на режим 60 гц
Некоторые современные мониторы с повышенной герцовкой не поддерживают 60 гц. Точную модель я тебе не назову, но суть в том, что никто как бы не гарантирует, что монитор будет поддерживать 60гц
Аноним 06/11/25 Чтв 20:58:20 1058864 14
>>1058861
К тому же непонятно, что ты там собрался замерять, если у тебя стратегия "Всех принудительно под 60 гц". Просто делаешь игру под 60 гц и всё
Аноним 06/11/25 Чтв 21:01:33 1058866 15
>>1058853
портируй свою машину под Эльбрус

>>1058864
не замерять, а исчислять. а ты почему так не сделал?
Аноним 06/11/25 Чтв 21:09:06 1058868 16
О, еще топ тема. Кароч я не храню OP_EXECUTE отдельным байтом.
Когда вм считывает байт скрипта, она смотрит на старший бит. если там 0 - значит это обычный опкод (OP_ADD, OP_DRAW и т.д) и она тупо его выполняет. Если же там 1 - это сигнал, что перед нами вызов функции. вм считывает добавочный байт и из полученных 15 бит получает адрес функции которую нужно выполнить. То есть для вызова функции нужно 2 байта а не 3. Но адресное пространство уменьшается до 32кб. Но так как на данный момент у меня сцены занимают максимум 2кб - то это ваще ни о чем ограничение. + эта оптимизация сама по себе уменьшает размер скрипта, как бы делая адресацию более компактной. ваще нойс
Аноним 06/11/25 Чтв 21:11:15 1058869 17
Отрицательные числа и вычитание? да это же просто wrap around. компилятор к отрицательным числам тупо добавляет 65536 и всё.
То есть если мы хотим к 100 прибавить -10, то мы прибавляем (-10+65536). таким образом результат переполнится до 90. Ну разве не топчик?
Аноним 06/11/25 Чтв 21:12:44 1058870 18
Float? а нафига он нужен? есть же числа с фиксированной точкой. работаешь со значениями << 6, а при рендере обратно делишь через >> 6. и всё, можно делать медленные движения, каунтеры анимаций и прочее.
Аноним 06/11/25 Чтв 21:13:11 1058871 19
>>1058847 (OP)

это неуловимый глазу статтеринг. курсор по экрану двигаешь, тебе статтерит? а он подвержен этой проблеме

его можно заметить только если в твоей игре а) низкий фпс, б) игра считается в низком разрешении и для рендера апскейлится. достаточно повергнуть низкий фпс и апскейл, и статтеринга не будет
Аноним 06/11/25 Чтв 21:14:57 1058873 20
>>1058871
курсор быстро проходит расстояние и всё. а в игре долго и плавно перемещается персонаж например, или фон движется.
Аноним 06/11/25 Чтв 21:16:12 1058874 21
Тред не читал, но Forth-систему одобряю. Топ тема.
Аноним 06/11/25 Чтв 21:16:28 1058875 22
Такс, шо ещё.
так как это моя личная вм (да и потому что это форт), там можно в переменных юзать вопросительные знаки. Самое то для предикатов:
: near_exit? player_x @ 30300 < ;
: near_puff? player_x @ 31000 32000 >< ;
: near_gamepad? player_x @ 32000 32600 >< ;
: near_tv? player_x @ 33000 34500 >< ;
: near_console? player_x @ 35500 36500 >< ;
: near_speaker? player_x @ 37200 38200 >< ;
: near_vend? player_x @ 40500 41000 >< ;
: near_trash? player_x @ 41600 > ;

: has_gamepad? has_gamepad 1= ;

: player_state_normal? player_state @ PLAYER_STATE_NORMAL = ;
: player_state_license? player_state @ PLAYER_STATE_LICENSE = ;
: player_state_floppy? player_state @ PLAYER_STATE_FLOPPY = ;
: player_state_gamepad? player_state @ PLAYER_STATE_GAMEPAD = ;
: player_state_keymap? player_state @ PLAYER_STATE_KEYMAP = ;
: player_state_playing? player_state @ PLAYER_STATE_PLAYING = ;

: console_ready? console_ready 1= ;

: can_use_tv? near_tv? player_state_normal? and ;
: can_use_speaker? near_speaker? player_state_normal? and ;
: volume_not_max? volume @ 4 < ;
: volume_not_min? volume @ 0 > ;
: can_increase_volume? can_use_speaker? volume_not_max? and ;
: can_decrease_volume? can_use_speaker? volume_not_min? and ;

: can_check_trash? near_trash? player_state_normal? and ;
: can_use_trash? player_state_floppy? player_state_gamepad? or near_trash? and ;
Аноним 06/11/25 Чтв 21:18:48 1058876 23
>>1058874
баловство это сплошное
Аноним 06/11/25 Чтв 21:21:12 1058878 24
>>1058874
О да, братан! ваще улёт.
просто сравни
форт:
: sum_n_dif
2dup - -rot + . .
;
5 3 sum_n_dif
8 и 2

луа:
function sum_n_dif(a,b)
return a+b, a-b
end

sum(5,3)

и в исходниках форт занимает меньше места, а в байткоде в зависимости от реализации так ваще топ.
луа сохранит кучу дичи, а в моей вм этот скрипт займет
OP_PUSH8 5 OP_PUSH 3 ADDR
5 байт вместо сотни с чем-то у луа. Но я не хочу сказать, что луа плохая, просто она более мощная и универсальная, но мне то это нафиг не надо в игре.
Пивет C, не умеющему возвращать 2 значения.
Это кста тоже топ. Не нужно в вм и компиляторе ничего усложнять. Нужно вернуть несколько знаений - оставь их на стэке и всё. никаких механизмов возврата, стэкфреймов и прочего. AST? нафиг не надо. Токенайзер? тупо по пробелам разбил и всё. Форт это мегапростая штука
Аноним 06/11/25 Чтв 21:22:21 1058879 25
Аноним 06/11/25 Чтв 21:27:45 1058883 26
Ах да, еще суть в чем. Раньше я делал так, что переменные сразу инициализировались при объявлении. Но пришел к выводу, что это от лукавого (баловство). ведь часто их приходится сбрасывать в начальное состояние. и тогда у нас появляется 2 источника истины. Лучше просто создавать переменную, а значение ей забивать в init или еще где нить. И вот отсюда какая то тема появилась. В байткоде мне не нужно хранить OP_CREATE_VAR условную. я коньпеллятор тупо считает количество переменных и фигачит это в виде всего 2 байт. а вм при загрузке скрипта считывает это число и выделяет нужное количество памяти.
не через malloc, разумеется. тупо размер меняет текущей позиции и всё. malloc ваще кал. у меня в движке всё заранее просчитано, какой атлас, сколько чего. огнёрий
Аноним 06/11/25 Чтв 21:29:33 1058885 27
>>1058883
а какая в этом польза для народного хозяйства?
Аноним 06/11/25 Чтв 21:29:39 1058886 28
еще несомненный плюс вм - горячая перезагрузка. поменял значение в скрипте, в игре нажал кнопочку R - и всё применилось. никакой коньпелляции, не нужно опять доходить до нужного места. ваще шикарно
Аноним 06/11/25 Чтв 21:31:05 1058887 29
>>1058885
ну как. скрипт с 100 переменными будет весить не 100 байт, а фиксированно 2.
Аноним 06/11/25 Чтв 21:32:46 1058888 30
>>1058887
объём RAM не является проблемой для современных пека, особенно на таких малых масштабах
Аноним 06/11/25 Чтв 21:33:12 1058889 31
Ну и разумеется кроссплатформенность. Код движка у меня сейчас 2000 строк примерно (всё в одном .c файле для простоты). а код скриптов будет огого. Так вот, чтоб портировать игру куда угодно (или чтоб свалить с C+SDL3 на любую другую технологию, язык и т.д) нужно будет переписать только то, что хранится в 2000 строк C кода, а скрипты трогать не придется. ну разве не ультимативный опчек?
Аноним 06/11/25 Чтв 21:35:16 1058891 32
>>1058888
А это не только ram, но и место на диске.
Ты спросил про пользу. Это польза. Одно дело сделать костыль, от которого только вред. Здесь же только плюсы, минусов нет.
Если переменная не предполагает никаких изменений, то тогда делаешь ее константой. если предполагает - то чтоб не было багов из-за двух источников истины - выставляй её строго в определенной функции (init/reset) и т.д
Аноним 06/11/25 Чтв 21:41:46 1058893 33
а еще плюшки всякие мелкие есть. Если мы пушим на стэк число, которое меньше 256, то коньпеллятор юзает OP_PUSH8, если больше или равно - то OP_PUSH16.
вм в зависимости от опкода будет знать, сколько байт ей считывать. это тоже экономия на куче всяких маленьких чисел по 1 байту.
Аноним 06/11/25 Чтв 21:44:32 1058894 34
Ну а связь между скриптом и движком мегапростая. Это обычная система колбэков.
коньпеллятор находит адреса обязательных для скрипта слов:
: on_load ;
: on_keydown ;
: on_keyup ;
: on_update ;
: on_draw ;
и сохраняет их в конце скрипта.
вм при загрузке берет эти значения и запоминает. а дальше все просто - C цикл при нужных событиях тупо прыгает по этим адресам. В конце колбэка виртуальная машина останавливается и C часть дальше работает
Аноним 06/11/25 Чтв 21:46:48 1058895 35
>>1058891
Еще скажи что из одежды у тебя только трусы и густая борода, А еще ты босиком по снегу ходишь и пристаешь молодежи рассказывая как экононить память на переменных.
Аноним 06/11/25 Чтв 21:48:24 1058896 36
причем зацените прикол. на самом деле при нажатии клавиш sdl3 вызывает колбэк из core.f который назвывается on_raw_keydown. и именно там происходит логика работы с биндингом и отсеиванием.
например, если мы сейчас биндим клавиши, то тогда мы вызываем on_keydown. или же если нажата клавиша, которая уже забинжена. В противном случае on_keydown просто не вызывается. То есть в скриптах не нужно постоянно отсеивать, нажата ли именно реально зареганная клавиша, или просто левая. вм не вызовет колбэк если это левая какая-то клавиша
Аноним 06/11/25 Чтв 21:49:58 1058897 37
>>1058895
Не, ну погоди. а что плохого то в экономии памяти? я согласен, что это не критично. но и поводов не экономить не вижу.
Аноним 06/11/25 Чтв 21:52:57 1058898 38
О, а зацените еще топ тему. кароч при самом старте игры в первый раз игрока просят забиндить клавиши. Таким образом даже если у тебя раздолбаны wasd + стрелки ты можешь сразу же без всяких переходов произвести удобный биндинг. Разумеется позже всегда можно их переделать. Так вот, чтоб узнать, впервые ли запущена игра или нет, есть топ способ.
я просто чекаю, что клавиша 1 не равна клавише 2 в массиве забинженых клавиш. Прикол в том, что по умолчанию там хранятся ноли, а биндинг не позволяет забить одну и ту же клавишу на разные действия. поэтому если ты прошел биндинг, то в памяти эти значения будут разнцми. если нет - одинаковыми. Ну разве не топ тема?
Аноним 06/11/25 Чтв 21:57:56 1058900 39
тэкс, а это утилиты из core.f для работы с флагами.

: flag_mask 15 & 1 swap << ;
: flag_addr 4 >> flags + ;

: flag+
dup \ flag flag
flag_addr \ flag addr
swap \ addr flag
flag_mask \ addr mask
over @ \ addr mask addr_val
| \ addr mask|addr_val
swap \ mask|addr_val addr
! \ addr = addr_val|mask
;

: flag-
dup \ flag flag
flag_addr \ flag addr
swap \ addr flag
flag_mask ~ \ addr ~mask
over @ \ addr ~mask addr_val
& \ addr ~mask&addr_val
swap \ ~mask&addr_val addr
! \ addr = ~mask&addr_val
;

: flag1? dup flag_mask swap flag_addr @ & 0 = if 0 else 1 then ;
: flag0? flag1? 0 = if 1 else 0 then ;

: flag~
dup \ flag flag
flag_addr \ flag addr
swap \ addr flag
flag_mask \ addr mask
over @ \ addr mask addr_val
^ \ addr mask^addr_val
swap \ mask^addr_val addr
! \ addr = mask^addr_val
;

нафига тратить на бинарный флаг 16 бит, если можно тратить 1 бит, верно?
итого, что мы имеем. моя ram из 64кб делится на 32кб глобальной, и 32кб локальной для конкретной сцены. Но!
4096 переменных из глобальной памяти отданы под 65536 флагов.
таким образом у меня в игре доступно (32768 - 4096) глобальных переменных, 32768 локальных переменных и флаги я тоже условно делю на глобальные и локальные.
Аноним 06/11/25 Чтв 22:02:25 1058901 40
а еще зацените подход. мега удобный и простой. вместо того, чтоб мутить простыни из ifdef/ifndef для разделения на debug/release билд я пошел другим путем. как я уже писал - весь C код у меня хранится в одном файле. Когда игра будет готова, я просто скопирую его и отредактирую под release версию. то есть сделаю так, чтоб ассеты грузились не из реальных .bmp файлов, а по смещению в assets.pack и из своего простого формата сжатого через самопальный lz4. Воть. то есть будет 2 файла. один для дебага, второй для релиза.
Аноним 06/11/25 Чтв 22:06:50 1058902 41
а суть в том, что у меня уже юзается простой совместимый способ для работы как с debug так и release версией. смотрите, у меня в папке textures например лежит файл menu.bmp
но я не пишу его в скриптах под этим именем. компилятор превращает его в T_MENU
и это по сути просто индекс ассета от 0 до 65535
в дебаг версии у меня генерится массив путей для текстур, где textures[0] = "menu"
а в релиз версии будет юзаться массив смещений. Круто, да? мне не нужно будет ничего менять. просто в зависимости от того релизный у меня билд или нет, движок будет какой-нибудь M_PLAYER интерпретировать либо как "textures/" + textures[666] + ".bmp", либо как fseek(asset_offsets[666])
Аноним 06/11/25 Чтв 22:08:07 1058903 42
Аноним 06/11/25 Чтв 22:09:06 1058904 43
и да. в дебаг режиме я подключаю компилятор, чтоб можно было на лету компилить скрипты, а в релиз версии он не нужен будет, ибо в assets.pack будут уже в скомпиленном виде скрипты
Аноним 06/11/25 Чтв 22:10:18 1058905 44
>>1058903
А это и сейчас круто. Никаких систем cmake и прочего. компиляция через tcc мгновенная. всё чётко работает без сюрпризов. в финальном билде никакого bloated кода нет.
Аноним 06/11/25 Чтв 22:13:11 1058906 45
Раньше на godot сидел, это реально кошмар. То одно api сломают, то другое. то ли дело SDL. мажорные релизы раз в 11 лет выпускают, и то я с sdl2 на sdl3 за пару часов перешел. Вот что значит юзать зависимости только для самой базы, а остальное своё. никаких сюрпризов, максимальная гибкость и удобство.
SDL ваще шикарная штука. Решает ровно те задачи, которые платформозависимы, а то, что везде одинаково - мутишь сам. Ваще никак не лезет в твои дела.
Даже эти ужасные vulkan/metal абстрагирует. У тебя просто единый api для загрузки/отрисовки текстур.
Аноним 06/11/25 Чтв 22:16:46 1058907 46
>>1058906
игру-то покажи
мне всё равно, на чём ты писал скрипт - на си шарпе, луа или самодельном форте
Аноним 06/11/25 Чтв 22:19:21 1058908 47
>>1058907
Я хочу всё чётко сделать. план такой - добавлю сохранение кадра в bmp и открою игру в минимальном окне (320x180). похожу там, поделаю что-нибудь, а потом уже через какой-нить ffmpeg увеличю и соберу хоть в gif хоть в видос. а то если просто записывать - будет не то. не смогу всё величие передать.
так что ваще огонь будет
Аноним 06/11/25 Чтв 22:26:01 1058909 48
>>1058908
>величие
От величия обычно таблетки помогают
Хороший тролинг
Аноним 06/11/25 Чтв 22:26:52 1058910 49
Кста, насчёт игры. она будет абсолютно без текста. Как limbo, inside, и в какой-то степени animal well / yume nikki. то есть в последних двух, конечно, есть текст, но от него смысла почти и нет.
Только представьте - игра сразу доступна каждому, у кого есть комп и интернет. Никаких тебе проблем с RTL текстом, никаких китайских огромных атласов и гемора с отрисовкой их символов в низком разрешении. никакой арабской вязи, которая не приемлет monospace символы а требует единый красиво связный текст. Ну и конечно же нет проблем с тем, что переводчики не так передатут шутку или идиомы какие. Ну и в принципе, обычно переводят на самые популярные языки, а остальные в пролёте. здесь же все люди мира смогут играть. Шо ещё. Так как в игре не требуется мышка, то и на консоли можно портануть. Я уже реализовал некоторое количество загадок. Кароч в игре будет один связный мир, и куча загадок. но все они будут уникальны и не будут повторяться. Причем для базового прохождения будут не очень сложные, а опционально для 100 процентов придется попотеть.
Кста, настройки игры происходят в самой игре. Это и регулировка громкости, и выбор полный экран/окно. а использоваться будут лишь клавиши вверх вниз влево вправо и одна еще чтоб попадать в сцену menu (escape, например)
Аноним 06/11/25 Чтв 22:28:09 1058911 50
>>1058909
Не, это реально будет артефакт. я 4 года изучал нужные технологии, мутил прототипы, и вот, наконец начал мутить. Архитектурно всё готово, несколько сцен есть, идеи для загадок тоже. еще лет 6 и можно будет релизить
Аноним 06/11/25 Чтв 22:35:43 1058912 51
можно кста опционально выпускать релиз в одном файле. разрабы SDL не рекомендуют статичную линковку, и я их понимаю. но никто не мешает выпустить в двух вариантах. Картинки хорошо жмутся, звуки и музыка - синтезированные, так что ассеты будут мало весить. Их можно либо в конец файла закинуть через cat, например. но некоторые антивирусы считают такое поведение как вредоносное. можно просто все ассеты в C массив поместить и они тупо в оперативу попадут. Так как выбор билда будет, то проблем не возникнет. Встроить ассеты в бинарник это даже еще проще чем в assets.pack, так как не придется получать путь, по которому лежит бинарник. Тупо обращаешься к массиву и готово
Аноним 07/11/25 Птн 07:47:26 1058937 52
>>1058847 (OP)
>если же вы делаете pixel perfect игру с условным виртуальным разрешением 320x180
>>1058908
>открою игру в минимальном окне (320x180)

вот и причина статтеринга
Аноним 07/11/25 Птн 12:13:05 1058956 53
>>1058937
Не, статтеринг это не про количество пройденных пикселей за кадр, а про равномерность.
Аноним 07/11/25 Птн 12:15:08 1058957 54
>>1058874
там не совсем форт система. никакого словаря и шитого кода нет. просто скрипты компилятся в байткод, а потом вм его выполняет. поэтому я и написал, что вм форт подобная. от форта я взял синтаксис (@ ! . dup drop swap over), стэковый подход и обратную польскую нотацию.
Аноним 07/11/25 Птн 12:20:53 1058960 55
Игра - это функция. на вход подаются нажатия клавиш, на выход - массив пикселей и аудиосэмплы. топ?
Аноним 07/11/25 Птн 12:26:46 1058962 56
>>1058871
а еще твой взгляд фиксируется на месте, куда ты хочешь попасть курсором. ты лишь переферийно палишь, когда пора стопорить курсор. а в игре ты наоборот следуешь глазами за движущимся персонажем. это два совершенно разных момента.
Аноним 07/11/25 Птн 12:30:28 1058963 57
98af77b7192493d[...].png 95Кб, 800x494
800x494
>>1058960
Никто не оценит. Вопрос не имеет смысла. Если решил быть как Пэй Мэй то сиди один на горе, а не демонстрируй свое "вяличие" перед мимокроками, а то плохо кончишь, как Пэй Мэй.
Аноним 07/11/25 Птн 12:30:58 1058964 58
>>1058957
то есть всё самое худшее

ведь плюс форт-системы в том, что её можно запустить на картошке без ОС и в том, что пользователь может написать свой словарь, а не в обратной нотации и прямой манипуляции стэком
Аноним 07/11/25 Птн 12:31:49 1058965 59
>>1058962
я вот сейчас проследил за своим виндовым курсором и заметил что он как-то дёргается. надо писать свою ОС.
Аноним 07/11/25 Птн 13:53:47 1058980 60
>>1058965
ну, или можно юзать курсор по назначению - не следить за ним, а наводить в нужную точку.
>>1058964
Наоборот. скрипты компактные, компилятор и вм просты как палка.
>>1058963
Я просто максимально по простому рассказал шо такое игра. А то вдруг кому то слишком сложны все эти ваши тиринги, статтеринги, fixed timestepы и прочее. такие дела
Аноним 07/11/25 Птн 13:57:19 1058982 61
>>1058964
плюс скорость исполнения лютая. вм либо функцию по индексу вызывает (опкод), либо прыгает в нужную позицию в коде (функция). никаких поисков по строкам нет. ни хэш таблиц не надо, ни malloc. всё чётко, быстро, просто, компактно. ух! УХ!
Аноним 07/11/25 Птн 13:58:31 1058983 62
>>1058980
Для fixed timestep-ов и прочего необязательно говной обмазываться и писать свою ОС. Это базовые понятия и они применимы везде, а не только в шизе где экономят память на переменных.
Аноним 07/11/25 Птн 16:21:56 1059027 63
>>1058983
>Для fixed timestep-ов и прочего необязательно говной обмазываться и писать свою ОС
Никто и не спорит. ОС уже написаны, курсоры уже статтерят. Если их использовать по назначению, то никаких проблем с ними нет.
Усё так
Аноним 07/11/25 Птн 16:23:56 1059030 64
>>1058983
>экономить память - это шиза
Прикольное мировоззрение
Аноним 07/11/25 Птн 16:36:29 1059032 65
>>1059030
если ты сэкономил 100 гигабайт - ты молодец
100 байт - шиз
Аноним 07/11/25 Птн 16:40:14 1059034 66
>>1058957
>шитого кода нет
>скрипты компилятся в байткод
>>1058980
>компилятор и вм просты как палка
>>1058982
>плюс скорость исполнения лютая
Шитый код быстрее и проще байткода.

Байткод - это вот такая портянка:
>switch (code) {
>case 0: a(); break;
>case 1: b(); break;
>case 2: c(); break;
>...
>case 255: z(); break;
>}
А шитый код - это pointer'ы функций.

Шитый код выполняется "как есть".
Аноним 07/11/25 Птн 16:42:13 1059035 67
>>1059032
А четкая линия где проходит, не подскажешь? если сто мегабайт например всего сэкономил - это уже шиз или еще нет?
Аноним 07/11/25 Птн 16:44:20 1059036 68
>>1059035
смотря сколько денег ты сэкономишь и какой у тебя бюджет
Аноним 07/11/25 Птн 16:44:57 1059037 69
>>1059034
А вот и не портянка. никаких case. есть массив указателей на функции и ты просто их через индекс вызываешь.
Аноним 07/11/25 Птн 16:51:01 1059038 70
>>1059037
>есть массив указателей на функции
Тогда это уже получается шитый код?

Байткод - это 1 байт на одну функцию.
Шитый код - это 4 (x32) или 8 (x64) байт.
Аноним 07/11/25 Птн 16:53:23 1059040 71
>>1059038
шитый код в классической форт системе при вызове условного draw должен пройти по словарю и найти где такая запись есть. это дольше чем смещение + дереференс
Аноним 07/11/25 Птн 17:00:13 1059041 72
>>1059038
>это 4 (x32) или 8 (x64) байт.
некроссплатформенные скрипты какие-то...
Аноним 07/11/25 Птн 17:08:23 1059044 73
>>1059038
>Тогда это уже получается шитый код?
Не. в форте все функции фигачатся в словарь.
а тут есть разделение - самая база (опкоды типа swap add over + высокоуровневые опкоды типа draw play_sfx blit) это реальные функции на C. Есть массив указателей на эти функции, и скрипты тупо юзают 1байтовый индекс для их вызова. если же мы говорим о пользовательских функциях, типа update_enemies, то это уже слово в скрипте. и вот оно вызывается используя 16бит (1 бит чтоб сообщить, что это вызов функции, а не базовый опкод), и 15 бит адрес. Кроссплатформенно и компактно.
Огнёрий одним словом
Аноним 07/11/25 Птн 17:48:39 1059055 74
>>1059038
кароч, вижу, что слишком сложно для тебя. давай разжую всё.
Смотри - при вызове слова, определенного в скрипте, мы просто кладем в возвратный стэк значение и прыгаем по 15битному адресу. никаких массивов, дереференса и прочего. считал адрес - перешел. всё, изи.
но весь код невозможно написать на форте, ведь вм должна уметь выполнять базовые вещи, из которых ты уже и будешь формировать слова. И вот уже в этом случае мы пишем код на C. и для его вызова мутим массив функций. Теперь то дошло до тебя?
Аноним 07/11/25 Птн 18:39:38 1059067 75
>>1059040
>должен пройти по словарю и найти
>>1059044
>все функции фигачатся в словарь
Ты/вы путаешь/те интерпретацию с компиляцией.

Форт компилирует одну функцию сразу после ввода команды ";" и дальше вставляет указатель функции в процессе компиляции следующих функций. Именно поэтому Форт позволяет переопределять функции. Именно поэтому новая функция повлияет лишь на новейшие функции, записанные уже после того, как произошло переопределение функции.

Т.е. во время выполнения кода Форт не производит "поиска по словарю", он просто бегает по адресам, скомпилированным во время определения слов (т.е. функций). Поиск происходит только тогда, когда ты определяешь значения новых слов (функций).

Именно поэтому Форт быстрее всех скриптовых ЯП, поскольку он компилируется в шитый код сразу, а производительность шитого кода примерно равна машинному, потому что он выполняется самой этой машиной (процессором), а не каким-то там C/C++.
Аноним 07/11/25 Птн 18:47:17 1059069 76
>>1059067
то есть ты предлагаешь в финальном билде тащить компилятор, а скрипты в исходном виде? оо да, вот это угар. смех да и только.
зато пользователь сможет переопределять слова, как жить то без этого
Аноним 07/11/25 Птн 18:55:52 1059071 77
>>1059067
>выполняется самой этой машиной (процессором), а не каким-то там C/C++.
>Именно поэтому Форт быстрее всех скриптовых ЯП
Ого, ничего себе. Процессор быстрее чем вм. вот это да
Аноним 08/11/25 Суб 02:02:11 1059130 78
Аноним 08/11/25 Суб 07:30:09 1059142 79
>>1059130
dcpu-16 был регистровым вроде.
регистровая вм геморная во внутреннем устройстве. меня forth этим и зацепил.
есть просто стэк (2 стэка, включая возвратный) и ты туда просто кидаешь значения и работаешь с ними. главное соблюдать порядок, и ничего вручную указывать не придется. шикарно просто поток данных. форт напрямую готов к тому, чтоб на нем писать, не нужно поверх делать высокоуровневую обвязку как в луа, которая внутри с 5 версии тоже регистровая.
Аноним 08/11/25 Суб 10:56:33 1059147 80
>>1058847 (OP)
Кстати, можно вместо определения частоты монитора и frameskip просто юзать аккумулятор. Главное не вычитать из аккумулятора время, а просто обнулять его при переполнении.
Ставим значение не 33, а, например, 30 чтоб гарантировано переполнялось и всё.
Таким образом монитор с частотой 60гц будет прибавлять по 16-17мс, то есть гарантировано каждые 2 кадра будет происходить тик (32 > 30). и так для любой частоты. то есть нужно просто обнулять аккумулятор, чтоб никакие остатки не накапливались.
Проверка для большой частоты. Если монитор 1000гц, то он будет добавлять по 1 в аккумулятор. значит каждые 30 кадров будет происходить тик. Правда в таком случае нужно назвать аккумулятор как-то по другому. Хоть он и копит значение до 30, но остаток то не накапливается. Логичнее его просто счётчиком назвать и всё. Изи бризи таймер сквизи. Пользуйтесь, не благодарите.
Аноним 08/11/25 Суб 11:54:51 1059155 81
>>1059147
условный 500 гц монитор будет сообщать то 1 то 2 мс (или ваще 0), и снова будет статтеринг из за разного времени между тиками. тогда уж надежнее не маленькие дельты замерять и копить, а просто проверять что get_ticks() - last_time >= 30.
Аноним 08/11/25 Суб 11:55:50 1059157 82
>>1058956
а у тебя что все спрайты с одной скоростью всегда двигаются? в общем, ничего тупее я на этой неделе еще не слышал
неудивительно, что оп даже ни одной гифки не показал, все увидят как скачет его ламерский апскейл
Аноним 08/11/25 Суб 12:00:31 1059158 83
>>1059157
>все спрайты с одной скоростью
Ох, ну давай я тебе по полочкам всё расставлю. Видимо это очень сложный момент для понимания.
Когда я говорю про равномерность, я говорю про то, что спрайт, какая бы у него скорость ни была, должен идти с этой скоростью равномерно. то есть если его скорость в пересчете на пиксели = 5 пикселей, то он каждый кадр должен ее проходить. Если он проходит то 5 пикселей, то 4, то опять 5, то 6, это будет дерганым движением. это и есть статтеринг. Дело не в том, что все спрайты имеют одинаковую скорость, или один спрайт не должен уметь разгоняться/замедляться. дело в том, что если у него в данный момент есть какая-то скорость, пусть и не линейная, он должен ее на уровне кадров поддерживать. Если он разоняется, то должно быть 5,6,7,8,9... или 5,5,5,5,6,6,6,7,7,8,9. но никак не 5,5,6,5,6,6,7,8,7,8,9. Понимаешь?
Аноним 08/11/25 Суб 12:13:27 1059160 84
>>1059158
твоя система не поддерживает произвольные скорости. в 60 фпс скорость 80 п/с будет так: 1 1 2 1 1 2 1 1 2
ты ничего с этим не сделаешь, я всегда найду такую скорость которая полностью ломает твоё говно
то есть в твоей параше спрайт может двигаться либо со скоростями 15, 30, 60, 120 п/с
ну и выглядит это естественно еще хуже чем статтеринг, 0 плавности, какие-то инкрементальные ускорения решающие несущетсвующую проблему

естественно когда ты свою хуйню апскейлишь в 10 раз, то она у тебя статтерит прыгая то на 10 то на 0 пикселей за кадр, и выглядит это как говно, но нам пишешь про единичные пиксели, потому что уже понял что в апскейле вся проблема, но не хочешь это признавать
Аноним 08/11/25 Суб 12:16:28 1059163 85
>>1059160
Разумеется это вранье. Статтеринг хуже. И какую такую ты там скорость собрался находить я не понял. Реверс инжиниринг сделаешь что ли?
Аноним 08/11/25 Суб 12:23:50 1059166 86
>>1059160
>может двигаться либо со скоростями 15, 30, 60, 120 п/с
Ого, в пиксельарте дискретная сетка. Не может быть.
Аноним 08/11/25 Суб 12:27:36 1059168 87
>>1059163
мне не нужно, я это в голове лучше тебя представляю, потому что прошелся уже по всем этим граблям в SDL

>>1059166
у адекватов всё плавно меняется, без скачков
Аноним 08/11/25 Суб 12:29:39 1059171 88
>>1059168
>мне не нужно
А ты и не можешь. Потому что в моей игре я пишу что и как будет двигаться. Твоё "а вот я могу там что-то поменять и всё сломается" это просто демагогия
Аноним 08/11/25 Суб 12:30:19 1059172 89
>>1059171
никто не видел твою игру и уже не увидит после позорища с апскейлом
Аноним 08/11/25 Суб 12:32:01 1059173 90
>>1059172
>позорища с апскейлом
>>1059168
>у адекватов всё плавно меняется
Кое кто явно не в курсе, что такое pixel perfect. Можешь поизучать на досуге
Аноним 08/11/25 Суб 12:33:59 1059175 91
>>1059173
пиксель-перфект это когда выглядит как позорное говнище где скорость меняется в 2 раза и скролл по 10 пикселей прыгает за раз
Аноним 08/11/25 Суб 12:35:04 1059176 92
>>1059175
> параша
> говнище
Ну точно не в курсе. Поизучай, может и слова новые узнаешь заодно
Аноним 08/11/25 Суб 12:38:44 1059178 93
>>1059176
именно эти 2 слова лучше всего описывают то что ты считаешь пиксель-перфектом и твою потешную текстовую безгифочную победу над статтерингом

у адекватов на экране не 180 пикселей, а 1080, и всё равно всё пиксель-перфектно
Аноним 08/11/25 Суб 12:51:41 1059182 94
>>1059178
Ты путаешь простой пиксель арт и pixel perfect. Понять разницу не сложно. Достаточно просто поизучать тему.
А статтеринг я победил, да. теперь движения без рывков. Которые, кстати, никуда не исчезают при не_pixel_perfect отрисовке.
Аноним 08/11/25 Суб 12:56:51 1059183 95
Аноним 11/11/25 Втр 08:04:10 1059651 96
>>1058848
Так результат тот же, не?
Кстати, tldr на оп-пост: замедляйте или ускоряйте игру соответственно частоте экрана игрока.
Аноним 11/11/25 Втр 11:30:44 1059671 97
>>1059651
конечно же нет.
Аккумулятор, который не переполняется стабильно каждые n кадров, создаст статтеринг.
Единственный способ уничтожить статтеринг - либо принудительно переключать режим монитора, либо обновлять логику через n целых кадров). Тогда тебе не придется полагаться на неточное время, которое капает в аккумулятор.
Аноним 11/11/25 Втр 11:36:11 1059672 98
>>1059671
да и если ты юзаешь интерполяцию - у тебя тоже будет статтеринг, потому что там при умножении на float у тебя нет контроля, сколько времени на каком пикселе у тебя пройдет.
Ты проходишь виртуальный юнит за секунду. Виртуальный юнит на определенном мониторе занял 7 пикселей. Частота 60гц.
Уже видно, что если ты растянешь 60 кадров на 7 пикселей, то каким то пикселям выпадет больше кадров, каким то меньше. привет, статтеринг
Аноним 11/11/25 Втр 12:25:20 1059678 99
Деды через PPU всё это делали и называли предотвращением субпиксельного движения. Оп здесь вообще долбоёб, называет статтерингом то, что не является статтерингом и костылями через анус решает проблемы, которые сам же и создал.
Аноним 11/11/25 Втр 12:28:20 1059681 100
>>1059678
в позиции 0 спрайт отрисовывался 2 кадра, в позиции 1 - 1 кадр. потом в позиции 2 опять два кадра
>называли предотвращением субпиксельного движения
Ууу, как все запущено...
Аноним 11/11/25 Втр 12:30:13 1059682 101
>>1059681
ты можешь языком трепать сколько угодно
выкладывай свою чудо-поделку без статтеринга, сами посмотрим своими глазами
Аноним 11/11/25 Втр 12:32:44 1059683 102
>>1059681
>в позиции 0 спрайт отрисовывался 2 кадра, в позиции 1 - 1 кадр. потом в позиции 2 опять два кадра
Если у тебя было линейное движение - то это баг в интерполяции и ты обосрался, чини. Если интерполяция работает, как положено, то такой ситуации не возникает вообще.
Аноним 11/11/25 Втр 12:36:15 1059684 103
>>1059683
>60 не делится на 7 без остатка
>это баг
Прикольно
Аноним 11/11/25 Втр 12:40:34 1059686 104
>>1059684
Так это потому что ты долбоёб. Алгоритм Брезенхэма, подсчёт PPU и правильная интерполяция. Всё, нет проблемы.
Аноним 11/11/25 Втр 12:43:58 1059687 105
>>1059686
Молодец, знаешь много умных слов. Но это не отменяет того, что 60 при делении на 7 не дает целого числа.
Аноним 11/11/25 Втр 13:00:29 1059692 106
>>1059686
посмотри на ОП-пик, это наверное игра ОПа

у него пиксельное ретро с разрешением 160 на 120 и 8 цветами

юнитом там является пиксель, и применение алгоритма брезенхейма не нужно т.к. пиксели рисуются как есть

>>1059687
это является проблемой только для безыгорников, которые дрочат форт вместо делания игр
Аноним 11/11/25 Втр 13:05:26 1059694 107
>>1059692
>это является проблемой
Да, и называется это статтерингом. Только равномерное распределение кадров на пиксель решает эту проблему
Аноним 11/11/25 Втр 13:13:17 1059696 108
>>1059694
нет, статтерингом называется другое, ты путаешь это с проблемой пиксель-перфектной сетки

лучше выложи скорее свой движок или игру на движке, демку какую-нибудь хотя бы
Аноним 11/11/25 Втр 13:24:40 1059702 109
>>1059687
>что 60 при делении на 7 не дает целого числа
Потому заебись, что это никогда не требуется.

>>1059692
>не нужно т.к. пиксели рисуются как есть
Он нужен хотя бы чтобы понять в каком именно пикселе рисовать сейчас, а в каком - в следующем кадре. С ним не будет такой хуйни, как долбоёб описывает, что у него движение рывками происходит. К тому же, даже если твоё пиксельное говно в разрешении 160х120 - ты никогда не будешь рендерить его в таком разрешении. И здесь опять опять нужен Брезенхэм.
Аноним 11/11/25 Втр 13:25:39 1059703 110
>>1059696
>проблемой пиксель-перфектной сетки
неравномерное распределение кадров никак не зависит от того, какая у тебя сетка.
Аноним 11/11/25 Втр 13:27:27 1059706 111
>>1059702
>это никогда не требуется
потребуется, конечно же.
частоты у мониторов разные. всегда можно подобрать частоту так, чтоб твой юнит проходил со статтерингом.
Аноним 11/11/25 Втр 13:31:29 1059708 112
>>1059703
так в твоём случае проблемы статтеринга и нет. ты запутался

ставишь фиксированную частоту кадров и перемещаешь на фиксированное число пикселей

а статтеринг появляется в играх с высоким разрешением и трёхмерной графикой

вспомнил очень старую статью на эту тему
https://gafferongames.com/post/fix_your_timestep/
Аноним 11/11/25 Втр 13:53:58 1059716 113
>>1059708
>ставишь фиксированную частоту кадров
ты про смену режима монитора?
если да, то я этот способ упоминал. у него свои минусы
Аноним 11/11/25 Втр 13:55:43 1059718 114
>>1059708
ну а статья - это классический аккумулятор, в который и время между кадрами неточное капает (croteam на это жаловались), и интерполяция через умножение на альфу, которая снова приводит к неравномерному распределению. зато всё детерменировано, это да.
Аноним 11/11/25 Втр 14:28:28 1059724 115
>>1059718
это не классический аккумулятор и он не пытается измерить время между кадрами

это решает проблему статтеринга но не решает проблему пиксельной сетки, потому что это разные проблемы. это ты смешал их в одну.

правда, при интерполяции появляется другая проблема - отображение отстаёт от симуляции на 1 кадр
Аноним 11/11/25 Втр 14:35:04 1059727 116
>>1059724
>он не пытается измерить время между кадрами
double newTime = time();
double frameTime = newTime - currentTime;
if ( frameTime > 0.25 )
frameTime = 0.25;
currentTime = newTime;

accumulator += frameTime;
frametime как бы даже в названии
Аноним 11/11/25 Втр 14:40:28 1059728 117
>>1059727
это для пополнения аккумулятора, а не для расчёта на сколько подвинуть симулируемый объект
Аноним 11/11/25 Втр 14:42:10 1059729 118
>>1059728
я ничего другого и не утверждал
>>1059718
аккумулятор, в который и время между кадрами неточное капает
Аноним 11/11/25 Втр 14:47:46 1059732 119
>>1059724
>это решает проблему статтеринга
нет, не решает. это решает повторяемость физики.
Аноним 11/11/25 Втр 15:00:56 1059737 120
Аноним 11/11/25 Втр 15:07:46 1059741 121
>>1059737
нет, статтерингом называется другое, ты путаешь это с проблемой недетерменированной физики
Аноним 11/11/25 Втр 15:10:25 1059744 122
>>1059741
статтеринг это и есть проблема недетерминированной физики/логики, когда твой апдейт игрового мира зависит от измерения частоты кадра, что нельзя сделать точно

а если он не зависит, то и статтеринга нет. проблема решена.
я не пытаюсь обесценить твой движок. лучше покажи его, чем вести пустые споры.
Аноним 11/11/25 Втр 15:13:55 1059747 123
>>1059744
>статтеринг это и есть проблема недетерминированной физики/логики
ну разумеется это неправда.
у тебя игра может выглядеть идеально плавно, но при проигрывании replay у тебя условный снаряд в worms полетел по другой траектории.
и в том и в том примере всё выглядело идеально гладко.
ты путаешь. вот один и тот же результат из раза в раз - это детерменированность. а неравномерное распределение кадров на пиксель это статтеринг
Аноним 11/11/25 Втр 15:23:38 1059751 124
>>1059747
статтеринг это "заикание", оно описывает такой эффект, когда твоя программа замеряет время кадра неправильно, т.к. точного механизма для этого не существует, и на основании этого двигает объекты

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

если ты думаешь, что это из-за пикселей на экране, то у тебя и при проигрывании видео будет "статтеринг"

может тебе стоит купить новый компьютер? возможно, твой пека просто не может выдавать даже 30 кадров в секунду. тут никакие программные ухищрения не помогут
Аноним 11/11/25 Втр 15:23:51 1059752 125
>>1059744
простой тебе пример.
у тебя двигается персонаж за 1 тик физики (60 гц, например) на 1 юнит.
ты для интер/экстраполяции умножаешь его cur_x на альфу, чтоб между тиками рисовать позиции. Но frametime всегда колеблется, и у тебя получается что и альфа сначала 0.16, потом 0.33, потом ваще 0.45. при умножении на нее у тебя каждый кадр будет разную финальную позицию выдавать.
но и это еще не все. ты можешь, как советовали crotek, выставить frametime в фиксированное значение, раз знаешь частоту, но даже если ты сделаешь так, включишь линейную фильтрацию (чтоб обьект плыл между пикселей, плавно меняя цвета пикселя от текущего к соседнему), то ты просто как бы увеличиваешь виртуальное разрешение. условный черный квадрат на белом фоне будет уметь перетекать очень мелкими шагами, а если контраст слабый, то таких шагов будет меньше.
Аноним 11/11/25 Втр 15:27:21 1059753 126
>>1059751
>при проигрывании видео будет "статтеринг"
конечно, на всяких panning это всегда заметно, если на 60гц смотришь 24 кадра видео, или на каком нибудь 144гц смотришь 60 fps
Аноним 11/11/25 Втр 15:35:02 1059756 127
>>1059751
>возможно, твой пека просто не может выдавать даже 30 кадров в секунду.
на джодоте - нет.
на топ движке sdl3 + форт - хоть тысячу. такие дела
Аноним 11/11/25 Втр 22:21:00 1059841 128
>>1059753
от 25 на 50 практический такой же статерринг. тут низкий фреймрейт на sample-and-hold дисплее виноват.
Аноним 11/11/25 Втр 23:23:52 1059850 129
>>1058847 (OP)
Кста, насчет звукового сопровождения.
кароч мутите максимально разнообразный звук длиной секунд 20 примерно. Чтоб там и скрежет был, потом пение птиц, еще какую нибудь дичь. Жмёте чем нибудь, например тем же adpcm на 15 строк кода и храните в ассетах.
Затем методом гранулярного синтеза тупо берете и бегаете по этому мегасэмплу одновременно в нескольких местах с разными скоростями и громкостями. можно не мутить полноценный adsr.
я вот юзаю ar. то есть сколько звук нарастает до заданной громкости и сколько затухает.
Я на одном только пении птиц замедленном лютые эмбиент текстуры выдавал. ух, УХ!
Ну и звуки можно соответственно мутить, просто сократив атаку и спад. В итоге 20 секунд моно аудио с сэмплрейтом в 44100 в ima_adpcm будет весить если не ошибаюсь в районе 450кб. Можно ваще в качестве сэмпла заюзать пак с ассетами (текстуры + скрипты), но не уверен, что там можно будет много клевых звуков получить.
воть. ну и звуковые файлы это будет тупо формат с относительными значениями времени между нотами и их длительностями.
Пользуйтесь наздоровье, братишки
Аноним 11/11/25 Втр 23:38:01 1059852 130
>>1059850
Ну и типа можно простенький hard knee limiter намутить. типа у меня в игре максимум может играть эмбиент окружение + 2 одновременных звука.
У них громкости будут 0.75.
значит даже если волны максимально неудачно совпадут, то пиковая громкость будет 0.75*3=2.25.
а нам нужно чтоб максимум был 1.0, а лучше чутка поменьше.
прижимать громкость будем значица начинать с 0.8.
множитель должен быть таким, чтоб 2.25 при умножении на него превращался в 1.0. если сэмплы тише 0.8 в сумме - то не трогаем, иначе берем 0.8 и прибавляем наше помноженное превышение. Я тестил кароч на трех одновременных звуках - ваще нойс, уж для эмбиента незаметно точно. Насчет резких ударных не скажу.
Не благодарите.
Аноним 12/11/25 Срд 00:07:23 1059854 131
Аноним 12/11/25 Срд 00:12:54 1059856 132
>>1059854
еще бы смешивания побольше. ощущение, будто максимум 2 прохода одновременных.
кста, чо только ща понял. у меня adpcm таблицы в глобальной области. но это имеет смысл, только если тебе нужно и кодирование и декодирование.
а так как в движке мне нужно только декодирование, то надо будет таблицы в функцию adpcm_decode прям запихать через static.
и назвать можно будет короче, и глобал лишний раз не засирать.
вот уж реально, когда рассказываешь резиновой уточке что то (или анончикам), то всякие топ озарения приходят. ваще огнёйс
Настройки X
Ответить в тред X
15000
Добавить файл/ctrl-v
Стикеры X
Избранное / Топ тредов