XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Jass
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

Ответ
 
silumin

offline
Опыт: 11,153
Активность:
Оптимизация кода

Что имеем


Для примера и для возможности объяснить наглядно привожу один полностью рабочий спелл:
» Spell
Код:
function Action_MithrilKnife_Move takes nothing returns nothing
local integer i = GetIndex(GetExpiredTimer())
local real pDx = GetUnitX(udg_DYN_Dummy[i])
local real pDy = GetUnitY(udg_DYN_Dummy[i])
local real dx = GetUnitX(udg_DYN_Target[i]) - pDx
local real dy = GetUnitY(udg_DYN_Target[i]) - pDy
local real Ang = Atan2(dy,dx)
  if SquareRoot(dx*dx+dy*dy)>100 then
    call SetUnitX(udg_DYN_Dummy[i],pDx+20*Cos(Ang))
    call SetUnitY(udg_DYN_Dummy[i],pDy+20*Sin(Ang))
    call SetUnitFacing(udg_DYN_Dummy[i],Ang*57.3)
    call TimerStart(udg_DYN_Timer[i],0.02,false,function Action_MithrilKnife_Move)
  else
    if GetRandomInt(0,99)<35 then
      call SetUnitVertexColor(udg_DYN_Dummy[i],255,255,255,0)
      call SetUnitAbilityLevel(udg_DYN_Dummy[i],'Ax02',GetUnitAbilityLevel(udg_Heroes[i],'Ax01'))
      call IssueTargetOrder(udg_DYN_Dummy[i],"acidbomb",udg_DYN_Target[i])
      call FText(null,udg_DYN_Target[i],"|cFFFF0700E?iaioa?aiea!|r")
      call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl",udg_DYN_Target[i],"chest"))
    else
      call RemoveUnit(udg_DYN_Dummy[i])
      call UnitDamageTarget(udg_Heroes[i],udg_DYN_Target[i],GetUnitAbilityLevel(udg_DYN_Hero[i],'Ax01')*GetHeroAgi(udg_DYN_Hero[i],true),true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
    endif
    call TimerStart(udg_DYN_TimerAlt[i],2.,false,function ClearCell) //<<<<<<<
  endif
endfunction

function CON_Cond_MithrilKnife takes nothing returns boolean
return GetSpellAbilityId() == 'Ax01'
endfunction

function CON_Act_MithrilKnife takes nothing returns nothing
local integer i = GetEmptyDynSlot()
    set udg_DYN_Hero[i] = GetSpellAbilityUnit()
    set udg_DYN_Target[i] = GetSpellTargetUnit()
    set udg_DYN_Dummy[i] = CreateUnit(GetOwningPlayer(udg_DYN_Hero[i]),'ux00',GetUnitX(udg_DYN_Hero[i]),GetUnitY(udg_DYN_Hero[i]),57.3*Atan2(GetUnitY(udg_DYN_Target[i])-GetUnitY(udg_DYN_Hero[i]),GetUnitX(udg_DYN_Target[i])-GetUnitX(udg_DYN_Hero[i])))
    call TimerStart(udg_DYN_Timer[i], 0.02, false, function Action_MithrilKnife_Move)
endfunction

Все спеллы делаю на параллельных массивах.
Меня постоянно напрягает вот этот участок кода, так и хочется убрать его с глаз долой:
» Code
Код:
local real pDx = GetUnitX(udg_DYN_Dummy[i])
local real pDy = GetUnitY(udg_DYN_Dummy[i])
local real dx = GetUnitX(udg_DYN_Target[i]) - pDx
local real dy = GetUnitY(udg_DYN_Target[i]) - pDy
local real Ang = Atan2(dy,dx)
  if SquareRoot(dx*dx+dy*dy)>100 then
    call SetUnitX(udg_DYN_Dummy[i],pDx+20*Cos(Ang))
    call SetUnitY(udg_DYN_Dummy[i],pDy+20*Sin(Ang))
    call SetUnitFacing(udg_DYN_Dummy[i],Ang*57.3)

Нетрудно заметить, что для вычисления угла Ang = Atan2(dy,dx) и расстояния SquareRoot(dx*dx+dy*dy) используются всего 2 переменные dx и dy. Поэтому родилась справедливая мысль затолкать их в отдельные функции, дабы облегчить создание новых триггспеллов, основанных на движении по полярным координатам.

Что решается


Т.к. для вычисления расстояния абсолютно безразлично, как мы будем отнимать координаты, всёравно результат возводится в квадратную степень, складывается и вычисляется корень. Как ни крути а полученное значение всегда будет положительным.
Но для вычисления угла все значения координат имеют большое значение. Соответственно для удобства обозначения функций лучше подойдёт класификация по требуемому углу.

У меня в карте пока только 4 глобальных массива от содержимого ячеек которых берутся координаты: udg_DYN_Hero[i] (юнит), udg_DYN_Target[i] (юнит), udg_DYN_Dummy[i] (юнит), udg_DYN_Point[i] (точка).
Я составил много (вроде 16) функций, необходимых для обозначения вычисляемого угла. Примеры выглядят так: fadHT (вычисляет угол между Hero и Taget), fadDP (вычисляет угол между Dummy и Point), fadDT (вычисляет угол между Dummy и Taget). Суть в том, что заглавные буквы (HT, DP, DT) подсказывают между кем и кем или кем и чем вычисляется угол.
Все 16 функций возвращают значение угла. Расстояние тоже вычисляется внутри этих функций, но записывается в глобальную переменную, имеющую назваии туже пару загавных букв.

Как всё выглядит на практике


Итак функция импользуемая в вышеприведённом спелле имеет такой код:
» Function
Код:
function fadDT takes integer i returns real
local real dx = GetUnitX(udg_DYN_Target[i]) - GetUnitX(udg_DYN_Dummy[i])
local real dy = GetUnitY(udg_DYN_Target[i]) - GetUnitY(udg_DYN_Dummy[i])
set udg_SysDT = SquareRoot(dx*dx+dy*dy)
return Atan2(dy,dx)
endfunction

Аргумент, передаваемый в функцию - есть индекс ячеек массивов. Возвращаемое значение, как уже говорил выше, - требуемый угол.

После всех переделок триггерный спелл выглядит так:
» Spell
Код:
function Action_MithrilKnife_Move takes nothing returns nothing
local integer i = GetIndex(GetExpiredTimer())
local real Ang = fadDT(i)
  if  udg_SysDT>100 then
    call SetUnitX(udg_DYN_Dummy[i],GetUnitX(udg_DYN_Dummy[i])+20*Cos(Ang))
    call SetUnitY(udg_DYN_Dummy[i],GetUnitY(udg_DYN_Dummy[i])+20*Sin(Ang))
    call SetUnitFacing(udg_DYN_Dummy[i],Ang*57.3)
    call TimerStart(udg_DYN_Timer[i],0.02,false,function Action_MithrilKnife_Move)
  else
    if GetRandomInt(0,99)<35 then
      call SetUnitVertexColor(udg_DYN_Dummy[i],255,255,255,0)
      call SetUnitAbilityLevel(udg_DYN_Dummy[i],'Ax02',GetUnitAbilityLevel(udg_Heroes[i],'Ax01'))
      call IssueTargetOrder(udg_DYN_Dummy[i],"acidbomb",udg_DYN_Target[i])
      call FText(null,udg_DYN_Target[i],"|cFFFF0700E?iaioa?aiea!|r")
      call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl",udg_DYN_Target[i],"chest"))
    else
      call RemoveUnit(udg_DYN_Dummy[i])
      call UnitDamageTarget(udg_Heroes[i],udg_DYN_Target[i],GetUnitAbilityLevel(udg_DYN_Hero[i],'Ax01')*GetHeroAgi(udg_DYN_Hero[i],true),true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
    endif
    call TimerStart(udg_DYN_TimerAlt[i],2.,false,function ClearCell) //<<<<<<<
  endif
endfunction

function CON_Cond_MithrilKnife takes nothing returns boolean
return GetSpellAbilityId() == 'Ax01'
endfunction

function CON_Act_MithrilKnife takes nothing returns nothing
local integer i = GetEmptyDynSlot()
    set udg_DYN_Hero[i] = GetSpellAbilityUnit()
    set udg_DYN_Target[i] = GetSpellTargetUnit()
    set udg_DYN_Dummy[i] = CreateUnit(GetOwningPlayer(udg_DYN_Hero[i]),'ux00',GetUnitX(udg_DYN_Hero[i]),GetUnitY(udg_DYN_Hero[i]),57.3*Atan2(GetUnitY(udg_DYN_Target[i])-GetUnitY(udg_DYN_Hero[i]),GetUnitX(udg_DYN_Target[i])-GetUnitX(udg_DYN_Hero[i])))
    call TimerStart(udg_DYN_Timer[i], 0.02, false, function Action_MithrilKnife_Move)
endfunction


Единственное, что начало немного напрягать, так это необходимость повторно брать координаты X и Y перемещаемого юнита:
Код:
call SetUnitX(udg_DYN_Dummy[i],GetUnitX(udg_DYN_Dummy[i])+20*Cos(Ang))
call SetUnitY(udg_DYN_Dummy[i],GetUnitY(udg_DYN_Dummy[i])+20*Sin(Ang))
В то время как раньше эти значения уже содержались в локальных переменных.

Собственно вопрос


Имеетли смысл такая косметическая оптимизация хотябы для удобства составления спеллов или это "шаг назад"?

п.с. Заранее спасибо всем, дочитавшим до конца. (:

Отредактировано silumin, 15.08.2010 в 20:51.
Старый 15.08.2010, 20:43
Hellfim
Новичок
offline
Опыт: 79,707
Активность:
Я бы сказал, что это шаг назад, шагом вперед можно назвать использование векторов.
Старый 15.08.2010, 20:59
silumin

offline
Опыт: 11,153
Активность:
Hellfim, я бы назвал это делом вкуса. Кто-то любит гуи, кто-то джасс, кто-то вджасс, кто-то сджасс. Равнозначно кто-то полярки, а кто-то вектора.

Да и вопрос поставлен совсем в другом аспекте.
Старый 15.08.2010, 21:14
Doc

offline
Опыт: 63,163
Активность:
use defines, luke.
Старый 15.08.2010, 22:35
AlexKARASb
Learning cpp
offline
Опыт: 22,103
Активность:
если расстояние тебе не важно то смысл выведения корня? Может проще и вроде считать вару меньше:
if (dx*dx+dy*dy)>10000.00 then
еще можно было таймер сразу сделать периодик 0.025, чтобы не запускать каждый раз, а просто в один момент запаузить и запустить непереодично его же через 2 сек
Ну отошел вижу я от темы =)
Запись тебе сократит, но ведь отправка и возврат требуют времени, хотя сократит время на написание.
Но вариант Дока хорош, задефайнить функцию вычисления угла и выйдет код скомпилированный то что надо.
Старый 15.08.2010, 22:52
Faion
Noblesse Oblige
offline
Опыт: 30,395
Активность:
У тебя в самом коде используются массивы, я считаю лучше бы было записать их в локалки, исполнялось бы быстрее, т.к. каждый раз когда ты вызываешь тот же udg_DYN_Dummy[i], сначало вызывается значение i, а потом ищется значение массива udg_DYN_Dummy[i], лучше 1 раз сделать так local unit u = udg_DYN_Dummy[i], быстрее работать будет, аналогично и с другими массивами поступи.
Старый 15.08.2010, 23:07
silumin

offline
Опыт: 11,153
Активность:
Faion, я все свои спеллы тестирую на загруженность. Триггерно запускаю сразу 10 кастов и увеличиваю с шагом +5 кастов. На моём слабом компе (Селерон 2.4Мгц, 512Мб, 128Мб видео) стабильно работает 15-30 кастов, взависимости от самого спелла. Ну а поскольку игроков в Варе максимум 12, то я очень доволен такой рабоспособностью.
Да и локалки типа юнит нужно обнулять, что тоже немаловажно для высокопереодического триггера.
Вопрос первого поста заключался как раз в том, насколько сильно тормознёт дополнительная функция работу спелла.

AlexKARASb, если таймер делать переодическим, то может возникнуть неадекватная работа спелла из-за наложения. Лично для меня надёжнее сделать все процедуры, вычисления, расчёты, а потом запускать таймер заново. Вариант без корня интересен, обязательно подумаю, точнее подумал, буду применять.
Старый 16.08.2010, 02:31
ZeToX2007

offline
Опыт: 7,009
Активность:
silumin:
стабильно работает 15-30 кастов
У меня цифра 400-500 была(причем в 3д). на таком-же компе. а 15-30 кастов будет вообще прекрасно..
ЗЫ: использовал локалки в jass'e тока в цыклах.
Старый 16.08.2010, 21:15
AlexKARASb
Learning cpp
offline
Опыт: 22,103
Активность:
400-500
сударь, а вы не привераете? 400-500 юнитов на карте да еще с одновременным кастом, что-то сомневаюсь в ваших словах. В 100та кастах еще можно поверить.
Старый 16.08.2010, 21:18
ZeToX2007

offline
Опыт: 7,009
Активность:
AlexKARASb:
сударь, а вы не привераете? 400-500 юнитов на карте да еще с одновременным кастом, что-то сомневаюсь в ваших словах. В 100та кастах еще можно поверить.
до 700 доходило с небольшими лагами.
Старый 16.08.2010, 21:20
AlexKARASb
Learning cpp
offline
Опыт: 22,103
Активность:
ZeToX2007, процессор значит у тебя хороший. Моя машина таких чудес не выдаст.
Старый 16.08.2010, 21:24
silumin

offline
Опыт: 11,153
Активность:
ZeToX2007, 400-500? Откровенная ложь! Даже отключая все активные мониторы, антивирус, прочее ПО. Производительность самого вара увеличивалась несущественно.

Ну было бы неплохо ещё трейлер в студию, очень хочется взглянуть как Селерон 2.4Мгц, 512Мб, 128Мб видео тянет 700 кастов с небольшими лагами.

п.с. лол!
Старый 16.08.2010, 21:57
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 20:54.