Пространство для звука

Лирика
Часто в 2D играх не заморачиваются над объёмным звуком. И зря. Когда звук имеет своё место, то игра начинает выглядеть совершенно по другому. В ней начинает чувствоваться объём. Вот ракета взорвалась где-то слева, вот справа слышны шаги врага. И это уже не обязательно видеть, вы это слышите. А если звучащее событие происходит где-то за экраном вы можете планировать свои действия в зависимости от звуков. Это очень здорого. Как этого добиться?

Теория
По настоящему объёмный звук нам, увы, не доступен. Но имея 2 колонки и регулятор громкости можно сделать много. Чем дальше звук от центра камеры — тем от тише. Чем левее — тем громче звучит левая колонка и тем тише — правая. В AS3 мы можем управлять громкостью (volume) и панорамой (pan) звучания. Воспользовавшись этим у нас получается следующее.

Код
public class Sfx3D {
  // модификатор громкости
  static public const VOL_MOD:Number = 0.8;
  // модификатор панорамы
  static public const PAN_MOD:Number = 2.5;
  // размер области от центра, в которой будет звук
  static public var size:Point = new Point(1, 1);
  // центр экрана с учётом игровой камеры
  static public var center:Point = new Point(1, 1);

  static public function play3D(snd:Sound, x:Number, y:Number):SoundChannel {
    if(snd == null) return null;
    // горизонтальный коэффициент
    var dist_x:Number = ((x - center.x) / size.x);
    // вертикальный коэффициент
    var dist_y:Number = ((y - center.y) / size.y);
    // горизонтальная громкость
    var vol_x:Number = (1 - Math.abs(dist_x * VOL_MOD));
    // вертикальная громкость
    var vol_y:Number = (1 - Math.abs(dist_y * VOL_MOD));
    // финальная громкость
    var vol:Number = Math.min(1, Math.max(0, Math.min(vol_x, vol_y)));
    // в простейшем случае звуки короткие и не меняют свою позицию, обновлять её
    // не надо и можно вообще не проигрывать звук, если громкость == 0
    if (vol <= 0) return null;
    // панорама
    var pan:Number = dist_x * PAN_MOD;
    pan = Math.min(1, Math.max( -1, pan));
    // и, наконец, можно играть звук
    var st:SoundTransform = new SoundTransform(vol, pan);
    return snd.play(0, 0, st);
  }
}

Используем так:
// ...ПРИ ИНИЦИАЛИЗВЦИИ ИГРЫ...
// половина экрана - область от центра до края экрана
Sfx3D.size.x = SCREEN_WIDTH * 0.5;
// по горизонтали
Sfx3D.size.y = SCREEN_HEIGHT * 0.5;
// тут можно умножать, скажем, на 0.7, чтобы виртуально увеличить экран,
// чтобы панорама рассчитывалась по другому, экспериментируйте. По сути
// это модификатор размера экрана (:


// ...ПРИ ОБНОВЛЕНИИ КАМЕРЫ...
// где CAMERA_X - это центр экрана по горизонтали
Sfx3D.center.x = CAMERA_X;
// где CAMERA_Y - это центр экрана по вертикали
Sfx3D.center.y = CAMERA_Y;

// ...ПРОИГРЫВАЕМ ЗВУК...
// SOUND - ранее созданный звук, POS_X и POS_Y - координаты звука на плоскости
Sfx3D.play3D(SOUND, POS_X, POS_Y);


Модификаторы.
Модификатор PAN_MOD позволяет немного усилить объём. Скажем, если у нас получается pan = 0.1, то не натренированное ухо может и не заметить, что звук несколько правее центра. Применяя модификатор у нас получается значение 0.25, что воспринимается несколько лучше. При этом pan всё равно обрезается от -1 до 1.
VOL_MOD позволят звучать звукам за пределами экрана. Подбирая разные коэффициенты можно добиться нужного эффекта для разных размеров экрана.

P.S. Нашёл баг блога. При сохранении в черновики обязательно наличие Меток, при этом сами Метки не сохраняются и приходится их писать по новой.
  • +31

