Jump to content


Скриптинг для ящиков (урок третий)


7 replies to this topic

#1 Axon Dezno

    Участник

  • Писатели
  • PipPipPip
  • 210 posts
  • Пол:М
  • Основной цех:Строители
  • Второй цех:Художники
  • SL Status: 

Posted 20.01.10 - 01:53

Предыдущий урок

Логик капитулир…



В этом уроке мы расмотрим логические принципы, по которым работают скрипты.

Собственно… первым делом скажу, что логика большинства скриптов основывается в основном на так называемых условиях.
Есть, правда, довольно небольшое количество скриптов, которые работают и на чистом ”матане” (математическом анализе),
это самые трудные для понимания типы скриптов, тем не менее постараюсь объяснить как первый тип, так и второй.

…но сперва, неплохо было бы разобраться с базовыми кирпичами из которых в ЛСЛ любая логика и собирается:

Я их тут буду представлять не с той позиции, с которой их обычно в большинстве уроков по скриптингу представляют
(200% которых рассчитаны на абсолютно не знакомых с предметом урока людей имеющих не менее, чем 2-4 годичный опыт работы в этой облаcти),
а с позиции привязки каждого условия или комбинации условий определённым языковым логическим конструкциям (скриптовый ЯЗЫК как никак… =О.х=)

if и else, они же, собственно, условия – дословно переводятся, как ”если” и ”иначе” и выполняют роли, полностью соответствующие своим названиям.

Поскольку if является условием это условие должно где-то задаваться. Задаётся условие в круглых скобках, а в фигурных
Скобках, которые идут ПОСЛЕ самого условия находится непосредственно код, выполняющийся при его срабатывании.

Чтобы яснее представить себе условие приведу его выполнение на примере обычного предложения:

Если на улице темно – идти спать.

Или, если близко к скриптовому синтаксису, то так:

Если(на улице темно) {идти спать;}

Последний вид записи называется псевдо код.

ПРАВИЛО: Если вы хотите сделать скрипт с как можно меньшим числом ошибок, ОБЯЗАТЕЛЬНО перед написанием самого кода
набросайте логический ”глобус” скрипта, чтобы потом каждый раз не вспоминать зачем вы вчера написали этот вот блок кода
и как он взаимодействует с остальными частями скрипта.


***

else является антиподом заданного условия и без самого условия существовать НЕ МОЖЕТ!!!

Чтобы яснее представить себе, почему оно не может существовать без самого условия попрошу вас чётко выполнить следующее
Условие:

В_любом_другом_случае{сесть на стул}

Как мы можем видеть в виду отсутствия самого ”случая” термин ”в любом другом” тут бессмысленен.

Связки вида if <=> else называются простыми условиями.
Условный пример таких простых условий будит следующим:

if(одна переменная равна другой) {выполнить ”код срабатывания”}
else{выполнить ”код исключения”}

Ну или составное простое условие:

if(на улице всё тает){сказать ”на улице весна”;}
if(на улице тепло){сказать ”на улице лето”;}
if(на улице опадают листья){сказать ”на улице осень”;}
if(на улице теплее чем дома){поискать шубу и валенки;}
else{включить функцию llMosk();}

Помимо простых условий if и else имеется ещё и составное else if(условие){код;},
которое дословно переводится, как ”если же…”.

Ну и естественно, имеются и комплексные условия:

if(на улице утро){плюхать на работу;}
else if (на улице уже день){обматерить всю округу и плюхать на работу в режиме ”лёгкий галоп в припрыжку”;}
else if (на улице выходной){почесать тыкву и бухнуться спать дальше;}
else {включить функцию llMosk();}

А теперь чешем репу, а не похоже ли комплексное условие на составное простое? =О.о=

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

Угу… значит с общим принципом работы ифов и элсов разобрались… однако конкретных скриптовых примеров так и не увидели…

Естествено не увидели, поскольку для их понимания надо ещё кое-что знать, а именно – логические операции.
Помните в предыдущем уроке я просил вас не путать ”|”(двоичное сложение) с ”||”(логическое ”или”)?

Вот все эти операции мы сейчас и рассмотрим.

Логические:

|| – знак означающий ”или”.
Пример: if(a==b || c==d){код} – если(a равно b или c равно d){выполнять код}

&& – знак означающий ”и”.
Пример: if(a==b && c==d){код} – если(a равно b и c равно d){выполнять код}

