Форум портала о WarCraft Форум портала о WarCraft
порно чат

Вернуться   Форум портала о WarCraft > Вселенная WarCraft > World Editor > Статьи
Регистрация Справка Пользователи Календарь Поиск Сообщения за день Все разделы прочитаны

Ответ
 
Опции темы Опции просмотра
Старый 24.07.2010, 22:19   #1
VAV
Pandora Directive
 
Аватар для VAV
 
Регистрация: 04.09.2002
Адрес: Area 404
Сообщений: 3,611
VAV как роза среди колючек VAV как роза среди колючек VAV как роза среди колючек
Отправить сообщение для VAV с помощью ICQ
По умолчанию Отлавливаем двойной клик мышью.

Отлавливаем двойной клик мышью.



Недавно на www.blizzard.ru появилать карта, созданная Тоддом. В ней надо было не дать саперам добежать до башни - кликать на них. Целью создания этой карты являлась тренировка кликов на юнитов при микроконтроле.

В этом примере мы рассмотрим другой алгоритм - обработку двойного щелчка мышью на юнита. Может быть, этот алгоритм пригодится в разных тренировочных картах. Я не знаю, если такое в карте Тодда и если есть, то как было реализовано - предложу свой алгоритм. При этом немного усложним задачу - дадим возможность кликать на юнитов разным игрокам в мультиплеере. В этом примере я расскажу совсем немного нового (все было описано в предыдущих статьях) - только о возможности использования одного триггера для абсолютно разных задач. Для понимания статьи желательно прочесть предудущую (об огнемете) и знать, как работают массивы и что такое null. Знания об очереди триггеров тоже будут нужны.
Итак, сначало продумаем реализацию алгоритма двойного щелчка. Для регистрации одного щелчка можно использовать событие - Unit is owned by (player) is (selected). Как зафиксировать повторный щелчок на тот же юнит? Очень просто - занести при первом щелчке юнита в глобальную переменную типа unit, а при повторном проверить, совпадает ли юнит, помещенный в переменную с юнитом, на которого кликнули. Также надо добавить сброс переменной после истечения заданного времени - если игрок кликнет на того же юнита позже чем надо, это не будет считаться как двойной клик. Очевидно, что можно использовать для регистрации первого и второго клика один и тот же триггер, но в нем будет проверка - является ли клик первым или вторым. В зависимости от этого будут разные действия. Для реализации возможности кликать нескольким игрокам надо помещать ссылки на юнитов не в одну переменную unit, а в unit array. Номером элемента будет номер игрока. Т.е. в первом элементе массива будет юнит, на которого кликнул первый игрок, во втором - второй и т.д.
Алгоритм можно представить так:
1. Кликаем на юнита.
2. Проверяем, совпадает ли юнит с записанным в массиве:
-> Да: Удаляем юнит из массива и вызываем нужные действия.
-> Нет: Заносим юнит в массив и ждем заданное время. Когда время истечет, удаляем юнит из массива.

Разумеется, здесь используется очередь триггеров - пусть одна копия этого триггера занесла юнита в массив и ждет заданное время. Если в это время другая копия будет добавлена в очередь (за это время игрок кликнет на юнита), то она проверит совпадение с записью в массиве (записи будет совпадать т.к. предыдущая копия УЖЕ занесла юнита в массив) - следовательно, будут выполнены нужные действия. Если игрок кликнул позже чем надо, то первая копия удалит юнит из массива после ожидания (но РАНЬШЕ, чем запустилась вторая копия), а новая копия повторно занесет юнит в массив т.к. ячейка будет уже пуста. Таким образом мы сделали максимальный интервал между кликами (чтобы они считались как двойной клик) или чувствительность. аналогичная ситуация будет, если 'кликнутый' юнит и юнит в массиве не будут совпадать. Тут есть одна тонкость, которая може все испортить - см. примечание о том, как ее избежать в конце статьи.

Для теста сделаем простой пример: пусть при двойном клике на юнита, не принадлежащего игроку, юнит взрывается. Все игроки, для которых действует это правило, помещаются в группу (player group) при старте. В приведенном примере туда помещен только один первый игрок. Максимальный интервал между кликами пусть будет 0,6 секунды. Я сделал простенькую карту-пример - надо спасти волшебницу от Абомов и Пит Лордов путем двойного кликанья на них Триггеры инициализации / победы / поражения я оставил обычными (не текстовыми) чтобы для людей, недавно работающих со скриптом, было понятней.

Итак, сам алгоритм. В нем используется две глобальные переменные:
Units: unit array (тут храним ссылки на 'кликнутых' юнитов).
Clickers: player group (здесь содержатся игроки, на которых действует скрипт).
Сам скрипт называется 'Click'. Думаю не стоит напоминать, что скрипт надо читать с последней функции.

// ------------------- Начало скрипта -------------------------

