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

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

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

Random с задаваемой вероятностью - аура.



Теперь рассмотрим пример о том, как создать более сложный аналог Rise Sceletons в виде ауры. Алгоритм можно без проблем сделать и как спелл.

Поставим перед собой такие задачи:

1. Эта аура будет окружать одного выбранного юнита и действовать на убитых вражеских юнитов. Игроки, являющиеся врагами, будут записаны в специальную группу игроков при инициализации карты.
2. Действие ауры заключается в создании юнитов из убитых врагов, но тип создаваемого юнита будет выбран случайным образом из заданного списка типов.
3. Вероятность выбора типа юнита может быть задана пользователем. Например, сильные юниты будут создаваться реже слабых.
4. Аура не должна действовать на героев, здания, механических и летающих юнитов.
5. Все параметры можно менять во время игры - допустим, добавлять новых юнитов в список или удалять их оттуда. Удаление производится присвоением ячейки массива типов значения 'no unit type'. Т.к. тип юнита - всего-лишь integer (см. пред. статьи), то этому значению соответствует ноль.
6. Алгоритм должен быть достаточно безопасным на случай, если создатель карты забудет установить какой-либо параметр.

Пусть типы юнитов будут задаваться массивом (RS_UTypes), соответствующие вероятности - массивом такого же размера (RS_UWeight). Реализовать произвольный выбор типа юнита рандомом из массива и его создание не является чем-то сложным. Можно определить размер массива и выбрать элемент с произвольным номером - от 1 до размера массива. Однако как реализовать задаваемую вероятность выбора? Итак, сначала попробуем расмотреть принцип действия алгоритма.
Элемент будем выбирать не по произвольному номеру из массива, а по другому критерию. Рассмотрим массив, определяющий вероятность выбора (RS_UWeight - его элементы - числа real). Пусть в обоих массивах будет 5 элементов:

RS_UTypes: xxx xxx xxx xxx xxx (типы юнитов)
RS_UWeight: 1.0 2.0 5.0 3.0 4.0 (величины, пропорциональные вероятности появления).

Теперь найдем сумму всех элементов второго массива. Она равна 15. А теперь выберем рандомом произвольное число от нуля до суммы элементов этого массива (в нашем случае - 15). По этому числу и определим произвольный юнит из первого массива. Пример:
0.5 - Берем первый элемент (т.к. меньше единицы)
3.0 - Берем третий элемент (это число меньше 1 + 2 + 5 = 8)
9.0 - Берем четвертый элемент (т.к. это число меньше 1 + 2 + 5 + 3 = 11)
То есть мы берем первый номер, сумма элементов до которого больше выбранного произвольного числа. Очевидно, чем больше число в ячейке второго массива, тем больший диапазон оно занимает в сумме и тем больше вероятность выбора этой ячейки рандомом. Таким образом мы организовали выбор произвольного номера ячейки с задаваемой вероятностью выбора. Нам нужно две функции, одна из которых находит и возвращает сумму значений в ячейках второго массива, а другая возвращает номер первой ячейки, сумма до которой большей заданного произвольного числа. Эти две функции можно совместить в одну, это будет сделано позже, пока рассмотрим их отдельно. Примеры:

function GetArraySum takes nothing returns real
// Эта функция находит сумму всех элементов второго массива
// и возвращает ее.
local integer Ref = 1
// Номер текущей ячейки в массиве.
local real TmpSum = 0
// Сюда помещаем набранную сумму.
loop
exitwhen (udg_RS_UTypes[Ref] == null) or (udg_RS_UTypes[Ref] == 0)
// Выходим из цикла, если находим конец массива типов юнитов (null)
// или находим значение 'no unit-type' - ноль.
if udg_RS_UWeight[Ref] != null then
// Это условие надо, если вдруг создатель карты забудет
// указать значение вероятности. В таком случае игра не вылетит,
// а посчитает эту вероятность равной нулю.
set TmpSum = TmpSum + udg_RS_UWeight[Ref]
// Если все нормально, то прибавляем значение текущей ячейки к сумме.
endif
set Ref = Ref + 1
// УКвеличиваем номер ячейки на единицу.
endloop
return TmpSum
// Возвращаем накопленную сумму.
endfunction

function GetArrayIndex takes real Erf returns integer
// А эта функция по заданному произвольному значению Erf
// возвращает номер первой ячейки массива вероятностей,
// значение в которой больше Erf.
local integer Ref = 1
// Номер текущей ячейки в массиве.
local real TmpSum = 0
// Сюда помещаем накопленную сумму и сравниваем ее с Erf.
loop
exitwhen (udg_RS_UTypes[Ref] == null) or (udg_RS_UTypes[Ref] == 0) or (TmpSum > Erf)
// Первые два условия аналогичны условиям предыдущей функции.
// Добавилось условие выхода из цикла, если накопленная сумма
// будет больше заданного произвольного числа.
if udg_RS_UWeight[Ref] != null then
set TmpSum = TmpSum + udg_RS_UWeight[Ref]
// Эти строки аналогичны строкам в предыдущей функции.
endif
set Ref = Ref + 1
// Прибавляем к номеру текущей ячейки единицу.
endloop
return Ref - 1
// Возвращаем номер предыдущей ячейки.
// Если что непонятно, рассмотрите каждый
// шаг такого цикла для разных случаев - тогда
// будет все понятно.
endfunction

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