> он же ”больше”
Пример: if(a>b){код} – если(a больше b){выполнять код}

< он же ”меньше”
Пример: if(a<b){код} – если(a меньше b){выполнять код}

! – знак отрицания. Вне условия может использоваться для обнуления числа.

Пример:
integer a=5;
default{state_entry(){llSay(0,(string)!a);}} //a тут равно 0.


Или для изменения значения с TRUE на FALSE или с FALSE на TRUE.

Пример:
integer a=0; integer b=1;
default{state_entry(){llSay(0,"!a="+(string)!a+" !b="+(string)!b);}}


== ну итак видно что это… - логический знак равенства, его так же можно использовать и с некоторыми другими знаками:

!= - не равно (в не зависимости от того больше или меньше).
Пример – if(a!=b){код} – если(a НЕ РАВНО b){выполнять код}

Работает так же и такой вариант – if(!(a==b)){код} – звучит, как ”если(не_верно_выражение(a=b)){выполнять код}”,
а не как ”если((a не равно b)){выполнять код}”, то есть в этом случае неравенство применяется ко всему выражению.

>= - больше или равно.
Пример: if(a>=b){код} – если(a больше или равно b){выполнять код}

<= - меньше или равно.
Пример: if(a<=b){код} – если(a меньше или равно b){выполнять код}

Бинарные математические (bitwise):

| (OR) - математический бинарный оператор сложения.
Где пригождается: при определении одновременного возвращения нескольких параметров
(ну, там PERM_MOVE|PERM_COPY|PERM_MODIFY|PERM_TRANSFER и т.п.)
Также: для запихивания нескольких параметров в один интегер с целью экономии места.

А вот об этом поподробнее:
Что из себя, собственно, представляют константы в курсе?
Шо? Нет?

А, ну константы представляют из себя то же, что и переменные, только в отличии от переменных при написании скрипта они уже объявлены.
Тобиш выглядят они как-то так - integer PERMISSION_DEBIT=2;
И соответственно, вместо константы можно написать и обычную цифру, результат будет один и тот же.

Э... а как можно в одну цифру запихать несколько параметров? =О.о=
Легко и просто - целое, десятичное число процессором анализируется не в десятичном, а в двоичном виде и 2 в десятичной системе счисления
при переводе в двоичную превращается в 0000 0000 0000 0000 0000 0000 0000 0010.

А теперь дам список некоторых констант:


2 он же - PERMISSION_DEBIT --> 0000 0000 0000 0000 0000 0000 0000 0010
4 он же – PERMISSION_TAKE_CONTROLS --> 0000 0000 0000 0000 0000 0000 0000 0100
8 он же – PERMISSION_REMAP_CONTROLS --> 0000 0000 0000 0000 0000 0000 0000 1000
16 он же – PERMISSION_TRIGGER_ANIMATION --> 0000 0000 0000 0000 0000 0000 0001 0000
32 он же – PERMISSION_ATTACH --> 0000 0000 0000 0000 0000 0000 0010 0000
64 он же – PERMISSION_RELEASE_OWNERSHIP --> 0000 0000 0000 0000 0000 0000 0100 0000
128 он же – PERMISSION_CHANGE_LINKS --> 0000 0000 0000 0000 0000 0000 1000 0000
256 он же – PERMISSION_CHANGE_JOINTS --> 0000 0000 0000 0000 0000 0001 0000 0000
512 он же – PERMISSION_CHANGE_PERMISSIONS --> 0000 0000 0000 0000 0000 0010 0000 0000
1024 он же - PERMISSION_TRACK_CAMERA --> 0000 0000 0000 0000 0000 0100 0000 0000
2048 он же - PERMISSION_CONTROL_CAMERA --> 0000 0000 0000 0000 0000 1000 0000 0000



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

Для танкистов покажу на примере:

PERMISSION_DEBIT|PERMISSION_TAKE_CONTROLS =
= 0000 0000 0000 0000 0000 0000 0000 0010 | 0000 0000 0000 0000 0000 0000 0000 0100 = 0000 0000 0000 0000 0000 0000 0000 0110 = 2|4=6