function Register takes nothing returns nothing
// Эта функция служит для регистрации триггера
// Click для группы игроков. Эта функция вызывается
// из инициализации для каждого игрока и
// регистрирует для него триггер.
call TriggerRegisterPlayerSelectionEventBJ(gg_trg_Click
,GetEnumPlayer(), true)
// Вот это вызов функции регистрации события -
// юнит, принадлежащий текущему (picked) игроку
// выделен. GetEnumPlayer() - аналог picked player
// в обычных триггерах и возвращает игрока.
endfunction

function SCondition takes nothing returns boolean
// Функция-условие. Поэтому и возвращает boolean.
// Можно поменять ее на свой лад - сделать все, что
// необходимо в карте.
return GetOwningPlayer(GetTriggerUnit()) != GetTriggerPlayer()
// В данном случае возвращаем true, если игрок,
// владеющий 'кликнутым' юнитом не является
// кликнувшим (Triggering Player) игроком.
endfunction

function SingleClick takes nothing returns nothing
// Это основной код - если удачно прошла проверка
// условия пред. функции, то запускается эта. В ней
// находится алгоритм для первого и второго клика.
local integer Index = GetConvertedPlayerId(GetTriggerPlayer())
// Это локальная переменная, в нее помещается
// номер игрока, который щелкнул на юнита. Далее
// используется как номер в массиве.
if (udg_Units[Index] == null) or (GetTriggerUnit() != udg_Units[Index]) then
// Вот и проверка - если элемент массива,
// соответствующий данному игроку пустой (null) или
// юнит, записанный в нем, не совпадает с тем, на
// которого кликнули, то дальше заносим юнит в
// массив и т.д. Первое условие можно исключить -
// оно лишнее, но служит для дополнительной
// безопасности (см. инфорамацию про null). Я решил
// перестраховаться Хотя и можно обойтись....
set udg_Units[Index] = GetTriggerUnit()
// Если условие выполнено и юниты разные, то
// заносим кликнутый юнит в массив. Теперь если
// кликнуть на него второй раз, новая копия этого
// триггера в очереди найдет условие ложным и
// выполнит пункт 'else'.
call TriggerSleepAction(0.6)
// Ждем заданное время. Если за это время запустится
// копия триггера и найдет совпадение юнитов, то
// будет выполнено действие 'второго клика'.
if udg_Units[Index] == GetTriggerUnit() then
// Это условие хоть и кажеся глупым, оно необходимо
// для того, чтобы избежать 'потери' клика - см. конец
// статьи.
set udg_Units[Index] = null
// Стираем юнит из массива, присваивая ему null.
endif
else
// А этот блок выполняется, если 'кликнутый' юнит
// совпал с юнитом в массиве.
set udg_Units[Index] = null
// Стираем запись юнита из массива.
call ExplodeUnitBJ(GetTriggerUnit())
// Взрываем юнит. Вместо этого можно тут поместить
// нужные действия или, еще лучше, создать в начале
// скрипта функцию действий и вызывать ее отсюда.
endif
endfunction

function ClickInit takes nothing returns nothing
// В этом скрипте один и тот же триггер - Click
// используется ДВА раза для совсем разных целей.
// Поэтому у него две функции инициализвации. Дело
// в том, что мы заносим при инициализации карты
// игроков в группу, а затем регистрируем для
// каждого игрока из этой группы событие триггера.
// Но функции инициализации всех триггеров
// запускаются РАНЬШЕ события map initialization.
// Поэтому група будет пустая. Для избежания этого
// рабочий триггер надо инициализировать,
// допустим, через 1/10 секунды после начала игры.
// Эта функция заново иницилизирует Click после 0.01
// секунды. Сначала см. InitTrig_Click()
call DestroyTrigger(gg_trg_Click)
// Уничтожаем выполняющийся триггер т.е. Click.
// после этого он станет равным null.
set gg_trg_Click = CreateTrigger()
// А теперь заново его создаем, но зададим ему
// другие события / условия / действия.
call ForForce(udg_Clickers, function Register)
// А вот теперь берем группу Clickers и для каждого
// игрока выполняем регистрацию события (функция
// Register() была описана в самом начале). Такая
// конструкция вторым аргументом требует
// ФУНКЦИЮ, а не ВОЗВРАЩАЕМОЕ ЕЙ значение -
// поэтому перед Register и стоит слово function.
// Более того, у функций, используемых в качестве
// аргумента в такой конструкции не должно быть ни
// входных аргументов, ни возвращаемого значения
// (takes nothing returns nothing). Это, кстати, очень
// большой недостаток JASS.
call TriggerAddCondition(gg_trg_Click, Condition(function SCondition))
// Добавляем триггеру условие. Не забываем писать
// для аргумента Condition(function Имя_условия).
call TriggerAddAction(gg_trg_Click, function SingleClick)
// Добавляем новое действие триггеру - функцию
// обработки клика.
endfunction

