

= jMonkeyEngine 3 Урок (2) - Знакомство с узлами

Предыдущая: <<документация/jme3_ru/начальная/знакомство_с_простым_приложением#,Знакомство с простым приложением>>,
Следующая: <<документация/jme3_ru/начальная/знакомство_с_ресурсами#,Знакомство с ресурсами>>, 
Английская версия: <<jme3/beginner/hello_node#,Hello Node>>


В этом учебном куре мы рассмотрим создание трехмерной сцены.


*  Это руководство предполагает что вы знаете что такое <<документация/jme3_ru/граф_сцены_и_другая_терминология_jme3#,Граф сцены>>.
*  Для ознакомления, посмотрите <<документация/jme3_ru/scene_graph_для_чайников#,Scene Graph для чайников>>.

При создании 3D игры


.  Вы создаете сцену для объектов, как игроки, здания, и т.д.
.  Вы добавляете объекты на сцену.
.  Вы двигаете, масштабируете, вращаете, раскрашиваете и оживляете их.

Вы узнаете, что граф сцены представляет собой 3D-мир, и почему корневой узел(rootNode) имеет важное значение. Вы узнаете, как создавать простые объекты, что бы они содержали пользовательские данные (например, очков здоровья), и как «преобразовать» их перемещение, масштабирование и вращение. Вы поймете разницу между этими двумя типами “Spatials в графе сцены: Узлы и геометрии.



== Пример кода

[source,java]

----
package jme3test.helloworld;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/** Пример 2 - Как использовать узлы в качестве ручки для манипулирования объектами в сцене.
  * Вы можете вращать, двигать и масштабировать объекты, манипулируя их родительскими узлами.
  * Корневой узел является особенным: Только то, что крепится к корневому узлу появляется на сцене.*/
public class HelloNode extends SimpleApplication {

    public static void main(String[] args){
        HelloNode app = new HelloNode();
        app.start();
    }

    @Override
    public void simpleInitApp() {

        /** создание синего куба на координатах (1,-1,1) */
        Box box1 = new Box(1,1,1);
        Geometry blue = new Geometry("Box", box1);
        blue.setLocalTranslation(new Vector3f(1,-1,1));
        Material mat1 = new Material(assetManager, 
                "Common/MatDefs/Misc/Unshaded.j3md");
        mat1.setColor("Color", ColorRGBA.Blue);
        blue.setMaterial(mat1);

        /** создание красного куба прямо над синим в точке (1,3,1) */
        Box box2 = new Box(1,1,1);      
        Geometry red = new Geometry("Box", box2);
        red.setLocalTranslation(new Vector3f(1,3,1));
        Material mat2 = new Material(assetManager, 
                "Common/MatDefs/Misc/Unshaded.j3md");
        mat2.setColor("Color", ColorRGBA.Red);
        red.setMaterial(mat2);

        /** Создание сводного узла в точке (0,0,0) и привязка его к корневому узлу */
        Node pivot = new Node("pivot");
        rootNode.attachChild(pivot); // размещение узла на сцене

        /** Прикрепление кубов к *свободному* узлу. (И вместе с ним к корневому)*/
        pivot.attachChild(blue);
        pivot.attachChild(red);
        /** Поворот свободного узла: учтите, что это повернет оба куба! */
        pivot.rotate(.4f,.4f,0f);
    }
}
----

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



== Терминология

В этом руководстве вы узнали новые термины:

[cols="2", options="header"]
|===

a|Что вы хотите сделать
a|Как сказать это в JME3 терминологии

a|Разместить 3D сцену
a|Заполнить граф сцены

a|Создание объектов сцены
a|Создание Spatials (например Geometry)

a|Сделать чтобы объект появился на сцене
a|Прикрепить объект к корневому узлу (rootNode)

a|Убрать объект со сцены
a|Открепить объект от корневого узла

a|Разместить, повернуть или изменить размер объекта
a|Преобразовать объект

|===

У каждого JME3 приложения есть корневой узел: Ваша игра автоматически наследует RootNode от SimpleApplication. Все что прикреплено к RootNode является частью графа сцены. Элементы графа сцены являются Spatial.


*  Spatial содержит положение, поворот и масштаб объекта.
*  Spatial может быть загружен, преобразован и сохранен.
*  Есть два типа Spatial: узлы и Geometry.
[cols="3", options="header"]
|===