Только это... 0000 0000 0000 0000 0000 0000 0000 0010 + 0000 0000 0000 0000 0000 0000 0000 0011 => 2|3 = 3 = 0000 0000 0000 0000 0000 0000 0000 0011 а не 5, потому, что под сложением подразумевается замена в битовом поле нуля на единицу, если там уже стоит единица, то никаких изменений не происходит. Ага, это вам не школьное сложение.

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

Тобиш, с помощью этих операторов можно проводить сравнение положения и количества единиц с заранее заданным эталоном.

Ладно, едем дальше.

& (AND) – математический оператор оставляющий в двух или более двоичных числах только совпадающие единицы.
Пример: 1001001 & 1000001 => 73 & 65 => 1000001
Ну или так: 1001001 & 1000001 & 1101011 => 73 & 65 & 107 => 65 или 1000001 – то есть среднее число в примере.
Заметим, что во втором примере получилось число, в котором совпали единицы ВО ВСЕХ ТРЁХ РЯДАХ! Это важно, поскольку рядов
Может быть и десять, и в результате опять же останется только число в котором совпадут все десять рядов единиц, ну или нуль получится.

Собственно, где это применяется. В основном в АО, для определения, какая в данный момент кнопка нажата:

//Копипаста из ЛСЛ-вики:
default
{
	state_entry()
	{
		llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
	}
	
	run_time_permissions(integer perm)
	{
		if(perm & PERMISSION_TAKE_CONTROLS)
		{
			llTakeControls(CONTROL_FWD|CONTROL_BACK,TRUE,TRUE);
		}
	}
	
	control(key controller, integer levels, integer edges) 
	{
    	 // Если нажата стрелка вверх (она же ”вперёд”)
    	 if ((levels & CONTROL_FWD) == CONTROL_FWD)  // <== Если врезультате ”эндования” осталась одна цифра…
	  //levels                00000000000000000000000000000011 // десятичное 3
	  //CONTROL_BACK          00000000000000000000000000000010 // десятичное 2
	  //-------------------------------------------------------------------------------
	  //levels & CONTROL_BACK 00000000000000000000000000000010 // десятичное 2

	 	 {
        	llOwnerSay(”Вперёд”);  //Сказать ответ овнеру.
    	 }

    	 // Если нажата стрелка вниз (она же ”назад”)
    	 if ((levels & CONTROL_BACK) == CONTROL_BACK) //<==Если в результате ”эндования” осталась одна цифра…
		 {
   	     	llOwnerSay(”Назад”); //Сказать ответ овнеру.
  	  	 }
	}
}

***

~ (NOT) – математический бинарный оператор, который обращает все биты числа (было, допустим 111, стало 000).
Применение - может использоваться для обнуления переменной, например:

integer a=4;
integer b=a & ~a;

По русски значение переменной b звучит примерно так – b равно результату отсеивания несовпадающих единиц у a и
инвертированного a (ну и поскольку у второго ”a” нету совпадающих единиц, получатся одни нули)

Бинарка: b= 100 & ~011 => 000

***

^ (XOR) – математический бинарный оператор, который при сXORивании двух и более переменных оставляет в их двоичных значениях
только не совпадающие единицы.

Пример:

100001^001100 => 101101 -> 33^12=45

100001^010010^001000^000100 => 111111 -> 33^18^8^4=63

Где его можно использовать?
Использовать его можно, для проверки вида "первая нопка нажата, а вторая нет ИЛИ вторая нажата, первая нет"

control(key controller, integer levels, integer edges)
{
// Если нажато только CONTROL_FWD, (!)ИЛИ(!) только CONTROL_RIGHT (но не и то и другое сразу).
if (((levels & CONTROL_FWD) == CONTROL_FWD) ^ ((levels & CONTROL_RIGHT) == CONTROL_RIGHT))
{
//Выполнять код.
}
}

***

>> (шаг в право) – математический бинарный оператор, который смещает всё битовое поле числа на указанное количество знаков
вправо.

Пример: 93>>4 => 5 или, если в двоичном представлении - 1011101>>4 => 101
Имейте в виду, что биты, которые ”ушли за горизонт” безвозвратно теряются.

Справка: сдвиг двоичного числа на один знак вправо равносилен делению десятичного числа на 2
с последуюшим отсечением дробной части:

93>>1 => 46 или 101110 плюс отсечённый дробный остаток 0.5,
обратное действие – 46*2=92 и плюс 0.5*2, какраз и будит 93.