function InitTrig_Click takes nothing returns nothing
// А это первая инициализация триггера Click. Он
// сначала будет использован для запуска функции
// своей второй инициализации через 0.1 секунду
// после начала игры. После второй инициализации
// он будет обрабатывать клики пользователей.
set gg_trg_Click = CreateTrigger()
// Создаем триггер из null.
call TriggerRegisterTimerEventSingle(gg_trg_Click, 0.10)
// Приписываем ему событие - запуск действия через
// 0.1 секунду после начала игры.
call TriggerAddAction(gg_trg_Click, function ClickInit)
// Добавляем действие - вторую инициализацию
// (функцию ClickInit()). Таким образом мы применили
// один триггер для абсолютно разных действий и
// сэкономили целую переменную типа 'триггер'!
endfunction

// -------------------- Конец скрипта -------------------------

Вопросы, которые могли возникнуть:

1. Возможна ли 'потеря' клика в случае, если мы после первого клика щелкнули в течение 0,6 секунд, но на другого юнита. Копия триггера из очереди не найдет совпадения и запишет юнит в массив. Но первая копия (которая до этого ждала), после конца ожидания сотрет юнит из массива. Таким образом, мы 'теряем' первый клик?
Для решения этой проблемы и введено дополнительное условие, которое кажется изначально лишним:

if udg_Units[Index] == GetTriggerUnit() then
set udg_Units[Index] = null
endif

Сразу после окончания wait мы заново проверяем, совпадает ли записанный в массив юнит с 'кликнутым'. Если совпадают, то за время ожидания пользователь не щелкнул на другом юните и можно смело стирать запись из массива. Если же нет - значит, другая копия поместила юнит в массив (пользователь кликнул на другом юните) и мы ничего не делаем. Таким образом, мы не 'теряем' клик.

2. Как настроить чувствительность?
Очень просто - меняем значение 0.6 в ТriggerSleepAction(0.6) на нужное. Чем оно меньше, тем труднее щелкать. Вроде экспериментальным путем определил интервал: 0.5 - 0.6 секунд.

3. А разница между двойным выделением мышью или по горячим клавишам есть? Ведь мы используем событие - Unit is selected ("Юнит выделен", а не "На юнита кликнули" - такого события нет).
К сожалению, нет - в Варкрафте невозможно распознать как был выделен юнит - мышью или горячей клавишей. Но в этой карте проблема не актуальна, т.к. двойной клик действует только на врагов, а их на горячие клавиши вешать нельзя. Для карт, где надо кликать на своих юнитов могу предложить частичное решение проблемы - при клике проверять, находится ли юнит на экране или нет. Если не находится, то его выделили 100% горячей клавишей и не надо ничего делать. Если да - тогда неизвестно, но можно предположить, что выделение было произведено все-таки мышью. Других идей у меня пока нет (это для RoC, может в TFT и можно различить - у меня его пока нет).

Автор: Caсоdemon
© WC3.RU, 2002-2010 гг
Нашли ошибки и недоработки в статье? Сообщите нам в раздел Поддержки! С уважением, WC3.RU
__________________
Смотри подругому !
VAV вне форума   Ответить с цитированием
Старый 12.08.2010, 14:38   #2
VAV
Pandora Directive
 
Аватар для VAV
 
Регистрация: 04.09.2002
Адрес: Area 404
Сообщений: 3,611
VAV как роза среди колючек VAV как роза среди колючек VAV как роза среди колючек
Отправить сообщение для VAV с помощью ICQ
По умолчанию Ответ: Отлавливаем двойной клик мышью.

Другие статьи:

Основы JASS.
JASS - Общие понятия.
Синтаксис JASS.
JASS - Библиотечные функции. Часть 1.
JASS - Библиотечные функции. Часть 2.
JASS - Операторы.
JASS - Выражения
JASS - Комментарии.
JASS - Функции
JASS - Типы
JASS - Заключение.
Faq по созданию рельефа.
Зачем так много триггеров?
Немного об оптимизации кода.
Текстовые триггеры
Создаем простейшие карты (карты для melee)
Событие с переменным периодом.
Познаем триггеры (Triggers)
Познаем регионы (locations)
Познаем переменные...
Отлавливаем двойной клик мышью.
Массивы и циклы в обычных и текстовых триггерах
Локальные переменные в Custom Text
Камеры, спецэффекты, карта, цвета, кэш...
Добавление своих функций в триггеры
Добавление своих моделей и др. в карту
Делаем очень большую карту...
Делаем огнемет.
Улучшенная версия огнемета...
Глюки worldeditor'a
Random с задаваемой вероятностью - аура.
__________________
Смотри подругому !
VAV вне форума   Ответить с цитированием
Ответ


Опции темы
Опции просмотра

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

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


Часовой пояс GMT +4, время: 18:09.

Design Developed by CompleteGFX
vBulletin® Version 3.6.7.
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Перевод: zCarot