WarCraft 3: Первые шаги

[vJass] Создание moving system
Теперь приступим к самому программированию. В vJass добавили возможность создания библиотек – области кода, которые после компиляции будут вставлены выше Custom script карты. Очень полезная вещь, позволяет систему держать в 1 триггере, облегчая импорт вашей наработки.
Удаляем стандартную инициализацию и создаем триггер «MovingSys», дальше «Правка->Конвертировать в текст», удаляем весь текст из триггера и пишем:
library MoveSys

endlibrary
Вот и создали библиотеку, теперь нам надо описать вектор.
Вектору необходимы 3 переменные для его координат:
library MoveSys

struct vector
    real x
    real y 
    real z
endstruct

endlibrary
Теперь добавим методы (те самые функции) для операций над вектором:
library MoveSys

struct vector
    real x
    real y 
    real z
    // В метод передается еще переменная this, которая позволяет обращаться к собственным переменным
    // пример this.x = 0 
    // Если поставить перед методом "static", то в метод не передастся переменная this
    static method GetFrom2Locs takes real ax,real ay,real az,real bx,real by,real bz returns vector
        local vector v = vector.create() // таким образом создается вектор
            // заполняем переменные, созданного вектора
            set v.x = bx-ax
            set v.y = by-ay
            set v.z = bz-az
            // Возращаем вектор, теперь, чтобы создать вектор можно написать vector.GetFrom2Locs(...)
            return v
    endmethod
    // Сумма векторов, допустим у вас есть 2 вектора - a.Summ(b,1) сложит их
    method Summ takes vector a returns nothing
        set this.x = this.x + a.x
        set this.y = this.y + a.y
        set this.z = this.z + a.z
    endmethod  
    // умножает вектор на число
    // пример a.Multiply(2) умножит вектор а на 2
    method Multiply takes real k returns nothing
        set this.x = k*this.x
        set this.y = k*this.y 
        set this.z = k*this.z 
    endmethod
    // Вычисляет длину вектора
    method Module takes nothing returns real
        return SquareRoot(this.x*this.x+this.y*this.y+this.z*this.z)
    endmethod
    // Нормирование ветора
    method Norm takes nothing returns nothing
    local real m = SquareRoot(this.x*this.x+this.y*this.y+this.z*this.z)
        set this.x = this.x/m
        set this.y = this.y/m
        set this.z = this.z/m
    endmethod
endstruct

endlibrary
Поздравляю, первый «кит» системы готов! Теперь обратим свой взор на объекты движения. Двигать мы будем юнитов, и чтобы к ним привязать векторы скорости, ускорения и силы, создадим еще одну структуру:
// Обзовем новую структуру "телом"
struct body
    unit u // Его будем двигать
    real m // масса юнита
    vector v // вектор скорость
    vector a // ускорение
    vector f // сила, приложенная к телу
    // метод создания тела с инициализированием переменных
    // пример создания тела: local body b = body.Create(unit,mass)
    // не путай с  body.create() это разные функции
    static method Create takes unit u, real mass returns body
        local body b = body.create()
            set b.u = u
            set b.m = mass
            set b.v = vector.create()
            set b.a = vector.create()
            set b.f = vector.create()
            // Это нужно для изменения координаты z
            call UnitAddAbility(u,'Amrf')
            call UnitRemoveAbility(u,'Amrf')  
        return b
    endmethod
    // уничтожение тела, когда оно больше не нужно
    method Destroy takes nothing returns nothing
        call this.v.destroy()
        call this.a.destroy()
        call this.f.destroy()
        call this.destroy()
    endmethod
endstruct

Основная часть движка

Теперь остался последний кит – сам цикл движения. Есть два способа двигать тело: обсчитывать координаты, а потом перемещать юнита в точку с этими координатами; брать за основу координаты юнита и перемещать относительно них. Может быть, вам не видно разницы между этими способами? Тогда приведу примеры:
set x = …
set y = …
set z = …
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0)
Это пример первого способа. Все вычисления производятся вне нативных функций, при этом не учитываются «желания» юнита передвигаться, поэтому придется делать еще триггер сброса приказа (или добавлять способность москитов), так как при попытке бежать юнит будет дергаться.
call SetUnitX(u,GetUnitX(u)+dx)
call SetUnitY(u, GetUnitY(u)+dy)
call SetUnitFlyHeight(u, GetUnitFlyHeight(u)+dz,0)
Второй способ. Теперь два движения будут складываться. Воспользуемся этим методом.
Создаем массив тел, по которому будет бегать цикл:
globals
    body array mvs_Sb [1000]
    integer mvs_si = -1 // при первой записи, элемент запишется в ячейку 0
endglobals
Теперь создадим функцию, которая нам запустит систему:
library MoveSys initializer IniMove // Здесь объявляется функция-инициализатор

…

private function Engine takes nothing returns nothing

endfunction

function IniMove takes nothing returns nothing
        // нам таймер в глобалку сохранять незачем, так как он работает всю игру
        // период 0.025 выбран в результате практики, при 0.05 видны "рывки"
        call TimerStart(CreateTimer(), 0.025, true, function Engine)
endfunction

endlibrary
Пора наполнить содержанием функцию «Engine»:
private function Engine takes nothing returns nothing
    local integer i = 0
    local body b
    loop
        exitwhen i > mvs_si
            if mvs_Sb__ != 0 then
                set b = mvs_Sb__ // для читаемости кода
                // 
                // Считаем ускорение тела и обнуляем силу, чтобы она не накапливалась
		if b.m > 0 then  //  для защиты системы от деления на нуль
                	set b.a.x = b.f.x/b.m
			set b.a.y = b.f.y/b.m
                	set b.a.z = b.f.z/b.m
		endif
                set b.f.x = 0
                set b.f.y = 0
                set b.f.z = 0
                // Теперь считаем скорость и перерасчитываем ее при изменении координат в точка/с
                set b.v.x = b.v.x + b.a.x
                set b.v.y = b.v.y + b.a.y
                set b.v.z = b.v.z + b.a.z
                // Финальная часть - передвигаем юнит
                call SetUnitX(b.u, GetUnitX(b.u) + b.v.x*0.025)
                call SetUnitY(b.u, GetUnitY(b.u) + b.v.y*0.025)
                call SetUnitFlyHeight(b.u, GetUnitFlyHeight(b.u) + b.v.z*0.025,0)
            else // Опа! Нашли удаленный с помощью body.Remove() элемент массива. Приговор: удалить и закрыть дырку последним элементом.
                set mvs_Sb__ = mvs_Sb[mvs_si]
                set mvs_Sb[mvs_si] = 0
                set mvs_si = mvs_si - 1
                set i = i - 1 // иначе тело, которым мы заткнули дырку, не обработается за это прохождение
				//  цикла, то есть пропустит квант времени
            endif
        set i = i + 1
    endloop
endfunction

Просмотров: 3 134

Комментарии пока отсутcтвуют