Сопсна, где это дело может пригодиться? А пригождается оно, если нужно быстро разделить ЧЁТНОЕ целое число на 2.
Так же желательно, чтобы в числе, которое вы собираетесь поделить на 2 несколько раз ВСЕ цифры были чётными.

***

<< (шаг в лево) – то же что и предыдущее, только биты смещаютсо влево.

Пример: 93<<4 => 1488 или, если в двоичном представлении – 1011101<<4 => 10111010000
Если число сдвинуть за ЛЕВУЮ границу битового поля, то ушедшие ”за бугор” знаки так же потеряются.

Справка: сдвиг двоичного числа на 1 знак влево равносилен умножению десятичного числа на 2
Пример: 93<<1 => 186 или 10111010 в двоичном представлении,
обратное действие – 186/2=93.

Пригождается для сверхбыстрого УМНОЖЕНИЯ чётных целых чисел на 2.

***

% (остаток) – э… вот это не совсем двоичный, но тоже полезный оператор. Возвращает остаток от деления.

Пример: 20 % 18 = 1 целая и 2 в остатке. Вот 2 и вернётся.

***

Угу… значит с ифами и его атрибутами ознакомились, теперь переползаем к разбору штуки под названием for

Данный счётчик нужен для многократного повторения одного и того же кода. Используется в основном, либо для посимвольного
анализа какой-либо команды, либо для посимвольной генерации команд.

Пример:

list a=["0","1","2","3","4","5","6","7","8","9"];	//Лист с цифрами.
string input_comand="Многабукав";	//Команда, которую надо побуквенно проанализировать.
string out;	//Переменная, в которую будит складироваться ответ.
integer out_num;	//Номер, показывающий сколько знаков из всего поступившего сообщения НЕ ЯВЛЯЮТСЯ ЦИФРАМИ.
integer i;	//Счётчик.

default
{
	state_entry()
	{
		for(i=0; i<llStringLength(input_comand); i++) //До тех пор, пока i равно нулю и i меньше общего числа знаков в
		//поступившей команде, плюс-плюсить переменную i.
		{
			if(llListFindList([llGetSubString(input_comand,i,i)],a)==-1) {out_num++;}
			//Дословный перевод – если поиск цифр в текущем символе команды не увенчался успехом, 
			//увеличить на 1 переменную out_num.
		}
		
		if(out_num!=i) {out="Переменная либо содержит и цифры и буквы, либо только цифры";} //Если количество букв меньше
		//общего количества знаков в сообщении, выдать соответствующий ответ.
		else{out="Переменная содержит только букавы";} //В любом другом случае (а он тута всего один получается),
		//выдать другой ответ.
		
		llOwnerSay(out); //Сопсна, сказать ответ.
	}
}


Как мы можем заметить, for у нас находится в state_entry, аналогичное действие можно было бы провернуть с помощью события
timer, но, вопервых, если бы нам таймер вдруг потребовался для какого-то другого блока кода, то пришлось бы либо выключать
и перезапускать таймер, либо гемороиться с внедрением ”параллельной” обработки нескольких разных блоков кода в одном событии
(а это, поверьте на слово ППЦ тот ещё), к тому же таймер срабатывает раз в определённый промежуток времени и имеет свои
Физические ограничения на минимальное срабатывание (главным образом в виду того, что, если скрипт работает на выдачу данных
на всеобщее обозрение, то при значении меньше 0.08-0.05 сек. из за не способности большинства интернет-каналов передавать
данные от клиента SL с такой скоростью, а так же из за мелких глюков в самом LSL, данные начнут теряться, а сам клиент будит
ощутимо тормозить).

Вобщем короче… представим себе такую ситуацию,- у нас нету for, и нам надо увеличить переменную i на 1,
ну, допустим, 200 раз – вопрос – сколько нам прийдётся ждать ответа, если таймер выставлен на 0.1, а результат нужен нам не
более, чем через 1.0-1.3 секунды? =О.о=

Воо! Доплюхало, теперь, зачем фор пригождается?

Для тех, кто совсем в танке, разжую – for нужен для сверхбыстрого многократного выполнения определённого блока кода, БЕЗ
Использования таймера и из любого события с сохранением и последующей (при необходимоcти) доработкой этих данных кодом,
Заключённым внутри этого события до закрытия самого события (впротивном случае прийдётся вызывать это событие повторно).

Ну и для наглядности два ИДЕНТИЧНЫХ примера обработки таких вычислений из под таймера и с помощью for:

