Сейчас 02:40:14 Суббота, 23 ноября, 2024 год
[ x ] Главная ⇒ Форум ⇐ RSS Файлы Cтатьи Картинки В о й т и   или   з а р е г и с т р и р о в а т ь с я


[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Модератор форума: PUVer, SirNikolas, Ty3uK  
[Статья] Создание MUI-спеллов - Часть 2
Ty3uKДата: Суббота, 25 Августа 2012, 22:54:37 | Сообщение # 1
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Всем добра, с Вами снова я! Сегодня, спустя 7 месяцев (простите, готовился к ЭГЕ - и я на бюджете) я представляю Вашему вниманию вторую статью цикла "Создание MUI-спеллов". Итак, мы начинаем. И пока я включаю музыку (Black Stone Cherry) самое время запастись попкорном. Ну а я кушаю арбузик :3

В прошлой статье мы изучили базовые состявляющие триггерных "муи"-заклинаний - понятие хэндл, использование хэша и таймеры. В сегодняшней статье мы изучим создание заклинание с периодическим эффектом. На роль "жертвы" возьмем приблизительный аналог "горелки" Акса из ДотЫ. Как обычно готовим каркас триггера:

Code
function Trig_Burning_Condition takes nothing returns boolean
   return GetSpellAbilityId() == 'A000'
endfunction

function Trig_Burning_Actions takes nothing returns nothing
endfunction

function InitTrig_Burning takes nothing returns nothing
      set gg_trg_Burning = CreateTrigger()
   call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT)
   call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition))
      call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions)
endfunction


Теперь продумаем последовательность действий, происходящих при использовании способности:
  • Мы кастуем на врага способность
  • Враг начинает гореть
  • Появляется эффект горения
  • Цель получает урон
  • Способность перестает действовать после N-секунд


Приступаем к написанию кода. Дальше будет полный спелл с последующим пояснением всего:
Code
function Trig_Burning_Condition takes nothing returns boolean
  return GetSpellAbilityId() == 'A000'
endfunction

function Trig_Burning_Timer takes nothing returns nothing
  local timer t = GetExpiredTimer()
  local integer hid = GetHandleId(t)
  //Грузим сохраненные переменные
  local unit caster = LoadUnitHandle(udg_hash, hid, 0)
  local unit target = LoadUnitHandle(udg_hash, hid, 1)
  local effect burning = LoadEffectHandle(udg_hash, hid, 2)
  local real time = LoadReal(udg_hash, hid, 3)
  if time <= 7. then //Если время меньше семи, то
   //мы наносим цели урон
   call UnitDamageTarget(caster, target, 20., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
   if GetWidgetLife(target) > .405 then //Если у цели есть еще здоровье, то
    set time = time + 1. //Увеличиваем время на 1 секунду
    call SaveReal(udg_hash, hid, 3, time) //и сохраняем его в хэш
   else //Если цель мертва, то мы
    call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
    call DestroyTimer(t) //Уничтожаем таймер
    call DestroyEffect(burning) //Уничтожаем эффект
    call FlushChildHashtable(udg_hash, hid) //Чистим хэш
   endif
  else //Если время больше 7, то все делаем аналогично:
   call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
   call DestroyTimer(t) //Уничтожаем таймер
   call DestroyEffect(burning) //Уничтожаем эффект
   call FlushChildHashtable(udg_hash, hid) //Чистим хэш
  endif
  set t = null
  set caster = null
  set target = null
  set burning = null
endfunction

function Trig_Burning_Actions takes nothing returns nothing
  local timer t = CreateTimer()
  local integer hid = GetHandleId(t)
  local unit caster = GetTriggerUnit()
  local unit target = GetSpellTargetUnit()
  local effect burning = AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", target, "origin") //Эффект горения
  //  Сохраняем все переменные в хэш   //
  call SaveAgentHandle(udg_hash, hid, 0, caster)
  call SaveAgentHandle(udg_hash, hid, 1, target)
  call SaveAgentHandle(udg_hash, hid, 2, burning)
     call SaveReal(udg_hash, hid, 3, 0.) //Время
  //==============================================//
  call TimerStart(t, 1., true, function Trig_Burning_Timer) //Запускаем периодический таймер
  set t = null
  set caster = null
  set target = null
  set burning = null
endfunction

function InitTrig_Burning takes nothing returns nothing
     set udg_hash = InitHashtable()
  set gg_trg_Burning = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition))
     call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions)
  //Здесь я просто отключу туман войны
  call FogEnable(false)
  call FogMaskEnable(false)
  //==================================