<a|  
a| Geometry 
a| Node 

a| Видимости: 
a| Geometry это видимый объект сцены. 
a| Node это невидимая “ручка для объектов сцены. 

a| Назначение: 
a| Geometry хранит внешний вид объекта. 
a| Node объединяет Geometry и другие узлы. 

a| Примеры: 
a| Куб, шар, игрок, здание, кусочек окружения, транспорт, ракета, NPC, и т.д. 
a| `rootNode` (корневой узел), этаж, объединяющий несколько территорий, пользовательский транспорт с узлом пассажиров, узел игрока и оружия, звуковой узел, и т.д. 

|===


== Разберемся в коде

Что происходит во фрагменте кода? Вы используете  метод ` simpleInitApp ()`, о котором мы говорили в первом уроке, для инициализации сцены.


.  Вы создаете первый куб.
**  Создается Box с размером (1,1,1).
**  Вох размещается в точке (1, -1,1) с помощью метода setLocalTranslation().
**  Box превращается в Geometry.
**  Создается синий материал.
**  Синий материал наносится на куб. 
[source,java]

----

    Box box1 = new Box(1,1,1);
    Geometry blue = new Geometry("Box", box1);
    blue.setLocalTranslation(new Vector3f(1,-1,1));
    Material mat1 = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.Blue);
    blue.setMaterial(mat1);
----


.  Вы создаете второй куб
**  Создается второй куб с таким же размером.
**  Куб перемещается в (1,3,1). Это прямо над первым кубом.
**  Box превращается в Geometry.
**  Создается красный материал. 
**  Красный материал применяется ко второму кубу. 
[source,java]

----

    Box box2 = new Box(1,1,1);
    Geometry red = new Geometry("Box", box2);
    red.setLocalTranslation(new Vector3f(1,3,1));
    Material mat2 = new Material(assetManager,
      "Common/MatDefs/Misc/Unshaded.j3md");
    mat2.setColor("Color", ColorRGBA.Red);
    red.setMaterial(mat2);
----


.  Вы создаете Node с именем pivot. 
**  К Node применяется имя “pivot.
**  По умолчанию Node размещен в точке (0,0,0). 
**  Node прикрепляется к rootNode.
**  Узел не имеет видимого проявления на сцене. 
[source,java]

----

    Node pivot = new Node("pivot");
    rootNode.attachChild(pivot);
----

Если вы запустите приложение приложение с кодом выше, сцена будет пустой. Это потому что узел невидим, и вы не прикрепили никакого видимого объекта к rootNode. 



.  Прикрепление кубов к узлу pivot. 
[source,java]

----

        pivot.attachChild(blue);
        pivot.attachChild(red);
----

Если вы запустите приложение с этим кодом, вы увидите два куба: красный расположен прямо над синим.


.  Поворот узла pivot.
[source,java]

----
        pivot.rotate( 0.4f , 0.4f , 0.0f );
----

 Если вы запустите сейчас, вы увидите что оба куба наклонены на одинаковый угол




=== Что такое Узел Стержень(Pivot)?

Вы можете преобразовать (например, поворот) Геометрию вокруг собственного центра, или вокруг заданного центра. Заданный центр для одной или нескольких Геометрий называется стержень.


*  В этом примере вы объединили две Геометрии путем присоединения их к одному узлу стержню. И использовали узел стержень в качестве ручки для вращения вместе двух Геометрий относительно общего центра. При повороте узла стержня поворачивает все подключенные Геометрии за один шаг. Узел стержень является центром вращения. Перед подключением других Геометрий, убедитесь, что узел стержень в точке (0,0,0). Преобразование родительского Узла что бы преобразовать все прикрепленные потомков Spatials является распространенной задачей. Вы будете использовать этот метод много в играх при перемещении нескольких Spatials вместе.  +
*Примеры:* Транспортное средство и его водителя двигаться вместе; планета и её луна движутся по орбите вокруг солнца. 
*  Контраст в этом случае с другим параметром: Если вы не создаете дополнительно узел стержень и преобразуете Геометрии, тогда каждое преобразование выполняется относительно изначальной Геометрии (относительно самой себя).  +
*Примеры:* Если повернуть каждый куб напрямую(используя red.rotate(0.1f , 0.2f , 0.3f); и blue.rotate(0.5f , 0.0f , 0.25f);), то каждый куб вращается отдельно вокруг его центра. Это похоже на планету, вращающуюся вокруг своего центра.


