М.Двач / Gamedev

202x51
статтеринг повержен 6 ноя, 19:50 1058847 В конец треда | Веб

Итак, дорогие друзья. Да-да, поздравьте меня. Я наконец нашёл единственный способ уничтожжить статтеринг.
На самом деле их два. Первый - выпустить игру строго под, например, 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 для изображений и байткод версий скриптов. Короче будет весело

Ответы588555887158937591475985069538

2 6 ноя, 20:00 2 1058848

Я не пишу движков, но делал иначе. Минимальный шаг движения - 1 пиксель. Допустим, нам нужно двигать на 1 пиксель в секунду, тогда аккумулируем пройденное время, как только оно достигнет секунду, двигаем на 1 пиксель. Кстати в некоторых старых (80-90-ые) играх движение основано на таймерах-аккумуляторах, а не на умножении на дельту.

Ответы5884959651

3 6 ноя, 20:03 3 1058849

>>1058848
с аккумулятором тот же прикол получается и статтеринг не исчезает. смотри, представь, что ты задал движение 1 пиксель в секунду.
на 60гц у тебя будет капать в аккумулятор по 16,6666. это не 1.0 делить на целое число как например 0.5, 0.25, 0.125. А значит у тебя каждый каждый раз когда аккумулятор переполняется, будет оставаться остаток. И в какой то момент это выльется в то, что на 1 тик ты потратил 60 кадров, а на какой-то 59. И это тоже будет бить в глаза, когда персонаж двигается-двигается, а потом раз и заикнулся на мгновение.

Ответы58850

4 6 ноя, 20:08 4 1058850

>>1058849
это происходит при условии, что у тебя фиксированный ФПС

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

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

(но на самом деле при шаге в пиксель погрешность накопления при 60 кадрах и так незаметна глазу)

Ответы58852

5 6 ноя, 20:17 5 1058852

>>1058850
Не совсем понял про какой фиксированный фпс идет речь.
при вызове свапа буферов vsync ждёт сигнала от монитора. Возможно ты говоришь об отрисовке без vsync или о технологиях вроде freesync.
Без vsync будет тиринг и всё равно нужно как-то спать между кадрами, чтобы не греть процессор лишний раз. А спать ОС точно не умеют. у них гранулярность в 15 мс. То есть ты либо юзаешь vsync, либо спишь с возможностью упустить момент, либо спишь немного и остальное время доводишь через busy loop, который тоже такой себе способ.
Возможно зависит от человека, но мне редкое заикание при накоплении аккумулятора даже сильнее заметно, чем постоянное движение рывками. При постоянных рывках глаз хотя бы привыкает к этому как к стилю что ли (вроде постоянно дергающейся графики в baba is you). а в случае, когда у тебя игра идёт плавно, а потом раз в секунду условно заикается - очень заметно.

Ответы58857

6 6 ноя, 20:29 6 1058853

Ну что ж. Перед тем как впаривать вам про мою вм, расскажу вам, а нафига ваще нужна вм.
Смтрите, в зависимости от того, что у вас за игра - вы будете использовать самые разные технологии.
В статичных играх типа косынки можно рисовать прям в окно без всяких 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

6 6 ноя, 20:29 6 1058853

Ну что ж. Перед тем как впаривать вам про мою вм, расскажу вам, а нафига ваще нужна вм.
Смтрите, в зависимости от того, что у вас за игра - вы будете использовать самые разные технологии.
В статичных играх типа косынки можно рисовать прям в окно без всяких 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

Показать весь текст
Ответы5886659130

7 6 ноя, 20:41 7 1058855

>>1058847 (OP)
>персонаж движется 1 пиксель за кадр
Игровой мир нихуя не должен знать о пикселях, только о юнитах. Так же он нихуя не должен знать о кадрах и частоте монитора, только о частоте своих обновлений, которая фиксирована. Ты апдейтишь игровой мир, рендеришь фрейм в котором интерполируешь состояние мира, переводишь все юниты в пиксели на экране и все. В таком случае похуй, сколько фреймов между апдейтами(т.е похуй, какая частота монитора), они все будут отображать интерполированное состояние и передвигать персонажа плавно по экрану. А что за шизу ты пытаешься сделать я так и не понял до конца.

Ответы58858

8 6 ноя, 20:42 8 1058856

Вооот, и кароч какие фишки есть еще у этой вм. forward declaration - это когда ты можешь вызывать функцию, которая объявлена ниже места откуда ты ее вызываешь.
Так же я упомянул, что вм банковая. Она устроена так. Код с 0 по 32767 байт (примерно) - это код, в который загружается глобальный скрипт. В нём будут глобальные переменные, нужные между сценами и раличные утилиты.
А область с 32768 по 65535 доступна нам для загрузки нашего текущего скрипта. Таким образом часто повторяющуюся логику и данные необходимые между сценами мы храним в core.f, а данные самой сцены - в текущем скрипте. Текущий скрипт по договоренности знает в каких адресах какие функции и переменные лежат, так как это зашито на уровне компилятора. Поэтому мы тупо можем компилить скрипты вбайткод и готово. Никакой линковки в рантайме, никаких хранений символов для резолвинга. Это шикарно. Да, это не луа, где можно комбинировать кучу скриптов как блоки, но для моей игры это идеально подходит. Я хочу сделать максимально разнообразную игру, поэтому часто повторяющегося кода у меня будет немного. Например, вместо того, чтобы создавать мега объект player.f, с кучей состояний, я лучше просто для каждой сцены запилю свой код. Ценой некоторого дублирования, упрощается архитектура.
Вот вам наглядный пример:
Персонаж в доме. Рисуем его без верхней одежды. Ходит быстро.
Персонаж на улице в пургу. Рисуем его в куртке и идет в пургу он медленно. Персонаж в локации с озером умеет плавать и т.д. То есть если взять всю игру целиком, то может показаться, что персонаж - это лютый раздутый объект с кучей состояний, но на самом деле это просто конкретный скрипт сцены содержит необходимый код. Это позволяет мутить ваще любую дичь. Вот прям ваще. Хочешь в каком то уровне сделать не вид сбоку а вид сверху? Без проблем, хочешь где-то головоломку от первого лица с взаимодействием с рычагами? без проблем. И самое топовое, что весь этот код не хранится в памяти. вм тупо в любой момент времени жрет всего лишь 64кб оперативки на хранение кода и данных.

8 6 ноя, 20:42 8 1058856