//Наглядный пример скрипта, за который надо хорошо выпороть его создателя. 
//НЕЕЕЕ!!! ТОКА НЕ МЕНЯ!!! =Х_____х= Эт я так… утрированно сказал, на счёт выпороть…

integer trigger=0;	//Триггер, включающий срабатывание расчета.
integer i;	//Сопстна, счётчик.
integer d_i;	//Дублирующая переменная счётчика.

default
{
	state_entry()
	{
		llSetTimerEvent(0.01);		//Устанавливаем таймер на одну сотую секунды.
		llListen(0,"",llGetOwner(),"");	//Устанавливаем листен.
	}
	
	timer()
	{
		if(trigger==1)	//Если триггер равен 1.
		{
			if(i<200)	//Если i меньше 200.
			{
				i++; //Плюс-плюсить i.
				//Ну и заодно выполнить какой-нить код.
			}
			if(i>=200) //Если i больше или равно 200.
			{

			d_i=i;	//Дублировать значение переменной i в переменную d_i.
			trigger=0;	//обнулить триггер, поскольку срабатывание этого условия свидетельствует об окончании расчета.
			llMessageLinked(LINK_THIS,0,"i-"+(string)d_i,"");	//Поскольку листен периодчески отказывается приминать
			//команды из того же скрипта, в котором он находится передаём результат с добавочным индексом через
			//месседжЛинкед
			i=0;	//Обнуляем счётчик ввиду окончания расчета.

			//Обратите вминание на постановку переменных в этом условии – i обнуляется ПОСЛЕ присвоения его значения
			//переменной d_i, иначе в d_i уйдёт ноль! 

			}
		}
		else{}
	}
	
	listen(integer channel, string name, key id, string msg)
	//Листен нужен для того, чтобы юзер мог хоть как-то взаимодействовать со скриптом.
	//расчет начанает производиться при поступлении от овнера любой команды.
	{
			trigger=1;	//При любой команде (только от овнера) из чата, включать расчет переменной.
	}
	
	link_message(integer sender_number, integer number, string msg, key id)
	{
		if(trigger==0 && d_i==200)	//О! А вот тут, как можем заметить сам код АБСОЛЮТНО не использует входные
		//переменные события. 
		//Но… по идее… если он их не использует… то как он тогда может работать?
		//Легко и не принуждённо – это событие активируется каждый раз, как только в содержащий текущий скрипт прим
		//приползает сообщение, самому событию до лампочки использует содержащийся в этом событии код поступившие
		//данные или нет, событие просто активируется, выполняет код и закрывается.
		{
			llOwnerSay("i="+(string)d_i+". время затраченное на рассчёт i - "+(string)((float)d_i/50)+" сек.");
			//Сказать хозяину значение i и время его расчета.
			//А вот терь вминание! Заметили там такую хреновину - (string)((float)d_i/50)?
			//пчечатать отдельно дополнительную переменную с расчетом значения d_i/50, то я эт самое…
			//образовал цифровую область в string-овом пространстве, путём выполнения математических вычислений
			//в скобках, с последующим преобразованием результата выражения в string, а, эт самое… float перед d_i
			//стоит потому, шо по факту d_i это integer, а не дробь, а ответ будет какраз дробью.

			d_i=0;		//Обнулить d_i
		}
	}
}

//А теперь сравните со следующим скриптом и почешите тыкву, а нужен ли вам весь вышеприведённый гемор.


Пример с for:

integer i;	//Сопстна, счётчик.

default
{
	state_entry()
	{
		llListen(0,"",llGetOwner(),"");	//Устанавливаем листен.
	}

   listen(integer channel, string name, key id, string msg)
   {
	  for(i=0; i<200; i++)
	  {
		//А тут, во время каждого плюс-плюсания переменной i выполняется код.
	  }

	  //Как можем заметить сразу после расчёта for прямо в этом же событии говорится ответ.
	  //В предыдущем примере, мне пришлось триггерить этот ответ с помощью анального обхода из ллМесседжЛинкед.
	  llOwnerSay("i="+(string)i+". время затраченное на рассчёт i – ХЗ");
   }
}


Так, для чего нужен for посмотрели, едем дальше.

while эта штука нужна примерно для того же, для чего for она выполняет определённый код до тех пор, пока верно основное
выражение.

