Показаны сообщения с ярлыком оружие. Показать все сообщения
Показаны сообщения с ярлыком оружие. Показать все сообщения

вторник, 28 октября 2008 г.

Щиты. Часть 1

Пуш и импульс.

Сегодня мы с вами поговорим о щитах. Правда, ни обзора производителей, марок и моделей пассивной защиты аватаров, ни подробного экономического анализа "почему одинаковые на вид модели так сильно различаются в цене", в этой статье вы не найдете. Мне хотелось бы уделить внимание лишь технической стороне вопроса, и уверен, это будет по-настоящему интересно.

Практически каждый житель SL неоднократно сталкивался с агрессивными действиями других аватаров - оружия в нашем онлайновом мире существует великое множество: от детальных копий образцов, популярных в оффлайновой жизни, до сугубо виртуальных моделей наподобие эпического БФГ – "Большого Финишного Глушила". Не стоит забывать и о так называемом скрытом оружии, которое для неискушенного пользователя, как правило, остается незаметным, но все же таким же опасным, как и его более приметные "собратья".

Однако, несмотря на все изообилие оружия, видов возможных атак не так уж много. А ведь именно от этого фактора зависит выбор щита. Заметим, что решения "все в одном" пока не достигли технического совершенства - такие щиты либо не слишком удобны в использовании, либо малофункциональны, но обо всем по порядку.

Начнем с самого распространенного вида атаки - толкательной (пуш). Пуш (от англ. to push – толкать) – это свойство симулятора сообщать объекту (физическому примитиву или аватару) направление и ускорение, или, проще говоря, возможность толкать объект в заданную сторону и с заданной силой. При помощи этой функции можно практически мгновенно переместить объект из одной точки в другую, например, оттолкнуть незваного посетителя от вашей двери или, наоборот, подсадить на высокую крышу. В оружейном производстве пуш, как правило, направлен строго вверх с максимальными значениями ускорения, что приводит к широко известным в народе "полетам в космос" - на высоту около трех миллионов метров, откуда потом приходится долго и нудно падать. Оружие этого типа самое распространенное, и в настоящее время его даже объявили "вне закона". На некоторых землях эту функцию вообще отключили, и такие места можно распознать по значку с перечеркнутой ладошкой.

Защититься от оружия, основанного на пуше, проще простого – нужно всего лишь сесть на какой-то нефизический предмет. Таким образом, ваш аватар приобретет свойства предмета, на котором сидит, а следовательно, станет "прозрачным" для пуш-пуль. Замечу, что способ "сесть на что-нибудь" по праву можно считать защитой номер один, поскольку он достаточно универсален и позволяет отразить большинство видов атакующих воздействий.

Щиты, разработанные на основе перехода в нефизическое состояние, как раз и представляют собой этот самый нефизический объект, снабженный специальными скриптами, которые дают вам возможность перемещаться в пространстве (просто сидя, скажем, на земле, этого сделать нельзя). Самым известным из них является распространяемый бесплатно "Anti-Griefing Vehicle", имеющий вид небольшого красного кубика.



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



Несколько секунд - и вы защищены от злобных атакующих маньяков. Теперь можно расслабиться и спокойно подумать, что предпринять. Если возникнет необходимость отойти подальше – просто нажимайте клавиши со стрелками и летите куда вздумается.

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

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

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

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

Оружейная. Делаем мину.

Говоря об оружии в целом, нельзя не вспомнить о всевозможных гранатах, бомбах и минах. Почему бы и нет? А давайте сделаем мину, которая будет тихо и спокойно лежать себе, пока на нее кто-нибудь по невнимательности своей не наступит. К слову сказать, мину сделать гораздо проще, нежели дубину, которую мы описывали в прошлой статье цикла "Оружейная".



Итак, для прообраза возьмем старую добрую, надежную противотанковую мину ТМ-46, изготовим ее из цилиндрических примов и приступим к самому главному - замесу взрывчатого вещества. ;)


Для начала давайте определим, от какого критерия должна срабатывать мина. Очевидно, что когда на нее кто-то наступит. Окей, берем уже известное нам по прошлой статье событие collision_start() и думаем дальше, что должно произойти после того, как нога аватара-раззявы наступит на наш "тортик".

Для разнообразия предлагаю использовать знаменитый push - llPushObject(). После того, как кто-то наступает на мину, мы вычисляем его идентификатор при помощи оператора llDetectedKey() и подкидываем вверх пушем:

llPushObject(llDetectedKey(0), force_amount*llRot2Up(llGetRot()), ZERO_VECTOR, FALSE).