Вооот, и кароч какие фишки есть еще у этой вм. forward declaration - это когда ты можешь вызывать функцию, которая объявлена ниже места откуда ты ее вызываешь.
Так же я упомянул, что вм банковая. Она устроена так. Код с 0 по 32767 байт (примерно) - это код, в который загружается глобальный скрипт. В нём будут глобальные переменные, нужные между сценами и раличные утилиты.
А область с 32768 по 65535 доступна нам для загрузки нашего текущего скрипта. Таким образом часто повторяющуюся логику и данные необходимые между сценами мы храним в core.f, а данные самой сцены - в текущем скрипте. Текущий скрипт по договоренности знает в каких адресах какие функции и переменные лежат, так как это зашито на уровне компилятора. Поэтому мы тупо можем компилить скрипты вбайткод и готово. Никакой линковки в рантайме, никаких хранений символов для резолвинга. Это шикарно. Да, это не луа, где можно комбинировать кучу скриптов как блоки, но для моей игры это идеально подходит. Я хочу сделать максимально разнообразную игру, поэтому часто повторяющегося кода у меня будет немного. Например, вместо того, чтобы создавать мега объект player.f, с кучей состояний, я лучше просто для каждой сцены запилю свой код. Ценой некоторого дублирования, упрощается архитектура.
Вот вам наглядный пример:
Персонаж в доме. Рисуем его без верхней одежды. Ходит быстро.
Персонаж на улице в пургу. Рисуем его в куртке и идет в пургу он медленно. Персонаж в локации с озером умеет плавать и т.д. То есть если взять всю игру целиком, то может показаться, что персонаж - это лютый раздутый объект с кучей состояний, но на самом деле это просто конкретный скрипт сцены содержит необходимый код. Это позволяет мутить ваще любую дичь. Вот прям ваще. Хочешь в каком то уровне сделать не вид сбоку а вид сверху? Без проблем, хочешь где-то головоломку от первого лица с взаимодействием с рычагами? без проблем. И самое топовое, что весь этот код не хранится в памяти. вм тупо в любой момент времени жрет всего лишь 64кб оперативки на хранение кода и данных.

Показать весь текст

9 6 ноя, 20:44 9 1058857

>>1058852
если у тебя включен vsync, то можешь мерять скорость спрайтов кол-вом кадров, как это сделано в старых дос играх, вот и всё, зачем тебе ещё какой-то источник синхронизации

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

>у них гранулярность в 15 мс.
попробуй написать проигрыватель миди-файлов без статтера
заикание звука для человека заметней, чем заикание движения

Ответы58859

10 6 ноя, 20:45 10 1058858

>>1058855
Это всё понятно, но это вызывает статтеринг. Смотри. Вот у тебя есть виртуальная единица. Условный метр/клетка. Она в реале будет занимать сколько-то пикселей на экране, согласен? И суть в том, что если ты не будешь двигаться с 1/0.5/0.25/0.125 пикселей в секунду, то из-за накоплений у тебя будет статтеринг, понимаешь? Да, если при переводе твоих юнито тебе повезло что на определенном мониторе с определенным разрешением у тебя при каждом рендере объект проходит фиксированное количество реальных пикселей - то поздравляю, статтеринга не будет. но и ситуации такой не будет

11 6 ноя, 20:48 11 1058859

>>1058857
>можешь мерять скорость спрайтов кол-вом кадров
И каковы твои дальнейщие действия? чтобы игра шла одинаково, тебе придется замедлять движение тем у кого герцовка выше, соглы? а замедлять ты обязан на такое значение, чтоб это было либо ровно пиксель, либо половина, либо четверть. То есть так замедлить, чтоб ты проходил реальный пиксель за одно и то же количество кадров каждый раз.

Ответы58861

12 6 ноя, 20:52 12 1058861

>>1058859
нет, игроку придётся переключиться на режим 60 гц если хочет помедленнее

т.к. мы по умолчанию делаем какой-то пиксельный кал для ностальгирующих 40-летних нердов, то он и так и так не окупится

Ответы5886258864

13 6 ноя, 20:56 13 1058862

>>1058861
>игроку придётся переключиться на режим 60 гц
Некоторые современные мониторы с повышенной герцовкой не поддерживают 60 гц. Точную модель я тебе не назову, но суть в том, что никто как бы не гарантирует, что монитор будет поддерживать 60гц

14 6 ноя, 20:58 14 1058864

>>1058861
К тому же непонятно, что ты там собрался замерять, если у тебя стратегия "Всех принудительно под 60 гц". Просто делаешь игру под 60 гц и всё

Ответы58866

15 6 ноя, 21:01 15 1058866

>>1058853
портируй свою машину под Эльбрус

>>1058864
не замерять, а исчислять. а ты почему так не сделал?

16 6 ноя, 21:09 16 1058868

О, еще топ тема. Кароч я не храню OP_EXECUTE отдельным байтом.
Когда вм считывает байт скрипта, она смотрит на старший бит. если там 0 - значит это обычный опкод (OP_ADD, OP_DRAW и т.д) и она тупо его выполняет. Если же там 1 - это сигнал, что перед нами вызов функции. вм считывает добавочный байт и из полученных 15 бит получает адрес функции которую нужно выполнить. То есть для вызова функции нужно 2 байта а не 3. Но адресное пространство уменьшается до 32кб. Но так как на данный момент у меня сцены занимают максимум 2кб - то это ваще ни о чем ограничение. + эта оптимизация сама по себе уменьшает размер скрипта, как бы делая адресацию более компактной. ваще нойс

17 6 ноя, 21:11 17 1058869

Отрицательные числа и вычитание? да это же просто wrap around. компилятор к отрицательным числам тупо добавляет 65536 и всё.
То есть если мы хотим к 100 прибавить -10, то мы прибавляем (-10+65536). таким образом результат переполнится до 90. Ну разве не топчик?

18 6 ноя, 21:12 18 1058870

Float? а нафига он нужен? есть же числа с фиксированной точкой. работаешь со значениями << 6, а при рендере обратно делишь через >> 6. и всё, можно делать медленные движения, каунтеры анимаций и прочее.

19 6 ноя, 21:13 19 1058871

>>1058847 (OP)

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

его можно заметить только если в твоей игре а) низкий фпс, б) игра считается в низком разрешении и для рендера апскейлится. достаточно повергнуть низкий фпс и апскейл, и статтеринга не будет

Ответы5887358962

