Пятница, 24.01.2025, 11:39
| RSS
F.O.R.U.M.
Главная | Описание класса object_binder - Форум
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Описание класса object_binder
frontДата: Вторник, 13.10.2009, 16:31 | Сообщение # 1
Admin
Группа: Администраторы
Сообщений: 152
Репутация: 229
Статус: Оффлайн
Один из важнейших классов в скриптовой модели сталкера - это object_binder. Он используется для того, чтобы присоединять к клиентским объектам собственные обработчики событий.
Для того, чтобы использовать object_binder надо:
1. Создать на его основе свой класс. Теорбазис смотри в статье "Наследование от экспортированных классов".
2. В нужном месте с помощью функций set_callback указать методы класса в качестве функций-обработчиков событий.
3. Указать этот класс в свойстве script_binding в секции объекта в файле *.ltx
После создания объекта на основе этой секции будет автоматически создан объект, написанного Вами класса, а его методы начнут вызываться по указанным событиям. Получается, что наш объект как-бы присоединился к клиентскому. Отсюда и его название "биндер" от слова bind (англ. присоединять, привязывать)

Теперь подробности. Класс object_binder - это экспортированный класс CScriptBinderObject.
"Его (псевдо)описание:"
Код
class object_binder {
game_object* object;

object_binder(game_object*);

void save(net_packet*);
void update(number);
void reload(string);
void net_export(net_packet*);
bool net_save_relevant();
void load(reader*);
void net_destroy();
void reinit();
void net_Relcase(game_object*);
bool net_spawn(cse_alife_object*);
void net_import(net_packet*);
};
Прежде, чем разобраться с коллбеками, разберём методы класса. В каком-то смысле - они тоже обработчики событий, поскольку вызываются в определённые моменты жизни объекта.
object_binder(game_object*) - конструктор. Хоть и показан на манер C++ как метод, но, как и для многих других классов, вызывается как отдельная глобальная функция. Аргумент - клиентский объект, к которому будем биндиться.
"Несколько забегая вперёд опишу сейчас, как создавать биндер"
В каком-либо модуле, обычно в том-же, где и класс биндера, должна быть функция следующего вида:
Код
function init(obj)
local new_binder = my_binder(obj) -- создаём объект биндера
obj:bind_object(new_binder) -- присобачиваем его к объекту
endздесь my_binder - это новый класс биндера.
Теперь эту функцию надо указать в секции объекта. Допустим, модуль называется my_cool_binder.script. В этом случае в секции объекта будет такая запись:
script_binding = my_cool_binder.init
При создании объекта на основе этой секции будет вызвана эта функция, которая с помощью метода bind_object класа game_object присоединит наш биндер к свежесозданному объекту.


 
frontДата: Вторник, 13.10.2009, 16:31 | Сообщение # 2
Admin
Группа: Администраторы
Сообщений: 152
Репутация: 229
Статус: Оффлайн
object - свойство на чтение. Указывает на объект типа game_object, к которому присоединён биндер. Обращаться к свойству надо так: self.object
net_spawn(cse_alife_object*) - вызывается при переходе объекта в онлайн. Аргумент - серверный объект для того клиентского, к которому присоединились. Метод должен вернуть логическое значение. true, если успешно, false, если что-либо не срослось, и в этом случае объект не будет создан, а в логе появится запись: Failed to spawn entity '<имя секции>'
net_destroy() - аналогично вызывается при уходе в оффлайн, т.е. при удалении данного объекта.
update(delta) - очень важный метод. Для большинства объектов этот метод вызывается постоянно, причём период его срабатывания зависит от расстояния до актора. Минимальный период составлял около 40 мс при нахождении предмета в инвентаре ГГ (если он там конечно может находиться). При удалении объекта от ГГ период растёт линейно вплоть до расстояния в 200 метров. На таком расстоянии период составляет одну секунду. После этого расстояния остаётся равным примерно одной секунде. Аргумент этого метода - целое число, которое в точности равно числу миллисекунд, прошедших с прошлого вызова. Правда в точности оно равно только если меньше 1000. Если выше, то оно просто равно 1000, при том, что реальное время может несколько отличаться (может быть больше на пару десятков миллисекунд). Вероятно, это позволяет точно просчитывать физику движения (в том случае, если я решил управлять ею из скрипта). Судя по комментариям в скриптах для некоторых объектов метод update не вызывается.
load(reader*) - метод для загрузки состояния объекта
save(net_packet*) - метод для сохранения состояния объекта
Здесь есть некоторый непонятки. Точно известно, что вызываются эти методы не для всех объектов. Как минимум вызываются для монстров, сталкеров и ГГ. А вот для, скажем, гранаты вызываться не хочет (ни load, ни save). C парностью вызова этих методов судя по всему такая ситуация. save вызывается при сохранении игры (если вообще вызывается), а load вызывается при создании биндера (как описано выше) только в том случае, если до этого был сделан хоть один save. Ну или иными словами, если есть чего загружать. А есть чего загружать, если до этого что-то сохранили.
reload(section) - вызывается при создании объекта. Аргумент - имя секции, на основе которой создан объект.
reinit() - ещё один метод, который вызывается в самом начале. Аргументов нет. Обычно, именно в этом методе устанавливаются колбеки.
net_export(net_packet*) - назначение неизвестно. примеров использования нет
net_import(net_packet*) - назначение неизвестно. примеров использования нет
net_save_relevant() - назначение неизвестно. Если его определяют, то как возвращающий всегда true
net_Relcase(game_object*) - назначение неизвестно. Один пример использования в bind_smart_terrain.script