В переменную force_amount поместим любое произвольное большое число - ну пусть это будет 10 000 000, не особо важно. Множитель же этой переменной - функция llRot2Up() от текущего значения вращения аватара на момент срабатывания мины - просто небольшая хитрость, смысл которой я не буду сейчас разъяснять. Просто запомните, что вектор импульса <0,0,10000000> работать не будет так же, как работает эта конструкция.

Можно сделать так, что мина будет срабатывать только на технику, например, на автомобили, или же, напротив, только на аватаров. Для этого нужно проверить, что именно вызвало событие - физический объект или аватар. Делается это при помощи оператора llDetectedType(). Например, нас интересует только живая сила врага. Значит, в скрипте пишем:

if(llDetectedType(0) & AGENT)
{
llPushObject(llDetectedKey(0), force_amount*llRot2Up(llGetRot()), ZERO_VECTOR, FALSE);
}


Кроме того, для совместимости с бэтлграундами неплохо добавить значение damage - пишем llSetDamage(5000).

Ну и осталось нам совсем немного - добавить спецэффектов, как то звук взрыва, дым, огонь и т.п. Кладем в инвентарь мины заранее подобранный звук и пару текстур, дыма и пламени. Звук вообще неплохо бы подгрузить заранее, поэтому рекомендую описать событие on_rez() и вставить туда команду заблаговременной подгрузки звука:

on_rez(integer start)
{
llPreloadSound("Explode");
}


Соответственно, звук нужно проиграть в нужный момент - добавляем перед пушем оператор llTriggerSound("Explode", 1.0), которая и проиграет звук, причем, звук будет как бы вокруг, а не четко привязан к самой мине, что важно в данном случае для нас - его будет далеко слышно.

Ну и дым с огнем. Тут с одной стороны все просто, а с другой - не очень. Дело в том, что существуют специализированные функции, которые призваны для того, чтобы создавать, особо не заморачиваясь, такие вещи, как взрывы, дымы, облака, брызги и т.п. Но отчего-то их вытесняют из языка LSL, предлагая использовать новомодную систему частиц llParticleSystem() во всех случаях жизни. Не буду спорить, система мощная, но работа с ней требует все-таки довольно глубокого понимания вопроса.

Итак, нужная нам команда - llMakeExplosion(integer particles, float scale, float velocity, float lifetime, float arc, string texture, vector offset). Немного расскажу про ее аргументы.

  • particles - количество частиц, которое будет сгенерено при вызове функции;

  • scale - максимальный размер частицы;

  • velocity - скорость распространения частиц;

  • lifetime - время жизни частицы;

  • arc - конус выброса частиц;

  • texture - текстура, которая будет натянута на каждую частицу;

  • offset - расстояние от источника до фактического образования частиц.



Итого, остается прописать два раза эту функцию - сначала для огня, затем для дыма:

llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "Fire", ZERO_VECTOR);
llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "Smoke", ZERO_VECTOR);


И самое последнее - раз мина взорвалась, то ее быть и не должно. Последней строчкой пишем llDie(), которое уберет сработавшую мину с земли.

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

Оружейная. Делаем дубину.

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



В SL существует масса оружия, от холодного до стратегического, подробнее об можно прочитать в обзорах заслуженного ветерана и владельца русского военного острова Fynist Island Dmitry Willis'а, где он рассказывает о видах всевозможных стрелялок и пулялок, а также где такое можно приобрести, ну а мы в рамках цикла "Оружейная" научимся самостоятельно изготавливать себе всевозможные военные побрякушки. Начнем мы с холодного оружия и для начала изготовим дубину для битья супостатов по бокам. Сразу скажу, что принцип действия любого оружия в SL один и тот же независимо от визуального воплощения - нечто вылетает из него, долетает до врага и входит с ним в соприкосновение; а дальше выполняет деструктивные методы - push, impulse или damage.

Итак, создадим некую дубину - пусть она у нас будет иметь вид классической бейсбольной биты.



Далее нужно приладить ее к руке - "одеть" на правую руку и отрегулировать положение.





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

Итак, рассмотрим основные части нашего будущего скрипта:

1. Захват и опрос клавиш управления при "одевании" дубины;
2. Проигрывание анимации в момент удара;
3. Нанесение урона врагу;
4. Спецэффекты (звуки, брызги крови etc).


"Одеванием" заведует событие attach(key id), возникающее при одевании чего либо на аватар, либо снятии этого чего-либо. Параметр, который появляется при этом событии - ключ-идентификатор аватара id. Таким образом, если предмет одет и возникло событие, то в переменной id появляется значение ключа, а если же предмет снят, то id будет равно значению NULL_KEY. Проверяя это значение по возникновению события attach мы всегда можем узнать, одели или сняли предмет.