20 6 ноя, 21:14 20 1058873

>>1058871
курсор быстро проходит расстояние и всё. а в игре долго и плавно перемещается персонаж например, или фон движется.

21 6 ноя, 21:16 21 1058874

Тред не читал, но Forth-систему одобряю. Топ тема.

Ответы588765887858957

22 6 ноя, 21:16 22 1058875

Такс, шо ещё.
так как это моя личная вм (да и потому что это форт), там можно в переменных юзать вопросительные знаки. Самое то для предикатов:
: 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 ;

22 6 ноя, 21:16 22 1058875

Такс, шо ещё.
так как это моя личная вм (да и потому что это форт), там можно в переменных юзать вопросительные знаки. Самое то для предикатов:
: 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 ;

Показать весь текст

23 6 ноя, 21:18 23 1058876

>>1058874
баловство это сплошное

24 6 ноя, 21:21 24 1058878

>>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? нафиг не надо. Токенайзер? тупо по пробелам разбил и всё. Форт это мегапростая штука

Ответы58879

25 6 ноя, 21:22 25 1058879

>>1058878
6 байт, не 5, точно

26 6 ноя, 21:27 26 1058883

Ах да, еще суть в чем. Раньше я делал так, что переменные сразу инициализировались при объявлении. Но пришел к выводу, что это от лукавого (баловство). ведь часто их приходится сбрасывать в начальное состояние. и тогда у нас появляется 2 источника истины. Лучше просто создавать переменную, а значение ей забивать в init или еще где нить. И вот отсюда какая то тема появилась. В байткоде мне не нужно хранить OP_CREATE_VAR условную. я коньпеллятор тупо считает количество переменных и фигачит это в виде всего 2 байт. а вм при загрузке скрипта считывает это число и выделяет нужное количество памяти.
не через malloc, разумеется. тупо размер меняет текущей позиции и всё. malloc ваще кал. у меня в движке всё заранее просчитано, какой атлас, сколько чего. огнёрий

Ответы58885

27 6 ноя, 21:29 27 1058885

>>1058883
а какая в этом польза для народного хозяйства?

Ответы58887

28 6 ноя, 21:29 28 1058886

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

29 6 ноя, 21:31 29 1058887

>>1058885
ну как. скрипт с 100 переменными будет весить не 100 байт, а фиксированно 2.

Ответы58888

30 6 ноя, 21:32 30 1058888

>>1058887
объём RAM не является проблемой для современных пека, особенно на таких малых масштабах

Ответы58891

31 6 ноя, 21:33 31 1058889

Ну и разумеется кроссплатформенность. Код движка у меня сейчас 2000 строк примерно (всё в одном .c файле для простоты). а код скриптов будет огого. Так вот, чтоб портировать игру куда угодно (или чтоб свалить с C+SDL3 на любую другую технологию, язык и т.д) нужно будет переписать только то, что хранится в 2000 строк C кода, а скрипты трогать не придется. ну разве не ультимативный опчек?

32 6 ноя, 21:35 32 1058891

>>1058888
А это не только ram, но и место на диске.
Ты спросил про пользу. Это польза. Одно дело сделать костыль, от которого только вред. Здесь же только плюсы, минусов нет.
Если переменная не предполагает никаких изменений, то тогда делаешь ее константой. если предполагает - то чтоб не было багов из-за двух источников истины - выставляй её строго в определенной функции (init/reset) и т.д

Ответы58895

33 6 ноя, 21:41 33 1058893

а еще плюшки всякие мелкие есть. Если мы пушим на стэк число, которое меньше 256, то коньпеллятор юзает OP_PUSH8, если больше или равно - то OP_PUSH16.
вм в зависимости от опкода будет знать, сколько байт ей считывать. это тоже экономия на куче всяких маленьких чисел по 1 байту.

34 6 ноя, 21:44 34 1058894

Ну а связь между скриптом и движком мегапростая. Это обычная система колбэков.
коньпеллятор находит адреса обязательных для скрипта слов:
: on_load ;
: on_keydown ;
: on_keyup ;
: on_update ;
: on_draw ;
и сохраняет их в конце скрипта.
вм при загрузке берет эти значения и запоминает. а дальше все просто - C цикл при нужных событиях тупо прыгает по этим адресам. В конце колбэка виртуальная машина останавливается и C часть дальше работает

35 6 ноя, 21:46 35 1058895

>>1058891
Еще скажи что из одежды у тебя только трусы и густая борода, А еще ты босиком по снегу ходишь и пристаешь молодежи рассказывая как экононить память на переменных.

Ответы58897

36 6 ноя, 21:48 36 1058896

причем зацените прикол. на самом деле при нажатии клавиш sdl3 вызывает колбэк из core.f который назвывается on_raw_keydown. и именно там происходит логика работы с биндингом и отсеиванием.
например, если мы сейчас биндим клавиши, то тогда мы вызываем on_keydown. или же если нажата клавиша, которая уже забинжена. В противном случае on_keydown просто не вызывается. То есть в скриптах не нужно постоянно отсеивать, нажата ли именно реально зареганная клавиша, или просто левая. вм не вызовет колбэк если это левая какая-то клавиша

37 6 ноя, 21:49 37 1058897

>>1058895
Не, ну погоди. а что плохого то в экономии памяти? я согласен, что это не критично. но и поводов не экономить не вижу.

38 6 ноя, 21:52 38 1058898

О, а зацените еще топ тему. кароч при самом старте игры в первый раз игрока просят забиндить клавиши. Таким образом даже если у тебя раздолбаны wasd + стрелки ты можешь сразу же без всяких переходов произвести удобный биндинг. Разумеется позже всегда можно их переделать. Так вот, чтоб узнать, впервые ли запущена игра или нет, есть топ способ.
я просто чекаю, что клавиша 1 не равна клавише 2 в массиве забинженых клавиш. Прикол в том, что по умолчанию там хранятся ноли, а биндинг не позволяет забить одну и ту же клавишу на разные действия. поэтому если ты прошел биндинг, то в памяти эти значения будут разнцми. если нет - одинаковыми. Ну разве не топ тема?

39 6 ноя, 21:57 39 1058900

тэкс, а это утилиты из 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 локальных переменных и флаги я тоже условно делю на глобальные и локальные.

39 6 ноя, 21:57 39 1058900

тэкс, а это утилиты из 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 локальных переменных и флаги я тоже условно делю на глобальные и локальные.

Показать весь текст
Ответы58903

