Added by , published
Алгоритмы, Наработки и Способности
Способ реализации:
vJass
Тип:
Наработка
Версия Warcraft:
1.26
Пример того как можно использовать тип Directional у источника света вне DNC моделей.
Можно заметить что каждый раз свет падает под разными углами.
Суть в том чтобы использовать модель dummy.mdx со 180 анимациями и крепить к ней эффектом модель с источником света. Таким образом можно управлять направлением света молнии с помощью поворота юнита и сменой его анимации.
устройство модели с источником света
Version {
	FormatVersion 800,
}
Model "L_1.2" {
	BlendTime 0,
	BoundsRadius 1.0E9,
}
Sequences 2 {
	Anim "Stand" {
		Interval { 1000, 1100 },
		BoundsRadius 1.0E9,
	}
	Anim "Death" {
		Interval { 10000, 10100 },
		NonLooping,
		BoundsRadius 1.0E9,
	}
}
GlobalSequences 1 {
	Duration 0,
}
Light "Lightning" {
	ObjectId 0,
	Directional,
	static AttenuationStart 0.0,
	static AttenuationEnd 0.0,
	static Intensity 1.2,
	static Color { 1.0, 0.7529412, 0.5019608 },
	static AmbIntensity 0.0,
	static AmbColor { 0.0, 0.0, 0.0 },
	Visibility 2 {
		DontInterp,
		1000: 1.0,
		10000: 0.0,
	}
	Rotation 1 {
		DontInterp,
		GlobalSeqId 0,
		0: { 0.707107, 0.0, -0.707107, 0.0 },
	}
}
PivotPoints 1 {
	{ 0.0, 0.0, 0.0 },
}

На каждый вариант интенсивности и цвета света нужно делать отдельную модель.
  • Intensity - Означает яркость вспышки света (в данном примере 1.2).
  • Color - Означает цвет вспышки света (в данном примере светло-голубой).
