Проблема состоит в том, что когда я приказываю проиграть 1 звук в двух разных точках, он проигрывается только в первой. Есть догадка что это такой баг варовского движка, но это моя первая попытка работать со звуком, так что может чего-то не понимаю.
код
globals
   hashtable Hash = InitHashtable() // Хеш-таблица
   real gateOnCasterOffset = 265 // Расстояние до портала, появляющегося перед героем
   real ManaPerSecond = 4.5 //Магии в секунду
   real relaseTime = 9.3 // Время в сек, за которое портал активируется
   real gateUnkeep = 0.45 // Время (в секундах), через которое порталы перестануть работать в случае срыва/отмены заклинания
   real spellSoundFullDistance = 450.0 // Расстояние, на котором звук заклинания проигрывается на полную громкость
   real spellSoundScatteringDistance = 1450.0 // Расстояние, на котором громкость звукового эффекта угасает (если расстояние больше - эффект вовсе не слышен)
   endglobals

function GetUnitZ takes unit WhichUnit returns real
   local location UnitLoc = Location(GetUnitX(WhichUnit),GetUnitY(WhichUnit))
   local real UnitZ = GetLocationZ(UnitLoc)+GetUnitFlyHeight(WhichUnit)
   call RemoveLocation(UnitLoc)
   return UnitZ
endfunction
   
function removePortals takes nothing returns nothing //Функция для удаления порталов спустя некоторое время после завершения каста

   local sound soundHandle01


   local timer LifetimeAfterCast = GetExpiredTimer()
   local integer LifetimeAftercastEnd = GetHandleId(LifetimeAfterCast)

   local unit gateOnCaster = LoadUnitHandle(Hash,LifetimeAftercastEnd,0)
   local unit gateOnTargetPoint = LoadUnitHandle(Hash,LifetimeAftercastEnd,1)
   local unit Caster = LoadUnitHandle(Hash,LifetimeAftercastEnd,5)
      
      call KillUnit(gateOnCaster) // Юнитов не удаляю, так как предполагаю что это будет делать отдельная система
      call SetUnitAnimation(gateOnCaster, "Death")
      call KillUnit(gateOnTargetPoint)
      call SetUnitAnimation(gateOnTargetPoint, "Death")
      call IssueImmediateOrder(Caster,"stop")
  
      call DestroyTimer(GetExpiredTimer())
      set gateOnCaster = null
      set gateOnTargetPoint = null
  
endfunction

function gateActivating takes nothing returns nothing //Активируем порталы


    local timer gateActivatingTimer = GetExpiredTimer() //Ловим таймер, по истечению которого должны заработать порталы
    local integer Id1 = GetHandleId(gateActivatingTimer) //Берём его id
    
    local unit gateOnCaster = LoadUnitHandle(Hash,Id1,0) //Выгружаем юнита (портал возле героя) по id таймера, в ячейке 0
    local unit gateOnTargetPoint = LoadUnitHandle(Hash,Id1,1) //Выгружаем юнита (портал в точке применения скилла) по id таймера, в ячейке 1
    
    local location OnCaster = GetUnitLoc(gateOnCaster) //Считываем локацию (положение) портала возле героя. Т.к. чтоб задать точку перенесения порталу нужна именно локация
    local location OnTargetPoint = GetUnitLoc(gateOnTargetPoint) //Считываем локацию (положение) портала в точке применения скилла. Т.к. чтоб задать точку перенесения порталу нужна именно локация
    
    call UnitAddAbility(gateOnCaster,'GFGT') // Тут добавляем абилку портала порталу возле героя
    call WaygateSetDestination(gateOnCaster, GetLocationX(OnTargetPoint), GetLocationY(OnTargetPoint)) //Тут, задаём точку ("Локацию"), в которую будет телепортировать юнитов портал возле героя
    call WaygateActivate (gateOnCaster,true) //Тут запускаем портал возле героя
    
    call UnitAddAbility(gateOnTargetPoint,'GFGT') // Тут добавляем абилку портала порталу в точке применения скилла
    call WaygateSetDestination(gateOnTargetPoint, GetLocationX(OnCaster), GetLocationY(OnCaster)) //Тут, задаём точку ("Локацию"), в которую будет телепортировать юнитов портал в точке применения скилла
    call WaygateActivate (gateOnTargetPoint,true) //Тут запускаем портал в точке применения скилла
     
    call RemoveLocation(OnCaster) //Тут очищаем записи о переменной типа "Локация"/удаляем локацию
    call RemoveLocation(OnTargetPoint) //Тут очищаем записи о переменной типа "Локация"/удаляем локацию
    
    set gateOnCaster = null //Удаляем записи о юните (портеле возле героя), внутри этой функции
    set gateOnTargetPoint = null //Удаляем записи о юните (портеле в точке применения скилла), внутри этой функции
    
    call DestroyTimer(gateActivatingTimer) //Удаляем таймер, запустивший эту функцию
    