40 6 ноя, 22:02 40 1058901

а еще зацените подход. мега удобный и простой. вместо того, чтоб мутить простыни из ifdef/ifndef для разделения на debug/release билд я пошел другим путем. как я уже писал - весь C код у меня хранится в одном файле. Когда игра будет готова, я просто скопирую его и отредактирую под release версию. то есть сделаю так, чтоб ассеты грузились не из реальных .bmp файлов, а по смещению в assets.pack и из своего простого формата сжатого через самопальный lz4. Воть. то есть будет 2 файла. один для дебага, второй для релиза.

Ответы58903

41 6 ноя, 22:06 41 1058902

а суть в том, что у меня уже юзается простой совместимый способ для работы как с debug так и release версией. смотрите, у меня в папке textures например лежит файл menu.bmp
но я не пишу его в скриптах под этим именем. компилятор превращает его в T_MENU
и это по сути просто индекс ассета от 0 до 65535
в дебаг версии у меня генерится массив путей для текстур, где textures[0] = "menu"
а в релиз версии будет юзаться массив смещений. Круто, да? мне не нужно будет ничего менять. просто в зависимости от того релизный у меня билд или нет, движок будет какой-нибудь M_PLAYER интерпретировать либо как "textures/" + textures[666] + ".bmp", либо как fseek(asset_offsets[666])

Ответы58903

42 6 ноя, 22:08 42 1058903

>>1058902
>>1058901
>>1058900
круто для 1993 года

Ответы58905

43 6 ноя, 22:09 43 1058904

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

44 6 ноя, 22:10 44 1058905

>>1058903
А это и сейчас круто. Никаких систем cmake и прочего. компиляция через tcc мгновенная. всё чётко работает без сюрпризов. в финальном билде никакого bloated кода нет.

45 6 ноя, 22:13 45 1058906

Раньше на godot сидел, это реально кошмар. То одно api сломают, то другое. то ли дело SDL. мажорные релизы раз в 11 лет выпускают, и то я с sdl2 на sdl3 за пару часов перешел. Вот что значит юзать зависимости только для самой базы, а остальное своё. никаких сюрпризов, максимальная гибкость и удобство.
SDL ваще шикарная штука. Решает ровно те задачи, которые платформозависимы, а то, что везде одинаково - мутишь сам. Ваще никак не лезет в твои дела.
Даже эти ужасные vulkan/metal абстрагирует. У тебя просто единый api для загрузки/отрисовки текстур.

Ответы58907

46 6 ноя, 22:16 46 1058907

>>1058906
игру-то покажи
мне всё равно, на чём ты писал скрипт - на си шарпе, луа или самодельном форте

Ответы58908

47 6 ноя, 22:19 47 1058908

>>1058907
Я хочу всё чётко сделать. план такой - добавлю сохранение кадра в bmp и открою игру в минимальном окне (320x180). похожу там, поделаю что-нибудь, а потом уже через какой-нить ffmpeg увеличю и соберу хоть в gif хоть в видос. а то если просто записывать - будет не то. не смогу всё величие передать.
так что ваще огонь будет

Ответы5890958937

48 6 ноя, 22:26 48 1058909

>>1058908
>величие
От величия обычно таблетки помогают
Хороший тролинг

Ответы58911

49 6 ноя, 22:26 49 1058910

Кста, насчёт игры. она будет абсолютно без текста. Как limbo, inside, и в какой-то степени animal well / yume nikki. то есть в последних двух, конечно, есть текст, но от него смысла почти и нет.
Только представьте - игра сразу доступна каждому, у кого есть комп и интернет. Никаких тебе проблем с RTL текстом, никаких китайских огромных атласов и гемора с отрисовкой их символов в низком разрешении. никакой арабской вязи, которая не приемлет monospace символы а требует единый красиво связный текст. Ну и конечно же нет проблем с тем, что переводчики не так передатут шутку или идиомы какие. Ну и в принципе, обычно переводят на самые популярные языки, а остальные в пролёте. здесь же все люди мира смогут играть. Шо ещё. Так как в игре не требуется мышка, то и на консоли можно портануть. Я уже реализовал некоторое количество загадок. Кароч в игре будет один связный мир, и куча загадок. но все они будут уникальны и не будут повторяться. Причем для базового прохождения будут не очень сложные, а опционально для 100 процентов придется попотеть.
Кста, настройки игры происходят в самой игре. Это и регулировка громкости, и выбор полный экран/окно. а использоваться будут лишь клавиши вверх вниз влево вправо и одна еще чтоб попадать в сцену menu (escape, например)

50 6 ноя, 22:28 50 1058911

>>1058909
Не, это реально будет артефакт. я 4 года изучал нужные технологии, мутил прототипы, и вот, наконец начал мутить. Архитектурно всё готово, несколько сцен есть, идеи для загадок тоже. еще лет 6 и можно будет релизить

51 6 ноя, 22:35 51 1058912

можно кста опционально выпускать релиз в одном файле. разрабы SDL не рекомендуют статичную линковку, и я их понимаю. но никто не мешает выпустить в двух вариантах. Картинки хорошо жмутся, звуки и музыка - синтезированные, так что ассеты будут мало весить. Их можно либо в конец файла закинуть через cat, например. но некоторые антивирусы считают такое поведение как вредоносное. можно просто все ассеты в C массив поместить и они тупо в оперативу попадут. Так как выбор билда будет, то проблем не возникнет. Встроить ассеты в бинарник это даже еще проще чем в assets.pack, так как не придется получать путь, по которому лежит бинарник. Тупо обращаешься к массиву и готово

52 7 ноя, 07:47 52 1058937

>>1058847 (OP)
>если же вы делаете pixel perfect игру с условным виртуальным разрешением 320x180
>>1058908
>открою игру в минимальном окне (320x180)

вот и причина статтеринга

Ответы58956

53 7 ноя, 12:13 53 1058956

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

Ответы59157

54 7 ноя, 12:15 54 1058957

>>1058874
там не совсем форт система. никакого словаря и шитого кода нет. просто скрипты компилятся в байткод, а потом вм его выполняет. поэтому я и написал, что вм форт подобная. от форта я взял синтаксис (@ ! . dup drop swap over), стэковый подход и обратную польскую нотацию.

Ответы5896459034

55 7 ноя, 12:20 55 1058960

Игра - это функция. на вход подаются нажатия клавиш, на выход - массив пикселей и аудиосэмплы. топ?