Итак, сам алгоритм:

1. Событие - юнит, принадлежащий игроку вражеской группы, умирает.
2. Проверяем, подходит ли он (не герой, не здание и т.д.). Если нет - выход, если подходит - действуем дальше.
3. Находим сумму элементов второго массива (первая функция). Это необязательно делать при каждом запуске триггера (можно было раз просчитать и поместить сумму в глобальную переменную), но мы же хотели получить возможность свободного изменения массивов - поэтому и проверять надо при каждом запуске
4. Берем произвольное real - число от нуля до этой суммы.
5. По этому числу определяем номер соответствующей ячейки в массиве типов (RS_UTypes).
6. Проверяем, не null ли там находится? Это надо на случай, если первая ячейка RS_UTypes содержала null или 0. В таком случае функция определения номера ячейки вернет единицу (см. ее алгоритм). А нам не надо вылетания игры. Если в ней был ноль - не страшно, функция создания юнита его проигнорирует.
7. Если все нормально, то создаем юнита выбранного типа и спецэффект, убираем старого юнита.

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

real RS_AuraDst - Расстояние, на которое действует аура.
unit RS_RefUnit - Сюда помещаем юнит - центр ауры.
integer array RS_UTypes - Список типов юнитов.
real array RS_UWeight - Список величин, пропорциональных вероятности появления соответствующего типа.
player group RS_Include - Группа игроков, на которых действует эта аура. Игроков сюда надо поместить при инициализации.

Пример содержит в себе обычный триггер, устанавливающий начальные значения всех переменных (типы юнитов, вероятности и т.д.). Он оставлен обычным для тех, кто будет устанавливать эти значения из обычных триггеров (не текстовых).

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

function Register takes nothing returns nothing
// Функция регистрации события для каждого игрока из
// заданной группы RS_Include. Как это работает - см.
// статью о двойном клике - тут ситуация аналогична.
call TriggerRegisterPlayerUnitEventSimple(gg_trg_Spawn,
GetEnumPlayer(),EVENT_PLAYER_UNIT_DEATH)
endfunction

function SpCondition takes nothing returns boolean
// Функция - условие для триггера.
if IsUnitAliveBJ(udg_RS_RefUnit) and (DistanceBetweenPoints(GetUnitLoc(udg_RS_RefUnit),
GetUnitLoc(GetDyingUnit())) <= udg_RS_AuraDst) then
// Проверяем жив ли юнит - центр ауры и расстояние от убитого юнита
// до него. Если хоть одно из этих условий не выполнено, возвращаем
// false - триггер дальше не запустится.
return (not (IsUnitType(GetDyingUnit(), UNIT_TYPE_HERO) or IsUnitType(GetDyingUnit(), UNIT_TYPE_STRUCTURE) or IsUnitType(GetDyingUnit(), UNIT_TYPE_FLYING) or IsUnitType(GetDyingUnit(), UNIT_TYPE_MECHANICAL)))
// Если первое условие выполнено, проверяем, не является ли юнит
// одним из типов, из которых нельзя создавать 'скелетов'.
else
return false
endif
endfunction

function GetArrayData takes real Erf returns real
// Эта функция совмещает в себе две описанные ранее.
// Если Erf == 0, то она работает как первая (находит
// сумму ячеек), если Erf != 0 - работает как вторая.
local integer Ref = 1
local real TmpSum = 0
// Значение этих переменных см. в тех функциях.
loop
exitwhen (udg_RS_UTypes[Ref] == null) or (udg_RS_UTypes[Ref] == 0) or ((Erf != 0) and (TmpSum > Erf))
// Это условие объединяет в себе два - если Erf == 0, то последнее условие
// будет ложью в любом случае (т.к. and вернет false из-за того, что Erf == 0).
// В противном случае третье условие будет учитываться.
if udg_RS_UWeight[Ref] != null then
set TmpSum = TmpSum + udg_RS_UWeight[Ref]
// Эти строки были одинаковы для обеих функций -
// оставляем их без изменения.
endif
set Ref = Ref + 1
// Прибавляем к текущему номеру ячейки единицу.
[color=orange] endloop
if Erf != 0 then
// А теперь проверяем - что же надо вернуть.
return I2R(Ref - 1)
// Если Erf != 0 (работаем как вторая функция), то
// возвращаем переведенное в real значение пред. ячейки.
// (у нас функция возвращает real, поэтому мы пошли
// на такую хитрость. I2R можно было не писать, но
// так понятней).
else
return TmpSum
// Если Erf == 0 (работаем как первая функция),
// то возвращаем накопленную сумму.
endif
endfunction

