000155. Использование SCM-функций (опкод 0AB1)

Использование SCM-функций (опкод 0AB1)|Что же такое SCM-функция? На деле это выглядит так, словно мы вызываем опкод, который создали сами и, он выполняет определённые действия. С одной стороны, он похож на стандартный gosub, но принцип работы у SCM-функции другой - все переменные сохраняют свои значения, несмотря на то, что в функции используются те же переменные ( имеются ввиду локальные переменные )|BoPoH|BoPoH||||Основное назначение SCM-функций - укорачивание кода скрипта и улучшение его читабельности, если в нём используются одни и те же конструкции во многих местах. За вызов SCM-функции отвечает опкод 0AB1. Этот опкод добавляется библиотекой CLEO, поэтому если у вас её нет - опкод работать не будет. Давайте рассмотрим подробнее его параметры.

0AB1: call_scm_func @function num_params 1 10

Здесь: @function - это метка, указывающая на код нашей SCM-функции 1 - это количество параметров, которые мы передаём в функцию ( не более 32 ). Эти параметры используются функцией для выполнения определённых задач, связанных с этими параметрами. К примеру, если функция будет помещать актёра в определённую точку, то в неё мы будем передавать хендл актёра. 10 ( и т.п. ) - это те параметры, которые будут передаваться в функцию ( не более 32 ). Их количество должно совпадать со вторым параметром. Т.е. если вы вторым параметром указали число 3, значит после него вы должны указать 3 параметра, которые передадутся в функцию.

Например:

0AB1: call_scm_func @function num_params 4 $PLAYER_ACTOR 1@ 2@ 3@

Оставшиеся параметры ( переменные ) будут получать возвращаемые функцией данные. SCM-функция может возвращать одно и более значений при помощи опкода 0AB2. Если функция не возвращает параметров, то никаких переменных в конце опкода писать не нужно. Попробуем разобрать пример, приведённый ниже:

model.Load(#ELEGY)
038B: load_requested_models

while true
wait 0
 if
 0AB0: key_pressed 48
 then
 0AB1: call_scm_func @SpawnCarBeforePlayer 5 $PLAYER_ACTOR 0.0 5.0 0.0 #ELEGY 0@
 car.SetImmunities(0@, 1, 1, 1, 1, 1)
 while 0AB0: key_pressed 48
 wait 0
 end
 end
end

:SpawnCarBeforePlayer
04C4: store_coords_to 10@ 11@ 12@ from_actor 0@ with_offset 1@ 2@ 3@
13@ = car.Create(4@, 10@, 11@, 12@)
0AB2: ret 1 13@

По нажатию клавиши 0`, перед игроком заспаунится элегия и ей присвоятся иммунитеты. Рассмотрим вызов функции опкодом 0AB1. Здесь у нас 5 параметров, которые передаются в функцию, и одно возвращаемое значение - хендл созданной машины.

Сама SCM-функция находится за пределами основного цикла скрипта. Исполнение скрипта не должно дойти до неё иным способом, кроме как через опкод 0AB1. Проще говоря, SCM-функции лучше всего оставлять в конце скрипта, а не в середине и уж тем более, не в начале.

Исходя из того, что компилятор спокойно воспринимает изменения описаний опкодов, мы можем спокойно изменить внешний вид опкода 0AB1 под свои нужды. К примеру, так:

0AB1: @SpawnCarBeforePlayer 5 actor $PLAYER_ACTOR offset_XYZ 0.0 5.0 0.0 car_model #ELEGY get_handle_to 0@

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

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

Теперь разберём саму функцию. Параметры, которые передаются опкодом 0AB1 в функцию, записываются последовательно в локальные переменные функции, начиная с 0@. В нашем случае, в 0@ запишется хендл актёра, в 1@, 2@ и 3@ - смещение от актёра, а в 4@ - модель машины.

Заканчивается работа функции опкодом 0AB2.

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

SCM-функции могут быть вложенными, как и gosub-ы - можно использовать SCM-функции внутри других SCM-функций.

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

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

У SCM-функций есть одна особенность - если передавать в неё строковые переменные ( 13@v, например ), то в соответствующую локальную переменную функции запишется указатель на строковую переменную ( т.е. на переменную 13@ в данном примере). Для тех, кто не разбирается в работе с памятью, могу объяснить проще - строковые параметры в обычном виде в функцию передать нельзя. Если вы хотите передать строку, то нужно передавать её в виде 4 последовательных переменных, начиная с той, в которую записана строка. Например:

10@v = "Function"
0AB1: @AddGXT 4 10@ 11@ 12@ 13@

А в функции уже можно использовать её как строковую переменную:

:AddGXT
0ADF: add_dynamic_GXT_entry "MYTBL" text 0@v
0AB2: ret 0

Я думаю, на этом закончим урок. Я постарался раскрыть все известные мне детали, хотя мог что-нибудь и упустить. Если есть вопросы или вы хотели бы видеть урок по теме, которая вас интересует - пишите в комментариях.|1983|315|0||ispolzovanie_scm_funkcij_opkod_0ab1|1504597275

Last updated