Так же есть возможность смены цвета тумана под цвет молнии.
Если используете динамический туман, можете динамически менять соответствующие значения в глобалках чтобы оно автоматом подстраивалось.
Для отображения света нужно чтобы в момент создания и удаления эффекта на даммике он был в позиции цели камеры игрока. Так что в эти моменты дамми перемещается в GetCameraTargetPosition, это не вызывает десинха само по себе.
Однако свет изредка багуется если быстро дергать камеру, тогда свет пропадает только через 5 сек (дефолт значение в константах), пока думаю как решить эту проблему. Есть идеи у кого?
Вроде стало норм когда у модели dummy.mdx установил Bounds Radius большой и время смерти дамика увеличил (мб не успевала проиграться анимация смерти эффекта).
Звук пока не проигрывается с первого раза, нужно бы заюзать прелоад для этого, но у меня почему-то никак это не получалось - звук все равно не проигрывался. Еще конечно неплохо было бы заюзать какой-нибудь ресайклер для звуков, но то уже такое.
В карте - примере присутствуют:
3 модели источника света светло-голубого цвета разной интенсивности.
3 звука грома и фоновый звук дождя которые были скомунизжены отсюда.
Сама мапа взята отсюда.
код
library Storm initializer Init
    
    // Storm v1.1
    // by OVOgenez

    globals
        private constant integer DUMMY_ID  = 'h000'     // ИД дамми
        public  constant integer VAR_COUNT = 3          // Количество вариаций грозы
        
        private string array LightningPath              // Путь к моделям молнии
        private string array ThunderPath                // Путь к звукам грома
        
        private real   array L_intensity                // Интенсивность источников света
        private real   array L_red                      // Красный цвет источников света
        private real   array L_green                    // Зеленый цвет источников света
        private real   array L_blue                     // Синий цвет источников света
        
        //------------------------------------------
        // Эти значения можно динамически менять если используется динамический туман.
        //
        public boolean TF         = true                // Используется ли туман
        public integer TF_style   = 0                   // Стиль тумана
        public real    TF_zstart  = 1000.0              // Начало тумана по Z
        public real    TF_zend    = 3000.0              // Конец тумана по Z
        public real    TF_density = 0.0                 // Плотность тумана
        public real    TF_red     = 0.0                 // Красный цвет тумана
        public real    TF_green   = 0.0                 // Зеленый цвет тумана
        public real    TF_blue    = 0.0                 // Синий цвет тумана
        //------------------------------------------
        
        private location TempLocation = Location(0, 0)
    endglobals
    
    //==========================================
    // Инициализация значений массивов. НЕ связано с количеством вариаций VAR_COUNT.
    // 
    private function Init takes nothing returns nothing
        set LightningPath[1] = "L1.mdx"
        set LightningPath[2] = "L2.mdx"
        set LightningPath[3] = "L3.mdx"
        set ThunderPath[1]   = "T1.wav"
        set ThunderPath[2]   = "T2.wav"
        set ThunderPath[3]   = "T3.wav"
        
        set L_intensity[1] = 0.4
        set L_intensity[2] = 0.8
        set L_intensity[3] = 1.2
        set L_red[1]       = 0.5
        set L_red[2]       = 0.5
        set L_red[3]       = 0.5
        set L_green[1]     = 0.75
        set L_green[2]     = 0.75
        set L_green[3]     = 0.75
        set L_blue[1]      = 1.0
        set L_blue[2]      = 1.0
        set L_blue[3]      = 1.0
    endfunction
    
    private module Settings
        static constant real PERIOD = 0.03125  // Период таймера
        
        //------------------------------------------
        // Эти значения можно использовать в методах ниже для определения результата.
        //
        readonly integer Variant     // Вариация грозы            (от 1 до VAR_COUNT)
        readonly integer Count       // Количество вспышек света  (от 1 до ...)
        readonly integer Current     // Текущая вспышка света     (от 1 до Count; 0 во время задержки перед звуком)
        readonly integer Azimuth     // Горизонтальный угол света (от -180 до 180)
        readonly integer Zenith      // Вертикальный угол света   (от -90 до 90)
        readonly integer LIndex      // Индекс текущей модели молнии для массива LightningPath
        readonly integer TIndex      // Индекс текущего звука грома для массива ThunderPath
        //------------------------------------------
        
        //==========================================
        // Вернуть индекс модели молнии для массива LightningPath.
        //
        method operator GetLightningIndex takes nothing returns integer
            return .Variant
        endmethod
        
        //==========================================
        // Вернуть индекс звука грома для массива ThunderPath.
        //
        method operator GetThunderIndex takes nothing returns integer
            return .Variant
        endmethod
        
        //==========================================
        // Вернуть длительность вспышки молнии.
        //
        method operator GetPlayDuration takes nothing returns real
            return GetRandomInt(2, 4)*PERIOD
        endmethod
        
        //==========================================
        // Вернуть длительность между вспышками молнии.
        //
        method operator GetStopDuration takes nothing returns real
            return GetRandomInt(1, 3)*PERIOD
        endmethod
        
        //==========================================
        // Вернуть задержку до звука грома.
        //
        method operator GetSoundDelay takes nothing returns real
            return (10*(VAR_COUNT*VAR_COUNT - .Variant*.Variant) + GetRandomInt(0, 10))*PERIOD
        endmethod
        
        //==========================================
        // Вернуть громкость звука грома.
        //
        method operator GetSoundVolume takes nothing returns integer
            return GetRandomInt(R2I(127*0.8), 127)
        endmethod
        
        //==========================================
        // Вернуть высоту звука грома.
        //
        method operator GetSoundPitch takes nothing returns real
            return GetRandomReal(0.9, 1.1)
        endmethod
    endmodule
    
    //==================================================================================================================
    
    private module Array
        private static thistype array ARRAY
        private static integer SIZE = 0
        private integer INDEX = 0
        
        static method operator size takes nothing returns integer
            return SIZE
        endmethod
        static method operator [] takes integer i returns thistype
            return ARRAY[i]
        endmethod
        
        method remove takes nothing returns nothing
            if INDEX != 0 then
                set ARRAY[SIZE].INDEX = INDEX
                set ARRAY[INDEX] = ARRAY[SIZE]
                set ARRAY[SIZE] = 0
                set INDEX = 0
                set SIZE = SIZE - 1
            endif
        endmethod
        
        method add takes nothing returns nothing
            if INDEX == 0 then
                set SIZE = SIZE + 1
                set INDEX = SIZE
                set ARRAY[SIZE] = this
            endif
        endmethod
    endmodule
    
    private struct TStorm
        implement Array
        implement Settings
        
        private static boolean TempTF         = TF
        private static integer TempTF_style   = TF_style
        private static real    TempTF_zstart  = TF_zstart
        private static real    TempTF_zend    = TF_zend
        private static real    TempTF_density = TF_density
        private static real    TempTF_red     = TF_red
        private static real    TempTF_green   = TF_green
        private static real    TempTF_blue    = TF_blue
        
        private static timer Timer = CreateTimer()
        
        private unit    Dummy
        private effect  Effect
        private real    Time
        private boolean Mode
        
        private method toCamera takes nothing returns nothing
            call SetUnitX(.Dummy, GetCameraTargetPositionX())
            call SetUnitY(.Dummy, GetCameraTargetPositionY())
        endmethod
        
        method destroy takes nothing returns nothing
            call this.remove()
            // ====================
            call .toCamera()
            call DestroyEffect(.Effect)
            call KillUnit(.Dummy)
            set .Effect = null
            set .Dummy = null
            // ====================
            call this.deallocate()
        endmethod
        
        private method Function takes nothing returns nothing
            local sound s
            call .toCamera()
            set .Time = .Time - PERIOD
            if .Time <= 0 then
                if .Current > 0 and .Current <= .Count then
                    if not .Mode then
                        set .Mode = true
                        set .Time = .GetPlayDuration
                        set .LIndex = .GetLightningIndex
                        set .Effect = AddSpecialEffectTarget(LightningPath[.LIndex], .Dummy, "origin")
                    else
                        set .Mode = false
                        set .Time = .GetStopDuration
                        set .Current = .Current + 1
                        if .Current > .Count then
                            set .Current = 0
                            set .Time = .GetSoundDelay
                        endif
                        call DestroyEffect(.Effect)
                        set .Effect = null
                    endif
                elseif .Current == 0 then
                    set .TIndex = .GetThunderIndex
                    set s = CreateSound(ThunderPath[.TIndex], false, false, false, 10, 10, "DoodadsEAX")
                    call SetSoundChannel(s, 10)
                    call SetSoundVolume(s, .GetSoundVolume)
                    call SetSoundPitch(s, .GetSoundPitch)
                    call StartSound(s)
                    call KillSoundWhenDone(s)
                    set s = null
                    call this.destroy()
                endif
            endif
        endmethod
        
        private static method Periodic takes nothing returns nothing
            local real r = TF_red
            local real g = TF_green
            local real b = TF_blue
            local integer i = thistype.size
            loop
                exitwhen i <= 0
                call thistype[i].Function()
                if TF and thistype[i] > 0 and thistype[i].Count > 0 and thistype[i].Mode then
                    set r = r + L_red[thistype[i].LIndex]*L_intensity[thistype[i].LIndex]
                    set g = g + L_green[thistype[i].LIndex]*L_intensity[thistype[i].LIndex]
                    set b = b + L_blue[thistype[i].LIndex]*L_intensity[thistype[i].LIndex]
                endif
                set i = i - 1
            endloop
            if TF then
                if TempTF         != TF         or /*
                */ TempTF_style   != TF_style   or /*
                */ TempTF_zstart  != TF_zstart  or /*
                */ TempTF_zend    != TF_zend    or /*
                */ TempTF_density != TF_density or /*
                */ TempTF_red     != r          or /*
                */ TempTF_green   != g          or /*
                */ TempTF_blue    != b          then
                    set TempTF_style   = TF_style
                    set TempTF_zstart  = TF_zstart
                    set TempTF_zend    = TF_zend
                    set TempTF_density = TF_density
                    set TempTF_red     = r
                    set TempTF_green   = g
                    set TempTF_blue    = b
                    call SetTerrainFogEx(TF_style, TF_zstart, TF_zend, TF_density, RMinBJ(1, r), RMinBJ(1, g), RMinBJ(1, b))
                endif
            elseif TempTF != TF then
                call ResetTerrainFog()
            endif
            set TempTF = TF
        endmethod
        
        static method create takes integer variant, integer count, integer azimuth, integer zenith returns thistype
            local thistype this = thistype.allocate()
            // ====================
            set .Variant = variant
            set .Count = count
            set .Current = 1
            set .Azimuth = azimuth
            set .Zenith = zenith
            set .Time = 0
            set .Mode = false
            set .Dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0, 0, azimuth)
            call SetUnitAnimationByIndex(.Dummy, zenith + 90)
            // ====================
            call this.add()
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TimerStart(Timer, PERIOD, true, function thistype.Periodic)
        endmethod
    endstruct
    
    //==================================================================================================================
    
    //==========================================
    // Имитировать грозу:
    //   variant - Вариация грозы            (от 1 до VAR_COUNT)
    //   count   - Количество вспышек света  (от 1 до ...)
    //   azimuth - Горизонтальный угол света (от -180 до 180)
    //   zenith  - Вертикальный угол света   (от -90 до 90)
    //
    public function Imitate takes integer variant, integer count, integer azimuth, integer zenith returns nothing
        if variant > 0 and variant <= VAR_COUNT and count > 0 then
            call TStorm.create(variant, count, azimuth, zenith)
        endif
    endfunction
    
    //==========================================
    // Имитировать грозу (случайно):
    //   variant - Вариация грозы            (от 1 до VAR_COUNT)
    //
    public function ImitateSimple takes integer variant returns nothing
        call Imitate(variant, GetRandomInt(2, 4), GetRandomInt(-180, 180), GetRandomInt(-90, -30))
    endfunction