Пример:
integer a=1;
integer b=2;

default
{
	state_entry()
	{
		llSetTimerEvent(2.0);
	}

	timer()
	{
		while(a<b) //До тех пор, пока a меньше b говорить овнеру, что a меньше b.
		//Имейте в виду, что while будет ПОСТОЯННО выполняться, только в ПОВТОРЯЮЩИХСЦЯ событиях!
		//В не повторяющихся событиях навроде stste_entry срабатывание while-а произойдёт ьока 1 раз!
		{llOwnerSay(”a меньше b”);}
	}

	touch_start(integer x)
	{
		a=3;
	}
}


И такой вот примерчик:
integer a=1;

default
{
	state_entry()
	{
		llSetTimerEvent(2.0);
	}

	timer()
	{
	while(a)	//Во! Собственно, весь цимес примера в этой строчке, дословно переводится, как ”до тех пор, пока a равно TRUE”
	//Есть ещё обратный вариант - while(!a), что переводится, как ” до тех пор, пока a НЕ ТРУЪ”.
	//Естественно, такая компановка применима только в случае, если ”a” является булевым значением, тоезь либо TRUE, либо FALSE.
	{
		llOwnerSay(”a равно ТРУЪ”);
	}
   }

	touch_start(integer x)
	{
		a=FALSE;
	}
}
//Примечание: цикл while НЕ БУДЕТ запускаться, если переменная a ИЗНАЧАЛЬНО равна выполнению условия завершения.


Применяется while, в основном, как замена if-овым конструкциям вида if(a!=TRUE) и if(a==TRUE),
ну или полностью идентичным if(a==FALSE) и if(a!=FALSE), поскольку, если в скрипте много if-ов, то при анализе скрипта
довольно быстро можно запутаться.

Примечание: Цикл while запускается с довольно низким ”приоритетом”, то есть, в случае тормозов на симе, цикл будит самым медленно работающим куском кода.
Поэтому рекомендуется вместо них всё же if-ы юзать.