Кроме указанных методов есть и ещё два, которые есть в любом скриптовом классе на основе luabind, __init и __finalize
__finalize обычно не используется, но для полноты картины стоит упомянуть.
__init - вызывается при создании объекта и получает те-же аргументы, что и конструктор. Фактически - это и есть конструктор (см. теорбазис)
__finalize - вызывается при удалении объекта сборщиком мусора
Кроме того, поскольку мы пишем свой класс, то кроме указанных методов ничто не мешает добавить и свои. См. дальше про коллбеки. Также можно добавить и свои свойства. Это можно сделать так.
self.my_property = 1 -- добавили своё свойство
Надо только помнить, что если не сохранить это свойство в save, то оно пропадёт при уходе объекта в оффлайн. Что делать при этом с теми объектами, у которых save и/или load не вызывается, непонятно.


 
frontДата: Вторник, 13.10.2009, 16:31 | Сообщение # 3
Admin
Группа: Администраторы
Сообщений: 152
Репутация: 229
Статус: Оффлайн
"Жизненный цикл биндера примерно такой:"
1. Объект переходит в онлайн и вызывается функция init, прописанная в секции.
2. Там создаётся объект биндера, при этом вызывается конструктор __init
3. Биндер присоединяется к объекту и работа функции init заканчивается
4. Теперь пачкой вызываются методы биндера
reload
reinit
load - если вообще вызывается
netspawn
5. затем начинает вызываться update до тех пор, пока объект находится в пределах радиуса a-life.
6. при уходе объекта в оффлайн вызывается метод net_destroy (апдейты естественно до этого прекращаются)
7. наконец, несколько секунд спустя, объект удаляется окончательно сборщиком мусора. При этом вызывается __finalize

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