Ответы58963

56 7 ноя, 12:26 56 1058962

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

Ответы58965

95 Кб, 800x494
57 7 ноя, 12:30 57 1058963

>>1058960
Никто не оценит. Вопрос не имеет смысла. Если решил быть как Пэй Мэй то сиди один на горе, а не демонстрируй свое "вяличие" перед мимокроками, а то плохо кончишь, как Пэй Мэй.

Ответы58980

58 7 ноя, 12:30 58 1058964

>>1058957
то есть всё самое худшее

ведь плюс форт-системы в том, что её можно запустить на картошке без ОС и в том, что пользователь может написать свой словарь, а не в обратной нотации и прямой манипуляции стэком

Ответы5898058982

59 7 ноя, 12:31 59 1058965

>>1058962
я вот сейчас проследил за своим виндовым курсором и заметил что он как-то дёргается. надо писать свою ОС.

Ответы58980

60 7 ноя, 13:53 60 1058980

>>1058965
ну, или можно юзать курсор по назначению - не следить за ним, а наводить в нужную точку.
>>1058964
Наоборот. скрипты компактные, компилятор и вм просты как палка.
>>1058963
Я просто максимально по простому рассказал шо такое игра. А то вдруг кому то слишком сложны все эти ваши тиринги, статтеринги, fixed timestepы и прочее. такие дела

Ответы5898359034

61 7 ноя, 13:57 61 1058982

>>1058964
плюс скорость исполнения лютая. вм либо функцию по индексу вызывает (опкод), либо прыгает в нужную позицию в коде (функция). никаких поисков по строкам нет. ни хэш таблиц не надо, ни malloc. всё чётко, быстро, просто, компактно. ух! УХ!

Ответы59034

62 7 ноя, 13:58 62 1058983

>>1058980
Для fixed timestep-ов и прочего необязательно говной обмазываться и писать свою ОС. Это базовые понятия и они применимы везде, а не только в шизе где экономят память на переменных.

Ответы5902759030

63 7 ноя, 16:21 63 1059027

>>1058983
>Для fixed timestep-ов и прочего необязательно говной обмазываться и писать свою ОС
Никто и не спорит. ОС уже написаны, курсоры уже статтерят. Если их использовать по назначению, то никаких проблем с ними нет.
Усё так

64 7 ноя, 16:23 64 1059030

>>1058983
>экономить память - это шиза
Прикольное мировоззрение

Ответы59032

65 7 ноя, 16:36 65 1059032

>>1059030
если ты сэкономил 100 гигабайт - ты молодец
100 байт - шиз

Ответы59035

66 7 ноя, 16:40 66 1059034

>>1058957
>шитого кода нет
>скрипты компилятся в байткод
>>1058980
>компилятор и вм просты как палка
>>1058982
>плюс скорость исполнения лютая
Шитый код быстрее и проще байткода.

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

Шитый код выполняется "как есть".

Ответы59037

67 7 ноя, 16:42 67 1059035

>>1059032
А четкая линия где проходит, не подскажешь? если сто мегабайт например всего сэкономил - это уже шиз или еще нет?

Ответы59036

68 7 ноя, 16:44 68 1059036

>>1059035
смотря сколько денег ты сэкономишь и какой у тебя бюджет

69 7 ноя, 16:44 69 1059037

>>1059034
А вот и не портянка. никаких case. есть массив указателей на функции и ты просто их через индекс вызываешь.

Ответы59038

70 7 ноя, 16:51 70 1059038

>>1059037
>есть массив указателей на функции
Тогда это уже получается шитый код?

Байткод - это 1 байт на одну функцию.
Шитый код - это 4 (x32) или 8 (x64) байт.

Ответы59040590415904459055

71 7 ноя, 16:53 71 1059040

>>1059038
шитый код в классической форт системе при вызове условного draw должен пройти по словарю и найти где такая запись есть. это дольше чем смещение + дереференс

Ответы59067

72 7 ноя, 17:00 72 1059041

>>1059038
>это 4 (x32) или 8 (x64) байт.
некроссплатформенные скрипты какие-то...

73 7 ноя, 17:08 73 1059044

>>1059038
>Тогда это уже получается шитый код?
Не. в форте все функции фигачатся в словарь.
а тут есть разделение - самая база (опкоды типа swap add over + высокоуровневые опкоды типа draw play_sfx blit) это реальные функции на C. Есть массив указателей на эти функции, и скрипты тупо юзают 1байтовый индекс для их вызова. если же мы говорим о пользовательских функциях, типа update_enemies, то это уже слово в скрипте. и вот оно вызывается используя 16бит (1 бит чтоб сообщить, что это вызов функции, а не базовый опкод), и 15 бит адрес. Кроссплатформенно и компактно.
Огнёрий одним словом

Ответы59067

74 7 ноя, 17:48 74 1059055

>>1059038
кароч, вижу, что слишком сложно для тебя. давай разжую всё.
Смотри - при вызове слова, определенного в скрипте, мы просто кладем в возвратный стэк значение и прыгаем по 15битному адресу. никаких массивов, дереференса и прочего. считал адрес - перешел. всё, изи.
но весь код невозможно написать на форте, ведь вм должна уметь выполнять базовые вещи, из которых ты уже и будешь формировать слова. И вот уже в этом случае мы пишем код на C. и для его вызова мутим массив функций. Теперь то дошло до тебя?

75 7 ноя, 18:39 75 1059067

>>1059040
>должен пройти по словарю и найти
>>1059044
>все функции фигачатся в словарь
Ты/вы путаешь/те интерпретацию с компиляцией.

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

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

Именно поэтому Форт быстрее всех скриптовых ЯП, поскольку он компилируется в шитый код сразу, а производительность шитого кода примерно равна машинному, потому что он выполняется самой этой машиной (процессором), а не каким-то там C/C++.

Ответы5906959071

76 7 ноя, 18:47 76 1059069

>>1059067
то есть ты предлагаешь в финальном билде тащить компилятор, а скрипты в исходном виде? оо да, вот это угар. смех да и только.
зато пользователь сможет переопределять слова, как жить то без этого

77 7 ноя, 18:55 77 1059071

>>1059067
>выполняется самой этой машиной (процессором), а не каким-то там C/C++.
>Именно поэтому Форт быстрее всех скриптовых ЯП
Ого, ничего себе. Процессор быстрее чем вм. вот это да

78 8 ноя, 02:02 78 1059130