do{//Код} – Эта штука просто выполняет код. Идёт, как нашлёпка к while.

do{//Код} while(//Условие) – А вот это что-то типа ”условия наоборот” в сией конструкции, в отличии от if, СПЕРВА
выполняется код, а потом, только идёт проверка условия… честно говоря до меня не совсем доехало кому такое нужно и зачем,
но раз есть, значит надо по идее…

jump – Эта штука осуществляет переход от одного куска кода к другому, то есть это что-то типа аналога гиперссылки из HTML.

Сопстна, объясню, как правильно делать прыжок.

Для прыжка необходимо пометить начальную точку и конечную. Начальная помечается, как ”jump имя точки;”,
а конечная, как ”@имя точки;” (”@” перед именем и ”;” после ОБЯЗАТЕЛЬНЫ!!!)

Как это выглядит на практике:

integer a;
integer b;
integer c;
default
{
	state_entry()
	{
		a=1;
		jump end; //Прыгаем в обход кода.
		b=2;
		c=3;
		@end; 	//Продолжаем отсюда.
		llOwnerSay(”А равно ”+(string)a);
	}
}


Примечание: использование jump новяками в скриптинге НАСТОЯТЕЛЬНО НЕ РЕКОМЕНДУЕТСЯ!!!
Ввиду того, что с его помощью можно делать рекурсивные (вечные) циклы.

Сопстна, как определить, что вы не новичок – если вы можете написать скрипт АО с закрытыми глазами и на память помните,
зачем нужна любая функция языка LSL и каждый её атрибут, значит вы уже определённо не нуб.
(я лично такими знаниями похвастаться не могу, по этому обхожу джампы десятой дорогой).

Так, основные часто используемые операторы вроде бы изучили, давайте теперь посмотрим,
как создавать свои собственные функции.

Для начала, скажу, что функции бывают двух типов:

1 Исполняющие - типа llSetText, в них надо только забить параметры, всё остальное они сделают сами.
Используются в основном для выполнения каких-либо действий направленных ”наружу”, либо для промежуточных операций.

2 Преобразовывающие – навроде float llFrand(float rand), эти функции принимают значение (в скобках которое), преобразует,
проводя, через свой цыфроварительный тракт и выдаёт результат (float перед именем функции видим? Вот это основной признак преобразующей функции).

Начнём с создания исполняющих функций.

llMyFirstFunction(integer param)
{
	if(param==0){llOwnerSay(”Входной параметр функции равен нулю”);}
	else{llOwnerSay(”Входной параметр функции больше нуля”);}
}

default
{
	state_entry()
	{
		llMyFirstFunction(0);
	}
}


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

Также, как мы заметили, напечатанная нами функция находится перед дефолтным состоянием, что о чём говорит?
Угу, правельно, о том, что мы можем эту функцию вытащить целиком из этого скрипта и вставить в какой-нибудь другой,
или наклепать вагон универсальных функций и собирать из них скрипты, как из конструктора LEGO лишь чуууть-чуть
переписывая их при необходимости. Естественно, и при ”блочной” компоновке логику всёравно писать придётся (надо же чему-то
эти функции активировать), но не в таких количествах, как при написании монолитного кода.

Только, обращаю внимание на то, что функция будет работоспособной в любом скрипте, ТОЛЬКО, если все используемые ею переменные
содежатся В НЕЙ САМОЙ! Тоесть у универсальных функций не должно быть никаких ссылок на глобальные переменные текущего
скрипта, иначе если переменная, которую запрашивает функция после переноса в новый скрипт отсутствует, срипт,
само собой, не будет работать.

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



Так, исполняющие функции более-менее освоили, переходим к окучиванию преобразующих.

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

integer a=1;
integer b=3;

string llAplusB(integer a, integer b)	//Сопсна наша функция, обратите внимание – перед её названием стоит тип string
//возвращаемая переменная должна быть ТОГО ЖЕ ТИПА!!!
{
	integer pre_out=a+b;	//Объявляем переменную типа integer и в ней же рассчитываем результат.
	string out=(string)pre_out;	//Объявляем переменную типа string и сразу запихиваем туда преобразованниый pre_out.
	return out;	//Возвращаем ответ

	//Вышеприведённый код я дал сугубо для наглядности, а вообще всё это можно записать и покороче:
//return (string)(a+b);
//Звучит это, как ”вернуть только что созданную переменную типа string заключающую в себе сумму integer-ов a и b”.
}

default
{
	state_entry()
	{
		llOwnerSay(llAplusB(a,b));	//Ответ вернулся туда же, откуда была вызвана функция.
	}
}


Так же return может использоваться, как оператор принудительного завершения работы в исполняющих функциях.

Пример:
func_toucher(key avatar)
{
	if(avatar==llGetOwner()){llSay(0,”Халоу”);} //Если дотронулся обладатель скрипта, сказать ”Халоу”
	else {return;}	//Если нет завершить функцию.
}

default
{
	touch_start(integer x)
	{
		func_toucher(llDetectedKey(x));	//Сопсна сама функция.
	}
}


Это, то, что пока есть в наличии, извиняюсь если что-то не понятно написанно (если где нашли ошибку или не точность пишите в личку - исправлю).
Вышеприведённое является базисом НЕОБХОДИМЫМ для понимания скриптовой логики без чужой помощи.
Допиливаю урок (процесс допилки несколько затянулся в виду большого количества сложных скриптов, которые я хочу объяснить, но сперва мне их написать надо...), в виду того, что тут, на данный момент отсутствует объяснение сложной логики...
да и по простой не очень много пока имеетсо...

#2 Amaro

    Новичок на форуме

  • Пользователи
  • PipPip
  • 36 posts
  • Основной цех:Строители
  • Второй цех:Скриптеры
  • SL Status: 

Posted 20.01.10 - 03:54

jump - это очень, очень плохо. Лучше про него сразу забыть и никогда не вспоминать. Если только вместо break использовать, и то сомнительно.....

#3 Web

    Новичок на форуме

  • Пользователи
  • PipPip
  • 60 posts
  • Пол:М
  • Откуда:Харьков
  • Основной цех:Скриптеры
  • Второй цех:Строители
  • SL Status: 

Posted 03.03.10 - 08:50

Няааа :o

Как много понаписали-то! Дал же себе кто-то труд все то объяснить :P А то людики-то юзают обычно побитовое сравнение, и не задумываются обо всей этой побитовой мишуре - ну & и &, подумаешь =)) Спасяб ;)

View PostAmaro, on 20.1.2010, 2:54, said:

jump - это очень, очень плохо. Лучше про него сразу забыть и никогда не вспоминать. Если только вместо break использовать, и то сомнительно.....