endfunction

function gateEffect takes nothing returns nothing // Функция проверяет поддерживает ли герой спелл

    local timer gateEffectPeriodical = GetExpiredTimer() //Ловим истекающий таймер (тот, что периодический)
    local integer Id2 = GetHandleId(gateEffectPeriodical) //Считываем id этого таймера

    local unit gateOnCaster = LoadUnitHandle(Hash,Id2,0) //Выгружаем юнита (портал возле героя) за этим id из ячейки 0
    local unit gateOnTargetPoint = LoadUnitHandle(Hash,Id2,1) //Выгружаем юнита (портал в точке применения) за этим id из ячейки 0
    local unit Caster = LoadUnitHandle(Hash,Id2,2) //Выгружаем кастера из ячейки 0
    local timer stopTimer = LoadTimerHandle(Hash,Id2,3) //Выгружаем id таймера, по истечению которого активируются порталы
    
    local sound spellSoundOnCaster = LoadSoundHandle(Hash,Id2,5)
    local sound spellSoundOnTarget = LoadSoundHandle(Hash,Id2,6)
    
    local integer Id1

    local timer LifetimeAfterCast
    local integer LifetimeAftercastEnd

    if GetUnitCurrentOrder(Caster) != OrderId("darkportal") or GetUnitState(Caster,UNIT_STATE_MANA)<ManaPerSecond/20 then //Если приказ, который выполняет юнит не равен тому что надо (приказ указан в настройках абилки) или у кастера закончилась мана, тогда
    
      set LifetimeAfterCast = CreateTimer() // Создаём таймер, по истечению которого порталы перестанут работать
      call TimerStart(LifetimeAfterCast,gateUnkeep,false,function removePortals) //Стартуем этот таймер как одноразовый, который истечёт через "gateUnkeep" секунд и запустит функцию "removePortals"
      set LifetimeAftercastEnd = GetHandleId(LifetimeAfterCast) //Берём id этого таймера
      
      call SaveUnitHandle(Hash,LifetimeAftercastEnd,0,gateOnCaster) //Сохраняем за этим id портал возле героя
      call SaveUnitHandle(Hash,LifetimeAftercastEnd,1,gateOnTargetPoint) //Сохраняем за этим id портал у точки применения скилла
      call SaveUnitHandle(Hash,LifetimeAftercastEnd,5,Caster) //Сохраняем кастера
     
      set Id1 = LoadInteger(Hash,Id2,4) //Загружаем id таймера, по истечению которого порталы должны были бы активироватся
      
      set gateOnCaster = null //Обнуляем переменные
      set gateOnTargetPoint = null //Обнуляем переменные
      set Caster = null //Обнуляем переменные
                  
      
      call DestroyTimer(stopTimer) //Удаляем запись о таймере "stopTimer" внутри этой функции
      call DestroyTimer(gateEffectPeriodical) //Удаляем периодический таймер, запускающий эту функцию (В этот момент все периодические действия прекращаются)
      call FlushChildHashtable(Hash,Id1) //Очищаем хеш-таблицу за ключём по id, таймера, по истечению которого активируются порталы
      call FlushChildHashtable(Hash,Id2) //Очищаем хеш-таблицу за ключём по id, периодического таймера, вызывающего эту функцию
  
  else
  
       call SetUnitState(Caster, UNIT_STATE_MANA, GetUnitState(Caster,UNIT_STATE_MANA)-ManaPerSecond/20) //Иначе (если спелл не отменён и не закончилась мана) отнимать ManaPerSecond ед. магии
     
       if SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnCaster),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnCaster),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnCaster),2)) <= spellSoundFullDistance then
             call SetSoundVolume(spellSoundOnCaster,100)
       elseif SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnCaster),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnCaster),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnCaster),2)) >= spellSoundFullDistance and SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnCaster),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnCaster),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnCaster),2)) <= spellSoundScatteringDistance then
             call SetSoundVolume(spellSoundOnCaster,100-R2I(100*(SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnCaster),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnCaster),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnCaster),2))-spellSoundFullDistance)/(spellSoundScatteringDistance-spellSoundFullDistance)))
       else
             call SetSoundVolume(spellSoundOnCaster,0)
       endif
     
       if SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnTargetPoint),2)) <= spellSoundFullDistance then
             call SetSoundVolume(spellSoundOnTarget,100)
       elseif SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnTargetPoint),2)) >= spellSoundFullDistance and SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnTargetPoint),2)) <= spellSoundScatteringDistance then
             call SetSoundVolume(spellSoundOnTarget,100-R2I(100*(SquareRoot(Pow(GetCameraTargetPositionX()-GetUnitX(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionY()-GetUnitY(gateOnTargetPoint),2)+Pow(GetCameraTargetPositionZ()-GetUnitZ(gateOnTargetPoint),2))-spellSoundFullDistance)/(spellSoundScatteringDistance-spellSoundFullDistance)))
       else
             call SetSoundVolume(spellSoundOnTarget,0)
       endif

 endif
endfunction

function portal_Birth_Init takes nothing returns nothing //Появление портала
    
    //Названия переменных выдумываешь какие хочешь. Только учти что тебе с ними иметь дело
    local unit Caster
    local player casterOwner
    
    local real unitCasterGateX 
    local real unitCasterGateY
    local location spellTargetLoc
    
    local unit gateOnCaster
    local unit gateOnTargetPoint
    
    local timer gateActivatingTimer
    local timer gateEffectTimerPeriodic
    
    local integer Id1
    local integer Id2
    
    local sound spellSoundOnCaster
    local sound spellSoundOnTarget  
    
    if GetSpellAbilityId() == 'rift' then //Убедимся что кастуется нужная абилка, пишем свой код геройской абилки
      
      set Caster = GetTriggerUnit() // Заносим кастера в переменную
      set casterOwner = GetOwningPlayer(Caster)// Игрок-владелец кастера
      
      set unitCasterGateX = GetUnitX(Caster)+gateOnCasterOffset*Cos(GetUnitFacing(Caster)*bj_DEGTORAD) //Задаём координаты портала перед героем
      set unitCasterGateY = GetUnitY(Caster)+gateOnCasterOffset*Sin(GetUnitFacing(Caster)*bj_DEGTORAD) //Задаём координаты портала перед героем
      
      set spellTargetLoc = GetSpellTargetLoc() // Ловим локацию чтобы потом удалить
      
      set gateOnCaster = CreateUnit(casterOwner,'gate',unitCasterGateX,unitCasterGateY,180+GetUnitFacing(Caster)) // Создаёт портал перед героем
      set gateOnTargetPoint = CreateUnitAtLoc(casterOwner,'gate',spellTargetLoc,GetUnitFacing(Caster))// Создаёт портал в точке применения

      call SetUnitAnimation( gateOnCaster, "Birth" ) //Запускает анимацию появления портала перед героем
      call SetUnitAnimation( gateOnTargetPoint, "Birth" ) //Запускает анимацию появления портала перед героем
 
      set gateActivatingTimer = CreateTimer() //Создать таймер, по истечению которого порталам добавится и настроится абилка портала
      call TimerStart(gateActivatingTimer,relaseTime,false,function gateActivating) // Запускаем предыдущий таймер. По истечению таймера будет вызваня функция "gateActivating". Чтоб вызвать функцию, необходимо разместить её выше чем ту, в которой ее вызывают
      
      set Id1 = GetHandleId(gateActivatingTimer) //Берем id таймера, по истечению которого должен заработать потрал
      call SaveUnitHandle(Hash,Id1,0,gateOnCaster) //Сохраняем в хеш-таблицу портал перед героем, за ключём = id таймера,0, по истечению которого должен заработать потрал
      call SaveUnitHandle(Hash,Id1,1,gateOnTargetPoint) //Сохраняем в хеш-таблицу портал в точке применения скилла, за ключём = id таймера,1, по истечению которого должен заработать потрал
      
      set gateEffectTimerPeriodic = CreateTimer() //Создать периодический таймер, по истечению которого (много раз в секунду) будут выполнятся заданные функцией "gateEffect" действия
      call TimerStart(gateEffectTimerPeriodic,0.05,true,function gateEffect) // тут 20 раз в секунду отнимаем ману, проверяем какой приказ выполняет герой, не мёртв ли и прочее
      
      set spellSoundOnCaster = CreateSound("Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav",false,false,false,10,10,"SpellsEAX")
      call SetSoundParamsFromLabel(spellSoundOnCaster,"ShimmeringPortalBirth")
      call SetSoundDuration(spellSoundOnCaster,8529)
      call SetSoundChannel(spellSoundOnCaster,11)
      call StartSound(spellSoundOnCaster) //Запускаем звук появления портала (название переменной состоит из префикса "gg_snd_" и названия переменной в редакторе звука)
      
      set spellSoundOnTarget = CreateSound("Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav",false,false,false,10,10,"SpellsEAX")
      call SetSoundParamsFromLabel(spellSoundOnTarget,"ShimmeringPortalBirth")
      call SetSoundDuration(spellSoundOnTarget,8529)
      call SetSoundChannel(spellSoundOnTarget,11)
      call StartSound(spellSoundOnTarget) //Запускаем звук появления портала (название переменной состоит из префикса "gg_snd_" и названия переменной в редакторе звука)
      
      set Id2 = GetHandleId(gateEffectTimerPeriodic) //Берём id таймера, отвечающего за периодические действия
      call SaveUnitHandle(Hash,Id2,0,gateOnCaster) //Сохраняем за этим id портал перед героем
      call SaveUnitHandle(Hash,Id2,1,gateOnTargetPoint) //Сохраняем за этим id портал в точке применения
      call SaveUnitHandle(Hash,Id2,2,Caster) //Сохраняем кастующего юнита
      call SaveTimerHandle(Hash,Id2,3,gateActivatingTimer) //Сохраняем таймер, по истечению которого портал заработает
      call SaveInteger(Hash,Id2,4,Id1) //Сохраняем id таймера, по истечению которого портал заработает
      call SaveSoundHandle(Hash,Id2,5,spellSoundOnCaster)
      call SaveSoundHandle(Hash,Id2,6,spellSoundOnTarget)
      
      set Caster = null //Обнуляем переменную
      set casterOwner = null //Обнуляем переменную
      call RemoveLocation(spellTargetLoc) //Удаляем локацию
      set gateOnCaster = null //Обнуляем переменную
      set gateOnTargetPoint = null //Обнуляем переменную
      set gateActivatingTimer = null //Обнуляем переменную
      set gateEffectTimerPeriodic = null //Обнуляем переменную
      
    endif

endfunction

function InitTrig_spaceRift takes nothing returns nothing // инит
    set gg_trg_spaceRift = CreateTrigger() //Создаёт этот триггер (Инит)
    call TriggerRegisterAnyUnitEventBJ( gg_trg_spaceRift, EVENT_PLAYER_UNIT_SPELL_EFFECT ) //Ловит приведение любого скилла, любого юнита на карте в действие
    call TriggerAddAction( gg_trg_spaceRift, function portal_Birth_Init ) //Если произошло событие, передаёт соответствующие ему данные в функцию "portal_Birth_Init" и запускает её
endfunction

Карта прикреплена. Так будет легче понять "контекст".

Принятый ответ

Нашёл систему, где звук передаётся в таймер, который истекает через 0 секунд. В результате один и тот же звук может проигрываться несколько раз без всяких проблем. В инетике пишут мол это такой баг движка.
0
20
8 лет назад
0
тоже были траблы со звуком
0
21
8 лет назад
Отредактирован Raised
0
ssbbssc, это я уже пофиксил так:
код
set spellSoundOnTarget = CreateSound("Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav",false,false,false,10,10,"SpellsEAX") 
      call SetSoundParamsFromLabel(spellSoundOnTarget,"ShimmeringPortalBirth")
      call SetSoundDuration(spellSoundOnTarget,8529)
      call SetSoundChannel(spellSoundOnTarget,11)

//Скопировал с war3map.j файла в архиве  этой карты, предварительно настроив звук в редакторе и сохранив карту. Так проблем с проигрыванием звука больше наблюдать не доводилось. 

Но что забавно, если звук не один и тот-же - воспроизведёт без проблем, если один и тот же - воспроизведёт только первый. Чтобы воспроизвести второй - пришлось использовать хеш и таймер. Вангую что это каким-то образом связано с игровым движком, либо я краб.

Добавил небольшую (0.1 с) задержку перед запуском следующего звука и проблема решилась.
Загруженные файлы
0
20
8 лет назад
0
GF RaiseD:
тут слухи про 27 патч ходят
может пофиксят
хотя так и всю игру переписать легче
0
21
8 лет назад
0
Нашёл систему, где звук передаётся в таймер, который истекает через 0 секунд. В результате один и тот же звук может проигрываться несколько раз без всяких проблем. В инетике пишут мол это такой баг движка.
Принятый ответ
Чтобы оставить комментарий, пожалуйста, войдите на сайт.