>>1058853
Ты только что DCPU-16

Ответы59142

79 8 ноя, 07:30 79 1059142

>>1059130
dcpu-16 был регистровым вроде.
регистровая вм геморная во внутреннем устройстве. меня forth этим и зацепил.
есть просто стэк (2 стэка, включая возвратный) и ты туда просто кидаешь значения и работаешь с ними. главное соблюдать порядок, и ничего вручную указывать не придется. шикарно просто поток данных. форт напрямую готов к тому, чтоб на нем писать, не нужно поверх делать высокоуровневую обвязку как в луа, которая внутри с 5 версии тоже регистровая.

80 8 ноя, 10:56 80 1059147

>>1058847 (OP)
Кстати, можно вместо определения частоты монитора и frameskip просто юзать аккумулятор. Главное не вычитать из аккумулятора время, а просто обнулять его при переполнении.
Ставим значение не 33, а, например, 30 чтоб гарантировано переполнялось и всё.
Таким образом монитор с частотой 60гц будет прибавлять по 16-17мс, то есть гарантировано каждые 2 кадра будет происходить тик (32 > 30). и так для любой частоты. то есть нужно просто обнулять аккумулятор, чтоб никакие остатки не накапливались.
Проверка для большой частоты. Если монитор 1000гц, то он будет добавлять по 1 в аккумулятор. значит каждые 30 кадров будет происходить тик. Правда в таком случае нужно назвать аккумулятор как-то по другому. Хоть он и копит значение до 30, но остаток то не накапливается. Логичнее его просто счётчиком назвать и всё. Изи бризи таймер сквизи. Пользуйтесь, не благодарите.

Ответы59155

81 8 ноя, 11:54 81 1059155

>>1059147
условный 500 гц монитор будет сообщать то 1 то 2 мс (или ваще 0), и снова будет статтеринг из за разного времени между тиками. тогда уж надежнее не маленькие дельты замерять и копить, а просто проверять что get_ticks() - last_time >= 30.

82 8 ноя, 11:55 82 1059157

>>1058956
а у тебя что все спрайты с одной скоростью всегда двигаются? в общем, ничего тупее я на этой неделе еще не слышал
неудивительно, что оп даже ни одной гифки не показал, все увидят как скачет его ламерский апскейл

Ответы59158

83 8 ноя, 12:00 83 1059158

>>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. Понимаешь?

Ответы59160

84 8 ноя, 12:13 84 1059160

>>1059158
твоя система не поддерживает произвольные скорости. в 60 фпс скорость 80 п/с будет так: 1 1 2 1 1 2 1 1 2
ты ничего с этим не сделаешь, я всегда найду такую скорость которая полностью ломает твоё говно
то есть в твоей параше спрайт может двигаться либо со скоростями 15, 30, 60, 120 п/с
ну и выглядит это естественно еще хуже чем статтеринг, 0 плавности, какие-то инкрементальные ускорения решающие несущетсвующую проблему

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

Ответы5916359166

85 8 ноя, 12:16 85 1059163

>>1059160
Разумеется это вранье. Статтеринг хуже. И какую такую ты там скорость собрался находить я не понял. Реверс инжиниринг сделаешь что ли?

Ответы59168

86 8 ноя, 12:23 86 1059166

>>1059160
>может двигаться либо со скоростями 15, 30, 60, 120 п/с
Ого, в пиксельарте дискретная сетка. Не может быть.

Ответы59168

87 8 ноя, 12:27 87 1059168

>>1059163
мне не нужно, я это в голове лучше тебя представляю, потому что прошелся уже по всем этим граблям в SDL

>>1059166
у адекватов всё плавно меняется, без скачков

Ответы5917159173

88 8 ноя, 12:29 88 1059171

>>1059168
>мне не нужно
А ты и не можешь. Потому что в моей игре я пишу что и как будет двигаться. Твоё "а вот я могу там что-то поменять и всё сломается" это просто демагогия

Ответы59172

89 8 ноя, 12:30 89 1059172

>>1059171
никто не видел твою игру и уже не увидит после позорища с апскейлом

Ответы59173

90 8 ноя, 12:32 90 1059173

>>1059172
>позорища с апскейлом
>>1059168
>у адекватов всё плавно меняется
Кое кто явно не в курсе, что такое pixel perfect. Можешь поизучать на досуге

Ответы59175

91 8 ноя, 12:33 91 1059175

>>1059173
пиксель-перфект это когда выглядит как позорное говнище где скорость меняется в 2 раза и скролл по 10 пикселей прыгает за раз

Ответы59176

92 8 ноя, 12:35 92 1059176

>>1059175
> параша
> говнище
Ну точно не в курсе. Поизучай, может и слова новые узнаешь заодно

Ответы59178

93 8 ноя, 12:38 93 1059178

>>1059176
именно эти 2 слова лучше всего описывают то что ты считаешь пиксель-перфектом и твою потешную текстовую безгифочную победу над статтерингом

у адекватов на экране не 180 пикселей, а 1080, и всё равно всё пиксель-перфектно

Ответы59182

94 8 ноя, 12:51 94 1059182

>>1059178
Ты путаешь простой пиксель арт и pixel perfect. Понять разницу не сложно. Достаточно просто поизучать тему.
А статтеринг я победил, да. теперь движения без рывков. Которые, кстати, никуда не исчезают при не_pixel_perfect отрисовке.

Ответы59183

95 8 ноя, 12:56 95 1059183

>>1059182
удачи

96 11 ноя, 08:04 96 1059651

>>1058848
Так результат тот же, не?
Кстати, tldr на оп-пост: замедляйте или ускоряйте игру соответственно частоте экрана игрока.

Ответы59671

97 11 ноя, 11:30 97 1059671

>>1059651
конечно же нет.
Аккумулятор, который не переполняется стабильно каждые n кадров, создаст статтеринг.
Единственный способ уничтожить статтеринг - либо принудительно переключать режим монитора, либо обновлять логику через n целых кадров). Тогда тебе не придется полагаться на неточное время, которое капает в аккумулятор.

Ответы59672

98 11 ноя, 11:36 98 1059672

>>1059671
да и если ты юзаешь интерполяцию - у тебя тоже будет статтеринг, потому что там при умножении на float у тебя нет контроля, сколько времени на каком пикселе у тебя пройдет.
Ты проходишь виртуальный юнит за секунду. Виртуальный юнит на определенном мониторе занял 7 пикселей. Частота 60гц.
Уже видно, что если ты растянешь 60 кадров на 7 пикселей, то каким то пикселям выпадет больше кадров, каким то меньше. привет, статтеринг