В первую очередь, при одевании предмета, нужно проверить, если ли у нас право захватить клавиши управления и проигрывать анимации. Для этого существует оператор llGetPermissions(), возвращающий целое число, биты которого говорят о том, какие права в данный момент установлены. Нас интересуют следующие битовые флаги - PERMISSION_TRIGGER_ANIMATION, позволяющий проигрывать анимации, и PERMISSION_TAKE_CONTROLS, позволяющий перехватывать события нажатия кнопок. Если такие права у нас есть, то выполняем оператор llTakeControls(), в котором указываем, какие именно кнопки мы хотим захватить. Если же по каким-то причинам у нас нет прав на эти действия, то запрашиваем оные, исполняя llRequestPermissions(). Этот оператор вызовет появление в верхнем правом углу диалогового окна, предлагающего вручную разрешить применить к вашему аватару действия по захвату кнопок управления и назначения анимации.

К слову сказать, при "одевании" необходимые нам права выделяются автоматически, но на всякий случай мы предусматриваем корректную обработку возможной ошибки, связанной с выделением прав.

Ну и, если предмет был снят, нужно "освободить" клавиши, выполнив оператор llReleaseControls().

attach(key id)
{
if(id != NULL_KEY)
{
integer perm = llGetPermissions(); // запрашиваем права
if(perm == (PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION))
{
// если с правами все ОК, "захватываем" управление
llTakeControls(CONTROL_ML_LBUTTON | CONTROL_LBUTTON | CONTROL_UP | CONTROL_FWD | CONTROL_BACK | CONTROL_ROT_LEFT | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_RIGHT, TRUE, TRUE);
}
else
{
// если же нет, то просим нам дать эти права
llRequestPermissions(id, PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
}
}
else
{
llReleaseControls();
}
}


Еще один момент - при событии "одевания" и после ответа на запрос прав возникает еще одно событие - run_time_permissions(integer perm), где в perm хранится состояние выделенных прав. Нужно его также корректно обработать.

run_time_permissions(integer perm)
{
if (perm)
{
llTakeControls(CONTROL_ML_LBUTTON | CONTROL_LBUTTON | CONTROL_UP | CONTROL_FWD | CONTROL_BACK | CONTROL_ROT_LEFT | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_RIGHT | CONTROL_DOWN, TRUE, TRUE);
}
}


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

Событие, возникающее при нажатии на клавишу, называется control(key owner, integer level, integer edge). При его возникновении определяются параметры owner, содержащее ключ вашего аватара, level - битовое число, "знающее", какая кнопка нажата и не отпущена и edge, которое, в свою очередь "знает", какая кнопка удерживалась и теперь отпущена. Все действия, интересные нам должны обрабатываться только при нажатой и удерживаемой кнопке мыши - level & (CONTROL_ML_LBUTTON | CONTROL_LBUTTON) и если это так, выясняем, нажимается ли еще что-то в данный момент. Если нажимается, проигрываем анимацию удара при помощи оператора llStartAnimation().

control(key owner, integer level, integer edge)
{
if(level & (CONTROL_ML_LBUTTON | CONTROL_LBUTTON))
{
if(edge & (CONTROL_UP | CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_ROT_LEFT | CONTROL_RIGHT | CONTROL_ROT_RIGHT))
{
llStartAnimation("sword_strike_R");
}
}
}


После описания обработчика этих событий нашей дубиной можно вполне спокойно размахивать, пугая народ, но вот бить по-настоящему ей пока еще нельзя - этому ее нужно еще "научить". Существует несколько способов нанесения урона, но для простоты и понятности в дальнейшем мы рассмотрим сейчас эмиссионный способ, в котором нечто невидимо вылетает из нашей дубины и поражает противника. Согласен, не совсем корректно по отношению к природе дубины, но во-первых, это один из самых распространенных методов действия оружия, а во-вторых, когда в следующей статье мы будем делать пистолет, принцип стрельбы пульками будет уже вами освоен.

Итак, нам нужно создать некий объект, который бы при ударе вылетал из дубины и бил прямехонько в супостата. Делать мы это будем при помощи оператора llRezObject(), который позволяет выкидывать в пространство предмет, хранимый в инвентаре родительского объекта, по определенному вектору с определенной силой. Для этого мы сначала определяем, в какой стороне от нас враг и куда нужно пулять. Делается это операторами llGetRot() и llRot2Fwd(), первый из которых возвращает вращение вашего аватара (а предполагается, что вы бьете врага, стоя к нему лицом), а второй преобразует это вращение в направление. Кроме того, неплохо было бы знать и положение в пространстве нашего аватара, что мы сделаем при помощи оператора llGetPos(). Выделим это в отдельную пользовательскую функцию kick():

kick()
{
rotation rot = llGetRot();
vector pos = llGetPos();
vector dir = llRot2Fwd(rot);
pos += dir;

llRezObject("kick_bullet", pos, dir, ZERO_ROTATION, 0);
}

Самое время переходить к изготовлению пули. В качестве нашего kick_bullet будем использовать обыкновенный шарик небольшого диаметра. Собственно, форма объекта может быть любой - все равно в нашем случае он будет невидимым. Также нужно решить, как именно будет наносится урон врагу. Я бы рекомендовал использовать энергию импульса и damage, что позволит пользоваться нашей дубиной даже там, где выключен push, ну и на территориях, где разрешен damage (на так называемых бэттлграундах) и "убивать" ей супостатов.

Создаем скрипт в пуле и прописываем там, что нужно делать, если пуля попала во врага. Событие соприкосновения с чем-либо называется collision_start(integer num), где num - количество столкновений. Кроме того, нам интересно еще одно событие, возникающее при начале движения пули - moving_start(). Во-первых, мы должны придать пуле способность наносить повреждения - damage. Делается это при помощи оператора llSetDamage(), придать ей свойство физического объекта STATUS_PHYSICS оператором llSetStatus() и во-вторых, увеличить силу физического удара при помощи llSetForce(), чтобы нашим врагам мало не казалось. Для этого мы в событии collision_start() определяем массу врага через llGetObjectMass() и расчитываем необходимую силу удара.

Теперь немного об утилизации отработанных пулек. Для того, чтобы не засорять окружающее пространство, добавляем в пулю такой параметр, как lifetime. Это - количество времени, которое проживет пуля с момента выстрела (начала своего движения в пространстве). Кроме того, это время попутно нам сокращает радиус действия нашей дубины, что, согласитесь, немаловажно для реалистичности ее использования. Итак, в событии moving_start() мы запускаем таймер llSetTimerEvent(lifetime), который сработав, вызовет событие timer(), в котором при помощи оператора llDie() пуля самоуничтожится. Переменная shot введена для удобства настройки боевых качеств пули - если вы ее просто выложите на землю, то она не будет самоуничтожаться, а даст вам возможность спокойно настроить необходимые параметры скрипта. Ну и еще маленькая тонкость - чтобы наши пульки, вылетев за границу мира, не падали нам в папку Lost And Found и не засоряли инвентарь, добавим статус STATUS_DIE_AT_EDGE, который будет их удалять при пересечении границы мира.

vector velocity;
float damage = 20;
float lifetime = 0.27;
integer shot = FALSE;

default
{
state_entry()
{
llSetDamage(damage);
llSetAlpha(0.0, ALL_SIDES);
llSetStatus(STATUS_PHYSICS, TRUE);
llSetStatus(STATUS_DIE_AT_EDGE, TRUE);
llSetBuoyancy(1.0);
llSetForce(<10, 0, 0>, TRUE);
}

collision_start(integer total_number)
{
key kd = llDetectedKey(0);
float fMass = llGetObjectMass(kd);
integer mass = llRound(fMass);
llSetForce(<10*mass,0,0>,TRUE);
}

timer()
{
if(shot) llDie();
}

moving_start()
{
llSetTimerEvent(lifetime);
velocity = llGetVel() * 5;
float vmag = llVecMag(velocity);
if (vmag > 0.1){
shot = TRUE;
}
}
}


Ну вот, собственно, и все. Сохраняем скрипт, забираем пулю с земли и кладем ее в инвентарь дубины. После этого хватаем дубину в руки и идем гонять нехороших злых аватаров. Что касается спецэффектов в виде брызг крови, отлетающих зубов и т.п., то это несложно реализовать, добавив в событие collision_start() соответствующие функции. Какие именно - это мы рассмотрим в следующей статье цикла, когда будем создавать огнестрельное оружие, где спецэффекты более обоснованы. ;)

Немного от автора.
Этот материал в первую очередь предназначен для людей, способных к самообучению. Поэтому я специально не привожу полные листинги скриптов - моя цель состоит именно в том, чтобы читатель разобрался в принципе действия такого оружия, в принципе работы событий в скриптах и т.п., тогда ему не составит никакого труда сделать что-то свое, придав ему именно те свойства, которые требуются. А нахаляву метод "копипаста" не прокатит. ;)


ЛИТЕРАТУРА:
http://wiki.secondlife.com/
http://rpgstats.com/wiki/index.php?title=Main_Page