Добавлен , опубликован
Vlod поделился системой для работы со звуками, я немножко доработал, а если конкретнее - добавил периодическую проверку на расстояние между камерой и звуком
вариковские функции
native SetSoundDistanceCutoff takes sound soundHandle, real cutoff returns nothing
native SetSoundDistances takes sound soundHandle, real minDist, real maxDist returns nothing
при выходе и входе либо проигрывают заново звук либо прерывают его (в зависимости что вы указали в четвёртом аргументе функции CreateSound)

оригинал
код
library LibSound
struct Sound
    sound sound
    timer timer
    real time
    unit u
    real x
    real y
    integer volume
    integer tick
    boolean is_playing = false
    private static hashtable H = InitHashtable() // таблица для звуков, StringHash(string)
    private static hashtable H_T = InitHashtable() // таблица для таймеров, GetHandleId(timer)
    private static real MIN_DIST = 600 // дистанция где звук 100%
    private static real MAX_DIST = 990000 // растягивает звук. чем больше число, тем меньше будет слышно звук на другом конце карты
    private static real All_X = 0 // НАСТРОИТЬ All_X All_Y!!!
    private static real All_Y = 0 // X, Y, Z координаты точки, которая всегда видна (если нет, то создать)
    private static real All_Z = 600+50
    private static real All_DIST = 99000 // максимальное расстояние на карте
    private static real All_CUTOFF = 0
    private static real PTIME = 0.1
    
    
    // создает и прелоадит новый звук, не использовать!
    // для получения свободного звука из буфера используйте Sound.path2sound(string)
    static method create takes string path, real time returns thistype
        local thistype n = thistype.allocate()
        local integer hid = StringHash(path)
        local integer count = LoadInteger(H, hid, 0)
        set n.sound = CreateSound(path, false, true, false, 10, 10, "")
        set n.time = time
        set n.timer = CreateTimer()
        call SaveInteger(H_T, 0, GetHandleId(n.timer), n)
        set count = count + 1
        call SaveInteger(H, hid, 0, count)
        call SaveInteger(H, hid, count, n)
        
        call SetSoundPitch(n.sound, 1)
        call SetSoundDistances(n.sound, MIN_DIST, MAX_DIST)
        call SetSoundConeAngles(n.sound, 0.0, 0.0, 127)
        call SetSoundConeOrientation(n.sound, 0.0, 0.0, 0.0)
        call SetSoundChannel(n.sound, 40)
        return n
    endmethod
    
    // остановить звук
    method stop takes nothing returns integer
        if this > 0 and .is_playing then
            call PauseTimer(.timer)
            set .u = null
            set .is_playing = false
            call StopSound(.sound, false, false)
        endif
        return 0
    endmethod
    
    private static method sound_off takes nothing returns nothing
        local thistype snd = LoadInteger(H_T, 0, GetHandleId(GetExpiredTimer()))
        call snd.stop()
    endmethod
    
    private static method track takes nothing returns nothing
        local thistype snd = LoadInteger(H_T, 0, GetHandleId(GetExpiredTimer()))
        //call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "track snd"+I2S(snd))
        //call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "track snd.tick"+I2S(snd.tick))
        call SetSoundPosition(snd.sound, GetUnitX(snd.u), GetUnitY(snd.u), All_Z)
        set snd.tick = snd.tick - 1
        if snd.tick <= 0 then
            call snd.stop()
        endif
    endmethod
    
    // получить звук по пути (если нет свободного звука создется новый)
    static method path2sound takes string path returns thistype
        local integer hid = StringHash(path)
        local integer count = LoadInteger(H, hid, 0)
        local integer i = 1
        local Sound snd
        //call DisplayTextToPlayer(Player(0),0,0, "path2sound "+path)
        if count <= 0 then
            return 0
        endif
        loop
        exitwhen i > count
            set snd = LoadInteger(H, hid, i)
            if not snd.is_playing then
                return snd
            endif
        set i = i + 1
        endloop
        set snd = Sound.create(path, thistype(LoadInteger(H, hid, 1)).time)
        //call DisplayTextToPlayer(Player(0),0,0, "new snd "+I2S(snd))
        return snd
    endmethod
    
   private method prepare_for_all takes nothing returns nothing
        call SetSoundPosition(.sound, All_X, All_Y, All_Z)
        call SetSoundDistanceCutoff(.sound, All_DIST)
        //call SetSoundVolume(.sound, 0)
        
        //call SetSoundPitch(.sound, 1)
        //call SetSoundDistances(.sound, MinDist, MaxDist)
        //call SetSoundConeAngles(.sound, 0.0, 0.0, 127)
        //call SetSoundConeOrientation(.sound, 0.0, 0.0, 0.0)
        //call SetSoundChannel(.sound,40)
    endmethod
    
    // проиграть звук в точке
    method xy takes integer volume, real x, real y returns nothing
        if this <= 0 then
            return
        endif
        set .volume = volume
        set .x = x
        set .y = y
        call .prepare_for_all()
        call StopSound(.sound, false, false)
        call StartSound(.sound)

        call SetSoundPosition(.sound, x, y, All_Z)
        call SetSoundDistanceCutoff(.sound, All_CUTOFF)
        call SetSoundVolume(.sound, volume)
        
        set .is_playing = true
        if .u == null then
            call TimerStart(.timer, .time+PTIME, false, function thistype.sound_off)
        else
            set .tick = R2I(.time/PTIME) + 1
            call TimerStart(.timer, PTIME, true, function thistype.track)
        endif
        //call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "method xy .time "+R2S(.time))
    endmethod
    
    // проиграть звук на юните
    method on_unit takes integer volume, unit u returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        set .u = u
        call .xy(volume, x, y)
    endmethod
    
    method destroy takes nothing returns nothing
        // заглушка, удалять Sound не надо, они кешируются вместе с sound в hashtable
    endmethod
    
    // проиграть звук в точке
    static method play_xy takes string path, integer volume, real x, real y returns thistype
        local thistype snd = path2sound(path)
        call snd.xy(volume, x, y)
        return snd
    endmethod
    
    // проиграть звук на юните
    static method play_on_unit takes string path, integer volume, unit u returns thistype
        local thistype snd = path2sound(path)
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        set snd.u = u
        call snd.xy(volume, x, y)
        return snd
    endmethod

    // прелоадит звук в библиотеку. все звуки нужно прелоадить 1 раз.
    static method preload takes string path, real time returns nothing
        call thistype.create(path, time)
    endmethod
    