99 11 ноя, 12:25 99 1059678

Деды через PPU всё это делали и называли предотвращением субпиксельного движения. Оп здесь вообще долбоёб, называет статтерингом то, что не является статтерингом и костылями через анус решает проблемы, которые сам же и создал.

Ответы59681

100 11 ноя, 12:28 100 1059681

>>1059678
в позиции 0 спрайт отрисовывался 2 кадра, в позиции 1 - 1 кадр. потом в позиции 2 опять два кадра
>называли предотвращением субпиксельного движения
Ууу, как все запущено...

Ответы5968259683

101 11 ноя, 12:30 101 1059682

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

102 11 ноя, 12:32 102 1059683

>>1059681
>в позиции 0 спрайт отрисовывался 2 кадра, в позиции 1 - 1 кадр. потом в позиции 2 опять два кадра
Если у тебя было линейное движение - то это баг в интерполяции и ты обосрался, чини. Если интерполяция работает, как положено, то такой ситуации не возникает вообще.

Ответы59684

103 11 ноя, 12:36 103 1059684

>>1059683
>60 не делится на 7 без остатка
>это баг
Прикольно

Ответы59686

104 11 ноя, 12:40 104 1059686

>>1059684
Так это потому что ты долбоёб. Алгоритм Брезенхэма, подсчёт PPU и правильная интерполяция. Всё, нет проблемы.

Ответы5968759692

105 11 ноя, 12:43 105 1059687

>>1059686
Молодец, знаешь много умных слов. Но это не отменяет того, что 60 при делении на 7 не дает целого числа.

Ответы5969259702

106 11 ноя, 13:00 106 1059692

>>1059686
посмотри на ОП-пик, это наверное игра ОПа

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

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

>>1059687
это является проблемой только для безыгорников, которые дрочат форт вместо делания игр

Ответы5969459702

107 11 ноя, 13:05 107 1059694

>>1059692
>это является проблемой
Да, и называется это статтерингом. Только равномерное распределение кадров на пиксель решает эту проблему

Ответы59696

108 11 ноя, 13:13 108 1059696

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

лучше выложи скорее свой движок или игру на движке, демку какую-нибудь хотя бы

Ответы5970359737

109 11 ноя, 13:24 109 1059702

>>1059687
>что 60 при делении на 7 не дает целого числа
Потому заебись, что это никогда не требуется.

>>1059692
>не нужно т.к. пиксели рисуются как есть
Он нужен хотя бы чтобы понять в каком именно пикселе рисовать сейчас, а в каком - в следующем кадре. С ним не будет такой хуйни, как долбоёб описывает, что у него движение рывками происходит. К тому же, даже если твоё пиксельное говно в разрешении 160х120 - ты никогда не будешь рендерить его в таком разрешении. И здесь опять опять нужен Брезенхэм.

Ответы59706

110 11 ноя, 13:25 110 1059703

>>1059696
>проблемой пиксель-перфектной сетки
неравномерное распределение кадров никак не зависит от того, какая у тебя сетка.

Ответы59708

111 11 ноя, 13:27 111 1059706

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

112 11 ноя, 13:31 112 1059708

>>1059703
так в твоём случае проблемы статтеринга и нет. ты запутался

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

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

вспомнил очень старую статью на эту тему
https://gafferongames.com/post/fix_your_timestep/

Ответы5971659718

113 11 ноя, 13:53 113 1059716

>>1059708
>ставишь фиксированную частоту кадров
ты про смену режима монитора?
если да, то я этот способ упоминал. у него свои минусы

114 11 ноя, 13:55 114 1059718

>>1059708
ну а статья - это классический аккумулятор, в который и время между кадрами неточное капает (croteam на это жаловались), и интерполяция через умножение на альфу, которая снова приводит к неравномерному распределению. зато всё детерменировано, это да.

Ответы5972459729

115 11 ноя, 14:28 115 1059724

>>1059718
это не классический аккумулятор и он не пытается измерить время между кадрами

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

правда, при интерполяции появляется другая проблема - отображение отстаёт от симуляции на 1 кадр

Ответы5972759732

116 11 ноя, 14:35 116 1059727

>>1059724
>он не пытается измерить время между кадрами
double newTime = time();
double frameTime = newTime - currentTime;
if ( frameTime > 0.25 )
frameTime = 0.25;
currentTime = newTime;

accumulator += frameTime;
frametime как бы даже в названии

Ответы59728

117 11 ноя, 14:40 117 1059728

>>1059727
это для пополнения аккумулятора, а не для расчёта на сколько подвинуть симулируемый объект

Ответы59729

118 11 ноя, 14:42 118 1059729

>>1059728
я ничего другого и не утверждал
>>1059718
аккумулятор, в который и время между кадрами неточное капает

119 11 ноя, 14:47 119 1059732

>>1059724
>это решает проблему статтеринга
нет, не решает. это решает повторяемость физики.

Ответы59737

120 11 ноя, 15:00 120 1059737

>>1059732
>>1059696

Ответы59741

121 11 ноя, 15:07 121 1059741

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

Ответы59744

122 11 ноя, 15:10 122 1059744

>>1059741
статтеринг это и есть проблема недетерминированной физики/логики, когда твой апдейт игрового мира зависит от измерения частоты кадра, что нельзя сделать точно

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

Ответы5974759752

123 11 ноя, 15:13 123 1059747

>>1059744
>статтеринг это и есть проблема недетерминированной физики/логики
ну разумеется это неправда.
у тебя игра может выглядеть идеально плавно, но при проигрывании replay у тебя условный снаряд в worms полетел по другой траектории.
и в том и в том примере всё выглядело идеально гладко.
ты путаешь. вот один и тот же результат из раза в раз - это детерменированность. а неравномерное распределение кадров на пиксель это статтеринг

Ответы59751

124 11 ноя, 15:23 124 1059751

>>1059747
статтеринг это "заикание", оно описывает такой эффект, когда твоя программа замеряет время кадра неправильно, т.к. точного механизма для этого не существует, и на основании этого двигает объекты

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

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

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

Ответы5975359756

125 11 ноя, 15:23 125 1059752