endlibrary
`
LOADING AD...
1
21
1
Интересная концепция. Пожалуй, поюзаю)
Только у настоящих гроз, как правило, цвет почти белый, и не сильно много углов света и яркостей.
Потому не проще ли сделать 1 модельку на 10 анимаций stand, с разной случайностью, запихнуть туда источник света, прикрутить его к кости и задать кости повороты, а у самого источника заанимировать яркость и видимость, чтоб были разные вспышки?
К самой модели можно также молнию сделать и событийные объекты звуков грома, тоже разные.
А триггером по периоду создавать спецэффект в х у камеры и удалять его. Код всего из нескольких строк получится )
Replies (6)
0
15
0
EugeAl, можно и так, но изначально хотелось больше контроля из кода.
Саму модель молнии делать не считал нужным, т.к. цель в другом состояла. Но если делать какую-нибудь мапу от 1 лица то можно понаделать всяких отдельных эффектов молний чтобы на фоне скайбокса смотрелось.
1
15
1
Типо как тут.
0
21
0
OVOgenez, ну да, круто смотрится. Ок, понял.
А что за карта на скрине? Где скачать?)
0
15
0
EugeAl, это синематик youtu.be/6B9r0D1myMs
0
21
0
OVOgenez, шикарный, жуткий синематик. Офигенно)
Но я так понял, его не скачать как карту...
2
15
2
EugeAl, там в описании ссылка
1
25
1
Не обезательно спавнить даммика, есть функция смены глобального освещения SetDayNightModels(,)
Можно просто заменять ее на время вспышки.
Replies (1)
1
15
1
Jack-of-shadow, это если DNC не используется, может же быть не полный мрак. Плюс ты с этой функцией к моделе не достучишься, а значит и повернуть источник в коде не сможешь. Ну и то что DNC одна а дамиков можно наспамить несколько (типо несколько одновременных вспышек).
1
0
Одна из немногих работ, которую не стыдно похвалить и оценить в принципе. Очень рад, что такие Авторы еще остаются на плаву и не позволяют упасть нашему обществу Картоделов, только благодаря которому, еще держится в целом Варкрафт как игра спустя столько лет.
Спасибо)
Даже не знаю какие минусы тут особо подобрать, ну разве что, соглашусь с комментарием выше, про цвет, но это не супер критично все же.
Надеюсь, что увидим в будущем какие-нибудь еще работы, подобного плана.🐸
To leave a comment please sign in to the site.