== Как мне наполнить Граф сцены?
[cols="2", options="header"]
|===

a| Задача…? 
a| Решение! 

a| Создать Spatial 
a| Создать Сетку формы, обернуть её в Геометрию, и дать ей Материал. Например: 
[source,java]

----
Box mesh = new Box(Vector3f.ZERO, 1, 1, 1); // параллелепипед по умолчанию сетка
Geometry thing = new Geometry("thing", mesh); 
Material mat = new Material(assetManager,
   "Common/MatDefs/Misc/ShowNormals.j3md");
thing.setMaterial(mat);
----


a| Добавить объект на сцену 
a| Прикрепите к Spatial к `rootNode`, или к любому узлу, который подключен к rootNode. 
[source,java]

----
rootNode.attachChild(thing);
----


a| Удалить объекты из сцены 
a| Отсоединить Spatial от `rootNode`, и от любого узла, подключенного к rootNode. 
[source,java]

----
rootNode.detachChild(thing);
----

[source,java]

----
rootNode.detachAllChildren();
----


a| Найти Spatial на сцене по имени объекта, или ID, или его положение в иерархии “ родители-потомки. 
a| Посмотрите на узел, потомки или родителей:: 
[source,java]

----
Spatial thing = rootNode.getChild("thing");
----

[source,java]

----
Spatial twentyThird = rootNode.getChild(22);
----

[source,java]

----
Spatial parent = myNode.getParent();
----


a| Указать, что должно быть загружен при старте 
a| Все ваши инициализации прикрепить к `rootNode` в `simpleInitApp()`метод является частью сцены в начале игры. 

|===


== Как мне преобразовать Spatials?

Существует три типа 3D преобразований: Перемещение, Масштабирование и поворот.

[cols="4", options="header"]
|===

a| Перемещение движение Spatials 
a| ось-X 
a| ось-Y 
a| ось-Z 

a| Укажите новое расположение в трех измерениях: Насколько все это далеко от источника идет вправо-вверх-вперед?  +
Чтобы переместить Spatial _по_ конкретным координатам, таким как (0,40.2 f,-2), используйте: 
[source,java]

----
thing.setLocalTranslation( new Vector3f( 0.0f, 40.2f, -2.0f ) );
----

 Чтобы переместить Spatial _на_ определенную величину, например, выше вверх (y=40.2f) и дальше (z=-2.0f): 


[source,java]

----
thing.move( 0.0f, 40.2f, -2.0f );
----

a|+вправо -влево
a|+вправо -влево
a|+вперед -назад

|===
[cols="4", options="header"]
|===

a| Масштабирование изменяет Spatials 
a| ось-X 
a| ось-Y 
a| ось-Z 

a|Укажите коэффициент масштабирования в каждом измерении: длина, высота, ширина.  +
Значение между 0.0f 1,0f сжимает Spatial; больше, чем 1.0 f растягивает его; 1.0f сохраняет таким же.  +
Используя ту же величину для каждого измерения шкалы, пропорционально различные значения растягивают его. +
Для масштабирования Spatial в 10 раз длиннее, одной десятой высоты, и ширины: 
[source,java]

----
thing.scale( 10.0f, 0.1f, 1.0f );
----

a|длина
a|высота
a|ширина

|===
[cols="4", options="header"]
|===

a| Вращения, поворот Spatials 
a|ось-X 
a| ось-Y 
a| ось-Z 