endfunction


Я пометил комментариями все важные участки кода, которые и заставляют работать наш спелл. Обращу внимание на то, что периодические таймеры обязательно надо ставить на паузу перед уничтожением. Большинство спеллов на таймере представляют собой некоторое количество (зачастую очень большое) конструкций проверки условий (if then else) и действий, которые будут происходить после выполнения (или невыполнения) условия(-й). Так как я думаю, что из-за комментариев в коде бъяснять уже нечего, давайте отвлечемся от практики и окунемся в теорию. Мы с Вами займемся изучением некоторых кодерских фич, которые здорово облегчат и, зачастую, убыстрят ваш код. :)



Финт №1



Допустим, у нас есть функция, возвращающая какой-то тип (например, число):
Code
function abc takes nothing returns integer
  return 666
endfunction

До сих пор если бы Вам сказали занести это число в хэш, вы бы сделали так:
Code
local integer def = abc()
call SaveInteger(udg_hash, 0, 0, def)

Так можно, но кодеры со стажем обходят этап выделения памяти под переменную, которая будет просто сохранена в хэш. Ну а зачем? Делаем так:
Code
call SaveInteger(udg_hash, 0, 0, abc())

Так как функция возвращает значение, то она может вернуть его в аргумент функции, то есть мы можем не выделять память под локальную переменную, а просто сохранить в хэш возвращенное значение :) Так же вы можете сохранять в хэш составные операции, напрмер сложение строк, арифметические операции с числами (с точкой и без), например:
Code
call SaveInteger(udg_hash, 0, 0, abc() + 1)

сохранит в хэш число 667.



Финт №2



Возьмем все тот же спелл из статьи. У нас есть время, к которому мы прибавляем секунду, пока оно не станет больше либо равно 7. Так как в начале спелла время у нас равно 0 секунд, то мы можем не сохранять значение в хэш. Почему? При загрузке несуществующего значения из ячейки хэш-таблицы, мы получаем null. У числа он эквивалентен 0, у реальной - 0.000, у логической - false, а у типов-наследников handle или widget - ничему, то есть null. Согласно этому, в действиях мы можем не сохранять значение 0. в хэш, а уж тем более не выделять под него область памяти (см. Финт 1).

А теперь давайте перепишем наш код, использую полученные знания:
Code
function Trig_Burning_Condition takes nothing returns boolean
  return GetSpellAbilityId() == 'A000'
endfunction

function Trig_Burning_Timer takes nothing returns nothing
  local timer t = GetExpiredTimer()
  local integer hid = GetHandleId(t)
  //Грузим сохраненные переменные
  local unit caster = LoadUnitHandle(udg_hash, hid, 0)
  local unit target = LoadUnitHandle(udg_hash, hid, 1)
  local real time = LoadReal(udg_hash, hid, 3) //Мы не сохранили значение, поэтому получаем null, то есть 0.000
  if time <= 7. then //Если время меньше семи, то
   //мы наносим цели урон
   call UnitDamageTarget(caster, target, 20., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
   if GetWidgetLife(target) > .405 then //Если у цели есть еще здоровье, то
    call SaveReal(udg_hash, hid, 3, time + 1.) //Сохраняем время, увеличенное на 1 секунду в хэш
   else //Если цель мертва, то мы
    call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
    call DestroyTimer(t) //Уничтожаем таймер
    call DestroyEffect(LoadEffectHandle(udg_hash, hid, 2)) //Функция уничтожения берет эффект и вызывается один раз. Зачем переменная?
    call FlushChildHashtable(udg_hash, hid) //Чистим хэш
   endif
  else //Если время больше 7, то все делаем аналогично:
   call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
   call DestroyTimer(t) //Уничтожаем таймер
   call DestroyEffect(LoadEffectHandle(udg_hash, hid, 2)) //Функция уничтожения берет эффект и вызывается один раз. Зачем переменная?
   call FlushChildHashtable(udg_hash, hid) //Чистим хэш
  endif
  set t = null
  set caster = null
  set target = null
endfunction

function Trig_Burning_Actions takes nothing returns nothing
  local timer t = CreateTimer()
  local integer hid = GetHandleId(t)
  local unit target = GetSpellTargetUnit()
  //  Сохраняем все переменные в хэш   //
  call SaveAgentHandle(udg_hash, hid, 0, GetTriggerUnit()) //Кастер появился один раз, переменная не нужна
  call SaveAgentHandle(udg_hash, hid, 1, target) //А вот цель нужна будет для создания эффекта, поэтому она в переменной
     //Функция создания эффекта вернет нам тип effect, который мы спойкойно сохраняем в хэш
  call SaveAgentHandle(udg_hash, hid, 2, AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", target, "origin"))
  //==============================================//
  call TimerStart(t, 1., true, function Trig_Burning_Timer) //Запускаем периодический таймер
  set t = null
  set target = null
endfunction

function InitTrig_Burning takes nothing returns nothing
     set udg_hash = InitHashtable()
  set gg_trg_Burning = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition))
     call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions)
  //Здесь я просто отключу туман войны
  call FogEnable(false)
  call FogMaskEnable(false)
  //==================================