>>1059744
простой тебе пример.
у тебя двигается персонаж за 1 тик физики (60 гц, например) на 1 юнит.
ты для интер/экстраполяции умножаешь его cur_x на альфу, чтоб между тиками рисовать позиции. Но frametime всегда колеблется, и у тебя получается что и альфа сначала 0.16, потом 0.33, потом ваще 0.45. при умножении на нее у тебя каждый кадр будет разную финальную позицию выдавать.
но и это еще не все. ты можешь, как советовали crotek, выставить frametime в фиксированное значение, раз знаешь частоту, но даже если ты сделаешь так, включишь линейную фильтрацию (чтоб обьект плыл между пикселей, плавно меняя цвета пикселя от текущего к соседнему), то ты просто как бы увеличиваешь виртуальное разрешение. условный черный квадрат на белом фоне будет уметь перетекать очень мелкими шагами, а если контраст слабый, то таких шагов будет меньше.

126 11 ноя, 15:27 126 1059753

>>1059751
>при проигрывании видео будет "статтеринг"
конечно, на всяких panning это всегда заметно, если на 60гц смотришь 24 кадра видео, или на каком нибудь 144гц смотришь 60 fps

Ответы59841

127 11 ноя, 15:35 127 1059756

>>1059751
>возможно, твой пека просто не может выдавать даже 30 кадров в секунду.
на джодоте - нет.
на топ движке sdl3 + форт - хоть тысячу. такие дела

128 11 ноя, 22:21 128 1059841

>>1059753
от 25 на 50 практический такой же статерринг. тут низкий фреймрейт на sample-and-hold дисплее виноват.

129 11 ноя, 23:23 129 1059850

>>1058847 (OP)
Кста, насчет звукового сопровождения.
кароч мутите максимально разнообразный звук длиной секунд 20 примерно. Чтоб там и скрежет был, потом пение птиц, еще какую нибудь дичь. Жмёте чем нибудь, например тем же adpcm на 15 строк кода и храните в ассетах.
Затем методом гранулярного синтеза тупо берете и бегаете по этому мегасэмплу одновременно в нескольких местах с разными скоростями и громкостями. можно не мутить полноценный adsr.
я вот юзаю ar. то есть сколько звук нарастает до заданной громкости и сколько затухает.
Я на одном только пении птиц замедленном лютые эмбиент текстуры выдавал. ух, УХ!
Ну и звуки можно соответственно мутить, просто сократив атаку и спад. В итоге 20 секунд моно аудио с сэмплрейтом в 44100 в ima_adpcm будет весить если не ошибаюсь в районе 450кб. Можно ваще в качестве сэмпла заюзать пак с ассетами (текстуры + скрипты), но не уверен, что там можно будет много клевых звуков получить.
воть. ну и звуковые файлы это будет тупо формат с относительными значениями времени между нотами и их длительностями.
Пользуйтесь наздоровье, братишки

Ответы5985259854

130 11 ноя, 23:38 130 1059852

>>1059850
Ну и типа можно простенький hard knee limiter намутить. типа у меня в игре максимум может играть эмбиент окружение + 2 одновременных звука.
У них громкости будут 0.75.
значит даже если волны максимально неудачно совпадут, то пиковая громкость будет 0.75*3=2.25.
а нам нужно чтоб максимум был 1.0, а лучше чутка поменьше.
прижимать громкость будем значица начинать с 0.8.
множитель должен быть таким, чтоб 2.25 при умножении на него превращался в 1.0. если сэмплы тише 0.8 в сумме - то не трогаем, иначе берем 0.8 и прибавляем наше помноженное превышение. Я тестил кароч на трех одновременных звуках - ваще нойс, уж для эмбиента незаметно точно. Насчет резких ударных не скажу.
Не благодарите.

131 12 ноя, 00:07 131 1059854

>>1059850
отличные советы и музыка прекрасная получилась
https://vocaroo.com/128880AeMXZq

Ответы59856

132 12 ноя, 00:12 132 1059856

>>1059854
еще бы смешивания побольше. ощущение, будто максимум 2 прохода одновременных.
кста, чо только ща понял. у меня adpcm таблицы в глобальной области. но это имеет смысл, только если тебе нужно и кодирование и декодирование.
а так как в движке мне нужно только декодирование, то надо будет таблицы в функцию adpcm_decode прям запихать через static.
и назвать можно будет короче, и глобал лишний раз не засирать.
вот уж реально, когда рассказываешь резиновой уточке что то (или анончикам), то всякие топ озарения приходят. ваще огнёйс

254 Кб, 765x540
133 27 дек, 10:03 133 1066575

Это менюшка. тут можно регулировать громкость, переключаться между полноэкранный/оконным режимом, ребиндить клавиши, сбрасывать прогресс (выбрасывая геймпад или картридж в мусорку). ну и выйти можно.

69 Кб, 765x540
134 27 дек, 10:11 134 1066576

Ну а если просто заглянуть в мусорку, не держа ничего в руках, то можно там откопать лицензию (что кагбэ намекает, где ей самое место, и насколько всем интересно ее читать)

548 Кб, 765x540
135 27 дек, 11:01 135 1066584

805 Кб, 765x540
136 27 дек, 11:17 136 1066588

мини игра. пока тестово её разместил при переходе в левую область. С ней будет загадочка связана, необязательная для прохождения. ну или можно просто рекорд ставить.
держишь любую клавишу и фиолетовая сфера продолжает лететь (в фиксированном направлении). отпускаешь клавишу - фиолетовая сфера ждёт. тащемта нужно не столкнувшись с красными сферами как можно больше раз отрекошетить. слева "рекорды" разных персонажей мира

56 Кб, 765x540
137 15 янв, 17:33 137 1069242

Экран сохранения

138 17 янв, 21:03 138 1069538

>>1058847 (OP)
к логопеду запишись

Ответы69562

139 18 янв, 01:21 139 1069562

>>1069538
>Мелкобуква
>Учит других
Прикольно, прикольно.

Обновить тред
« /gd/В начало тредаВеб-версияНастройки/a//b//mu//s//vg/Все доски

Скачать тред только с превьюс превью и прикрепленными файлами

Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах.Подробнее

Пользуетесь скринридером — пишите, что можно улучшить.
Host-Food.ru — надёжный хостинг от 12 руб/мес | Free DNS
Зеркала М.Двача: m2ch.hk, m2ch.gq, m2ch.cf, m2ch.fapp.in, m2ch.lib, m2chfapiswfw2wcakptqp4o6ltgchvjuxfab7urxlduraomtlnhw5xad.onion