a|3-D вращение немного сложнее (<<документация/jme3_ru/вращение#,узнать подробности здесь>>). Вкратце: Вы можете вращаться вокруг трех осей: Тангаж, рыскание и крен.  Вы можете указать углы в градусах путем умножения степеней значения с `FastMath.DEG_TO_RAD`. +
Крен объекта на 180° вокруг оси Z: 
[source,java]

----
thing.rotate( 0f , 0f , 180*FastMath.DEG_TO_RAD );
----

 Совет: если в игре идея требует серьезной суммы оборотов, это стоит посмотреть в <<документация/jme3_ru/кватернионы#,кватернионы>> структура данных, которая может объединять и хранить обороты эффективно. 


[source,java]

----
thing.setLocalRotation( 
  new Quaternion().fromAngleAxis(180*FastMath.DEG_TO_RAD, new Vector3f(1,0,0)));
----

a|тангаж = кивок головой
a|рыскание = покачать головой
a|крен = склонить голову

|===


== Решение проблем со Spatials?

Если вы получаете неожиданные результаты, проверить, не совершили ли вы следующие распространенные ошибки :

[cols="2", options="header"]
|===

a| Проблема? 
a| Решение! 

a| Созданная Геометрия не появляется в сцене. 
a| Вы подключили её к rootNode?  +
Есть ли Материал?  +
Какова её локализация (место)? Может она за камерой или закрыто другой геометрией?  +
Может она слишком мелкая или слишком огромная что бы её можно было увидеть?  +
Это слишком далеко от камеры?  (Попробуйте link:http://jmonkeyengine.org/javadoc/com/jme3/renderer/Camera.html#setFrustumFar%28float%29[cam.setFrustumFar](111111f); см. далее) 

a| Spatial поворачивается самым неожиданным образом. 
a|Вы использовали радиан значения, а не градусы? (Если вы использовали градусы, умножьте их с FastMath.DEG_TO_RAD что бы преобразовать их в радианах)   +
Ты создавал Spatialв начале координат  (Vector.ZERO) перед перемещением? +
Может ты вращаешься не вокруг предполагаемого узла поворота, а вокруг чего-то другого? +
Вокруг той ли оси что хочешь, ты вращаешься? 

a| Геометрия имеет неожиданный цвет или материал. 
<a| Ты повторно использовать материал из другой геометрии и случайно изменила свои свойства? (Если да, то считаю это клонированием: mat2 = mat.clone(); )  

|===


== Как добавить пользовательские данные Spatials?

Многие Spatials представляют игровых персонажей или другие лица, с которыми игрок может взаимодействовать. Приведенный выше код, который поворачивает две коробки относительно общего центра (pivot) может быть использован например для космического корабля пристыковывающегося к орбитальной космической станции.


В зависимости от вашей игры, у игровых объектов, не только изменяется расположение,поворот и масштаб (те преобразования, о которых вы только что узнал). Игровые объекты также имеют особые свойства, такие как здоровье, инвентарь, снаряжение, прочность корпуса и топливо для космических аппаратов. В Java, вы представляете данные персонажа в качестве переменных класса, например, floats, Strings, или Arrays. 


Вы можете добавить пользовательские данные непосредственно к любому узлу или геометрии. *Вам не потребуется расширять класс Node включать переменные*!
Например, чтобы добавить пользовательский идентификационный номер узла, можно использовать:


[source,java]

----
pivot.setUserData( "pivot id", 42 );
----

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


[source,java]

----
int id = pivot.getUserData( "pivot id" ); 
----

С помощью различных Strings ключей (здесь ключевым является  `pivot id`), вы можете получить и задать несколько различных значений для любых данных Spatial необходимых для вашей игры. Когда вы начнете писать игру, вы можете добавить значение топлива в узел автомобиля, значение скорости в узел самолета или количеству золотых монет узлу игрока, и многое другое. Тем не менее, следует отметить, что только пользовательские объекты, которые реализуют сохранение могут быть приняты.



== Заключение

Вы узнали, что ваш 3D-сцена это граф сцены состоящий из Spatials: Видимые геометрии и невидимые узлы. Вы можете преобразовать Spatials, или прикрепить их к узлам и преобразовать узел. Вы знаете, самый простой способ, как добавить пользовательские свойства (такие, как здоровье игрока или скорости автомобиля) к Spatials.


Так как стандартные формы, такие как сферы и коробки(кубы) быстро надоедают, то переходите к следующей главе, где вы научитесь  <<документация/jme3_ru/начальная/знакомство_с_ресурсами#,загружать ресурсы, такие как 3D модели>>.

<tags><tag target="beginner" /><tag target="rootNode" /><tag target="node" /><tag target="intro" /><tag target="documentation" /><tag target="color" /><tag target="spatial" /><tag target="geometry" /><tag target="scenegraph" /><tag target="mesh" /></tags>