А Баба Яга - против! Ну и что вам джампы сделали? Нечитаемый код, значить, да?) А вы знаете, сколько СЕКУНД времени иногда позволяют спасти джампы?)) К примеру, иф+джамп занимает немножечко меньше времени, чем цикл, и работает множечко быстрее. Попробуйте вместо while-а и почувствуйте разницу :D

Вообще, "красивое программирование", "читаемый код" - это не про LSL, потому что код-то скорее всего как раз не будет никто читать... Тут приходится выбирать - или пишешь "красиво", без меток, без тайпкастинга (который иногда тоже спасает секунду-две, ей-богу) - или пишешь как Damen Hax - работает, а вот как работает - хрен разбересся =)
Кодю на LSL. Гадаю по звёздам. Ловлю крабов в заливе. Дорого.

#4 Axon Dezno

    Участник

  • Писатели
  • PipPipPip
  • 210 posts
  • Пол:М
  • Основной цех:Строители
  • Второй цех:Художники
  • SL Status: 

Posted 03.03.10 - 15:23

Блин... зарезервирую-ка я пару-тройку каментов, для продолжения урока, тут если шо пока тока 1/5 где-то от того шо будит.

Насчёт джампов - для новяков (а урок именно на них рассчтан) юзание джампов без наличия в тыкаве головы полной логической схемы работы
скрипта чревато обшЫрным и множественным разрывопереломом моска с далеко и шЫроко разлетающимися последствиями...
X________________________________________________x

Хто хочет - ищет способ, хто не хочет - ищет причину.

#5 Axon Dezno

    Участник

  • Писатели
  • PipPipPip
  • 210 posts
  • Пол:М
  • Основной цех:Строители
  • Второй цех:Художники
  • SL Status: 

Posted 03.03.10 - 15:23

И второй резервный камент...
X________________________________________________x

Хто хочет - ищет способ, хто не хочет - ищет причину.

#6 Web

    Новичок на форуме

  • Пользователи
  • PipPip
  • 60 posts
  • Пол:М
  • Откуда:Харьков
  • Основной цех:Скриптеры
  • Второй цех:Строители
  • SL Status: 

Posted 05.03.10 - 00:09

View PostAxon Dezno, on 3.3.2010, 14:23, said:

Насчёт джампов - для новяков (а урок именно на них рассчтан) юзание джампов без наличия в тыкаве головы полной логической схемы работы
скрипта чревато обшЫрным и множественным разрывопереломом моска с далеко и шЫроко разлетающимися последствиями...


Да, это да =) Некоторые просто делают джампы в другой конец скрипта... Обычно "прыгают" в начало либо конец "логического блока". Как правило, на расстояние 1-3 строчек прыгают, не дальше :o
Кодю на LSL. Гадаю по звёздам. Ловлю крабов в заливе. Дорого.

#7 Amaro

    Новичок на форуме

  • Пользователи
  • PipPip
  • 36 posts
  • Основной цех:Строители
  • Второй цех:Скриптеры
  • SL Status: 

Posted 10.03.10 - 14:04

View PostWeb, on 5.3.2010, 0:09, said:

View PostAxon Dezno, on 3.3.2010, 14:23, said:

Насчёт джампов - для новяков (а урок именно на них рассчтан) юзание джампов без наличия в тыкаве головы полной логической схемы работы
скрипта чревато обшЫрным и множественным разрывопереломом моска с далеко и шЫроко разлетающимися последствиями...


Да, это да =) Некоторые просто делают джампы в другой конец скрипта... Обычно "прыгают" в начало либо конец "логического блока". Как правило, на расстояние 1-3 строчек прыгают, не дальше :o



А из стейта в стейт никто не пробовал? Вот блин прям интересно стало, чо получиЦЦо =)

#8 Web

    Новичок на форуме

  • Пользователи
  • PipPip
  • 60 posts
  • Пол:М
  • Откуда:Харьков
  • Основной цех:Скриптеры
  • Второй цех:Строители
  • SL Status: 

Posted 09.08.10 - 13:26

View PostAmaro, on 10.03.10 - 14:04, said:

А из стейта в стейт никто не пробовал? Вот блин прям интересно стало, чо получиЦЦо =)

Ничего не получиЦЦо ;-)
Кодю на LSL. Гадаю по звёздам. Ловлю крабов в заливе. Дорого.