endfunction


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


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек


Сообщение отредактировал Ty3uK - Вторник, 28 Августа 2012, 09:45:51
 

SirNikolasДата: Воскресенье, 26 Августа 2012, 04:53:01 | Сообщение # 2
Группа: Модераторы
Сообщений: 6729
Награды: 1
Репутация: 1867
Блокировки:
Как-то коротко. Добавь удаление эффекта, если жертва кого-нибудь убивает.

 

lawsonДата: Воскресенье, 26 Августа 2012, 08:30:44 | Сообщение # 3
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Слишком много всего, к тому же ты используешь приемы которые новичкам сложно будет понять сразу например добавить эффект, и сразу в хэндл и сразу на юнита.
Quote (Ty3uK)
call SaveAgentHandle(udg_hash, hid, 2, AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", target, "origin"))

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


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!


Сообщение отредактировал lawson - Воскресенье, 26 Августа 2012, 08:32:47
 

Ty3uKДата: Воскресенье, 26 Августа 2012, 08:32:48 | Сообщение # 4
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
финтам надо когда-то учиться :)

╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

lawsonДата: Воскресенье, 26 Августа 2012, 08:39:16 | Сообщение # 5
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Ty3uK, ДА и вообще согласен, как -то коротко все расписано.
Когда на что то сслылаешься вроде
Quote (Ty3uK)
В таймере у нас откуда-то появилась реальная "время". НО ВЕДЬ МЫ НИЧЕГО НЕ СОХРАНЯЛИ!1!!1

или
Quote (Ty3uK)
мы наносим цели урон и увеличиваем значение времени на 1 секунду

то покажи какую переменную ты имеешь ввиду. А то говорю, что много кода сразу не разобраться будет.


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!
 

HexingДата: Воскресенье, 26 Августа 2012, 09:09:38 | Сообщение # 6
10 уровень
Группа: Проверенные
Сообщений: 1645
Награды: 1
Репутация: 432
Блокировки:
коротко, но годно, кто захочет научиться - потихоньку разберётся в этом коде, а почему не в каталог статей?

 

Ty3uKДата: Воскресенье, 26 Августа 2012, 09:22:56 | Сообщение # 7
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
я чуток перепишу - добавлю отслеживание смерти и приправлю код комментариями

╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

xomachДата: Понедельник, 27 Августа 2012, 08:35:10 | Сообщение # 8
7 уровень
Группа: Проверенные
Сообщений: 484
Награды: 0
Репутация: 128
Блокировки:
lawson, Hexing, Да вы совсем нас(читающих) за дебилов держите? facepalm . Чего тут может быть не понятного? А для тех, кто делал спелы на гуи, тут даже пояснения ни к чему.



Сообщение отредактировал xomach - Понедельник, 27 Августа 2012, 08:36:01
 

