Программирование поведения объекта — различия между версиями
(dWcIZhbbEhCadnQog) |
Raw mat (обсуждение | вклад) |
||
(не показаны 14 промежуточных версий 1 участника) | |||
Строка 1: | Строка 1: | ||
− | + | Перейдем от теории к практике. Перед прочтением этого документа рекомендуется ознакомиться с предыдущими. Здесь приведены конкретные рецепты - как и что делать, с примерами и их разбором. | |
+ | |||
+ | == Где хранить файлы? == | ||
+ | |||
+ | Ядро Диптауна использует DISS для хранения файлов. По-умолчанию, в корень DISS монтируется директория, которая находится в подкаталоге storage архива media. Таким образом, | ||
+ | * в linux это директория, которая расположена в $DEEPTOWN_MEDIA/storage | ||
+ | * в windows - INSTALL_DIR/Media/storage | ||
+ | |||
+ | DISS при первом запуске индексирует рекурсивно все файлы своего каталога (это необходимо для выполнения сложных запросов к файловой системе). Поэтому, если Вы добавляете или удаляете какие-либо файлы где-либо в пределах этой директории, нужно удалить файл '''_index''' в ее корне, чтобы при следующем запуске DISS переиндексировал каталог заново, и "увидел" бы внесенные изменения. | ||
+ | |||
+ | Далее в документе, везде, где указан путь к файлу, имеется ввиду DISS-путь. | ||
+ | |||
+ | Для хранения объектов, в корне DISS есть подкаталог media, в котором есть следующие подкаталоги: | ||
+ | * materials - для хранения файлов материалов; | ||
+ | * meshes - для графических 3d моделей; | ||
+ | * models - для файлов объектов; | ||
+ | * scenes - для файлов сцен; | ||
+ | * scripts - для файлов скриптов; | ||
+ | * textures - для текстур. | ||
+ | |||
+ | Отметим, что это лишь рекомендованные пути; Вы можете использовать любые другие в пределах DISS. | ||
+ | |||
+ | == Создание простого объекта == | ||
+ | |||
+ | Для того, чтобы создать объект, нужны | ||
+ | * файл материала; | ||
+ | * файл 3d модели в формате OGRE mesh; | ||
+ | * необходимые материалу текстуры. | ||
+ | |||
+ | Все это можно получить, воспользовавшись одним из конверторов, доступных на [http://www.ogre3d.org/index.php?option=com_content&task=view&id=413&Itemid=133 официальном сайте OGRE]. | ||
+ | |||
+ | Пожалуйста, обратите внимание, что все размеры в файле 3d модели должны указываться '''в метрах'''. | ||
+ | |||
+ | Если конвертер создает несколько файлов материалов, нужно объединить их в один (в произвольном порядке). Кроме того, нужно '''переправить пути к текстурам''' в файлах материалов на абсолютные DISS-пути (т.е. '''/media/textures/...'''). | ||
+ | |||
+ | Когда все это сделано, остается последний шаг - создать файл модели. Для этого нужно создать XML-файл объекта, который, в простейшем случае, выглядит следующим образом: | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <model bxd="http://dao.deeptown.org/bxd/model.bxd"> | ||
+ | <material_script node="путь к файлу материала" /> | ||
+ | <body name="main_body"> | ||
+ | <geometry> | ||
+ | <!-- описание геометрии объекта --> | ||
+ | </geometry> | ||
+ | <surfaces> | ||
+ | <mesh name="cube" node="путь к файлу 3d модели" /> | ||
+ | </surfaces> | ||
+ | </body> | ||
+ | </model> | ||
+ | </source> | ||
+ | |||
+ | Чтобы теперь сконвертировать этот файл в bxl, воспользуйтесь командой | ||
+ | bxd filename.model.xml -f filename.model | ||
+ | |||
+ | где filename.model.xml - имя исходного файла, filename.model - имя получаемого файла модели. | ||
+ | |||
+ | Для того, чтобы использовать объект локально, необходимо задать его UID, записав его в метаинформацию DISS. В той же директории, где находится этот файл, создайте файл '''.meta''' и напишите туда следующее: | ||
+ | [filename.model] | ||
+ | version = 0.1 | ||
+ | wso_node = 1 | ||
+ | wso_uid = 0101-0001-00000001-0000000000000001 | ||
+ | |||
+ | UID (последняя строчка) должен различаться для каждой копии объекта в пространстве. Если один и тот же объект нужно использовать в сцене несколько раз, можно задать ему несколько UID-ов в метаинформации - задав несколько записей wso_uid. | ||
+ | |||
+ | В самом UID-е можно менять только последний (самый длинный) блок цифр (это шестнадцатиричные цифры, т.е. от 0 до F). | ||
+ | |||
+ | Если в директории находится несколько объектов, дописывайте в .meta секции одну за другой. Файл .meta сканируется при создании индекса DISS, так что при внесении изменений нужно удалять '''.index'''. | ||
+ | |||
+ | === Создание динамического объекта === | ||
+ | |||
+ | Объект, приведенный в примере выше, будет статичным, т.е. все время находиться в одном и том же месте пространства (обладать бесконечной массой). Если нужен динамический объект, следует добавить в описание модели информацию о массе и моменте инерции объекта: | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <body name="main_body"> | ||
+ | <mass_data> | ||
+ | <mass value="1" /> | ||
+ | <inertia i11="1" i22="1" i33="1" i12="0" i13="0" i23="0" /> | ||
+ | </mass_data> | ||
+ | <!-- ... --> | ||
+ | </body> | ||
+ | </source> | ||
+ | |||
+ | Масса указывается в килограммах. | ||
+ | |||
+ | Момент инерции - в единицах СИ. Параметры - элементы симметричной матрицы инерции: | ||
+ | | i11 i12 i13 | | ||
+ | | i12 i22 i23 | | ||
+ | | i13 i23 i33 | | ||
+ | |||
+ | Вы можете не указывать тег '''inertia''' для простых тестов, но в этом случае объект будет вести себя неестественно. | ||
+ | |||
+ | == Связывание объекта со скриптом == | ||
+ | |||
+ | Для того, чтобы получить возможность программировать поведение объекта, нужно прописать в него адрес скрипта. Добавьте внутрь корневого XML-тега следующее: | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <externals> | ||
+ | <event_receiver url="gide:/media/scripts/my_object.gbc!MyObject" /> | ||
+ | </externals> | ||
+ | </source> | ||
+ | |||
+ | В этом случае объект будет передавать все свои события объекту гайд-класса MyObject, объявленного в файле /media/scripts/my_object.gbc. | ||
+ | |||
+ | В этом файле должен находиться '''скомпилированный''' код класса. Простейший, ничего не делающий код такого класса на [[K++]] выглядит следующим образом: | ||
+ | |||
+ | <source lang="kpp"> | ||
+ | package my_object; | ||
+ | |||
+ | import world; | ||
+ | |||
+ | class MyObject extends WorldObject | ||
+ | { | ||
+ | export function State_init() | ||
+ | { | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Напишите этот код в файл my_object.kpp в той же директории. Компилируется он командой | ||
+ | kpp my_object.kpp | ||
+ | |||
+ | == Добавление мотора == | ||
+ | |||
+ | Мотор - это интерфейс объекта, позволяющий контролировать его поведение. | ||
+ | |||
+ | Все моторы объекта должны быть описаны в его файле модели. Делается это добавлением тега '''motor''' в корневую секцию: | ||
+ | <source lang="xml"> | ||
+ | <motor name="engine" url="linear_speed:main_body:(0,1,0)" /> | ||
+ | </source> | ||
+ | |||
+ | Такой тег создаст мотор '''engine''' с типом '''linear_speed''', прикладывающий силу к телу '''main_body''' (параметр '''name''' в теге '''body''') по вектору направления (0,1,0) - т.е. вверх. | ||
+ | |||
+ | Всевозможные типы моторов описаны (''еще нигде не описаны, дать ссылку''). | ||
+ | |||
+ | По-умолчанию, мотор будет выключен. Для того, чтобы его включить, добавим в наш код следующее: | ||
+ | |||
+ | <source lang="kpp"> | ||
+ | export function State_init() | ||
+ | { | ||
+ | var engine = this.getMotor('engine'); // получили интерфейс мотора | ||
+ | // первый параметр - имя, указанное при создании мотора (см. выше) | ||
+ | engine.setParam('speed', 100); // максимальная скорость, сообщаемая объекту | ||
+ | engine.setParam('force', 100); // максимальная сила, которая будет приложена для разгона | ||
+ | engine.enable(); // включаем мотор | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Обработка пользовательского ввода == | ||
+ | |||
+ | Теперь научим наш объект реагировать на команду пользователя. | ||
+ | |||
+ | Например, нам нужно, чтобы мотор был включен только пока нажата клавиша A на клавиатуре. | ||
+ | |||
+ | Для этого нужно научить наш объект понимать две команды - "включить" и "выключить", а в конфигурации движка ввода написать отправку команды "включить" на нажатие клавиши, а "выключить" - на ее отпускание. | ||
+ | |||
+ | В файл '''/etc/world/input.conf''' в контекст '''local_control''' нужно добавить следующие строки: | ||
+ | on key_press check(key=A) do send_event:start_engine | ||
+ | on key_release check(key=A) do send_event:stop_engine | ||
+ | |||
+ | Таким образом, когда пользователь нажимает клавишу A, нашему объекту будет отправлено уведомление '''start_engine''', а когда отпускает - '''stop_engine'''. | ||
+ | |||
+ | Для того, чтобы обработать эти уведомления, напишем следующие два метода в наш класс: | ||
+ | |||
+ | <source lang="kpp"> | ||
+ | export function Control_start_engine(const bytea args) | ||
+ | { | ||
+ | this.getMotor('engine').enable(); | ||
+ | } | ||
+ | |||
+ | export function Control_stop_engine(const bytea args) | ||
+ | { | ||
+ | this.getMotor('engine').disable(); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | (предполагается, что параметры мотора были установлены при инициализации, т.е. в State_init). | ||
+ | |||
+ | == Создание сенсора == | ||
+ | |||
+ | Предположим, нам нужно, чтобы объект как-то реагировал на происходящее в окружающей среде. Для этого существуют '''сенсоры''' - интерфейсы, передающие информацию о происходящем. | ||
+ | |||
+ | На данный момент реализован только сенсор высоты, покажем работу с сенсорами на его примере: | ||
+ | |||
+ | <source lang="kpp"> | ||
+ | export function State_init() | ||
+ | { | ||
+ | // создаем сенсор высоты | ||
+ | this.createSensor('height:>100', 'too_high'); | ||
+ | } | ||
+ | |||
+ | export function Signal_too_high(const bytea args) | ||
+ | { | ||
+ | // метод будет вызван при срабатывании сенсора, т.е. когда абсолютная | ||
+ | // высота объекта превысит 100 метров. | ||
+ | this.getMotor('engine').disable(); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Имя функции-обработчика формируется из префикса '''Signal_''' и второго параметра функции '''createSensor'''. | ||
+ | |||
+ | == Создание сцены == | ||
+ | |||
+ | Для того, чтобы теперь посмотреть на созданные объекты, нужно создать сцену и разместить на ней эти объекты, и затем загрузить сцену в движок. | ||
+ | |||
+ | Сцена также описывается в формате BXL. XML-файл сцены выглядит довольно просто: | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <scene bxd="http://dao.deeptown.org/bxd/scene.bxd"> | ||
+ | <model uid="0101-0001-00000001-0000000000000002" | ||
+ | x="0.2" y="0" z="-5" | ||
+ | qw="1" qx="0" qy="0" qz="0" /> | ||
+ | </scene> | ||
+ | </source> | ||
+ | |||
+ | Тегов '''model''' может быть сколь угодно много. В параметрах этого тега указываются координаты объекта на сцене и кватернион его поворота. | ||
+ | |||
+ | == Пуск! == | ||
+ | |||
+ | И теперь остался последний момент - загрузить все это в движок. | ||
+ | |||
+ | Для этого нужно набрать консольную команду | ||
+ | world.local_start /media/scenes/filename.scene UID | ||
+ | |||
+ | Первый параметр - имя файла сцены, второй - UID объекта, которому будет передаваться ввод пользователя. |
Текущая версия на 15:12, 13 июля 2013
Перейдем от теории к практике. Перед прочтением этого документа рекомендуется ознакомиться с предыдущими. Здесь приведены конкретные рецепты - как и что делать, с примерами и их разбором.
Содержание |
[править] Где хранить файлы?
Ядро Диптауна использует DISS для хранения файлов. По-умолчанию, в корень DISS монтируется директория, которая находится в подкаталоге storage архива media. Таким образом,
- в linux это директория, которая расположена в $DEEPTOWN_MEDIA/storage
- в windows - INSTALL_DIR/Media/storage
DISS при первом запуске индексирует рекурсивно все файлы своего каталога (это необходимо для выполнения сложных запросов к файловой системе). Поэтому, если Вы добавляете или удаляете какие-либо файлы где-либо в пределах этой директории, нужно удалить файл _index в ее корне, чтобы при следующем запуске DISS переиндексировал каталог заново, и "увидел" бы внесенные изменения.
Далее в документе, везде, где указан путь к файлу, имеется ввиду DISS-путь.
Для хранения объектов, в корне DISS есть подкаталог media, в котором есть следующие подкаталоги:
- materials - для хранения файлов материалов;
- meshes - для графических 3d моделей;
- models - для файлов объектов;
- scenes - для файлов сцен;
- scripts - для файлов скриптов;
- textures - для текстур.
Отметим, что это лишь рекомендованные пути; Вы можете использовать любые другие в пределах DISS.
[править] Создание простого объекта
Для того, чтобы создать объект, нужны
- файл материала;
- файл 3d модели в формате OGRE mesh;
- необходимые материалу текстуры.
Все это можно получить, воспользовавшись одним из конверторов, доступных на официальном сайте OGRE.
Пожалуйста, обратите внимание, что все размеры в файле 3d модели должны указываться в метрах.
Если конвертер создает несколько файлов материалов, нужно объединить их в один (в произвольном порядке). Кроме того, нужно переправить пути к текстурам в файлах материалов на абсолютные DISS-пути (т.е. /media/textures/...).
Когда все это сделано, остается последний шаг - создать файл модели. Для этого нужно создать XML-файл объекта, который, в простейшем случае, выглядит следующим образом:
<source lang="xml"> <model bxd="http://dao.deeptown.org/bxd/model.bxd">
<material_script node="путь к файлу материала" /> <body name="main_body"> <geometry> </geometry> <surfaces> <mesh name="cube" node="путь к файлу 3d модели" /> </surfaces> </body>
</model> </source>
Чтобы теперь сконвертировать этот файл в bxl, воспользуйтесь командой
bxd filename.model.xml -f filename.model
где filename.model.xml - имя исходного файла, filename.model - имя получаемого файла модели.
Для того, чтобы использовать объект локально, необходимо задать его UID, записав его в метаинформацию DISS. В той же директории, где находится этот файл, создайте файл .meta и напишите туда следующее:
[filename.model] version = 0.1 wso_node = 1 wso_uid = 0101-0001-00000001-0000000000000001
UID (последняя строчка) должен различаться для каждой копии объекта в пространстве. Если один и тот же объект нужно использовать в сцене несколько раз, можно задать ему несколько UID-ов в метаинформации - задав несколько записей wso_uid.
В самом UID-е можно менять только последний (самый длинный) блок цифр (это шестнадцатиричные цифры, т.е. от 0 до F).
Если в директории находится несколько объектов, дописывайте в .meta секции одну за другой. Файл .meta сканируется при создании индекса DISS, так что при внесении изменений нужно удалять .index.
[править] Создание динамического объекта
Объект, приведенный в примере выше, будет статичным, т.е. все время находиться в одном и том же месте пространства (обладать бесконечной массой). Если нужен динамический объект, следует добавить в описание модели информацию о массе и моменте инерции объекта:
<source lang="xml"> <body name="main_body">
<mass_data> <mass value="1" /> <inertia i11="1" i22="1" i33="1" i12="0" i13="0" i23="0" /> </mass_data>
</body> </source>
Масса указывается в килограммах.
Момент инерции - в единицах СИ. Параметры - элементы симметричной матрицы инерции:
| i11 i12 i13 | | i12 i22 i23 | | i13 i23 i33 |
Вы можете не указывать тег inertia для простых тестов, но в этом случае объект будет вести себя неестественно.
[править] Связывание объекта со скриптом
Для того, чтобы получить возможность программировать поведение объекта, нужно прописать в него адрес скрипта. Добавьте внутрь корневого XML-тега следующее:
<source lang="xml"> <externals>
<event_receiver url="gide:/media/scripts/my_object.gbc!MyObject" />
</externals> </source>
В этом случае объект будет передавать все свои события объекту гайд-класса MyObject, объявленного в файле /media/scripts/my_object.gbc.
В этом файле должен находиться скомпилированный код класса. Простейший, ничего не делающий код такого класса на K++ выглядит следующим образом:
<source lang="kpp"> package my_object;
import world;
class MyObject extends WorldObject {
export function State_init() { }
} </source>
Напишите этот код в файл my_object.kpp в той же директории. Компилируется он командой
kpp my_object.kpp
[править] Добавление мотора
Мотор - это интерфейс объекта, позволяющий контролировать его поведение.
Все моторы объекта должны быть описаны в его файле модели. Делается это добавлением тега motor в корневую секцию: <source lang="xml"> <motor name="engine" url="linear_speed:main_body:(0,1,0)" /> </source>
Такой тег создаст мотор engine с типом linear_speed, прикладывающий силу к телу main_body (параметр name в теге body) по вектору направления (0,1,0) - т.е. вверх.
Всевозможные типы моторов описаны (еще нигде не описаны, дать ссылку).
По-умолчанию, мотор будет выключен. Для того, чтобы его включить, добавим в наш код следующее:
<source lang="kpp"> export function State_init() {
var engine = this.getMotor('engine'); // получили интерфейс мотора // первый параметр - имя, указанное при создании мотора (см. выше) engine.setParam('speed', 100); // максимальная скорость, сообщаемая объекту engine.setParam('force', 100); // максимальная сила, которая будет приложена для разгона engine.enable(); // включаем мотор
} </source>
[править] Обработка пользовательского ввода
Теперь научим наш объект реагировать на команду пользователя.
Например, нам нужно, чтобы мотор был включен только пока нажата клавиша A на клавиатуре.
Для этого нужно научить наш объект понимать две команды - "включить" и "выключить", а в конфигурации движка ввода написать отправку команды "включить" на нажатие клавиши, а "выключить" - на ее отпускание.
В файл /etc/world/input.conf в контекст local_control нужно добавить следующие строки:
on key_press check(key=A) do send_event:start_engine on key_release check(key=A) do send_event:stop_engine
Таким образом, когда пользователь нажимает клавишу A, нашему объекту будет отправлено уведомление start_engine, а когда отпускает - stop_engine.
Для того, чтобы обработать эти уведомления, напишем следующие два метода в наш класс:
<source lang="kpp"> export function Control_start_engine(const bytea args) {
this.getMotor('engine').enable();
}
export function Control_stop_engine(const bytea args) {
this.getMotor('engine').disable();
} </source>
(предполагается, что параметры мотора были установлены при инициализации, т.е. в State_init).
[править] Создание сенсора
Предположим, нам нужно, чтобы объект как-то реагировал на происходящее в окружающей среде. Для этого существуют сенсоры - интерфейсы, передающие информацию о происходящем.
На данный момент реализован только сенсор высоты, покажем работу с сенсорами на его примере:
<source lang="kpp"> export function State_init() {
// создаем сенсор высоты this.createSensor('height:>100', 'too_high');
}
export function Signal_too_high(const bytea args) {
// метод будет вызван при срабатывании сенсора, т.е. когда абсолютная // высота объекта превысит 100 метров. this.getMotor('engine').disable();
} </source>
Имя функции-обработчика формируется из префикса Signal_ и второго параметра функции createSensor.
[править] Создание сцены
Для того, чтобы теперь посмотреть на созданные объекты, нужно создать сцену и разместить на ней эти объекты, и затем загрузить сцену в движок.
Сцена также описывается в формате BXL. XML-файл сцены выглядит довольно просто:
<source lang="xml"> <scene bxd="http://dao.deeptown.org/bxd/scene.bxd">
<model uid="0101-0001-00000001-0000000000000002" x="0.2" y="0" z="-5" qw="1" qx="0" qy="0" qz="0" />
</scene> </source>
Тегов model может быть сколь угодно много. В параметрах этого тега указываются координаты объекта на сцене и кватернион его поворота.
[править] Пуск!
И теперь остался последний момент - загрузить все это в движок.
Для этого нужно набрать консольную команду
world.local_start /media/scenes/filename.scene UID
Первый параметр - имя файла сцены, второй - UID объекта, которому будет передаваться ввод пользователя.