Добавлен , опубликован

[vJass] Создание moving system

Содержание:
Теперь осталось научиться добавлять тело в этот стек. Для этого в структуру body добавьте такой метод:
    // Помещаем тело в стек для обработки движком + добавляем защиту от переполнения
    method Start takes nothing returns boolean
      if mvs_si < 1000 then  
        set mvs_si = mvs_si + 1
        set mvs_Sb[mvs_si] = this
        return true
      else
        return false
      endif
    endmethod
Теперь можно создать тело, задать ему начальные параметры и поместить в движок с помощью этого метода.
Последний штрих – сделать пространство объемным. Для этого к z юнита прибавлять разницу высот. Тогда главная часть движка примет вид:
globals
    body array mvs_Sb [1000]
    integer mvs_si = -1 // при первой записи, элемент запишется в ячейку 0
    location GZL = Location(0,0) 
endglobals

    function GetZ takes real X, real Y returns real        
        call MoveLocation(GZL,X,Y)
        return GetLocationZ(GZL)
    endfunction
    
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 SetUnitFlyHeight(b.u,GetUnitFlyHeight(b.u) - GetZ(GetUnitX(b.u) + b.v.x*0.025,GetUnitY(b.u) + b.v.y*0.025) + GetZ(GetUnitX(b.u),GetUnitY(b.u)),0)
                // Финальная часть - передвигаем юнит
                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

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

А в метод уничтожения тела корректируем вот так (при удалении в стеке все равно остается записано тело, точнее integer - номер ячейки где он хранился. ). На самом деле все переменные, ссылающиеся на структуру, являются типом integer, который есть номер ячейки массива, в котором вар хранит структуру:
    // уничтожение тела, когда оно больше не нужно
    method Destroy takes nothing returns nothing
    local integer i = 0
        loop // Ищем в массиве движка наше тело и убираем его от туда
            exitwhen i > mvs_si
                if mvs_Sb__ == this then
                    set mvs_Sb__ = mvs_Sb[mvs_si]
                    set mvs_Sb[mvs_si] = 0
                    set mvs_si = mvs_si - 1
                    set i = mvs_si // выход из цикла              
                endif
            set i = i + 1
        endloop
	// Удаление вложенных структур
        call this.v.destroy()
        call this.a.destroy()
        call this.f.destroy()
	// Удаление самой структуры
        call this.destroy()
    endmethod

Добавляем физические явления (удар об землю, трение и т.п.)

Теперь создадим отдел параметров системы для удобства настройки:
library MoveSys initializer IniMove

// Параметры системы, обычно их сюда помещают для удобства настройки системы
// private - переменная будет доступна только внутри библиотеки
globals
    private real g = 14 // Ускорение свободного падения
    private real Rest = 0.4 // Коэффициент восстановления после удара об землю
    private real mu = 0.4 // Коэффициент трения
endglobals

...

endlibrary
Советую всегда при создании систем выносить коэффициенты к заголовку библиотеки и давать пояснительные комментарии к ним. Это поможет разобраться в собственной системе через несколько месяцев.

Теперь удары об землю и границы карты:
private function Engine takes nothing returns nothing
    local integer i = 0
    local body b
    local real x
    local real y
    loop
        exitwhen i > mvs_si
            if mvs_Sb__ != 0 then
                set b = mvs_Sb__ // для читаемости кода
                // Удар об землю
                if GetUnitFlyHeight(b.u) < 2 then
                    if b.v.z < 0 then
                        set b.v.z = - b.v.z*Rest
                    endif
                endif
                // Границы карты
		set x = GetUnitX(b.u)
		set y = GetUnitY(b.u)
                if x < minX then
                    call SetUnitX(b.u,minX)
                elseif x > maxX then
                    call SetUnitX(b.u,maxX)
                endif
                if y < minY then
                    call SetUnitY(b.u,minY)
                elseif y > maxY then
                    call SetUnitY(b.u,maxY)
                endif
                // Считаем ускорение тела и обнуляем силу, чтобы она не накапливалась
                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 - g // на этой оси еще действует mg
		endif
		...
globals
	private real maxX
	private real maxY
	private real minX
	private real minY
endglobasl

function IniMove takes nothing returns nothing
        // нам таймер в глобалку сохранять незачем, так как он работает всю игру
        // периoд 0.025 выбран в результате практики, при 0.05 видны "рывки"
        call TimerStart(CreateTimer(), 0.025, true, function Engine)
	set maxX =  GetRectMaxX(bj_mapInitialPlayableArea) 
	set maxY =  GetRectMaxY(bj_mapInitialPlayableArea) 
	set minX =  GetRectMinX(bj_mapInitialPlayableArea) 
	set minY =  GetRectMinY(bj_mapInitialPlayableArea) 
endfunction


Сейчас добавим сухое трение:
Принцип такой, если на тело действует сила больше mu*(mg-Fz), тогда вычитаем из вектора силы тела силу трения, иначе отнимаем от скорости эту величину деленную на массу:
private function Engine takes nothing returns nothing
    local integer i = 0
    local body b
    local real x
    local real y
    loop
        exitwhen i > mvs_si
            if mvs_Sb__ != 0 then
                set b = mvs_Sb__ // для читаемости кода
                // Удар об землю
                if GetUnitFlyHeight(b.u) < 2 then
                    if b.v.z < 0 then
                        set b.v.z = - b.v.z*Rest
                    endif
                    // Обсчитываем трение
                    if b.m > 0 then
                            if SquareRoot(b.f.x*b.f.x + b.f.y*b.f.y) > mu*(b.m*g - b.f.z) then // силы трения недостаточно, чтобы останавливать тело
                                set b.f.x = b.f.x - b.f.x*mu*(b.m*g - b.f.z)/b.f.Module()
                                set b.f.y = b.f.y - b.f.y*mu*(b.m*g - b.f.z)/b.f.Module() 
                            elseif SquareRoot(b.v.x*b.v.x + b.v.y*b.v.y) < RAbsBJ(mu*(b.m*g - b.f.z))/b.m then // остановка тела
                                    set b.v.x = 0
                                    set b.v.y = 0
                            else // отнимаем от скорости
                                    if RAbsBJ(b.v.x) > 0 then
                                        set b.v.x = b.v.x - b.v.x*mu*(b.m*g - b.f.z)/(b.v.Module()*b.m)
                                    endif    
                                    if RAbsBJ(b.v.y) > 0 then
                                        set b.v.y = b.v.y - b.v.y*mu*(b.m*g - b.f.z)/(b.v.Module()*b.m)
                                    endif
                            endif
                    endif
                endif
                // Границы карты
		set x = GetUnitX(b.u)
		set y = GetUnitY(b.u)
                if x < minX then
                    call SetUnitX(b.u,minX)
                elseif x > maxX then
                    call SetUnitX(b.u,maxX)
                endif
                if y < minY then
                    call SetUnitY(b.u,minY)
                elseif y > maxY then
                    call SetUnitY(b.u,maxY)
                endif
		...

Заключение

Скелет системы готов, теперь на него можно наращивать остальные части, например: притяжение к земле, трение, столкновения с другими телами. В карте примере находится эта система, но уже с сухим трением и притяжением к земле. Для примера я сделал на основе этой системы способность «Прыжок», чтобы показать систему в действии. Теперь, освоив эту статью, вы можете легко делать реалистичные заклинания. Идеи черпал из моей физической библиотеки «Fisics Dynamik System», по сути, это та же система, но усложненная.

Содержание
Остались вопросы? Задайте их сообществу! Задать вопрос
`
ОЖИДАНИЕ РЕКЛАМЫ...