Комментарии (31)

0
Спасибо, обязательно попробую :)
+
+1
Баловался буквально только что тем же самым, но не столь универсально, а применительно к разрабатываемой игре. Я, т.к. мне было нужно, добавил еще и возможность двигать уже играемый звук.

Это в общем-то и в твой класс несложно добавить.

public class Sfx3DChannel
{
public function Sfx3DChannel(channel:SoundChannel):void
{
    this.channel = channel;
}
public function setPosition(x:Number, y:Number)
{
    var dist_x:Number = ((x - center.x) / size.x);
    // вертикальный коэффициент
    var dist_y:Number = ((y - center.y) / size.y);
    // горизонтальная громкость
    var vol_x:Number = (1 - Math.abs(dist_x * VOL_MOD));
    // вертикальная громкость
    var vol_y:Number = (1 - Math.abs(dist_y * VOL_MOD));
    // финальная громкость
    var vol:Number = Math.min(1, Math.max(0, Math.min(vol_x, vol_y)));
    // в простейшем случае звуки короткие и не меняют свою позицию, обновлять её
    // не надо и можно вообще не проигрывать звук, если громкость == 0
    if (vol <= 0) return null;
    // панорама
    var pan:Number = dist_x * PAN_MOD;
    pan = Math.min(1, Math.max( -1, pan));
    // и, наконец, можно играть звук
    var st:SoundTransform = new SoundTransform(vol, pan);
    channel.soundTransform = st;
}
private var channel:SoundChannel;
}


Ну и поменять твою play3d на

  static public function play3D(snd:Sound, x:Number, y:Number):Sfx3DChannel {
    if(snd == null) return null;
    var channel:Sfx3DChannel = new Sfx3DChannel(snd.play());
    channel.setPosition(x, y);
    return channel;
  }
+1
Можно даже проще сделать, но думаю это гораздо реже нужно. А тем, кому нужно, надеюсь, уловили общую мысль обновления в твоём коде, спасибо.
Одно замечание, лучше SoundTransform по новой не создавать, а использовать уже имеющийся в SoundChennel, просто помнить, что это геттер и сеттер.
var st:SoundTransform = channel.soundTransform;
st.volume = vol;
st.pan = pan;
channel.soundTransform = st;

Как-то так…
0
Ты удивишься, но этот самый геттер возвращает копию имеющегося SoundTransform, т.е. тоже создает новый. Твой код лучше тем, что если в этом трансформе есть что-то, что менять не надо, то оно сохранится. Да и сеттер тоже копирует данные из переданного объекта.
Так что с точки зрения уменьшения количества новых объектов, лучше всего так:

class A
{
var c:SoundChannel;
var t:SoundTransform;
function f(v, p)
{
  t.vol = v;
  t.pan = p; 
  c.soundTransform = t;
}
}
0
Забыл добавить, что оно того не стоит скорее всего :-)
Premature optimization is the root of all evil in programming. Donald Knuth.
0
Гм… Не знал, что он возвращает копию. Извращения какие. Спасибо.
0
Собственно, ты это очевидно знал, просто не задумывался, т.к. сам же написал:

var st:SoundTransform = channel.soundTransform;
st.volume = vol;
st.pan = pan;
channel.soundTransform = st;

Если бы тебе давали доступ к объекту, хранящемуся в channel, то работало бы просто так:

var t:SoundTransform = channel.soundTransform;
t.volume = vol;

И даже так:

channel.soundTransform.volume = vol;
0
А, ну да, точно, что это я… (:
0
Sfx3D.play3D(SOUND, POS_X, POS_Y);
0
Спасибо, поправил.
0
Кулл Эл, тхнкс.
+2
Не могу согласиться, если речь идет именно о флеш играх. Музыку оценит очень малая часть аудитории. Большинство играет без звука.

Но если говорить о более серьезных проектах, то да, очень часть именно музыка моментально ставит настроение всей игры и благодаря ей игрушка запоминается — создается атмосфера, иллюзия «объема».

Самый яркие примеры — Bully (PS2/Xbox 360/PC), World of Goo (Wii/PC/Mac/iPad). С самых первых моментов как запускаешь эти игры, понимаешь что тебя ждет нечто эпическое.
0
Автор вообще пишет не про музыку, а про звуки. Да, большой процент флеш аудитории сразу жмет mute, однако те, которые оставят, получат большой бонус в виде позиционированных взрывов, звуков моторов итд. В частности я у себя так и сделал, как-то слетело стерео и я остро ощутил как подсознательно пользовался этой дополнительной информацией. Скажем слышишь ты, что справа гул моторов — уже знаешь что сейчас придется бомбить дорогу справа :)
0
Кстати, вот вы большой и толстый издатель, соираете, небось, кучу статистики. Есть статистика, какой процент игроков играет без звука? Ну и интересно было бы другую разную статистику услышать. (:
+3
Большинство игроков вообще играет с физически выключенным звуком на компьютере :) Может как-нибудь сделаю пост про наши статистики.
0
Было бы очень интересно посмотреть.
0
На больших порталах отсутствие кнопки mute — одна из причин низких оценок.
И совсем не потому что музыка или звук плохой (хотя и это тоже).
1) просто от звука устают, все-таки много разнообразного и качественного звука в обычную flash игру не запихнешь
2) играют на работе, из школы, в универе на лекциях — там звук не положен :)
+2
Да, еще бы неплохо по умолчанию делать громкость 50%, а то и 25% от максимума. Ато бывает как включишь…
0
Можно еще добавлять копку play с лого спонсора после прелоадера с рекламой.
Это любят те кто сидят на медленных каналах и уходят «попить чаю», пока загружается игра.
0
В смысле просто лого, без ссылки? (тогда в чем профит?)
Или лого ссылкой, а под ним плей?
0
Я так понял, это для того, чтобы сама игра со всем ее многообразием звуков и музыки не начала внезапно играть на весь офис)).
0
второе конечно
0
Стоит отметить лишь что количество стерео флешек стремится к 0. Или это только моя практика? А Пост нравится. Держи плюс.) Беру на вооружение в свою игру если позволите;)
+3
На самом деле в 2Д играх кроме панорамирования вправо-влево нет реальной необходимости привязывать громкость звука к положению объекта в пространстве. Только в полноценной 3Д игре это имеет смысл. Да и учитывая, что флешки играются в окошке размером условно 640х480 пикселов, что явно занимает меньшую область монитора, даже стерео панорама может вызывать сомнение.
0
А если у тебя прокручиваемая карта и объект далеко за экраном?
0
Ну так стерео звука достаточно, если необходимо подзвучить то, что находится за пределами экрана
+3
Не знаю, как вам, а мне, в моей ещё не выпущенной игре, нравится, когда я бреду по уровню и звук становится всё громче и громче, пока, наконец, не появляется сам объект, издающий звуки.
0
Все может быть. Вполне возможно, что в отдельном частном случае будет очень удачно. Нисколько не спорю с этим. Тут как бы область экспериментов. Но очень во многих флеш играх в этом нет необходимости. В силу специфики игрового процесса.
0
Да, для различных пазлов и дрессапов — это лишнее. Скорее это нужно различным экшенам и прочим платформерам с уровнями больше экрана. Типа «применять с умом». (:
0
Человек привел отличный пример — менять звук по мере приближения к источнику.
Можно даже игровую механику на этом построить ;)
Кстати идея.
+1
Панорамный звук — это тема. Может кто знает игру Archibald's Adventure, так там эта тема реализована. Мне очень понравилось эта игра, в том числе из-за звукового оформления.

В своем платформере я тоже решил сделать звук объемным. Пишу игру на Flixel'e, и к своему счастью обнаружил, что в движке уже вся работа по панорамированию звука реализована. Игра реально получается «натуральнее».

Кстати, в движке можно подсмотреть, как оно там реализовано
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.