Теперь о коллбеках. Для того, чтобы с помощью функции set_callback и биндера сделать свой обработчик события надо:
1. Создать дополнительный метод в нашем классе биндера.
2. Указать этот метод как обработчик определённого события. Как правило, это делается в методе reinit.
Код
function my_binder:reinit()
object_binder.reinit(self)
self.object:set_callback(callback.use_object, self.use_callback, self)
end
function my_binder:use_callback(obj, who)
--здесь выполняю действия, которые надо сделать при использовании объекта (нажатии на нём клавиши 'F')
end
Подробно разбирать функцию set_callback не будем. Только важные сейчас моменты:
- первый аргумент - один из членов перечисления callback.
"Полный список констант из этого перечисления"
C++ class callback {
const action_animation = 20;
const action_movement = 18;
const action_object = 23;
const action_particle = 22;
const action_sound = 21;
const action_watch = 19;
const actor_sleep = 24;
const article_info = 12;
const death = 8;
const helicopter_on_hit = 26;
const helicopter_on_point = 25;
const hit = 16;
const inventory_info = 11;
const inventory_pda = 10;
const level_border_enter = 7;
const level_border_exit = 6;
const map_location_added = 14;
const on_item_drop = 28;
const on_item_take = 27;
const patrol_path_in_point = 9;
const script_animation = 29;
const sound = 17;
const take_item_from_box = 33;
const task_state = 13;
const trade_perform_operation = 3;
const trade_sell_buy_item = 2;
const trade_start = 0;
const trade_stop = 1;
const trader_global_anim_request = 30;
const trader_head_anim_request = 31;
const trader_sound_end = 32;
const use_object = 15;
const zone_enter = 4;
const zone_exit = 5;
};
В общем видно, что можно сделать. Даже иногда понятно, для какого объекта применимо. Надо заметить, что для конкретного типа объектов будут работать только конкретный набор коллбеков. Например, точно известно, что для неписей работает death и hit. Для ГГ работает death, on_item_drop и on_item_take. Для ящиков - use_object. А вот для гранат похоже ничего не работает.
Списка работающих коллбеков для каждого типа объектов пока ещё никто не составил.


 
frontДата: Вторник, 13.10.2009, 16:31 | Сообщение # 4
Admin
Группа: Администраторы
Сообщений: 152
Репутация: 229
Статус: Оффлайн
Теперь простой пример, который я сделал для ящика
"Содержимое файла my_cool_binder.script"
Код
function init(obj)
local new_binder = my_binder(obj)
obj:bind_object(new_binder)
end

class "my_binder" (object_binder)
function my_binder:__init(obj) super(obj)
get_console():execute("my_binder:__init")
end

function my_binder:reload(section)
get_console():execute("my_binder:reload")
object_binder.reload(self, section)
end

function my_binder:reinit()
get_console():execute("my_binder:reinit")
object_binder.reinit(self)
self.object:set_callback(callback.use_object, self.use_callback, self)
end

function my_binder:update(delta)
local actor_pos = db.actor:position()
local obj_pos = self.object:position()
local dist = actor_pos:distance_to(obj_pos)
get_console():execute("my_binder:update_dist="..dist.."_delta="..delta)
object_binder.update(self, delta)
end

function my_binder:net_spawn(data)
get_console():execute("my_binder:net_spawn")
return object_binder.net_spawn(self, data)
end

function my_binder:net_destroy()
get_console():execute("my_binder:net_destroy")
object_binder.net_destroy(self)
end

function my_binder:net_save_relevant()
get_console():execute("my_binder:net_save_relevant")
return true
end

function my_binder:save(packet)
get_console():execute("my_binder:save")
object_binder.save(self, packet)
end

function my_binder:load(reader)
get_console():execute("my_binder:load")
object_binder.load(self, reader)
end

function my_binder:use_callback(obj, who)
get_console():execute("my_binder:use_callback")
end

кроме того в файле gamedata\config\misc\devices.ltx
добавил новую секцию
Код
[inventory_box_my]:inventory_box
script_binding = my_cool_binder.init
это будет ящик с визуалом ящика с динамитом с одним только отличием от стандартного - у него будет нестандартный наш биндер.


 
  • Страница 1 из 1
  • 1
Поиск:

Для добавления необходима авторизация

Copyright Front Сайт оптимизирован под браузер Opera © 2025