Как сделать?
…
Если слово не подсвечивается синим в QGen, значит его нет в keywords.xml. Можешь взять здесь: https://mega.nz/folder/jLBCQaST#RhLC2SPD9BEAIbD3hcKSXg и разместить рядом с QGen.
Вынужден спросить, ты точно запускаешь игру в QSP версии 5.8.0? Потому что у меня твой код никаких ошибок не вызывает. Если запускаешь в 5.7.0, то естественно новый синтаксис там работать не будет.
Как сделать перфект луп звука в FMOD (3.31+) без вызовов API?
“А чё, такое разве можно?”
Оказывается, что можно…
Ниже приводится пример содержимого .wav файла в хексе. Волна очень короткая: всего два гребня, примерно 1.1 мсек, разрезано через середину, концы не нули! Если воспроизвести без цикла, то никто и не поймет, что же это было. В цикле, но без идеальной стыковки, вместо чистого тона будут слышны треск/щелчки.
В любом хекс-редакторе сохраняем содержимое как *.wav и проверяем. В “обычных” QuestSoftPlayer 5.7 и 5.8, использующих FMOD Ex, можно набрать (как пример) [play ‘test.wav’, 50] и услышать…
…чистый тон! (значит FMOD чётко и без разрыва соединил концы волны) Естественно, этот способ подойдет и для полноразмерных файлов (моно 22kHz и столь малый размер, были взяты просто чтобы пример был короче).
524946469A00000057415645666D7420
10000000010001002256000044AC0000
0200100064617461320000009A19F018
B716F112F80D1108A4011AFBE7F464EF
05EBF5E789E6B7E69BE8F9EBB3F060F6
BFFC43039E094E0F03146B174319736D
706C3C000000000000000000000027B1
00003C00000000000000000000000000
00000100000000000000000000000000
00000000000018000000000000000000
0000
RIFF(150 байт)”WAVE” (как всем хорошо известно, в RIFF в размер чанка не входят идентификатор и сама величина)
“fmt “(16 байт) моно 22kHz 16-бит.
“data”(50 байт) 25 сэмплов на единственном канале, от 0 до 24 по 2 байта (по 16 бит).
[и на этом должен быть конец, однако…]
“smpl”(60 байт) чудо инженерной мысли.
Кастомное мумбо-юмбо для RIFF WAVE от создателей SoundForge, а потом ещё и от создателей FMOD (в документации к FMOD переводят стрелки - “как в SoundForge”). Чанк “smpl” был придуман для MIDI и всего такого, в WAVE его не должно быть от слова “спецификация”, но разве это кого-то волнует? (теперь уже риторический вопрос, можно не отвечать, “кузнецы” ответили). Конечно же есть чанки специально придуманные для метаданных (в LIST INFO), но умельцами был выбран именно самодельный вариант…
Разбор чанка:
x00 (736D706C) [FCC идентификатор чанка] “smpl” точно так же как это будет в ASCII.
x04 (3C) [размер чанка] 60, каждый знает что в RIFF размер считается без FCC идентификатора и самого числа, 68-4-4=60.
x10 (27B1) [период сэмпла] 45351 наносекунда = 22kHz (только для SoundForge, а для FMOD можно нули).
x14 (3С) [MIDI нота] 60я нота “средняя C” (только для SoundForge, а для FMOD можно нули).
x24 (01) [число записей о циклах в чанке] всего 1 и есть.
x2C [записи о циклах] просто всё нулями, кроме x38.
x34 (00) [кастомные данные] начало по версии SoundForge, ноль т.к. сэмплы считаются с нулевого.
x38 (18) [кастомные данные] конец по версии SoundForge, 18 hex = 24 dec
Т.е. получается, луп пойнты на нулевом и самом последнем (24й в этом файле) сэмплах (хотя можно было расставить и не так, а для FMOD можно было назначить конец хоть на FFFFFF, там всё само подправится).
В этом чанке предусмотрено еще много других стандартных величин, но их даже SoundForge не задает, т.к. это вовсе и не MIDI файл (хотя как мы знаем, у кое-кого свое мнение на этот счёт), и практического смысла в этом нет.
Очевидно, что ЭТО издевательство над *.wav файлами работает только для FMOD и SoundForge, откуда эта забава (виоляция общепризнанных спецификаций WAVE) и появилась. Всё остальное П.О. просто !должно! проигнорировать этот чанк в RIFF WAVE.
PS: Т.е. в QuestSoftPlayer 5 всё-таки можно делать идеальные циклы фоновых звуков (раз 5.7 идет с FMOD Ex 4.30, значит с момента выхода 5.7 в каком-то там году? так получается?).
(Злой Механик)
Злой Механик,
погоди, я правильно понял? Грубо говоря wav зациклится самостоятельно, если мы добавим к нему чанк “smpl” c указанными данными? Не знаю, кто привёл тебя сюда, но храни господь этого человека!
Привет, если очень коротко: 1) да, 2) сам пришел (где хочу, там и хожу).
Если же развёрнуто:
Я вроде всё чётко выше описал, но если показалось сложным, то попробую “на пальцах”. В FMOD, который используют классические QSP 5.7 и 5.8, внутри есть нетипичный код. Инженеры-программисты написали движок так, что когда FMOD читает .wav файл, то помимо всего прочего, он ещё ищет кусочек данных (чанк) “smpl”, хотя по-задумке в .wav ничего подобного не должно быть! Если он есть и внутри всё “как надо”, код внутри движка FMOD !автоматически назначит! тип автоповтора, и при воспроизведении, звук будет повторяться, даже если вызов воспроизведения не подразумевал повторение (например QSP всегда делает воспроизведение “на раз”).
Самый простой и универсальный пример чанка “smpl” в хексе (для FMOD, не для SoundForge):
736D706C3C0000000000000000000000
00000000000000000000000000000000
00000000010000000000000000000000
0000000000000000FFFFFFFF00000000
00000000
Конечный сэмпл повтора задан просто наугад: FFFFFFFF, но даже если он выходит за реальные пределы (а этот индекс МАКСИМАЛЬНО возможный), в FMOD всё скорректируется на последний (т.е в этом способе не нужно считать сэмпы для каждого отдельного случая, и ДА, это потому что так написали FMOD).
Просто в хекс редакторе добавляем чанк в самый конец .wav файла. Однако FMOD при чтении .wav четко учитывает размер родительского чанка “RIFF” (структура контейнера RIFF подразумевает понятие иерархии). Поэтому идем в самый верх .wav файла. В хексе видно, что он начинается на “RIFF”, а вот сразу за ним и идут 4 байта величины размера (для примера A1B2C300). Теперь считаем. Чанк “smpl” имеет размер (полный, вместе в FCC) 44 hex = 3С+4+4 (смотри пост выше, почему так). Просто складываем 44 и (как пример C3B2A1) в 16-системе (C3B2A1+44=C3B2E5 - значит меняем A1B2C300 в файле на E5B2C300). Почему наоборот? Если и правда спросил, ответ: “По кочану!” - в настольных системах используется Интеловское направление записи от младших разрядов к старшим (а не как у людей принято от старших к младшим). Связано это с устройством электрических цепей Интел-подобных процессоров.
Вот теперь, если этот .wav файл запустить в QSP, он будет крутиться до “второго пришествия”. Притом движок FMOD написали так, что он еще и концы волны попробует соединить без разрыва (однако если перепад у концов уж слишком большой, обязательно появится дисторция сигнала, но это должно быть очевидно. Хотя и совсем другая тема).
И есть еще один способ зацикливания, но он выглядит как перемотанный изолентой да ещё с торчащими гвоздями.
FMOD (тот что идёт с QSP) умеет читать .xm/.it/.s3m/.mod файлы - это как раз трекеры/аудио секвенсоры.
Если супер-коротко: качаем трекер, вставляем наш звуковой файл как сэмпл секвенсора на “среднюю С” ноту (чтобы тон не поменялся) и пишем композицию на бесконечный повтор одного инструмента на одной ноте, состоящего только лишь из одного сэмпла - нашего файла.
Проблема метода - в странных уровнях громкости на QSP, притом что в других .MOD проигрывателях всё норм.
По второму вопросу: Уважаю людей и сообщества которые что-то сами реально делают, а не только беконечно пи***ят “о котиках”. Я подумал, что если кто-то использует звуки в своих работах, эта инфа может быть очень полезной. Учитывая что FMOD - это все-таки не ерунда, в QSP можно делать озвучку вполне на уровне “больших игр”.
(супер-странно что зацикливание не добавили в комманду QSP play, типа : play $path, volume, loop. Каналы звука и объектные обработчики сложных звуков в QSP можно написать и самому, а вот повтор без дисторции сигнала - никак)
PS. Если ещё есть желание “хлебнуть мудрости из-горла”, там выше пост был про генерацию. Могу кое-что показать/рассказать про R.N.G.2D (да 2D, нет не простой последовательный) именно для (вот сюрприз) QSP. (~27 операций)
(Злой Механик)
Я ступил, тут можно файлы вкладывать… Вот 2 файла - попробуй их в QSP запустить. Погляди в хексе, чем они отличаются.
snd.rar
С такими тонами на начале и конце этот звук тольно нативно зацикливается, все прочие танцы с бубном бесполезны. И в QSP он-таки зацикливается на 5 из 5.
да, у меня 5.7.0 - версию я поменяю
Вот вопрос: я в основном для телефона квест пишу, а на андроид версии проблем не будет, как думаешь?
Злой Механик,
спасибо большое. Обязательно погляжу.
Дамир Белялов,
на андроиде пока что только 5.7.0 версия работает.
Возможно в скором времени появится qSpider для Андроид, он на 5.8.0, так что если у тебя долгострой, можешь сразу для qSpider делать. Можно запускать, например, с размером экрана 320х600 и смотреть, как на нём будет игра выглядеть.
Как сделать в QSP генератор псевдослучайных чисел?
“Нормально никак не сделать… Можно конечно, написать высокоуровневые преобразования в двоичное представление, а потом ещё всякие там XOR’ы, да shift’ы туда-сюда. Правда это может не потянуть вычислительный бюджет конкретной задачи…”
А, если еще сделать так, что RNG не последовательный (да ещё сохраняет ключ для следующей генерации), а например принимает еще 2 произволных аргумента, и мы можем сразу взять 100500-е значение?
“Ну, тогда можно написать высокоуровневые преобразования для двойной точности, к ним в догонку полиноминалы для тригонометрии, а потом как например в некоторых шейдерах - cosine (относительно скоростные) мумбо-юмбо, там ведь как раз-таки координаты пикселей используются. Но с вычислительным бюджетом проблем только прибавится…”
Ну раз так, тогда хочу всё то же самое, но без высокоуровневых костылей и изоленты.
“Иди-ка ты на *** с такими замашками!”
Цена вопроса:
- RNG, промежуточные ключи генерации нафиг не должны быть (их ведь ещё складывать куда-то надо).
- У нас есть только математика QSP +*[mod] (только хардкор, только так)
- Числа 32-битные, знаковые целые (однако, минимальное отрицательное, похоже прикрыто! уже что-то)
- Скорость максимальная (Вообще это отдельная тема, но сегодня пощадим нейроны).
- Значит, пермутатация будет нихрена не “косиновая” как в пиксельных шейдерах обычно делают…
- а ещё скажем твёрдое “нет” XOR-shift’ам и прочим “мурмурам”…
Теперь, если приглядеться: Умножение-то с переполнением! Как впрочим и сложение. Остаток от деления - тоже в наличии. А про простые числа даже в лесу слышали.
Так, есть ли в природе такой метод для QSP, или нет (без высокоуровневых преобразований)?
Есть как минимум один!
Только перед этим примем вынужденное условие: метод не подходит для научного, промышленного, военного применения.
Т.е. не будем исползовать в качестве решения в профессиональной криптографии, квантовой физике, разработке вооружений и всего такого прочего, однако нам с избытком хватит для компьютерных игр.
Плюсом является скорость - примерно 28 “обычных” QSP операций.
Используется как: number = func(”f_rng_2d”, seed, min, max, x, y)
if (args[3] = 0): args[3] = 13763
if (args[4] = 0): args[4] = 10613
args[0] += args[3] * args[3] * args[4]
args[0] *= args[4] * args[4] * args[3]
args[0] += args[4]
args[0] = (args[0] * 16807) mod 127773
args[0] += args[3]
if (args[0] < 0): args[0] += 2147483647
args[0] = ((args[0] mod 127773) * 16807) mod 127773
args[0] = (args[0] * 16807) mod 127773
result = args[1] + ((max(args[1], args[2]) - args[1] + 1) * (args[0] - 1) / 127772)
Ниже прикреплён файл с примером.
_test_rng_2d.rar
seed - произволная величина, условно “карта” генерации.
min, max - как и в rand() и с тем же умыслом.
x, y - дополнительные числовые аргументы.
При одном и том же сиде, для одних и тех же x и y, результат (очевидно, ведь в этом и весь смысл) гарантирован.
При изменении сида даже на единицу - avalanche effect гарантируется.
Для турбо-мудрых: Существут проблема “хорошего” сида, очень похожая на ту, что есть в XOR-shift алгоритмах. Однако в масштабах компьютерных игр, никто ничего не заметит (впрочем как и в случае с XOR-shift). Так же поскольку ВСЯ пермутация производится в 32-битных величинах (!), не стоит использовать экстремальные (в бинарном аспекте) значения x и y. Но всё работает достаточно хорошо, и границу (в десятеричном выражении) я бы обозначил на десятках миллонов. При больших значениях (миллиард), могут начать проявляться паттерны.
Из-за очень высокой скорости алгоритма, можно использовать полученную псевдо-случайную величину как сид для повторных генераций, иммитируя реально космически-гигантские объемы данных. Вообще я ничего не написал про применение, считая что все кому надо и так знают. Для тек кто не понял - пример: карта выдуманной случайно сгенерированной галактики (но по размеру приблеженной к реальной) в компьютерной игре может занимать… одно число (сид). Или как записать квадриллион случайно сгенерированных свойств… опять-таки одним числом (сидом), а потом еще и разом получить любое из них. Теперь вы видите, как “они это делают”.
Все дело в распределении результатов - оно ни на бит не случайное(!), но НАСТОЛЬКО приближенно к нему, что без статистичеких метов анализа, никто и никогда не поймет. Вот именно в этом и есть вся суть ВСЕХ подобных алгоритмов.
(Злой Механик)
Сделал по старинке через раздел, убрал loop. Скажите, получается я еще и массив неправильно прописал? Мне нужен капитальный массив с координатами и типом объекта, а плеер мне пишет про ошибку синтаксиса.
! Общая генерация границ мира
max_X[1]=24 & !допустим это максимальные границы карты №1
max_Y[1]=24
x_a=1
y_a=1
!Случайное наполнение
y_a=1
:loopY
x_a=1
:loopX
type_sym=rand(1,5)
if type_sym=1: $x[x_a,y_a,map,'символ']='<font color="gray">.</font>' & $x[x_a,y_a,map,'описание']='камушки' & $x[x_a,y_a,map,'тип']='поверхность'
if type_sym=2: $x[x_a,y_a,map,'символ']='<font color=rgb(80,50,50)>.</font>' & $x[x_a,y_a,map,'описание']='пол' & $x[x_a,y_a,map,'тип']='поверхность'
if type_sym=3: $x[x_a,y_a,map,'символ']='<font color="green">ш</font>' & $x[x_a,y_a,map,'описание']='куст' & $x[x_a,y_a,map,'тип']='поверхность'
if type_sym=4: $x[x_a,y_a,map,'символ']='Т' & $x[x_a,y_a,map,'описание']='дерево' & $x[x_a,y_a,map,'тип']='стена'
if type_sym=5: $x[x_a,y_a,map,'символ']='.' & $x[x_a,y_a,map,'описание']='пол' & $x[x_a,y_a,map,'тип']='поверхность'
if x_a<=max_X[1]: x_a+=1 and jump'loopX'
if y_a<=max_Y[1]: y_a+=1 and jump'loopY'
ByltrcДамир Белялов,
Индекс исправь - [x_a,y_a,map,’символ’] -> [’<<x_a>>,<<y_a>>,<<map>>, символ’]
Привет! Спасибо массив перестал вылетать. К сожалению, карта из символов не прорисовывается. То ли массив не записывается, то ли я неграмотно его записал, опечатался, но я снова облажался, увы. Может у вас взор незамыленный, сможете найти, почему не прогружается рандомное наполнение мира. У меня из-за этой поганой сетки стоит проект весь((
Основные локации связанные с проблемой это boot ($scr_map_draw), gen_world, travel - на ней по идее должна была нарисоваться карта для игрока.
project.zip
Дамир Белялов,
x y у тебя где задается? У тебя они равны 0, а для значений меньше единицы массив не заполнен.
задается в локации gen_world в цикле, в начале процесса x_a=1 потом в цикле при x_a<maxX[1] переменная x_a+=1 и снова цикл выполняется
Дамир Белялов,
Ты для отрисовки карты используешь переменную x, а не x_a.
Придётся все-таки заменять? Я думал воспользуюсь временно при генерации переменной x_a, а персонаж когда будет перемещаться то буду использовать x.