Звуки и музыка
Вот решил выложить свои классы по работе со звуком и музыкой. Они у меня уже не менялись где-то четыре игры подряд, так что можно считать, что их функционал подходить под большинство игр. Классы используют выкладываемые ранее модули ageMath и ageVector (они уже включены в исходники примера в конце поста).
Звуковой класс может воспроизводить только одиночные звуки. Петли и зацикленные эффекты планируется добавить в будущем. Просто пока мне это было не нужно в предыдущих играх
класс может также воспроизводить объемный звук, учитывая расстояние до объекта и его положение относительно слушателя.
Музыкальный класс может воспроизводить зациклено музыкальные треки а также обеспечивать плавную смену музыки путём затухания.
Классы все статические, поэтому создавать их экземпляры не требуется. У всех классов можно менять громкость и отключать звук.
Смотрим пример (жмём на жёлтые кружочки, чтобы было весело):
Использовать класс sounds весьма просто. Для начала надо в функции init написать те, звуки которые будут загружены:
//Здесь втыкаем свои звуки snd['mysnd'] = new mysnd();
потом надо инициализировать класс в основной программе:
sounds.init(500,400);
Вот вообщем-то и всё! Теперь мы можем вызывать воспроизведение звука где нам это необходимо:
sounds.PlaySnd('mysnd',1,b1);
первый параметр это имя звука, которое мы задали в функции init. Второй – это сила звука, для взрывов например можно ставить 2 или 3. Последний параметр это эмиттер звука. Его координаты будут использованы для расчёта затухания звука, а также его панорамирования. Если задать только имя звука – он будет воспроизведён без учёта положения. Все звуки воспроизводятся относительно координат sounds.cenX и sounds.cenY. По умолчанию это центр экрана (все расчёты ведутся в глобальных координатах Stage). Также можно привязать слушателя к конкретному объекту, для этого необходимо будет менять cenX и cenY в основной программе (не забывая переводить координаты в глобальную систему координат).
Громкость звуков можно менять с помощью переменной sounds.vol. Отключить все звуки можно задав переменной sounds.sndEnable=false.
Класс sounds:
// Управление пространственными звуками в игре package main { import flash.media.SoundTransform; import flash.display.*; import flash.utils.*; import flash.geom.*; dynamic public class sounds extends Object { static var snd:Object; // хэш со звуками static var stageRadius:Number; // примерный радиус видимой области static public var vol:Number=0.9; // громкость static public var sndEnable:Boolean=true; // включён ли звук //Координаты слушателя static public var cenX:Number=0; static public var cenY:Number=0; //внутренние переменные static var v:ageVector= new ageVector(); static var p:Point; //Инициализация класса. Передаём в функцию размеры флешки static public function init(stageWidth:Number, stageHeight:Number) { // создадим хеш звуков snd = new Object(); //Здесь втыкаем свои звуки snd['mysnd'] = new mysnd(); // вычислим примерный радиус видимой области экрана stageRadius = (stageWidth)/1.4; //Центр по умолчанию в центре экрана. Можно привязать к какому-либо объекту cenX=stageWidth*0.5; cenY=stageHeight*0.5; } // Воспроизводит звук с учетом пространства // snd_name - имя звука // dv - множитель для громкости // obj - эмиттер звука static public function PlaySnd(snd_name:String,dv:Number=1.0,obj:*=null):void { var t:SoundTransform; //Если звук отключён - выходим if(!sndEnable || vol<0.001) return; //Без объёмного звука if(obj==null) { t = new SoundTransform(); t.volume = vol; snd[snd_name].play(0, 0, t); } else { t= new SoundTransform(); //Все расчёты ведём в глобальной системе координат p=obj.parent.localToGlobal(new Point(obj.x,obj.y)); v.x=p.x-cenX; v.y=p.y-cenY; // Ставим громкость в зависимости от расстояния t.volume = ageMath.trimToRange(vol * ageMath.RemapVal(v.len(),0,dv * stageRadius,1,0),0,1); // стерео в зависимости от положения источника звука t.pan = ageMath.trimToRange(ageMath.RemapVal(v.x,-stageRadius/2,stageRadius/2,-1,1),-1,1); // воспроизводим snd[snd_name].play(0, 0, t); } } } }
Класс музыки musicEnv инициализируется аналогичным образом. В init прописываем все треки которые будут использованы в нашей игре.
//Здесь втыкаем свои муз треки mus['mus1'] = new mus1(); mus['mus2'] = new mus2(); mus['mus3'] = new mus3();
далее в основной программе инициализируем класс с музыкой:
musicEnv.init();
и его уже можно использовать! Включаем одну музыку:
musicEnv.fadeTo('mus1');
потом плавно с затуханием меняем на другую:
musicEnv.fadeTo('mus2');
а так можно остановить музыку:
musicEnv.played(false);
чтобы отключить музыку в программе необходимо также использовать переменную sounds.sndEnable:
musicEnv.musEnable=false; musicEnv.played(false);
включить музыку обратно:
musicEnv.musEnable=true; musicEnv.played(true);
звук музыки регулируется с помощью переменной sounds.vol, скорость фейдинга – sounds.volFadeSpeed
Класс musicEnv:
// Управление пространственными звуками в игре package main { import flash.media.SoundTransform; import flash.display.*; import flash.geom.*; import flash.utils.*; import flash.events.*; import flash.media.*; dynamic public class musicEnv extends Object { static var mus:Object; // хэш с музыкой static public var vol:Number=0.9; // громкость static public var volFadeSpeed:Number=0.01; //Скорость фейдинга static public var musEnable:Boolean=true; // включёна ли музыка static public var curMusName:String=""; // Имя текущего трека //внутренние переменные static var curCh:SoundChannel; //Текущий канал static var offCh:SoundChannel; //Затухающий канал static var curVol:Number=0; static var curPos:Number=0; static var offVol:Number=0; static var isplayed:Boolean=false; static var timerVolFader:Timer= new Timer(10); static var t1:SoundTransform = new SoundTransform(); static var t2:SoundTransform = new SoundTransform(); static public function init() { timerVolFader.stop(); // создадим хеш с музыкой mus = new Object(); //Здесь втыкаем свои муз треки mus['mus1'] = new mus1(); mus['mus2'] = new mus2(); mus['mus3'] = new mus3(); } //Плавная смена трека на musname static public function fadeTo(musname:String) { curMusName=musname; //Если музыка отключена - выходим if(!musEnable) return; isplayed=true; var t:SoundTransform; t = new SoundTransform(); t.volume = 0; if(offCh) offCh.stop(); offCh=curCh; curCh=mus[musname].play(0, int.MAX_VALUE, t); curVol=0; offVol=vol; if(!timerVolFader.running) { timerVolFader.addEventListener(TimerEvent.TIMER, timerVolFader_Timer, false, 0, true); timerVolFader.start(); } } //Остановить или запустить воспроизведение музыки static public function played(flg:Boolean):void { if(flg==isplayed) return; if(flg) { if(curMusName!="") fadeTo(curMusName); } else { isplayed=false; if(curCh) { curCh.stop(); curPos=curCh.position; } if(offCh) offCh.stop(); offVol=0; curVol=vol; if(timerVolFader.running) { timerVolFader.stop(); timerVolFader.removeEventListener(TimerEvent.TIMER, timerVolFader_Timer); } } } //Плавный фейдинг музыки static public function timerVolFader_Timer(event:TimerEvent):void { if(offCh!=null) { if(offVol>0) offVol -= volFadeSpeed; else offVol=0; t1.volume = offVol; offCh.soundTransform=t1; } if(curCh!=null && (offVol<vol/2 || offCh==null)) { if(curVol<vol) curVol += volFadeSpeed; else curVol=vol; t2.volume = curVol; curCh.soundTransform=t2; } //Если достигли порогового значения громкости if(curVol>vol) { if(offCh!=null) offCh.stop(); timerVolFader.stop(); timerVolFader.removeEventListener(TimerEvent.TIMER, timerVolFader_Timer); } } } }
Вот вообщем то и всё. Думаю новичкам класс может пригодиться. Исходники примера тут: http://www.anegmetex.com/devblog/files/SoundAndMusic.rar
Категория: Полезные классы