function Spawn takes nothing returns nothing
// Функция - действие триггера.
local location ULoc = GetUnitLoc(GetDyingUnit())
// Запоминаем в локальную переменную координаты
// убитого юнита.
local integer Index = R2I(GetArrayData(GetRandomReal(0,GetArrayData(0)))
)
// В другую - сразу произвольный номер. Переводим
// возвращаемое функцией GetArrayData значение в integer.
// Аргумент у нее - произвольное число от нуля до значения
// суммы, которую возвращает эта же функция, но с нулевым
// аргументом. Таким образом, мы сразу получаем произвольный
// номер ячейки с учетом вероятностей.
if udg_RS_UTypes[Index] == null then
// Если в ячейке массива типов оказался null (это возможно,
// когда первая ячейка RS_UTypes содержит null или 0 - создатель
// карты забыл заполнить массивы при инициализации ), то сразу
// выходим из триггера.
return
endif
call TriggerSleepAction(2)
// Ждем 2 секунды, это надо для того, чтобы закончилась анимация
// убитого юнита.
call AddSpecialEffectLocBJ(ULoc, " Abilities\\Spells\\Human\\Resurrect\\ResurrectTarg
et.mdl")
// Добавляем спецэффект - в данном случае это 'Human Ressurect Magic'.
call RemoveUnit(GetDyingUnit())
// Убираем юнита - чтобы следов не осталось.
call TriggerSleepAction(0.5)
// Ждем еще 0.5 секунды - чтобы спецэффект был в самом разгаре.
call CreateUnitAtLoc(GetOwningPlayer(udg_RS_RefUnit),ud
g_RS_UTypes[Index],ULoc,bj_UNIT_FACING)
// Создаем юнита для владельца RS_UTypes
// нужного типа на месте убитого юнита.
endfunction

function SpawnInit takes nothing returns nothing
// Это повторная функция инициализации триггера.
// Принцип действия двойной инициализации описан в пред.
// статье (распознание двойного клика). Такая
// инициализация нужна для того, чтобы группа игроков
// RS_Include не была пустая при запуске триггера.
call DestroyTrigger(gg_trg_Spawn)
set gg_trg_Spawn = CreateTrigger()
call ForForce(udg_RS_Include, function Register)
// Аналогично - длякаждого игрока из группы
// регистрируем событие триггера функцией Register.
// (см. пред. статью)
call TriggerAddCondition(gg_trg_Spawn, Condition(function SpCondition))
// Добавляем функцию - условие.
call TriggerAddAction(gg_trg_Spawn, function Spawn)
// Добавляем функцию - действие.
endfunction

function InitTrig_Spawn takes nothing returns nothing
// Функция первичной инициализации. Создает повторную
// инициализацию этого же триггера через 0.1 секунду
// после начала игры.
set gg_trg_Spawn = CreateTrigger()
call TriggerRegisterTimerEventSingle(gg_trg_Spawn, 0.1)
// Регистрируем событие - запуск через 0.1 после начала игры.
call TriggerAddAction(gg_trg_Spawn, function SpawnInit)
// Регистрируем действие - повторную инициализацию.
endfunction

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

Дополнения и вопросы к статье:

1. Random real в Варкрафте обладает одной плохой характеристикой - числа со значениями, близкими к нулю, выпадают редко. Проверено - дома лежит целый лист статистики В основном выпадают числа из середины диапазона - поэтому ячейки с малыми значениями лучше помещать в середину массива, а самое большое значение - в первую ячейку. Пример: 10.3 3.5 2.4 1.0 1.5 4.0 7.0

2. Я писал все поздно ночью (в 3 часа ) и очень хотел спать. Поэтому где-то может вылезти ошибка. Но вроде проверял - все нормально работало. Если кто-нибудь обнаружит баг - пишите мне на личку blizzard.ru или почту alexey_bh@mail.ru

3. Рекомендуется делать размеры обоих массивов одинаковыми. Разница в размерах не приведет к ошибке, но скажется на рассчете вероятностей каждого юнита. Юниты с нулевой вероятностью создаваться не будут!

4. Как задать параметры из обычного триггера? В прилагаемой карте есть такой триггер, с него можно взять пример. Если надо в игре удалить часть массива, то ячейке массива RS_UTypes с номером, начиная с которого надо удалить данные, присваиваем значение 'no unit-type'. Со вторым массивом не надо ничего делать.

5. В примере скрипт немного изменен (не принципиально) - использована лишняя локальная переменная, зато скрипт эффективнее работает, если первое значение массива типов - null и экономит немного памяти

P.S. Пример прилагается. Можно доработать этот скрипт - следующие идеи, на мой взгляд, будут интересны:
- Сделать поддержку нескольких юнитов, создающих ауру.
- Сделать несколько уровней ауры (чем выше уровень, тем больше шанс получить сильных юнитов).
- Сделать включение / выключение (как способность) + потребление маны героя.
- Сделать summoned - юниты.
- Сделать разделение по типам - например, из воздушных юнитов получаются воздушные, из наземных - разные скелеты и т.д.

Думаю, этот скрипт может быть использован (без доработки, с поддержкой одного юнита) в одиночных РПГ и Ums - картах.

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

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

Основы 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, время: 03:25.

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