endstruct

endlibrary




доработка
код
library SoundLib
globals
    private constant hashtable H = InitHashtable( )
    private constant real MaxDistance    = 9999999.00 // макс дистанція звуку
    private constant real VisibleXForAll = 0.00 // видимі координати кожному гравцю у будь-який час гри
    private constant real VisibleYForAll = 0.00
    
    private constant location LFZ = Location( 0.00, 0.00 )
endglobals

function GetLocZ takes real x, real y returns real
    call MoveLocation( LFZ, x, y )
    return GetLocationZ( LFZ )
endfunction

struct Sound
    private static hashtable HS = InitHashtable( ) // таблица для звуков, StringHash(string)
    private static real PTIME   = 0.05
    boolean is_playing = false
    integer volume
    integer tick
    sound sound
    timer timer
    unit u
    real x
    real y
    real z
    real time
    real minDist
    real maxDist
    
    // создает и прелоадит новый звук, не использовать!
    // для получения свободного звука из буфера используйте Sound.path2sound(string)
    static method create takes string path, real time returns thistype
        local thistype n = thistype.allocate( )
        local integer hid = StringHash( path )
        local integer count = LoadInteger( HS, hid, 0 ) + 1
        
        set n.sound = CreateSound( path, false, true, false, 10, 10, "" )
        set n.time  = time
        set n.timer = CreateTimer( )
        call SaveInteger( H, 0, GetHandleId( n.timer ), n )
        call SaveInteger( HS, hid, 0, count )
        call SaveInteger( HS, hid, count, n )
        
        call SetSoundDistanceCutoff( n.sound, MaxDistance )
        call SetSoundDistances( n.sound, MaxDistance, MaxDistance )
        call SetSoundConeAngles( n.sound, MaxDistance, MaxDistance, 127 )
        
        return n
    endmethod
    
    method destroy takes nothing returns nothing
        // заглушка, удалять Sound не надо, они кешируются вместе с sound в hashtable
    endmethod
    
    // остановить звук
    method stop takes nothing returns integer
        if this > 0 and .is_playing then
            call PauseTimer( .timer )
            call StopSound( .sound, false, false )
            set .u = null
            set .is_playing = false
        endif
        
        return 0
    endmethod
    
    static method track takes nothing returns nothing
        local thistype snd = LoadInteger( H, 0, GetHandleId( GetExpiredTimer( ) ) )
        local real x
        local real y
        local real z
        local real d
        
        if snd.u != null then
            if GetUnitTypeId( snd.u ) > 0 then
                set x = snd.x
                set y = snd.y
                set z = snd.z
                
                set snd.x = GetUnitX( snd.u )
                set snd.y = GetUnitY( snd.u )
                set snd.z = GetLocZ( snd.x, snd.y ) + GetUnitFlyHeight( snd.u )
                
                if x != snd.x or y != snd.y or z != snd.z then
                    call SetSoundPosition( snd.sound, snd.x, snd.y, snd.z )
                endif
            else
                set snd.u = null
            endif
        endif
            
        set x = GetCameraTargetPositionX( )
        set y = GetCameraTargetPositionY( )
        set z = GetCameraTargetPositionZ( )
        set d = SquareRoot( ( x - snd.x ) * ( x - snd.x ) + ( y - snd.y ) * ( y - snd.y ) + ( z - snd.z ) * ( z - snd.z ) )
        
        if d > snd.minDist then
            if d > snd.maxDist then
                call SetSoundVolume( snd.sound, 0 )
            else
                call SetSoundVolume( snd.sound, R2I( snd.volume * ( 1.00 - ( ( d - snd.minDist ) / ( snd.maxDist - snd.minDist ) ) ) ) )
            endif
        else
            call SetSoundVolume( snd.sound, snd.volume )
        endif
        
        set snd.tick = snd.tick - 1
        
        if snd.tick <= 0 then
            call snd.stop( )
        else
            call TimerStart( snd.timer, PTIME, false, function thistype.track )
        endif
    endmethod
    
    // проиграть звук в точке
    method play takes nothing returns nothing
        if this <= 0 then
            return
        endif
        
        call SetSoundPosition( .sound, VisibleXForAll, VisibleYForAll, 0.00 )
        call StartSound( .sound )
        
        call SetSoundPosition( .sound, .x, .y, z )
        call SetSoundVolume( .sound, .volume )
        call SetSoundPitch( .sound, GetRandomReal( 0.98, 1.02 ) )
        call SetSoundConeOrientation( .sound, .x, .y, z )
        
        set .is_playing = true
        
        set .tick = R2I( .time / PTIME ) + 1
        call TimerStart( .timer, 0.00, false, function thistype.track )
    endmethod
    
    // получить звук по пути (если нет свободного звука создется новый)
    static method path2sound takes string path returns thistype
        local integer hid = StringHash( path )
        local integer count = LoadInteger( HS, hid, 0 )
        local integer i = 1
        local Sound snd
        
        if count <= 0 then
            return 0
        endif
        
        loop
            exitwhen i > count
            set snd = LoadInteger( HS, hid, i )
            
            if not snd.is_playing then
                return snd
            endif 
            
            set i = i + 1
        endloop
        
        return Sound.create( path, thistype( LoadInteger( HS, hid, 1 ) ).time )
    endmethod
    
    // проиграть звук в точке
    static method playPoint takes string path, real x, real y, real mind, real maxd, integer vol returns thistype
        local thistype snd = path2sound( path )
        
        set snd.x = x
        set snd.y = y
        set snd.z = GetLocZ( snd.x, snd.y )
        set snd.minDist = mind
        set snd.maxDist = maxd
        set snd.volume  = vol
        call snd.play( )
        
        return snd
    endmethod
    
    // проиграть звук на юните
    static method playUnit takes string path, unit u, real mind, real maxd, integer vol returns thistype
        local thistype snd = path2sound( path )
        
        set snd.u = u
        set snd.x = GetUnitX( u )
        set snd.y = GetUnitY( u )
        set snd.z = GetLocZ( snd.x, snd.y ) + GetUnitFlyHeight( u )
        set snd.minDist = mind
        set snd.maxDist = maxd
        set snd.volume  = vol
        call snd.play( )
        
        return snd
    endmethod

    // прелоадит звук в библиотеку. все звуки нужно прелоадить 1 раз.
    static method preload takes string path, real time returns nothing
        call thistype.create( path, time )
    endmethod