HexingДата: Понедельник, 27 Августа 2012, 09:55:46 | Сообщение # 9
10 уровень
Группа: Проверенные
Сообщений: 1645
Награды: 1
Репутация: 432
Блокировки:
Quote (xomach)
lawson, Hexing, Да вы совсем нас(читающих) за дебилов держите? . Чего тут может быть не понятного? А для тех, кто делал спелы на гуи, тут даже пояснения ни к чему.

держать читателей за дебилов = писать сразу для большинства ;)


 

xomachДата: Понедельник, 27 Августа 2012, 12:32:38 | Сообщение # 10
7 уровень
Группа: Проверенные
Сообщений: 484
Награды: 0
Репутация: 128
Блокировки:
Quote (Hexing)
держать читателей за дебилов = писать сразу для большинства

:)


 

DreiiДата: Понедельник, 27 Августа 2012, 12:55:19 | Сообщение # 11
10 уровень
Группа: Проверенные
Сообщений: 4991
Награды: 0
Репутация: 603
Блокировки:
Quote (Ty3uK )
Дата: Суббота, 14 Январь 2012, 22:48:27 | Сообщение # 9
Благодарю, завтра напишу вторую

Quote (Ty3uK)
Дата: Суббота, 25 Август 2012, 22:54:37 | Сообщение # 1


 

Ty3uKДата: Вторник, 28 Августа 2012, 09:46:08 | Сообщение # 12
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Начало статьи прочти

Добавлено (28 Августа 2012, 09:46:08)
---------------------------------------------
Обновил статью


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

HexingДата: Вторник, 28 Августа 2012, 10:24:53 | Сообщение # 13
10 уровень
Группа: Проверенные
Сообщений: 1645
Награды: 1
Репутация: 432
Блокировки:
Ty3uK, долго смеялся над "финтами", всё таки это не финт, а способ работы с хэшем что-ли ahaha
за исключением того, что можно обойтись без хэша, статья отличная :)


 

SirNikolasДата: Вторник, 28 Августа 2012, 10:26:18 | Сообщение # 14
Группа: Модераторы
Сообщений: 6729
Награды: 1
Репутация: 1867
Блокировки:
Quote (Ty3uK)
else //Если цель мертва, то мы
call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
call DestroyTimer(t) //Уничтожаем таймер
call DestroyEffect(burning) //Уничтожаем эффект
call FlushChildHashtable(udg_hash, hid) //Чистим хэш
endif
else //Если время больше 7, то все делаем аналогично:
call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
call DestroyTimer(t) //Уничтожаем таймер
call DestroyEffect(burning) //Уничтожаем эффект
call FlushChildHashtable(udg_hash, hid) //Чистим хэш
Как-то криво, не находишь?
Code
call UnitDamageTarget(caster, target, 20., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
//мы наносим цели урон
if time < 7. and GetWidgetLife(target) > .405 then //Если время меньше семи и у цели еще есть здоровье, то
     set time = time + 1. //Увеличиваем время на 1 секунду
     call SaveReal(udg_hash, hid, 3, time) //и сохраняем его в хэш
else //Если время больше или равно 7 либо цель мертва, то
     call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!)
     call DestroyTimer(t) //Уничтожаем таймер
     call DestroyEffect(burning) //Уничтожаем эффект
     call FlushChildHashtable(udg_hash, hid) //Чистим хэш
endif


 

Ty3uKДата: Вторник, 28 Августа 2012, 10:31:39 | Сообщение # 15
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
SirNikolas, потому что писал для новичков
Я приблизительно так и хотел, но статья не направлена на оптимизацию, а так все понятно


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

lawsonДата: Вторник, 28 Августа 2012, 12:08:41 | Сообщение # 16
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Ty3uK, Теперь статью одобряю, и пусть вам будет счастье, дорогие читатели(не дебилы)!

Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!
 

Ty3uKДата: Вторник, 28 Августа 2012, 12:16:52 | Сообщение # 17
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

  • Страница 1 из 1
  • 1
Поиск:

Copyright © 2006 - 2024 Warcraft3FT.info При копировании материалов c сайта ставьте, пожалуйста, активную обратную ссылку на нас • Design by gReeB04ki ©
Хостинг от uCoz