Апрель 15th, 2011 at 16:47
Огромное спасибо! Стереоэффект и зависимость от расстояния – просто и со вкусом.
Апрель 17th, 2011 at 14:57
Делал как то так же, только у меня один единственный SoundSystem и туда можно addSound() addMusic() ну а потом конечно playSound() playMusic(). Ну и мне больше по душе жесткий Stop() либо Pause() еще можно Shut() тогда звук всё еще играет но просто не слышно до вызова Scream(). Кстати о конструкции Object[var_name] я вспомнил совсем недавно во время рефакторинга – очень удобная штука. Кстати зачем совать в инит конкретику если её можно вынести за пределы этого контроллера? Мне больше понравилась конструкция public function addSound(sound_name:String, sound_res:Class):void{mSounds[sound_name]=new sound_res();} Ресурсы ложишь во время инита игры допустим или левела. Там уже пишешь pSoundSystem.addSound(’boom’, sound_boom); Чисто для удобство реиспользования технологии. В любом случае спасибо за статью, кстати пока у меня рефакторинг надо поднять тему менеджжера кеш объектов. А то у меня до сих пор индексы (хоть и в векторе).
Апрель 17th, 2011 at 18:40
>Кстати зачем совать в инит конкретику
ну динамически добавлять мне звуки нет необходимости, так что так проще, меньше букавок и всё в одном месте. Хотя конечно с точки зрения архитектуры не очень. Но мне важней первое
Апрель 20th, 2011 at 20:08
Привет Олег! Спасибо за шаровый класс… В “хозяйстве” пригодится …
Напрямую я его вряд ли стану использовать, но как “пища для ума”, для написания своего “саунд менеджера”, оч. поможет.
Апрель 26th, 2011 at 17:53
Олег подскажите как с вами связаться, и насколько реально у вас заказать игру.
пишите на эмэй если будет время.
Апрель 26th, 2011 at 22:04
отписал на емайл
Июнь 7th, 2011 at 14:19
Олег, здравствуйте.
А не подскажете, почему могут быть ошибки такого рода?
Dropbox\BL 5.0\main\sounds.as, Line 71
1178: Attempted access of inaccessible property x through a reference with static type ageVector.
Dropbox\BL 5.0\main\sounds.as, Line 75 1195: Attempted access of inaccessible method len through a reference with static type ageVector.
И.т.д.
Июнь 9th, 2011 at 1:44
ageVector подключен?
Июнь 9th, 2011 at 11:53
Да, конечно.
Ваш исходник из этой статьи запускается без проблем.
Ну я в принципе закомментировал позиционирование звука. (теперь ageVector и ageMath не используются)
В любом случае, спасибо за класс. Он небольшой и будет интересно в нем разобраться.