endstruct
endlibrary

`
ОЖИДАНИЕ РЕКЛАМЫ...
20
Зачем это когда есть
native AttachSoundToUnit            takes sound soundHandle, unit whichUnit returns nothing
Ответы (19)
28
KaneThaumaturge, подумай получше в чём суть ресурса, если не получится то чуть позже разжую
28
KaneThaumaturge, а, тьфу, ты про отдельный участок кода? Выделил бы его, а то ощущение словно про всё говоришь
20
rsfghd, Ну да, я не понял зачем метод track. В итоге остается функционал сейва пути и вроде как все. Но мне кажется что использовать для одного пути один звук не совсем правильно. Так что в целом да вопрос зачем это про все)
28
KaneThaumaturge, если юнит вдруг удалится из игры, что будет со звуком?
28
KaneThaumaturge, и в смысле для одного пути один звук неправильно? Ты же в курсе что у каждого звука в варе свой уникальный путь?
20
rsfghd, предпологаю что он будет на трупе проигрываться.
rsfghd, Причем тут это? Путь к звуку уникальный да, но может же быть несколько звуков с одним путем?
28
KaneThaumaturge, несколько звуков с одним путём? Это где в варе такое?
28
KaneThaumaturge, я не про смерть юнита, а его удаление из игры, о каком трупе идёт речь?
20
rsfghd, зачем интервалом чекать? Когда юнит удаляется ты подчищаешь все что висит на нем и так. Просто несколько звуков создаёшь с одним путем. А что не так?
20
KaneThaumaturge, я не то чтобы шарю за звуки поэтому интересно чем эта либа полезна
28
KaneThaumaturge, как ты создашь несколько звуков с одним путём? Можно пример?
28
KaneThaumaturge, эта либа полезна тем, что ты можешь создавать звуки в коде без необходимости прелоада как с локальным звуком и нет бага при выходе за радиус проигрывания звука
20
Sound = CreateSound(EXPLODE_DELAY_SOUND, false, true, true, 10, 10, "SpellsEAX");
SetSoundParamsFromLabel(Sound, "ChatroomTimerTick");
SetSoundDuration(Sound, 476);
SetSoundChannel(Sound, 11);
SetSoundVolume(Sound, 150);
SetSoundPitch(Sound, 1.0);
SetSoundDistances(Sound, 750.0, 100000.0);
SetSoundDistanceCutoff(Sound, 2000.0);
Ну вот один, потом такой же можно сделать. Я не совсем понял в чем проблема и зачем нужен прелоад?
28
KaneThaumaturge, типа в ChatroomTimerTick несколько звуков находятся? Как быть с импортным?
По поводу прелоада xgm.guru/p/wc3/183142
20
rsfghd, понял про прелоад. Полезно, не знал. Нет, просто локальный звук делаешь и все или два глобальных с одним путем.
28
KaneThaumaturge, как это 2 глобальных с одним путём? Это разные звуки будут ведь? Не представляю ка это должно выглядеть
20
rsfghd, да локалки утекают, просто две глобалки sound с одним путем. Не понял тебя.
28
KaneThaumaturge, один путь к чему? Это будут 2 глобалки с одинаковым звуком или разными? Я тоже не могу уловить ход твоих мыслей. Думаю проще было бы уже продемонстрировать
Чтобы оставить комментарий, пожалуйста, войдите на сайт.