<?xml version="1.0" encoding="windows-1251"?>
<rss version="2.0">
<channel>
  <title>Unity</title>
  <link>https://gamedev.ru/unity/</link>
  <description>Создание игр на движке Unity</description>
  <language>ru</language>
  <generator>http://skif.qrim.ru/</generator>
  <image>
    <url>https://gamedev.ru/sects/logo/?s=703</url>
    <title>Unity</title>
    <link>https://gamedev.ru/unity/</link>
  </image>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/Ziva</guid>
  <pubDate>Tue, 25 Jan 2022 08:06:59 GMT</pubDate>
  <title>Unity приобрела Ziva Dynamics</title>
  <link>https://gamedev.ru/unity/Ziva</link>
  <comments>https://gamedev.ru/unity/forum/?id=266740</comments>
  <category>Unity</category>
  <category>Ziva Dynamics</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Компания Unity пополнила свои покупки компанией Ziva Dynamics – лидером в симуляции компьютерных персонажей. В линейку приобретений компании уже входят Weta Digital, &lt;a href=&quot;https://gamedev.ru/unity/SpeedTree&quot;&gt;SpeedTree&lt;/a&gt;, SyncSketch, Pixyz, and RestAR.&lt;/p&gt;
&lt;p&gt;Поскольку движения каждого человека уникальны и сложны, то компьютерная симуляция достоверных и физически верных движений – достаточно комплексная задача, требующая экспертного понимания. Инструменты для художников компании Ziva Dynamics обладают технологиями, которые справляются с этой задачей. Вместе с инструментами симуляций предоставляется доступ к платным и бесплатным ассетам с персонажами.&lt;/p&gt;

&lt;p&gt;Кроме того, Ziva имеет технологию ZivaRT для динамической мимики лица. В этой технологии применяется машинное обучение.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/unity_ziva.png&quot; alt=&quot;Unity Ziva | Unity приобрела Ziva Dynamics&quot; title=&quot;Unity Ziva&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Специально для анонса был создан компьютерный персонаж Emma, демонстрирующая возможности технологии.&lt;/p&gt;

&lt;p&gt;
&lt;div class=&quot;video-container&quot;&gt;&lt;div class=&quot;video&quot; id=&quot;tube1&quot; data-value=&quot;xeBpp3GcScM&quot;&gt;&lt;img data-value=&quot;xeBpp3GcScM&quot; src=&quot;https://img.youtube.com/vi/xeBpp3GcScM/maxresdefault.jpg&quot; onload=&quot;skif.loadYoutubeImage(event)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;img class=&quot;play&quot; src=&quot;https://gamedev.ru/_img/youtube-play.svg&quot; onclick=&quot;skif.insertYoutube(1)&quot; onMouseover=&quot;skif.insertYoutube(1)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Подробности в блоге Unity:
&lt;br&gt;
&lt;a href=&quot;https://blog.unity.com/technology/welcome-ziva-dynamics&quot;&gt;https://blog.unity.com/technology/welcome-ziva-dynamics&lt;/a&gt;&lt;/p&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/articles/SpringJointRope</guid>
  <pubDate>Fri, 24 Dec 2021 16:56:57 GMT</pubDate>
  <title>Создание веревки в Unity на базовых SpringJoint.</title>
  <link>https://gamedev.ru/unity/articles/SpringJointRope</link>
  <category>Программирование</category>
  <category>физика</category>
  <category>SpringJoint</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;В этой статье я расскажу к какому способу создания веревок (тросов, цепей) я смог прийти в ходе разработки игры для конкурса Платформеров на GameDev.ru. Пример будет разобран на 2Д проекте, но думаю нет никаких сложностей адаптировать его для 3Д проекта.&lt;/p&gt;
&lt;p&gt;Создаем пустую сцену.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead1&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(1, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler1&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(1, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159427_1640355249_1.png&quot; alt=&quot;1 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;1&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Далее хотелось бы поиметь два объекта-конца. Они могут быть как подвижные, так и неподвижные. Главное, что они должны иметь RigidBody2D компонент. В данном примере я зафиксировал концы в пространстве через Freeze Position.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead2&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(2, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler2&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(2, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159428_1640355257_2.png&quot; alt=&quot;2 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;2&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Затем нам понадобится префаб объекта-звена, из которого будет состоять веревка. Для использования сил и гравитации звено должно иметь компонент RigidBody2D, а для связей два модуля SpringJoint2D – по одному на левую и правую связь.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead3&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(3, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler3&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(3, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159429_1640355269_3.png&quot; alt=&quot;3 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;3&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Теперь нам нужен скрипт, который мы повесим на один из концов, например, на левый. Данный скрипт должен обладать публичными GameObject целевого объекта и образца веревки. Поместим в указанные поля, подготовленные объекты.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead4&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(4, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler4&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(4, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159430_1640355277_4.png&quot; alt=&quot;4 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;4&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Также нам потребуется скрипт для звена цепи, чтобы строить нашу цепь. Данный скрипт должен обладать публичными GameObject целевого объекта, объекта-источника и образца веревки.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead5&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(5, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler5&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(5, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159431_1640355285_5.png&quot; alt=&quot;5 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;5&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Теперь напишем скрипт инициализации создания первого звена (это скрипт для объекта-конца).&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; RopeExampleScript : MonoBehaviour
{
    &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; GameObject target, samplerope;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; step = &lt;span class=&quot;digit&quot;&gt;0.2&lt;/span&gt;f; &lt;span class=&quot;comment&quot;&gt;//шаг веревки&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;// Start is called before the first frame update&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Start&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {
        &lt;span class=&quot;comment&quot;&gt;// определяем вектор нашей цепи&lt;/span&gt;
        Vector3 tarvec = target.transform.position - transform.position;
        &lt;span class=&quot;comment&quot;&gt;// создаем образец звена и помещаем в нужную позицию сдвигаясь на один шаг&lt;/span&gt;
        GameObject newrope = Instantiate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;samplerope, transform.position + 
            tarvec.normalized * step, Quaternion.identity&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;// получаем доступ к параметрам скрипта звена&lt;/span&gt;
        RopeNodeExampleScript newrope_rnes = newrope.GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;RopeNodeExampleScript&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;// указываем левую связь&lt;/span&gt;
        newrope_rnes.lbond = gameObject;
        &lt;span class=&quot;comment&quot;&gt;// указываем объект-цель&lt;/span&gt;
        newrope_rnes.target = target;

        &lt;span class=&quot;comment&quot;&gt;// добавляем на объект-источник пружинную связь&lt;/span&gt;
        SpringJoint2D source_sj = gameObject.AddComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;SpringJoint2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;// эти параметры подбираются экспериментально по вкусу для своей игры&lt;/span&gt;
        source_sj.frequency = &lt;span class=&quot;digit&quot;&gt;25&lt;/span&gt;;
        source_sj.dampingRatio = &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;// присоединяем к этой связи наше новое звено&lt;/span&gt;
        source_sj.connectedBody = newrope.GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;Rigidbody2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    }
}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;&lt;p&gt;Теперь напишем скрипт для звена цепи.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; RopeNodeExampleScript : MonoBehaviour
{
    &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; GameObject target, ropesample, lbond, rbond;

    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; step = &lt;span class=&quot;digit&quot;&gt;0.2&lt;/span&gt;f; &lt;span class=&quot;comment&quot;&gt;//шаг веревки &lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; SpringJoint2D&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt; sj; &lt;span class=&quot;comment&quot;&gt;//все компоненты пружины&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;// Start is called before the first frame update&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Start&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {
        &lt;span class=&quot;comment&quot;&gt;// Получаем все компоненты пружины&lt;/span&gt;
        sj = GetComponents&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;SpringJoint2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;// Выключаем их (это нужно, чтобы корректно настроить связи).&lt;/span&gt;
        &lt;span class=&quot;comment&quot;&gt;// Можно изначально держать их выключеными.&lt;/span&gt;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.enabled = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.enabled = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;

        &lt;span class=&quot;comment&quot;&gt;// определяем вектор нашей цепи&lt;/span&gt;
        Vector3 tarvec = target.transform.position - transform.position;
        &lt;span class=&quot;comment&quot;&gt;// проверяем, если дистанция до целевого объекта больше шага, то создаем звено&lt;/span&gt;
        &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;tarvec.magnitude &lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt; step&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
        {
            &lt;span class=&quot;comment&quot;&gt;// создаем звено&lt;/span&gt;
            GameObject newrope = Instantiate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;ropesample, transform.position +
                tarvec.normalized * step, Quaternion.identity&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;comment&quot;&gt;// получаем доступ к скрипту нового звена&lt;/span&gt;
            RopeNodeExampleScript newrope_rope = newrope.GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;RopeNodeExampleScript&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;comment&quot;&gt;// устанавливаем левую связь для нового звена&lt;/span&gt;
            newrope_rope.lbond = gameObject;
            &lt;span class=&quot;comment&quot;&gt;// устанавливаем объект-цель для нового звена&lt;/span&gt;
            newrope_rope.target = target;
            &lt;span class=&quot;comment&quot;&gt;// устанавливаем правую связь для текущего звена&lt;/span&gt;
            rbond = newrope;

        }
        &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// если дистанция до целевого объекта меньше шага, то замыкаем цепь на нем&lt;/span&gt;
        {
            &lt;span class=&quot;comment&quot;&gt;//замыкаем правую связь на объекте-цели&lt;/span&gt;
            rbond = target;
            &lt;span class=&quot;comment&quot;&gt;//добавляем пружинную связь на объект цель&lt;/span&gt;
            SpringJoint2D ropeknot_sj = target.AddComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;SpringJoint2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
            &lt;span class=&quot;comment&quot;&gt;//эти параметры подбираются экспериментально по вкусу для своей игры&lt;/span&gt;
            ropeknot_sj.frequency = &lt;span class=&quot;digit&quot;&gt;25&lt;/span&gt;;
            ropeknot_sj.dampingRatio = &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;;
            &lt;span class=&quot;comment&quot;&gt;//присоединяем к этой связи наше звено&lt;/span&gt;
            ropeknot_sj.connectedBody = GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;Rigidbody2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        }
        
        &lt;span class=&quot;comment&quot;&gt;//присоединяем к пружинам левое и правое тело&lt;/span&gt;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.connectedBody = lbond.GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;Rigidbody2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.connectedBody = rbond.GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;Rigidbody2D&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;comment&quot;&gt;//активируем пружинные модули&lt;/span&gt;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.enabled = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
        sj&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;.enabled = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
    }
}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;&lt;p&gt;Настраиваем пружинные связи для нашего префаба-звена.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159432_1640355292_6.png&quot; alt=&quot;6 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;6&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Запускаем и радуемся.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/159433_1640355300_7.png&quot; alt=&quot;7 | Создание веревки в Unity на базовых SpringJoint.&quot; title=&quot;7&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Дальнейшие трансформации делайте на свой вкус и цвет. Надеюсь, этот материал будет полезен кому-либо.&lt;/p&gt;

&lt;p&gt;Тема конкурса Платформеров:
&lt;br&gt;
&lt;a href=&quot;https://gamedev.ru/projects/forum/?id=263782&quot;&gt;https://gamedev.ru/projects/forum/?id=263782&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/SpeedTree</guid>
  <pubDate>Thu, 22 Jul 2021 07:22:36 GMT</pubDate>
  <title>Unity приобрела создателя SpeedTree</title>
  <link>https://gamedev.ru/unity/SpeedTree</link>
  <comments>https://gamedev.ru/unity/forum/?id=278101</comments>
  <category>SpeedTree</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Компания Unity приобрела компанию Interactive Data Visualization (IDV) &amp;mdash; разработчика пакета SpeedTree. SpeedTree &amp;mdash; инструмент моделирования растительности для игр и компьютерных симуляций.&lt;/p&gt;

&lt;p&gt;Трехмерное моделирование вручную и с нуля растительности, такой как трава и деревья, достаточно сложное и трудоемкое занятие. Однако SpeedTree через автоматизацию позволяет ускорить и облегчить этот процесс. Инструмент SpeedTree достаточно популярен и занимает лидирующие позиции на рынке. SpeedTree использовался для моделирования растительности в таких проектах, как Horizon: Zero Dawn, Call of Duty: War Zone, The Witcher 3, Assassin’s Creed: Valhalla, Hitman III, and Ghost of Tsushima.&lt;/p&gt;

&lt;p&gt;
&lt;div class=&quot;video-container&quot;&gt;&lt;div class=&quot;video&quot; id=&quot;tube2&quot; data-value=&quot;vXFQPixRQOI&quot;&gt;&lt;img data-value=&quot;vXFQPixRQOI&quot; src=&quot;https://img.youtube.com/vi/vXFQPixRQOI/maxresdefault.jpg&quot; onload=&quot;skif.loadYoutubeImage(event)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;img class=&quot;play&quot; src=&quot;https://gamedev.ru/_img/youtube-play.svg&quot; onclick=&quot;skif.insertYoutube(2)&quot; onMouseover=&quot;skif.insertYoutube(2)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/articles/CBLG</guid>
  <pubDate>Fri, 15 May 2020 04:34:07 GMT</pubDate>
  <title>Контент-ориентированная генерация уровня в Unity в конкурсе «Храм Хаоса»</title>
  <link>https://gamedev.ru/unity/articles/CBLG</link>
  <comments>https://gamedev.ru/unity/forum/?id=243722</comments>
  <category>Общее</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Я, как и обещал, делюсь своими идеями насчет процедурной генерации уровней. Все то, что я опишу далее, ни в коем разе не претендует на истину, новаторство и, тем более, на что-то амбициозно-гениальное. Все это родилось лишь из непонимания общепринятых принципов современной процедурщины и математики, а также жгучего желания облегчить себе жизнь.&lt;/p&gt;
&lt;p&gt;Описаный мною подход к генерации помимо очевидных плюсов имеет и ряд жестких минусов. Так что идеальным его, конечно, не назовешь. Но, идеальных методов и не существует. Этот подход я использовал в своей недоделанной игре, которая была создана на &lt;a href=&quot;https://gamedev.ru/projects/forum/?id=242302&quot;&gt;конкурс &amp;laquo;Храм Хаоса&amp;raquo;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Пролог&lt;/h2&gt;
&lt;p&gt;Сначала был массив…&lt;/p&gt;

&lt;p&gt;Нет, сначала конечно же был вопрос самому себе &amp;mdash; зачем мне это?&amp;nbsp; С этим вопросом я обратился к интернету. И бездушная машина выдала мне ответ: процедурная генерация уровней &amp;mdash; вопрос достаточно старый и обсосанный, чтобы давать конкретные ответы.&lt;/p&gt;

&lt;p&gt;Нет, миллион статей все же я нашел. И, по правде сказать, они были довольно интересные. Множество техник, философские рассуждения на тему &amp;laquo;процедурщина, какая бы ни была, не заменит дизайнера&amp;raquo;, споры и срачи в комментариях. Все это было живо и весело, пока дело не дошло до математики. Времени на курсы матана и линейки у меня не было, поэтому я пошел по пути простого поиска готовых решений на любом псевдоязыке, дабы потом перевести это все на С#. И тут начинается настоящее приключение.&lt;/p&gt;

&lt;h2&gt;Классика жанра.&lt;/h2&gt;
&lt;p&gt;Так уж вышло, что почти все процедурные генераторы уровней сводятся к двум параллелям: процедурные лабиринты и процедурные данжены.&amp;nbsp; Думаю, определение обоих понятий понятны и без особых иллюстраций. Лабиринты &amp;mdash; фактически это куча коридоров, разделенных стенами. Дажены &amp;mdash; это массивы блоков, либо заполненных (стена), либо пустых (пол). В принципе, все алгоритмы так или иначе эксплуатируют одну из этих двух параллелей (либо совмещают их). Классические подходы к генерации &amp;mdash; это как раз совмещение обоих вариантов, когда в несколько проходов генерируется лабиринт, затем частично в стиле данжен-генераторов лабиринт заполняется либо большими помещениями (комнатами), либо непроходимыми темными зонами стен.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Сейчас в меня, возможно , начнут кидать тухлыми яйцами с ссылками на алгоритмы. Но подумайте глубже &amp;mdash; ведь все они так и сделаны.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;И есть одно общее, можно сказать, один мегаэлемент, объединяющий абсолютно все алгоритмы и методы генерации. Это МАССИВ. Чаще двумерный, в отдельных упоротых случаях &amp;mdash; многомерный. Массив, задача которого хранить нагенерённые позиции зон. В классическом данжен-генераторе значения элементов в массиве это 0 и 1, стена или пол, а индексы &amp;mdash; координаты зоны в пространстве. В лабиринтах зачастую чуток сложнее, но за рамки квадратного массива это не выходит.&lt;/p&gt;

&lt;p&gt;У этих подходов, при всех их несомненных плюсах, есть одно НО.&amp;nbsp; Массив всегда квадратный (если его искусственно не обрезают), а элемент массива представляет собой одну зону одинакового размера с остальными. То есть как бы мы сложно не задействовали алгоритмы генерации &amp;mdash; базовый строительный элемент у нас всегда одного размера, да и чаще всего квадратный (ну или многоугольник с четным количеством граней).&lt;/p&gt;

&lt;p&gt;Это обстоятельство поначалу никак не заботило меня. Моя задача была &amp;mdash; запустить хоть какую-то генерацию. Я планировал сделать ряд вложенных генераторов, дабы усложнить уровень, но…&lt;/p&gt;

&lt;h2&gt;Первая попытка. Печальная.&lt;/h2&gt;
&lt;p&gt;Но, забегая вперед, скажу, что это привело лишь к убогой фрактализации всего. И это было отвратительно.&lt;/p&gt;

&lt;p&gt;Я взял за основу один из многочисленных алгоритмов данжен-генератора. Смысла его описывать здесь не вижу, их много, и я взял тот, который уже был написан на С#, и мне оставалось лишь незначительно переписать его под Юнити-варимый вариант.&lt;/p&gt;

&lt;p&gt;Я планировал так.&amp;nbsp; Первый проход алгоритма &amp;mdash; глобальный генератор &amp;mdash; создает макро-уровень, где каждый элемент массива &amp;mdash; зона 100х100 юнитов (префаб). 1 &amp;mdash; зона, заполненная &amp;laquo;зданием&amp;raquo;, 0 &amp;mdash; пустая зона улицы. Далее, каждая зона &amp;laquo;здания&amp;raquo; внутри себя запускает свой генератор, который расставляет зоны поменьше (допустим 20х20), но с другими префабами, которые представляли собой либо комнаты (1), либо проходы без стен (0). Помимо всего прочего, каждый префаб комнат и проходов тоже мог содержать в себе генератор пропсов, но уже без особых алгоритмов.&lt;/p&gt;

&lt;p&gt;Как мы помним из моей первой демки &amp;mdash; все было плохо. Генерилось все хорошо, но фрактализация была жуткая, occlusion culling всего этого работал плохо, отчего тормозило дико. В конце концов, я добавил поворот на рандомный угол каждой мегазоны, что поначалу показалось мне интересным, но превратило игру еще в б&lt;i&gt;о&lt;/i&gt;льшую кашу…&lt;/p&gt;

&lt;p&gt;Я отчаялся. Отчаялся и начал думать…&lt;/p&gt;

&lt;h2&gt;Пытки разума&lt;/h2&gt;
&lt;p&gt;Думал я мучительно. Смотрел кучу видосиков, читал статейки. Но везде у меня приходило в голову одно и то же &amp;mdash; &amp;laquo;это все не то!&amp;raquo;. Я вспомнил главного рандом-монстра игровой истории &amp;mdash; Дьяблу. Вспомнил как круто все там было, как здорово, мегарандомно, но и мега-слажено одновременно. И вдруг я стал понимать. В Дьябле все было круто потому что игра была &amp;mdash; изометрия про данжены!&lt;/p&gt;

&lt;p&gt;Но у меня &amp;mdash; экшен! Да еще и из головы! И в памяти начал всплывать угарный угар, в который я долго рубался давным давно &amp;mdash; многими противоречиво любимо-ненавидимый Хеллгейт. Точной информации о методах генерации уровней в Хеллгейте всемирный разум мне не дал (возможно, я плохо искал), но вспоминая уровни, я начал, похоже, догадываться, что мне нужно делать для экшена. Так начали проявляться первые наброски идеи под названием CBLG (Content-Based Level Generator).&lt;/p&gt;

&lt;h2&gt;Чё за кантент, ё?&lt;/h2&gt;
&lt;p&gt;Казалось бы, при чем тут контент? Ответ на этот вопрос будет немного ниже.&lt;/p&gt;

&lt;p&gt;А начну я с другого. Что мы знаем об экшенах? Особенно о современных экшенах, старых консольных экшенах? В основном то, что мы бежим по коридорам и комнатам, стреляем врагов, ищем ключи… Мы знаем, что в экшенах нет сетчатых уровней, лабиринтов в классике, где коридоры имеют шаг, углы 90 градусов, а комнаты статичны и пропорциональны. В экшенах окружающий нас контент &amp;mdash; многообразен. Однообразие Вольфенштейна закончилось вместе с ним. А значит, генерация блоками здесь уже неуместна.&amp;nbsp; Нужно, чтобы коридоры были кривыми, разной длины, комнаты были любой геометрии. Были спуски, подъемы, ямы, непонятные формы пола и потолка.&lt;/p&gt;

&lt;p&gt;Становится ясно, что реализация всего этого массивами становится затруднительной, где-то даже сомнительной затеей. Что же тогда? Как гененировать? Ведь тогда генерация каждого последующего элемента геометрии уровня должна зависеть от предыдущего? То есть генерация контента должна быть в прямой и абсолютной зависимости от самого контента!&lt;/p&gt;

&lt;p&gt;Вот оно! Решение!&lt;/p&gt;

&lt;h2&gt;Основа&lt;/h2&gt;
&lt;p&gt;Любой коридор имеет вход и выход. Любая комната имеет вход и выход, даже несколько. Всё, во что мы можем войти, имеет место, откуда мы входим &amp;mdash; начало. И если это не тупик &amp;mdash; имеет выход &amp;mdash; конец. Если мы выходим &amp;laquo;откуда-то&amp;raquo;, то мы входим в новое &amp;laquo;куда-то&amp;raquo;. То есть &lt;i&gt;конец одного места совмещен с началом другого места&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;Неважно, коридор это, комната, улица &amp;mdash; везде в шутерах есть входы и выходы. главное &amp;mdash; их совместить. Только их. Конкретные точки в пространстве! Зная, где расположены эти точки, нам уже, по большому счету, неважно ни размер зоны, ни форма, ничего. Важно только где у зоны выход&amp;nbsp; и где у следующей зоны вход.&lt;/p&gt;

&lt;p&gt;Для упрощения задачи, примем за правило, что у зоны может быть один (и только один!) &amp;laquo;вход&amp;raquo; и сколь угодно выходов.&amp;nbsp; Останется лишь правильно сорентировать следующую зону в пространстве относительно выхода предыдущей. И в этом деле Unity &amp;mdash; очень большой молодец :)&lt;/p&gt;

&lt;h2&gt;Симуляция на пальцах&lt;/h2&gt;
&lt;p&gt;Префабы в Юнити &amp;mdash; пожалуй, самая полезная из доступных штук. Они лучше всего подходят к нашей задаче. Максимально. Если бы их не было &amp;mdash; их пришлось бы придумать :)&amp;nbsp; &lt;/p&gt;

&lt;p&gt;Итак, пусть у нас есть 2 префаба геометрии уровня: коридор и комната. У каждого префаба есть &amp;laquo;вход&amp;raquo; &amp;mdash; для удобства работы вход &amp;mdash; это точка &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt; внутри префаба, относительно которой строится вся геометрия. Обычно это уровень пола, центр дверного проема, а дальше вся геометрия строится вдоль оси Z в положительную сторону, при этом в высоту и ширину (Y и X) в любые стороны. Но Z &amp;mdash; строго от нуля и вперед!&lt;/p&gt;

&lt;p&gt;Это первый важный элемент нашей системы. Вход в зону &amp;mdash; всегда &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt;!&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_01.png&quot; alt=&quot;level_gen_01 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_01&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Далее, мы создаем объект (префаб) под названием zoneExit, ставим в него пару примитивов, симулируя визуально стрелочку вдоль оси Z. Задача &amp;mdash; конец стрелочки внутри префаба zoneExit должен находиться в точке &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt;, сама стрелочка тянется вдоль оси Z от минуса к нулю. По X и по Y &amp;mdash; строго по нулям! Это нужно нам для визуальной ориентации выхода из зоны и последующего использования этой ориентации в генераторе.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_02.png&quot; alt=&quot;level_gen_02 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_02&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Также, для собственного удобства и контроля, сделаем префаб zoneEntry. Он не участвует в генераторе и его можно потом удалить вообще. Но он помогает контролировать точку входа при создании нового префаба зоны, чтобы не потерять из виду &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt; в пылу дизайнерской страсти. Этот префаб &amp;mdash; точная противоположность zoneExit-а. Он начинается строго в &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt;, и тянется вдоль Z вперед, представляя собой какую-нибудь узнаваемую фигуру:&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_03.png&quot; alt=&quot;level_gen_03 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_03&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Его мы размещаем в наших префабах зон строго в 0.0.0 и направлении forward (Z), строим дальше всю зону исключительно в положительной плоскости по Z (по X и Y при этом ограничений нет)&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_04.png&quot; alt=&quot;level_gen_04 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_04&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Размещаем&amp;nbsp; zoneExit-ы&amp;nbsp; в наших префабах зон так, чтобы начало предполагаемой последующей зоны встало именно так, как нам нужно. Мы знаем одно &amp;mdash; кончик &amp;laquo;стрелочки&amp;raquo; zoneExit, её originPoint, по-замыслу совпадет с &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt; новой зоны, а направление нашей стрелочки &amp;mdash; укажет ориентацию следующей зоны относительно выхода этой. Я для простоты вращал exitPoint-ы только по Y, это и логично &amp;mdash; смысла следующий коридор наклонять относительно горизонта нет. Мы же бегать хотим :)&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_05.png&quot; alt=&quot;level_gen_05 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_05&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;br&gt;
&lt;p&gt; 
&lt;br&gt;
Если мы правильно все поставили, то в результате генерации зоны скрепятся довольно точно:&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_06.png&quot; alt=&quot;level_gen_06 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_06&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_07.png&quot; alt=&quot;level_gen_07 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_07&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;br&gt;
&lt;p&gt; 
&lt;br&gt;
И в зависимости от того, на какой угол по Y была повернута zoneExit &amp;mdash; наш генератор возьмет этот угол за основу и повернет следующую зону на него же:
&lt;br&gt;
 
&lt;br&gt;
&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_08.png&quot; alt=&quot;level_gen_08 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_08&quot;&gt;&lt;/div&gt;
&lt;br&gt;
 
&lt;br&gt;
Таким образом, в зависимости от положения zoneExit-ов в каждом префабе зоны &amp;mdash; у нас будет строится несетчатая и довольно разнообразная геометрия уровня. Все будет зависеть ТОЛЬКО ОТ количества разнообразных префабов зон!&lt;/p&gt;

&lt;p&gt;Чуете? Content-Based!&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_09.png&quot; alt=&quot;level_gen_09 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_09&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_10.png&quot; alt=&quot;level_gen_10 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_10&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;h2&gt;Дальше &amp;mdash; Больше!&lt;/h2&gt;
&lt;p&gt;Вся суть генератора &amp;mdash; в отсутствии единого генератора как такового. Главный участник генерации у нас &amp;mdash; префаб генератора зоны. Он не содержит геометрии, только скрипт генератора одной (!) зоны. После появления, он инстансит из массива префабов случайный префаб зоны себе в дочерние объекты, обходит все его zoneExit-ы, с некоторой вероятностью спавнит в них &lt;i&gt;свои копии, указывая им position и rotation соответствующих exitZone-ов&lt;/i&gt;, и замолкает. Первый префаб мы кладем просто на уровень в глобальную точку &lt;nobr&gt;(0, 0, 0)&lt;/nobr&gt;. У меня к ней придвинута статичная зона джунглей, где стартует игрок.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_11.png&quot; alt=&quot;level_gen_11 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_11&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Ограничение на размер уровня хранится в глобальной переменной maxZonesCount, а также каждый префаб инкрементирует глобальный счетчик zonesCount в случае, если он не превысит максимальный предел. Если следующая зона должна быть последней ( текущее значение zonesCount == maxZonesCount-1), то префаб вместо следующей зоны генерит Финальную Зону (зону босса например, или как в моей игре &amp;mdash; зону с дверью, от которой нужно найти ключи), инкрементирует zonesCount последний раз, и дальше остальные префабы уже не могут ничего строить. В силу невозможности определения последовательности отработки скриптов в инстансах &amp;mdash; все происходит достаточно случайно и зона босса может появиться где угодно.&lt;/p&gt;

&lt;p&gt;Генерация почти закончена. Однако, в силу описанного выше механизма &amp;laquo;вероятности&amp;raquo; спавна на месте zoneExit-а нового префаба (типа RandomRange(0,5)&amp;gt;6), не все zoneExit-ы будут обработаны их генераторами для генерации в них следующих зон. Поэтому на сцене останется много свободных неудаленных zoneExit-ов &amp;mdash; по сути, дырок в пустоту. Вот как раз префаб финальной зоны (босса) их всех соберет и заспавнит на них тупиковые зоны (это могут быть тупики, комнаты с лутом, секреты &amp;mdash; все что угодно, что не имеет выхода. Даже комнаты с порталами). Согласно удобным инструментам Unity, все zoneExit-ы имеют тэг &amp;laquo;zoneExit&amp;raquo;, по которому финальная зона их и находит, и, согласно такому же алгоритму позиционирования, как и основном генераторе, спавнит на их месте зоны тупиков.&lt;/p&gt;

&lt;p&gt;Всё, генерация геометрии завершена. Но на этом процесс не заканчивается…&lt;/p&gt;

&lt;h2&gt;Наполняем геометрию&lt;/h2&gt;
&lt;p&gt;Каждый префаб зоны у нас определяет только геометрию &amp;mdash; стены, пол, иногда потолок. Но нам же интересно, чтобы было еще и визуальное разнообразие! Поэтому для каждого префаба зоны мы делаем кучу префабов пропсов. Я их все строю в редакторе прямо внутри префаба, расставляю так, чтобы потом спавнить префаб пропсов прямо внутри префаба геометрии &amp;mdash; и все стояло на своих местах. В игре у меня 5&amp;mdash;6 префабов окружения для каждого типа зоны. Простейший скрипт propsSpawner в каждом префабе геометрии содержит паблик-массив префабов наборов пропсов, задача его &amp;mdash; заспавнить случайный из них себе в дочерний объект. То же самое с врагами.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_12.png&quot; alt=&quot;level_gen_12 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_12&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;И опять &amp;mdash; чем больше разнообразных префабов &amp;mdash; тем разнообразнее генерация!&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/level_gen_13.png&quot; alt=&quot;level_gen_13 | Контент-ориентированная генерация уровня в Unity в конкурсе &amp;laquo;Храм Хаоса&amp;raquo;&quot; title=&quot;level_gen_13&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;h2&gt;Не все так радужно&lt;/h2&gt;
&lt;p&gt;Ну и о минусах. Без них ничего не бывает.&lt;/p&gt;

&lt;p&gt;Во-первых, главный минус &amp;mdash; нужно много контента. Чем больше &amp;mdash; тем лучше, разнообразие будет зависеть от этого сильно. На то оно и Content-Based.&lt;/p&gt;

&lt;p&gt;Во-вторых, это все еще существующая проблема оверлаппинга зон. В принципе, она решаема. Я планировал добавить каждому префабу зоны объект-контроллер с колижин-боксом, покрывающим весь префаб и в случае, если при генерации два таких объекта пересеклись &amp;mdash; перезапускать всю генерацию заново. Это увеличит время &amp;laquo;загрузки&amp;raquo; уровня, но все же позволит полностью избавиться от проблемы. У себя же я частично решил это введя длиииииинные спуски вниз, чтобы развести зоны по вертикали друг от друга.&lt;/p&gt;

&lt;p&gt;В-третьих, это уже моя личная проблема &amp;mdash; разобраться с навмешами внутри этих префабов, а так же с offmesh-links для того, чтобы враги умели гнаться за тобой между зонами. Но это уже личные мелочи. У меня враги дальше зоны префаба не выбегают…&lt;/p&gt;

&lt;p&gt;Если в описанном алгоритме найдете еще что-либо из минусов &amp;mdash; буду рад их обсудить :)&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/stackoverflowdocs</guid>
  <pubDate>Sun, 24 Jul 2016 16:46:29 GMT</pubDate>
  <title>На Stackoverflow добавлена community-документация</title>
  <link>https://gamedev.ru/unity/stackoverflowdocs</link>
  <category>документация</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;На &lt;a href=&quot;http://stackoverflow.com&quot;&gt;Stackoverflow&lt;/a&gt; в рамках проекта Documentation создан раздел для &lt;a href=&quot;http://stackoverflow.com/documentation/unity3d&quot;&gt;Unity&lt;/a&gt;.
&lt;br&gt;
Это позволяет пользоваться документацией к Unity, созданной сообществом.&lt;/p&gt;

&lt;p&gt;У такой документации есть преимущества:&lt;ul&gt;&lt;li&gt;Любую недоработку в документации может исправить кто угодно.&lt;/li&gt;&lt;li&gt;В любой момент можно добавить недостающую информацию.&lt;/li&gt;&lt;li&gt;Карма, полученная в рамках этого проекта, идёт в общую карму stackoverflow.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Есть и недостатки:&lt;ul&gt;&lt;li&gt;Данный ресурс не является официальным источником информации.&lt;/li&gt;&lt;li&gt;Поиск нужного раздела может быть затруднён, так как проект страдает от последствий унификации интерфейса с основным сайтом stackoverflow.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Запросы на добавление недостающей документации можно добавлять через кнопку &amp;quot;Request Topic&amp;quot;.
&lt;br&gt;
Также можно голосовать за добавление недостающих разделов на &lt;a href=&quot;http://stackoverflow.com/documentation/unity3d&quot;&gt;главной странице&lt;/a&gt; тега Unity.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/articles/Unity_Character_Motor</guid>
  <pubDate>Wed, 29 Jun 2016 05:08:30 GMT</pubDate>
  <title>Unity Character Motor</title>
  <link>https://gamedev.ru/unity/articles/Unity_Character_Motor</link>
  <comments>https://gamedev.ru/unity/forum/?id=215796</comments>
  <category>Общее</category>
  <category>Unity</category>
  <category>Unity3D</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Когда-то давно, еще во времена Unity 3, мне стало интересно как работает физика персонажа. И я заглянул в класс CharacterMotor. Класс был написан на JavaScript, был огромный, страшный и непонятный. Я решил переписать его на C#, попутно отрефакторив. Недавно я вспомнил про свой старый CharacterMotor, решил еще немного подправить его и поделиться им. Тем более, тема физики персонажа не очень популярная (я вообще не видел никакой информации), хотя довольно интересная.&lt;/p&gt;
&lt;p&gt;==1. Введение==&lt;/p&gt;

&lt;p&gt;Я взял за основу CharacterMotor из Unity 3. В Unity 4 CharacterMotor не менялся, а вот в Unity 5, это абсолютно новый класс. Новый CharacterMotor, значительно уменьшился в коде, и видимо, и в функционале. Я не особо разбирался в нем и почти не использовал его, но заметил, что скольжение с крутого склона теперь не работает, и вообще качество кода мне не понравилось. Видимо писался он на скорую руку. Также Unity 5 перешел на новый PhysX 3, но в CharacterController я никаких изменений не заметил. Так что, думаю, мой CharacterMotor не устарел.&lt;/p&gt;

&lt;div style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/118186_1467163730_character_motor.jpg&quot; alt=&quot;Character Motor | Unity Character Motor&quot; title=&quot;Character Motor&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;==2. CharacterController==&lt;/p&gt;

&lt;p&gt;Обычно персонаж не является обычным физическим объектом и работает по своим законам физики. В Unity для персонажа используется CharacterController. Это комбинация коллайдера в форме капсулы и метода Move. Метод Move двигает персонажа в указанную позицию, обрабатывая при этом коллизию. Обработка коллизии тут тоже не обычная, например, коллизия обрабатывается так, чтобы персонаж мог свободно подниматься на небольшие склоны, но не мог на большие, или auto stepping - фича, которая позволяет персонажу подниматься на маленькие препятствия. CharacterController не совместим с RigidBody, это значит, что он не может взаимодействовать с другими физическими объектами. Это создает некоторые проблемы: персонаж не может двигать другой физический объект, а другой объект не может сдвинуть персонажа. Кинематические объекты вообще свободно проходят сквозь персонажа, что делает проблематичным создание лифтов и движущихся платформ.&lt;/p&gt;

&lt;p&gt;==3. CharacterMotor==&lt;/p&gt;

&lt;p&gt;Вместо RigidBody мы должны использовать свой класс, в Unity это CharacterMotor. Хотя, CharacterMotor &amp;mdash; это намного больше, чем RigidBody. CharacterMotor отвечает за любые движения нашего персонажа, например: ходьба, бег, скольжение, падение, прыжки, движение на платформе, на лифте, подъем по вертикальной лестнице, плавание. Все это реализуется, фактически, с помощью различных ухищрений, а не законов физики. Ведь персонажи обычно ведут себя не по законам, например: персонаж может немного управляться во время прыжка или падения, или просто резко останавливаться или менять направление движения. В Painkiller Дэниел мог ускоряться, просто прыгая без разбега. Конечно это все не сильно нарушает законов игровой физики, и можно было бы попытаться сделать максимально все физически корректно, но ведь проще просто ограничить скорость падения, чем вычислять сопротивление воздуха.&lt;/p&gt;

&lt;p&gt;Я разделил мой CharacterMotor на 3 partial класса: CharacterMotor, CharacterMotor_Movement и CharacterMotor_Jumping. В оригинальном классе был еще класс отвечающий за движение на платформе, но я убрал его.&lt;/p&gt;

&lt;p&gt;Базовая часть CharacterMotor реализует следующие:&lt;ul&gt;&lt;li&gt;Событие FixedUpdate, в котором вызываются все функции, необходимые для вычисления скорости. В Оригинальном классе можно было выбирать: использовать Update или FixedUpdate, но я убрал это т.к. движение игрока в FixedUpdate выглядит вполне плавно и требует меньше вычислительной нагрузки.&lt;/li&gt;&lt;li&gt;Событие OnControllerColliderHit, в котором определяется стоит ли персонаж на земле или нет. Здесь есть одна маленькая деталь, персонаж не может встать на землю, если он находится в состоянии прыжка и его скорость направлена вверх. Это нужно для того, чтобы когда игрок прыгает на крутой склон и касается его, то прыжок продолжался.&lt;/li&gt;&lt;li&gt;ApplyDownOffset и CancelDownOffset. Иногда персонаж может отрываться от земли. Чтобы предотвратить это, мы как бы применяем дополнительную гравитацию. Метод ApplyDownOffset смещает позицию персонажа вниз. Если после этого смещения персонаж все же не коснулся земли, то это смещение нужно отменить методом CancelDownOffset. Благодаря этому персонаж может с разбегу перейти на очень крутой склон, не оторвавшись от земли. Но можно поспорить хорошо это или плохо, ибо парой это выглядит не реалистично и странно. Возможно будет лучше уменьшить смещение.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

 &lt;div id=&quot;spoilerHead6&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(6, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler6&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(6, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections.Generic;


&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;AddComponentMenu &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Character/CharacterMotor&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;RequireComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;typeof&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;CharacterController&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; partial &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; CharacterMotor : MonoBehaviour {
  
  &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; CharacterController controller {
    get; &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; set;
  }
  
  internal Vector3 velocity;
  internal Collider ground;
  internal Vector3 groundPoint, groundNormal;

  internal Vector3 inputMoveDirection; &lt;span class=&quot;comment&quot;&gt;// направление движения&lt;/span&gt;
  internal &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; inputAcceleration; &lt;span class=&quot;comment&quot;&gt;// ускоренное движение&lt;/span&gt;
  internal &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; inputJump; &lt;span class=&quot;comment&quot;&gt;// прыжок&lt;/span&gt;
  internal &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; inputJumpHolding; &lt;span class=&quot;comment&quot;&gt;// усиленный прыжок&lt;/span&gt;


  &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; isGrounded {
    get {
      &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; ground != null;
    }
  }

  &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; isSliding {
    get {
      &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; isGrounded &amp;amp;&amp;amp; groundNormal.y &lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;= Mathf.Cos&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; controller.slopeLimit * Mathf.Deg2Rad &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    }
  }


  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Awake&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    controller = GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;CharacterController&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }

  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; OnEnable&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    velocity = controller.velocity;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; FixedUpdate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    ApplyMoving&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    ApplyGravity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    ApplyJumping&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    var prevVelocity = velocity;
    Move&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    ApplyBraking&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;prevVelocity, ref velocity&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Move&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;//Debug.DrawRay( transform.position, velocity, Color.blue );&lt;/span&gt;

    var offset = ApplyDownOffset&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    ground = null;
    groundPoint = groundNormal = Vector3.zero;
    controller.Move&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity * Time.deltaTime &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    velocity = controller.velocity;

    CancelDownOffset&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; offset &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    &lt;span class=&quot;comment&quot;&gt;//if(isGrounded) {&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;//    Debug.DrawRay( groundPoint, groundNormal, Color.red );&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;//}&lt;/span&gt;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; ApplyDownOffset&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// из-за недостаточной гравитации персонаж может отрываться от земли&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// для предотвращения этого, применяем смещение вниз&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isGrounded &amp;amp;&amp;amp; !isJumping&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; downOffset = Mathf.Max&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; controller.stepOffset, GetXZ&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.magnitude &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      velocity -= Vector3.up * downOffset;
      &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; downOffset;
    }
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; CancelDownOffset&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; downOffset&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// если персонаж не коснулся земли, то мы должны отменить смещение&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!isGrounded &amp;amp;&amp;amp; downOffset != &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      transform.position += Vector3.up * downOffset * Time.deltaTime; &lt;span class=&quot;comment&quot;&gt;// offset за кадр&lt;/span&gt;
      velocity += Vector3.up * downOffset; &lt;span class=&quot;comment&quot;&gt;// offset за секунду&lt;/span&gt;
    }
  }


  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; OnControllerColliderHit&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;ControllerColliderHit hit&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;//Debug.DrawRay(hit.point, hit.normal, Color.green);&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; !&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isJumping &amp;amp;&amp;amp; velocity.y &lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { &lt;span class=&quot;comment&quot;&gt;// во время прыжка персонаж не может встать на землю&lt;/span&gt;
      &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;hit.normal.y &lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt;.Epsilon &amp;amp;&amp;amp; IsBelowPart&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;hit.point.y&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
        &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;hit.normal.y &lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt; groundNormal.y&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { &lt;span class=&quot;comment&quot;&gt;// выбираем самую горизонтальную поверхность&lt;/span&gt;
          ground = hit.collider;
          groundPoint = hit.point;
          groundNormal = hit.normal;
        }
      }
    }
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; IsBelowPart&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; y&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; bottom = transform.position.y + controller.center.y - controller.height/&lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt; + controller.radius;
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; y &lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt; bottom;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; Vector3 GetXZ&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector3 v&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;new&lt;/span&gt; Vector3&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; v.x, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, v.z &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }


}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;==4. CharacterMotor_Movement==&lt;/p&gt;

&lt;p&gt;Эта часть класса отвечает за движения и гравитацию, и реализует следующие:&lt;ul&gt;&lt;li&gt;ApplyMoving. Эта функция отвечает за ходьбу и скольжение по крутому склону. Кстати, двигаться персонаж может и находясь в воздухе. Движение реализуется не силами или импульсами, а прямым изменением скорости с помощью Vector3.MoveTowards. У этого подхода есть свои плюсы и минусы. Главный плюс - мы напрямую задаем нужную нам скорость. Другая особенность - автоматически реализуется сила трения и сила сопротивления воздуха, но плюс это или минус, затрудняюсь сказать т.к. при нормальной симуляции физики, силы суммируются, но в данной реализации, если персонаж уже имеет скорость и при этом еще и сам движется, то обе эти скорости не будут суммироваться. На мой взгляд эта функция - самое спорное место, т.к. по логике вещей, скольжению здесь не место, ведь скольжение происходит под действием гравитации, а не из-за движение игрока.&lt;/li&gt;
&lt;li&gt;GetDesiredVelocity. Функция вычисляет желаемую скорость ходьбы или бега.&lt;/li&gt;&lt;li&gt;GetDirectionSpeedFactor. Когда игрок идет боком или задом, то его скорость должна быть меньше.&lt;/li&gt;&lt;li&gt;GetSlopeSpeedFactor. Когда игрок идет вверх по склону, то его скорость тоже уменьшается.&lt;/li&gt;
&lt;li&gt;GetDesiredSlidingVelocity. Функция вычисляет желаемую скорость скольжения. Когда персонаж находится на крутом склоне, то он должен скользить вниз по склону, при этом игрок может немного управлять своим движением.&lt;/li&gt;
&lt;li&gt;AdjustVelocityToGround. Выравнивание вектора скорости вдоль земли. Эта функция должна обеспечить лучшую соприкасаемость персонажа с землей. Но так как этим занимается ApplyDownOffset, то вероятно в этой функции нет никакого смысла.&lt;/li&gt;
&lt;li&gt;ApplyBraking. Когда персонаж упирается в стену, то надо уменьшить его скорость, чтобы он не бежал вдоль стену, уперевшись в нее носом.&lt;/li&gt;&lt;li&gt;ApplyGravity. Тут все просто и ясно.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

 &lt;div id=&quot;spoilerHead7&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(7, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler7&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(7, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre class=&quot;small&quot;&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;


partial &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; CharacterMotor {

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; Gravity = &lt;span class=&quot;digit&quot;&gt;20&lt;/span&gt;;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; MaxFallSpeed = &lt;span class=&quot;digit&quot;&gt;20&lt;/span&gt;;

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; WalkSpeed = &lt;span class=&quot;digit&quot;&gt;6&lt;/span&gt;;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; RunSpeed = &lt;span class=&quot;digit&quot;&gt;9&lt;/span&gt;;

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; SlidingSpeed = &lt;span class=&quot;digit&quot;&gt;0.4&lt;/span&gt;f;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; SlidingSideSpeed = &lt;span class=&quot;digit&quot;&gt;1.0&lt;/span&gt;f;

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GroundAcceleration = &lt;span class=&quot;digit&quot;&gt;20&lt;/span&gt;;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; AirAcceleration = &lt;span class=&quot;digit&quot;&gt;5&lt;/span&gt;;


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; ApplyMoving&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    Vector3 desiredVelocity;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isSliding&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      desiredVelocity = GetDesiredSlidingVelocity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    } &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt; {
      desiredVelocity = GetDesiredVelocity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    }

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isGrounded&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      desiredVelocity = AdjustVelocityToGround&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; desiredVelocity, groundNormal &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    } &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt; {
      desiredVelocity.y = velocity.y;
    }

    velocity = Vector3.MoveTowards&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity, desiredVelocity, GetAcceleration&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; isGrounded &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; * Time.deltaTime &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; ApplyGravity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!isJumping&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; velocity.y = Mathf.Min&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity.y, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// auto stepping может толкать персонажа вверх, предотвращаем это.&lt;/span&gt;

    velocity.y -= Gravity * Time.deltaTime;
    velocity.y = Mathf.Max&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity.y, -MaxFallSpeed &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; ApplyBraking&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector3 prevVelocity, ref Vector3 realVelocity&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// когда Character упирается в стену, он может начать движение вдоль стены&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// чтобы предотвратить это, определяем на сколько перпендикулярно Character уперся в стену и тормозим скорость на этот коэфициент&lt;/span&gt;

    var hPrevVelocity = GetXZ&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; prevVelocity &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// горизонтальные скорости&lt;/span&gt;
    var hRealVelocity = GetXZ&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; realVelocity &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;hPrevVelocity != Vector3.zero&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; braking = Vector3.Dot&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; hRealVelocity, hPrevVelocity &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; / hPrevVelocity.sqrMagnitude; &lt;span class=&quot;comment&quot;&gt;// 1 - векторы одинаковые; 0 - векторы разные&lt;/span&gt;
      braking = Mathf.Clamp01&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; braking &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      realVelocity.x *= braking;
      realVelocity.z *= braking;
    }
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 GetDesiredVelocity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; speed = GetSpeed&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; inputAcceleration &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    speed *= GetDirectionSpeedFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; transform.forward, inputMoveDirection &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isGrounded&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; speed *= GetSlopeSpeedFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; controller.velocity &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; inputMoveDirection * speed;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetDirectionSpeedFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector3 characterDirection, Vector3 moveDirection&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// если игрок идет задом или боком, то скорость должна быть меньше&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; dir = Vector3.Dot&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; characterDirection, moveDirection.normalized &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// 1 - идем вперед. 0 - боком. -1 - задом.&lt;/span&gt;
    dir = Mathf.InverseLerp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; -&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;, dir &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// from [-1, 1] to [0, 1]&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; Mathf.Lerp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;0.8&lt;/span&gt;f, &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;, dir &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// to [0.8, 1] backward / sideways, forward&lt;/span&gt;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetSlopeSpeedFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector3 velocity&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// уменьшаем скорость, когда игрок идет вверх по склону&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// velocity само принимает направление склона, в результате вызова Move&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; slopeAngle = Mathf.Asin&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;velocity.normalized.y&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; * Mathf.Rad2Deg; &lt;span class=&quot;comment&quot;&gt;// 0 - движемся горизонтально&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; t = Mathf.InverseLerp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;digit&quot;&gt;90&lt;/span&gt;, slopeAngle &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// [0, 90] to [0, 1]&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt; - t;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 GetDesiredSlidingVelocity&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    Vector3 slopeDir = GetXZ&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;groundNormal&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.normalized;
    Vector3 sideDir = &lt;span class=&quot;key&quot;&gt;new&lt;/span&gt; Vector3&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; slopeDir.z, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, -slopeDir.x &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; slopeDot = Vector3.Dot&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; inputMoveDirection, slopeDir &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// +-1 - движемся вдоль направления спуска. 0 - перпендикулярно спуску.&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; sideDot = Vector3.Dot&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; inputMoveDirection, sideDir &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// +-1 - движемся перпендикулярно спуску. 0 - вдоль спуска.&lt;/span&gt;

    var desiredVelocity = slopeDir; &lt;span class=&quot;comment&quot;&gt;// скольжение в направлении спуска&lt;/span&gt;
    desiredVelocity += slopeDir * slopeDot * SlidingSpeed; &lt;span class=&quot;comment&quot;&gt;// движение в направлении спуска&lt;/span&gt;
    desiredVelocity += sideDir * sideDot * SlidingSideSpeed; &lt;span class=&quot;comment&quot;&gt;// движение перпендикулярно спуску&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;// чем круче склон, тем больше скорость&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; slopeAngle = Vector3.Angle&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; groundNormal, Vector3.up &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// 0 - горизонтальная поверхность. 90 - вертикальная стена.&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; speed = Gravity * &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;slopeAngle / &lt;span class=&quot;digit&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; desiredVelocity * speed;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; Vector3 AdjustVelocityToGround&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector3 velocity, Vector3 groundNormal&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// выравниваем вектор скорости вдоль земли&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// это обеспечивает лучшую соприкасаемость игрока с землей&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// но т.к. этим занимается ApplyDownOffset, то данный метод вероятно не обязателен&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; Vector3.ProjectOnPlane&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; velocity, groundNormal &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.normalized * velocity.magnitude;
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetSpeed&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; run&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; run ? RunSpeed : WalkSpeed;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetAcceleration&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; grounded&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; grounded ? GroundAcceleration : AirAcceleration;
  }

}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;==5. CharacterMotor_Jumping==&lt;/p&gt;

&lt;p&gt;Это последняя часть класса, ответственная за прыжок. Она реализует фактически один ApplyJumping, в котором есть пара особенностей:&lt;ul&gt;&lt;li&gt;Игрок может регулировать высоту прыжка, отпуская или зажимая кнопку.&lt;/li&gt;&lt;li&gt;Направление прыжка зависит от нормали земли. Причем можно задать степерь влияния земли на обычной поверхности и крутом склоне.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

 &lt;div id=&quot;spoilerHead8&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(8, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler8&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(8, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;


partial &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; CharacterMotor {

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; BaseJumpHeight = &lt;span class=&quot;digit&quot;&gt;0.5&lt;/span&gt;f;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; ExtraJumpHeight = &lt;span class=&quot;digit&quot;&gt;0.75&lt;/span&gt;f;

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; readonly &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; baseJumpForce = GetJumpForce&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; BaseJumpHeight &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

  &lt;span class=&quot;comment&quot;&gt;// Как сильно нормаль земли влияет на направление прыжка&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;// 0 - Прыжок вертикальный (нормаль не влияет), 1 - Прыжок перпендикулярно земли (вдоль нормали)&lt;/span&gt;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GroundJumpImpactFactor = &lt;span class=&quot;digit&quot;&gt;0.1&lt;/span&gt;f;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; SteepGroundJumpImpactFactor = &lt;span class=&quot;digit&quot;&gt;0.5&lt;/span&gt;f; &lt;span class=&quot;comment&quot;&gt;// Когда игрок скользит по крутому спуску&lt;/span&gt;

  internal &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; isJumping;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; jumpStartTime;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 jumpDir;
  &lt;span class=&quot;comment&quot;&gt;//private float startY, maxY;&lt;/span&gt;

  
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; ApplyJumping&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isJumping &amp;amp;&amp;amp; isGrounded&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { &lt;span class=&quot;comment&quot;&gt;// stop jump&lt;/span&gt;
      isJumping = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;

      &lt;span class=&quot;comment&quot;&gt;//Debug.Log( maxY - startY ); // высота прыжка&lt;/span&gt;
    }

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;inputJump &amp;amp;&amp;amp; isGrounded&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { &lt;span class=&quot;comment&quot;&gt;// start jump&lt;/span&gt;
      isJumping = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
      jumpStartTime = Time.time;
      jumpDir = Vector3.Slerp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; Vector3.up, groundNormal, GetGroundJumpImpactFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; isSliding &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

      velocity.y = &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
      velocity += jumpDir * baseJumpForce;

      &lt;span class=&quot;comment&quot;&gt;//startY = maxY = transform.position.y;&lt;/span&gt;
    }

    &lt;span class=&quot;comment&quot;&gt;//if(isJumping) {&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;//    maxY = Mathf.Max( maxY, transform.position.y );&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;//}&lt;/span&gt;

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;isJumping &amp;amp;&amp;amp; inputJumpHolding&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { &lt;span class=&quot;comment&quot;&gt;// extra jump&lt;/span&gt;
      &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; jumpHoldingTime = Time.time - jumpStartTime;
      &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;jumpHoldingTime &lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt; ExtraJumpHeight / baseJumpForce&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
        velocity += jumpDir * Gravity * Time.deltaTime;
      }
    }
  }


  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetJumpForce&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; targetJumpHeight&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; Mathf.Sqrt&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt; * Gravity * targetJumpHeight &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetGroundJumpImpactFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; sliding&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; sliding ? SteepGroundJumpImpactFactor : GroundJumpImpactFactor;
  }

}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;==6. FPSInputController==&lt;/p&gt;

&lt;p&gt;Чтобы сделать полноценного, управляемого персонажа, нам нужно еще пара вспомогательных классов. Первый – обрабатывает ввод. Этот класс очень простой, но в нем есть две особенности:&lt;ul&gt;&lt;li&gt;Персонаж может прыгнуть, если кнопка прыжка была нажата в последние 0.2 секунды. Это сделано для того, чтобы игрок мог не ловить момент, когда персонаж коснется земли, а мог нажимать кнопку чуть раньше.&lt;/li&gt;&lt;li&gt;Используя аналоговые джойстики, игрок может регулировать скорость движения персонажа. Скорость с джойстика находится в промежутке от 0 до 1. Это скорость возводится в квадрат, чтобы игрок мог лучше управлять маленькой скоростью или что-то типа того. Т.е. если игрок сдвинул джойстик лишь на 0.5, то скорость будет 0.25. А если джойстик на значении 1, то и будет 1. Сам я это не тестировал, и не уверен удобно ли это вообще.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Кстати, FPS расшифровывается, по идее, как first person shooter.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead9&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(9, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler9&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(9, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;


&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;AddComponentMenu&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Character/FPSInputController&amp;quot;&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;RequireComponent&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; typeof&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;CharacterMotor&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; FPSInputController : MonoBehaviour {
  
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; CharacterMotor motor;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; jumpPressedTime = -&lt;span class=&quot;digit&quot;&gt;100&lt;/span&gt;;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; prevJumping = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;


  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Awake &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    motor = GetComponent&lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;CharacterMotor&lt;span class=&quot;bracket&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }

  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Update &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; Input.GetButtonDown&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Jump&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; jumpPressedTime = Time.time;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; !Input.GetButton&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Jump&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; jumpPressedTime = -&lt;span class=&quot;digit&quot;&gt;100&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; !prevJumping &amp;amp;&amp;amp; motor.isJumping &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; jumpPressedTime = -&lt;span class=&quot;digit&quot;&gt;100&lt;/span&gt;; &lt;span class=&quot;comment&quot;&gt;// чтобы предотвратить двойной прыжок&lt;/span&gt;
    prevJumping = motor.isJumping;

    motor.inputMoveDirection = GetInputMoveDirection&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    motor.inputJump = Time.time - jumpPressedTime &lt;span class=&quot;bracket&quot;&gt;&amp;lt;&lt;/span&gt;= &lt;span class=&quot;digit&quot;&gt;0.2&lt;/span&gt;f; &lt;span class=&quot;comment&quot;&gt;// кнопка была нажата в последние 0.2 секунды&lt;/span&gt;
    motor.inputJumpHolding = Input.GetButton&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Jump&amp;quot;&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    motor.inputAcceleration = Input.GetKey&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; KeyCode.LeftShift &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }
  

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 GetInputMoveDirection&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    var h = Input.GetAxis&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Horizontal&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    var v = Input.GetAxis&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Vertical&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    Vector3 direction = &lt;span class=&quot;key&quot;&gt;new&lt;/span&gt; Vector3&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;h, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, v&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;direction != Vector3.zero&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
      &lt;span class=&quot;comment&quot;&gt;// Делаем input dir более чувствительным к крайним значениям и менее чувствительна к средним.&lt;/span&gt;
      &lt;span class=&quot;comment&quot;&gt;// Это позволит легче контролировать медленную скоростью при использовании аналогового джойстика&lt;/span&gt;
      &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; len = direction.magnitude;
      len = Mathf.Min&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;, len&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      len = len * len;
      direction = direction.normalized * len;
      &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; transform.TransformDirection&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;direction&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    }
    &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; Vector3.zero;
  }

  
}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;==7. MouseLook==&lt;/p&gt;

&lt;p&gt;Это последний и простейший класс, ответственный за вращение камеры. Точнее, камера вращается только по оси X, а по оси Y вращается весь персонаж.&lt;/p&gt;

 &lt;div id=&quot;spoilerHead10&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(10, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler10&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(10, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;


&lt;span class=&quot;bracket&quot;&gt;[&lt;/span&gt;AddComponentMenu&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Character/MouseLook&amp;quot;&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; MouseLook : MonoBehaviour {

  &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;enum&lt;/span&gt; Axis {
    X, Y, XY
  }

  &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; Axis axis = Axis.XY;

  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; Sensitivity = &lt;span class=&quot;digit&quot;&gt;5&lt;/span&gt;f;
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; MinY = -&lt;span class=&quot;digit&quot;&gt;88&lt;/span&gt;f, MaxY = &lt;span class=&quot;digit&quot;&gt;88&lt;/span&gt;f;
  
  &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector2 angles = Vector2.zero;
  

  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; OnEnable&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    angles = transform.localEulerAngles;
  }
  
  &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Update &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Cursor.visible&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt;;
    
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; x = Input.GetAxis&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Mouse X&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; y = Input.GetAxis&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Mouse Y&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;  
    Vector2 delta = &lt;span class=&quot;key&quot;&gt;new&lt;/span&gt; Vector2&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;-y, x&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    
    angles += delta * Sensitivity;
    angles.x = Mathf.Clamp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;angles.x, MinY, MaxY&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;axis == Axis.Y&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; angles.x = transform.localEulerAngles.x;
    &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;axis == Axis.X&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; angles.y = transform.localEulerAngles.y;
    
    Quaternion targetRotation = Quaternion.Euler&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;angles&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
    transform.localRotation = Quaternion.Slerp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt; transform.localRotation, targetRotation, &lt;span class=&quot;digit&quot;&gt;40&lt;/span&gt; * Time.deltaTime &lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
  }
  
}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;==8. Заключение==&lt;/p&gt;

&lt;p&gt;Я переписал эти юнити скрипты практически до неузнаваемости, что-то добавил, что-то удалил, но тем не менее принципиально ничего не изменилось. Оригинальные скрипты для Unity 4 и 5, вы можете посмотреть в корне репозитория. Код, надеюсь, я довел до нормального состояния. Но, как я сказал, принципиально ничего не изменилось, то некоторые вещи мне не нравятся. Также есть еще много вещей, которые можно было бы добавить. Поэтому я буду стараться обновлять репозиторий, и буду рад вашим комментариям и предложениям.&lt;/p&gt;

&lt;p&gt;Заметил пару багов. Когда CharacterController стоит на сфере, то нормаль поверхности сферы направлена внутрь сферы. Из-за этого персонаж не понимает, что он стоит на земле. На втором видео, в момент 3:07, видно странное разрешение коллизии, которое приводит к тому, что CharacterController резко толкается вниз, когда напрыгивает на такую платформу.&lt;/p&gt;

&lt;p&gt;Официальный гид по CharacterController: &lt;a href=&quot;http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/CharacterControllers.html&quot;&gt;http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/g&amp;hellip; trollers.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Репозиторий: &lt;a href=&quot;https://bitbucket.org/dddenisss/unity-character-motor&quot;&gt;https://bitbucket.org/dddenisss/unity-character-motor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
&lt;div class=&quot;video-container&quot;&gt;&lt;div class=&quot;video&quot; id=&quot;tube3&quot; data-value=&quot;NmZVWA81L5k&quot;&gt;&lt;img data-value=&quot;NmZVWA81L5k&quot; src=&quot;https://img.youtube.com/vi/NmZVWA81L5k/maxresdefault.jpg&quot; onload=&quot;skif.loadYoutubeImage(event)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;img class=&quot;play&quot; src=&quot;https://gamedev.ru/_img/youtube-play.svg&quot; onclick=&quot;skif.insertYoutube(3)&quot; onMouseover=&quot;skif.insertYoutube(3)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;
&lt;div class=&quot;video-container&quot;&gt;&lt;div class=&quot;video&quot; id=&quot;tube4&quot; data-value=&quot;fmmdPLUIh-Q&quot;&gt;&lt;img data-value=&quot;fmmdPLUIh-Q&quot; src=&quot;https://img.youtube.com/vi/fmmdPLUIh-Q/maxresdefault.jpg&quot; onload=&quot;skif.loadYoutubeImage(event)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;img class=&quot;play&quot; src=&quot;https://gamedev.ru/_img/youtube-play.svg&quot; onclick=&quot;skif.insertYoutube(4)&quot; onMouseover=&quot;skif.insertYoutube(4)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unity5_4dropswebplayer</guid>
  <pubDate>Thu, 14 Jan 2016 19:07:45 GMT</pubDate>
  <title>В Unity 5.4 будет прекращена поддержка WebPlayer</title>
  <link>https://gamedev.ru/unity/unity5_4dropswebplayer</link>
  <comments>https://gamedev.ru/unity/forum/?id=209751</comments>
  <category>beta</category>
  <category>editor</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Согласно &lt;a href=&quot;http://unity3d.com/unity/beta&quot;&gt;Unity 5.4.0b1 release notes&lt;/a&gt; и &lt;a href=&quot;http://blogs.unity3d.com/ru/2015/10/08/unity-web-player-roadmap/&quot;&gt;Роадмап Unity Web Player&lt;/a&gt;, в Unity 5.4, выпуск которого запланирован на март 2016 года, будет прекращена поддержка платформы &lt;a href=&quot;https://unity3d.com/ru/webplayer&quot;&gt;WebPlayer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Официальный блог Unity3D &lt;a href=&quot;http://blogs.unity3d.com/ru/2015/05/07/%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-o-unity-web-player-%D0%B8-webgl/&quot;&gt;рекомендует переводить игры&lt;/a&gt;, использовавшие WebPlayer, на платформу WebGL.
&lt;br&gt;
Тем не менее, сами Unity признают, что WebGL всё ещё &lt;a href=&quot;http://blogs.unity3d.com/ru/2015/05/28/web-publishing-following-chrome-npapi-deprecation/&quot;&gt;накладывает серьёзные ограничения&lt;/a&gt; на некоторые игры, изначально создаваемые для WebPlayer.&lt;/p&gt;

&lt;p&gt;Чтобы помочь разработчикам сориентироваться в новой технологии WebGL, на форуме Unity была создана специальная тема &lt;a href=&quot;http://forum.unity3d.com/threads/webgl-roadmap.334408/&quot;&gt;WebGL Roadmap&lt;/a&gt;, которая помогает понять, какие ограничения есть у WebGL и как их можно обойти. Тема была создана полгода назад, поэтому рекомендуется следить за &lt;a href=&quot;https://unity3d.com/ru/unity/roadmap&quot;&gt;Unity roadmap&lt;/a&gt; и &lt;a href=&quot;http://blogs.unity3d.com/&quot;&gt;официальным блогом&lt;/a&gt;, чтобы быть в курсе изменений платформы WebGL.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unityeditorlinux</guid>
  <pubDate>Sun, 12 Jul 2015 18:46:17 GMT</pubDate>
  <title>Редактор Unity появится на Linux</title>
  <link>https://gamedev.ru/unity/unityeditorlinux</link>
  <comments>https://gamedev.ru/unity/forum/?id=204396</comments>
  <category>editor</category>
  <category>Linux</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Unity Technologies планируют &lt;a href=&quot;http://blogs.unity3d.com/2015/07/01/the-state-of-unity-on-linux/&quot;&gt;портировать редактор Unity на Linux&lt;/a&gt;.
&lt;br&gt;
Он обязательно будет поддерживать Ubuntu версии не менее 12.04 и будет настолько же функциональным, как и его версии для Mac OS и Windows, кроме сборки игр под iOS.
&lt;br&gt;
В данный момент редактор существует в виде отдельной экспериментальной ветки внутри компании. Unity Technologies планируют в скором времени собрать экспериментальную сборку редактора и представить её публике.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unityvideotutors</guid>
  <pubDate>Mon, 29 Jun 2015 19:14:28 GMT</pubDate>
  <title>Видеоуроки по программированию на C# Unity для начинающих</title>
  <link>https://gamedev.ru/unity/unityvideotutors</link>
  <comments>https://gamedev.ru/unity/forum/?id=203300</comments>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Программирование на C# под Unity.
&lt;br&gt;
&lt;a href=&quot;https://www.youtube.com/playlist?list=PLY7lEIeNqQdOsBi2QxPVgqLVecv3Br4TM&quot;&gt;Смотрим на Ютубе.&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/Unity4_6relased</guid>
  <pubDate>Thu, 27 Nov 2014 16:57:40 GMT</pubDate>
  <title>Вышел Unity 4.6</title>
  <link>https://gamedev.ru/unity/Unity4_6relased</link>
  <category>UI</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Сегодня вышла новая версия Unity 4.6. Главным нововведением в этой версии стала новая система создания пользовательского интерфейса, которая позволяет легко настраивать интерфейс игры в редакторе.&lt;/p&gt;
&lt;p&gt;Более того, Unity сразу &lt;a href=&quot;http://blogs.unity3d.com/2014/11/26/4-6-is-released-with-source-for-ui-system/&quot;&gt;открыла исходный код новой системы UI&lt;/a&gt; для всех желающих, который доступен по ссылке &lt;a href=&quot;https://bitbucket.org/Unity-Technologies/ui&quot;&gt;https://bitbucket.org/Unity-Technologies/ui&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Кроме непосредственно релиза новой версии, в блоге Unity появилась &lt;a href=&quot;http://blogs.unity3d.com/2014/11/24/sustained-engineering-plan-for-unity-4-5-and-4-6/&quot;&gt;информация о датах и содержимом ближайших релизов&lt;/a&gt; текущей версии движка. Карта выпусков изображена на картинке:&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/unity4_6.png&quot; alt=&quot;Unity 4.6 | Вышел Unity 4.6&quot; title=&quot;Unity 4.6&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;a href=&quot;http://unity3d.com/unity/whats-new&quot;&gt;http://unity3d.com/unity/whats-new&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/Unity4_6OpenBeta</guid>
  <pubDate>Thu, 21 Aug 2014 19:49:06 GMT</pubDate>
  <title>Открытая бета Unity 4.6 доступна для скачивания</title>
  <link>https://gamedev.ru/unity/Unity4_6OpenBeta</link>
  <comments>https://gamedev.ru/unity/forum/?id=192452</comments>
  <category>beta</category>
  <category>UI</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/beta46-header.jpg&quot; alt=&quot;Unity4.6 Open Beta | Открытая бета Unity 4.6 доступна для скачивания&quot; title=&quot;Unity4.6 Open Beta&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Стала доступной открытая бета-версия Unity 4.6: &lt;a href=&quot;http://unity3d.com/unity/beta/4.6&quot;&gt;http://unity3d.com/unity/beta/4.6&lt;/a&gt;
&lt;br&gt;
Главное новшество в ней - новая система UI, которая наконец-таки соответствует современным стандартам.&lt;/p&gt;

&lt;p&gt;Приятным бонусом также является исходный код встроенных в Unity шейдеров, который можно скачать на той же странице. Это является следствием инициативы Unity по открытию исхдных кодов своих компонентов: &lt;a href=&quot;http://blogs.unity3d.com/2014/08/20/more-open-source-initiatives-from-unity/&quot;&gt;http://blogs.unity3d.com/2014/08/20/more-open-source-initiatives-from-unity/&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unityvs</guid>
  <pubDate>Tue, 12 Aug 2014 15:58:53 GMT</pubDate>
  <title>Visual Studio Tools for Unity стал бесплатным</title>
  <link>https://gamedev.ru/unity/unityvs</link>
  <comments>https://gamedev.ru/unity/forum/?id=192353</comments>
  <category>Visual Studio</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;29 июля в блоге Microsoft без лишнего шума появилась новость о том, что официально выпущен бесплатный плагин для Visual Studio под названием Visual Studio Tools for Unity, ранее известный как UnityVS.
&lt;br&gt;
Его ключевой особенностью является возможность отладки проекта Unity в Visual Studio. Без этого плагина отладка возможна только с помощью MonoDevelop.&lt;/p&gt;

&lt;p&gt;Ссылки на скачивание:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/6e536faa-ce73-494a-a746-6a14753015f1&quot;&gt;Visual Studio 2010 Tools for Unity&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/7ab11d2a-f413-4ed6-b3de-ff1d05157714&quot;&gt;Visual Studio 2012 Tools for Unity&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/20b80b8c-659b-45ef-96c1-437828fe7cf2&quot;&gt;Visual Studio 2013 Tools for Unity&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;br&gt;
&lt;a href=&quot;http://unityvs.com/documentation/configuration/&quot;&gt;Инструкция по установке&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/?id=6843</guid>
  <pubDate>Mon, 07 Jul 2014 19:20:16 GMT</pubDate>
  <title>Unity про поддержку Metal</title>
  <link>https://gamedev.ru/unity/?id=6843</link>
  <category>Apple</category>
  <category>metal</category>
  <category>Unity</category>
  <category>WWDC</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;http://blogs.unity3d.com/wp-content/themes/UnityHD/images/large.jpg&quot; alt=&quot;Изображение&quot; /&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;br&gt;
&lt;p&gt;На недавней WWDC компанией Apple был представлен новый графический API под названием Metal, отличительной особенностью которого стала высокая эффективность, низкий уровень издержек и оптимизация под чип A7. Metal предоставляет разработчикам возможность воспользоваться всеми преимуществами устройств на iOS и добиться намного большего уровня реалистичности, детализации и интерактивности в играх, чем когда бы то ни было. 
&lt;br&gt;
Unity очень скоро добавит поддержку Metal в свой инструментарий. Новая &lt;a href=&quot;http://blogs.unity3d.com/2014/07/03/metal-a-new-graphics-api-for-ios-8&quot;&gt;запись&lt;/a&gt; в блоге поможет узнать подробности о новой технологии, и разъяснит, почему это очень хороший вариант для разработчиков.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unity4_5released</guid>
  <pubDate>Tue, 27 May 2014 16:51:06 GMT</pubDate>
  <title>Вышел Unity 4.5</title>
  <link>https://gamedev.ru/unity/unity4_5released</link>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Сегодня вышла новая версия Unity 4.5&lt;/p&gt;

&lt;p&gt;В ней исправлено более 450 багов, а также добавлены новые возможности:&lt;ul&gt;&lt;li&gt;Добавлена поддержка OpenGL ES 3.0 для новейших устройств iOS&lt;/li&gt;&lt;li&gt;Улучшена 2D физика, в том числе добавлены новые виды гизмо и соединений, добавлена возможность перемещать rigibody в режиме проигрывания.&lt;/li&gt;&lt;li&gt;Сцены загружаются быстрее За счёт переработки сериализации MonoBehaviour&lt;/li&gt;&lt;li&gt;Намного ускорена компиляция шейдеров, т.к. они компилируются только для текущей платформы, а не для всех сразу.&lt;/li&gt;&lt;li&gt;В среде разработки все мобильные платформы вынесены в отдельные модули, что позволяет обновлять их независимо, без необходимости обновления всего Unity.&lt;/li&gt;&lt;li&gt;Обновлён Unity Remote - инструмента удалённой отладки мобильных приложений Android и iOS&lt;/li&gt;&lt;li&gt;В Pro-версии появилась поддержка стереоскопического рендеринга &lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Полный список изменений можно найти &lt;a href=&quot;http://unity3d.com/unity/whats-new/unity-4.5&quot;&gt;здесь&lt;/a&gt;.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/psmpublicpreview</guid>
  <pubDate>Wed, 09 Apr 2014 12:45:15 GMT</pubDate>
  <title>Появился public preview Unity для PlayStation®Mobile</title>
  <link>https://gamedev.ru/unity/psmpublicpreview</link>
  <category>PlayStation</category>
  <category>Vita</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Как сообщается в &lt;a href=&quot;http://blogs.unity3d.com/2014/04/08/playstationvita-for-all-the-unity-for-playstationmobile-public-preview/&quot;&gt;официальном блоге Unity&lt;/a&gt;, появилась предварительная версия Unity для PlayStation®Mobile.
&lt;br&gt;
Достаточно &lt;a href=&quot;https://psm.playstation.net/portal/en/index.html#register&quot;&gt;зарегистрироваться&lt;/a&gt; на портале как разработчику , затем скачать &lt;a href=&quot;https://psm.playstation.net/static/general/dev/en/sdk_assets.html&quot;&gt;необходимые библиотеки&lt;/a&gt;, и вы сможете разворачивать на PS Vita игры, созданные в Unity.&lt;/p&gt;

&lt;p&gt;В отличие от &lt;a href=&quot;http://blogs.unity3d.com/2014/01/15/playstation-vita-deployment-is-here/&quot;&gt;предыдущего варианта&lt;/a&gt; развёртывания Unity на PS Vita, развёртывание приложений Unity для PlayStation®Mobile не даёт полный доступ как к функциям самой консоли, так и к PSN, средствам отладки, интеграции с Visual Studio и многим другим преимуществам. Кроме того, на данный момент приложения, созданные в Unity для PlayStation®Mobile нельзя публиковать в PSN.&lt;/p&gt;

&lt;p&gt;Видео проекта Angry Bots, который идёт в комплекте с редактором Unity, запущенного на PS Vita:
&lt;br&gt;

&lt;div class=&quot;video-container&quot;&gt;&lt;div class=&quot;video&quot; id=&quot;tube5&quot; data-value=&quot;fOWif2BTqeI&quot;&gt;&lt;img data-value=&quot;fOWif2BTqeI&quot; src=&quot;https://img.youtube.com/vi/fOWif2BTqeI/maxresdefault.jpg&quot; onload=&quot;skif.loadYoutubeImage(event)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;img class=&quot;play&quot; src=&quot;https://gamedev.ru/_img/youtube-play.svg&quot; onclick=&quot;skif.insertYoutube(5)&quot; onMouseover=&quot;skif.insertYoutube(5)&quot; alt=&quot;Запустить видео по клику - Как делать игры&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;b&gt;UPD&lt;/b&gt;: владельцам аккаунтов PSN в странах бывшего СНГ не удастся использовать Unity для PlayStation®Mobile, т.к. эта система требует PSM Publisher key, а жители стран бывшего СНГ &lt;a href=&quot;https://en-support.psm.playstation.net/app/answers/detail/a_id/41/related/1/session/L2F2LzEvdGltZS8xMzk3MDY0ODkzL3NpZC9HS21NNW5SbA%3D%3D&quot;&gt;могут получить только ключ PSM Developer&lt;/a&gt;, которого недостаточно для развёртывания приложений PSM на PS Vita даже локально.&lt;/p&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unity5preorder</guid>
  <pubDate>Tue, 18 Mar 2014 19:21:32 GMT</pubDate>
  <title>Появилась возможность сделать предзаказ Unity 5</title>
  <link>https://gamedev.ru/unity/unity5preorder</link>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;На сайте &lt;a href=&quot;http://unity3d.com/5&quot;&gt;Unity&lt;/a&gt; появилась реклама новой версии Unity3D пятой версии.
&lt;br&gt;
В ней будут:&lt;ul&gt;&lt;li&gt;Физически корректные шейдеры для всех платформ&lt;/li&gt;&lt;li&gt;Кроссплатформенное глобальное освещение&lt;/li&gt;&lt;li&gt;Улучшенный редактор звука&lt;/li&gt;&lt;li&gt;Поддержка WebGL&lt;/li&gt;&lt;li&gt;Поддержка редактором 64-битной архитектуры&lt;/li&gt;&lt;li&gt;Унифицированный код показа рекламы для всех мобильных платформ (один из сервисов Unity Cloud)&lt;/li&gt;&lt;li&gt;Новые эффекты для двумерной физики&lt;/li&gt;&lt;/ul&gt;
&lt;br&gt;
и много прочего (некоторые возможности доступны только в Pro версии).&lt;/p&gt;

&lt;p&gt;Посмотреть видео с новыми возможностями пятого Unity можно тут: &lt;a href=&quot;http://www.youtube.com/watch?v=tSfakMeW0lw&quot;&gt;http://www.youtube.com/watch?v=tSfakMeW0lw&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/psvitapub</guid>
  <pubDate>Wed, 15 Jan 2014 16:34:11 GMT</pubDate>
  <title>В Unity3D добавляется публикация для Playstation Vita</title>
  <link>https://gamedev.ru/unity/psvitapub</link>
  <category>PlayStation</category>
  <category>Sony</category>
  <category>Vita</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Как сообщается в &lt;a href=&quot;http://blogs.unity3d.com/2014/01/15/playstation-vita-deployment-is-here/&quot;&gt;официальном блоге Unity&lt;/a&gt;, в Unity 4.3 добавляется возможность публикации игр для платформы Playstation Vita.&lt;/p&gt;&lt;p&gt;Если у разработчика есть лицензионное соглашение с SCE для PS Vita, то он может воспользоваться этой возможностью.&lt;/p&gt;&lt;p&gt;Это, в том числе, позволит разработчикам использовать такие особенности платформы, как:
&lt;br&gt;
&lt;li&gt;Датчики движения&lt;/li&gt;
&lt;li&gt;Переднюю и заднюю камеру&lt;/li&gt;
&lt;li&gt;Два аналоговых стика&lt;/li&gt;
&lt;li&gt;Заднюю сенсорную панель&lt;/li&gt;&lt;/p&gt;
&lt;p&gt;Также это позволит интегрировать в игру все возможности PSN, включая трофеи, друзей и подбор противников.&lt;/p&gt;&lt;p&gt;Чтобы стать лицензионным разработчиком для PS Vita, необходимо зарегистрировать свою компанию по ссылке &lt;a href=&quot;https://www.companyregistration.playstation.com/SCEInternetApplications/AreaNewLicensee/MainSystem/CFModules/NewLicensee/snl_master_template.cfm&quot;&gt;&lt;a href=&quot;https://www.companyregistration.playstation.com/&quot;&gt;https://www.companyregistration.playstation.com/&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/tutors</guid>
  <pubDate>Sun, 15 Sep 2013 10:37:13 GMT</pubDate>
  <title>Основы</title>
  <link>https://gamedev.ru/unity/tutors</link>
  <comments>https://gamedev.ru/unity/forum/?id=180683</comments>
  <category>fundamental</category>
  <category>tutors</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;В шапке сообщества есть ссылка на начальные уроки по Юнити. На самом деле на официальном сайте Юнити есть целый раздел с видеоуроками по основам - &lt;a href=&quot;http://unity3d.com/learn/tutorials/modules&quot;&gt;УРОКИ&lt;/a&gt;
&lt;br&gt;
На данный момент есть 54 урока для начинающих и 12 уроков для разработчиков со средним знанием движка.
&lt;br&gt;
Уроки охватывают широкий спектр владения движком - от интерфейса до написания скриптов.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/2dtools</guid>
  <pubDate>Fri, 30 Aug 2013 06:29:31 GMT</pubDate>
  <title>Unity добавляет встроенные инструменты для 2D игр в Unity 4.3</title>
  <link>https://gamedev.ru/unity/2dtools</link>
  <comments>https://gamedev.ru/unity/forum/?id=181414</comments>
  <category>2D</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Согласно &lt;a href=&quot;http://blogs.unity3d.com/2013/08/28/unity-native-2d-tools/&quot;&gt;официальному блогу Unity&lt;/a&gt;, в Unity 4.3 появится встроенная поддержка инструментов для двумерных игр (включая бесплатную версию движка) + специальный демонстранционный проект.
&lt;br&gt;
В состав этих инструментов входит менеджер спрайтов и новый раздел компонентов двумерной физики.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unitymobilefreeaddons</guid>
  <pubDate>Wed, 22 May 2013 07:20:34 GMT</pubDate>
  <title>Базовые аддоны для Android и iOS теперь бесплатны для инди-разработчиков</title>
  <link>https://gamedev.ru/unity/unitymobilefreeaddons</link>
  <category>Android</category>
  <category>iOS</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Как сообщает Unity в своём &lt;a href=&quot;http://blogs.unity3d.com/2013/05/21/putting-the-power-of-unity-in-the-hands-of-every-mobile-developer/&quot;&gt;блоге&lt;/a&gt;, начиная с последней версии Unity (судя по всему, это версия 4.1.2), аддоны для Android и iOS становятся абсолютно бесплатными. Это правило распространяется на всех независимых разработчиков (тех, чей годовой оборот не превышает 100 000$). Остальные обязаны купить платную версию аддонов.&lt;/p&gt;&lt;p&gt;Аддоны для Windows 8 и Blackberry 10 также станут бесплатными в ближайшие месяцы.&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/flashsunset</guid>
  <pubDate>Wed, 24 Apr 2013 15:48:37 GMT</pubDate>
  <title>Unity прекращает поддержку Flash</title>
  <link>https://gamedev.ru/unity/flashsunset</link>
  <comments>https://gamedev.ru/unity/forum/?id=175706</comments>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Как сообщает Unity в своём &lt;a href=&quot;http://blogs.unity3d.com/2013/04/23/sunsetting-flash/&quot;&gt;блоге&lt;/a&gt;, компания прекращает выдачу новых лицензий на разработку под платформу Adobe Flash. Текущие владельцы лицензий будут поддерживаться вместе с редактором Unity3D версий 4.X.
&lt;br&gt;
В качестве одной из причин для такого шага Unity называет прекращение поддержки Flash самой фирмой Adobe в будущем, и напоминает, что для публикации веб версий игр по прежнему можно будет пользоваться Unity Web Player, который показал беспрецедентное количество установок (200 миллионов, при этом 1/3 геймеров на Facebookпользуются этим плеером).&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unite2013</guid>
  <pubDate>Wed, 23 Jan 2013 13:02:35 GMT</pubDate>
  <title>Открыта регистрация на Unite 2013</title>
  <link>https://gamedev.ru/unity/unite2013</link>
  <category>мероприятия</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/vancouverbanner460x200.png&quot; alt=&quot;vancouverbanner460x200 | Открыта регистрация на Unite 2013&quot; title=&quot;vancouverbanner460x200&quot;&gt;&lt;/p&gt;
&lt;br&gt;
В этом году главная конференция разработчиков Unity пройдёт в Ванкувере, Канада с 28 по 30 августа. Конференция состоится в Vancouver Convention Centre, расположенном на берегу гавани.
&lt;br&gt;
Цена посещения порядка 300-340$ + 100$ за тренировочный день, целиком состоящий из уроков для новичков.
&lt;p&gt;Подробнее на &lt;a href=&quot;https://unite2013.eventbrite.com/&quot;&gt;https://unite2013.eventbrite.com/&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/articles/panandpinchzoom</guid>
  <pubDate>Sat, 20 Oct 2012 16:15:18 GMT</pubDate>
  <title>Реализуем multi-touch жесты самостоятельно</title>
  <link>https://gamedev.ru/unity/articles/panandpinchzoom</link>
  <comments>https://gamedev.ru/unity/forum/?id=168321</comments>
  <category>Программирование</category>
  <category>Android</category>
  <category>iOS</category>
  <category>touch</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;В этой статье будет рассмотрена реализация pan (перемещения) и pinch zoom (масштабирования двумя пальцами) изображения. В результате получится некий аналог приложения для просмотра фотографий.&lt;/p&gt;
&lt;br&gt;
==Часть первая (с картинками). Подготовка==&lt;p&gt;
&lt;br&gt;
Создадим новый проект и настроим камеру на ортогональную проекцию (свойство Projection = Orthographic). Компоненты &lt;b&gt;GUILayer&lt;/b&gt;, &lt;b&gt;Flare Layer&lt;/b&gt; и &lt;b&gt;Audio Listener&lt;/b&gt; можно отключить или вообще удалить за ненадобностью.&lt;/p&gt;&lt;p&gt;После этого сохраним нашу сцену (Ctrl+S), назвав её как-нибудь, например, MainScene.unity. В итоге экран редактора дожен выглядеть примерно так:&lt;/p&gt;
 &lt;div id=&quot;spoilerHead11&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(11, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler11&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(11, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/83352_1354204778_1.png&quot; alt=&quot;1 | Реализуем multi-touch жесты самостоятельно&quot; title=&quot;1&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;/p&gt;&lt;p&gt;Теперь зададим режим отрисовки объектов (если в игре не будет ничего, кроме спрайтов): 
&lt;br&gt;
&lt;i&gt;Edit -&amp;gt; Project Settings -&amp;gt; Player -&amp;gt; Other settings -&amp;gt; Rendering -&amp;gt; Rendering Path = Vertex Lit&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Нам нужно создать какой-то объект, который будет отображаться на экране, чтобы мы видели, как он двигается. Для этой цели могла бы подойти плоскость (Game Object -&amp;gt;Create Other -&amp;gt; Plane), но она состоит из 400 треугольников, что неоправданно много для отображения одного спрайта. Поэтому воспользуемся скриптом отсюда &lt;a href=&quot;http://wiki.unity3d.com/index.php/CreatePlane&quot;&gt;&lt;a href=&quot;http://wiki.unity3d.com/index.php/CreatePlane&quot;&gt;http://wiki.unity3d.com/index.php/CreatePlane&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Создадим папку Editor в папке Assets и добавим туда новый файл CreatePlane.cs, текст которого можно найти по ссылке выше.&lt;/p&gt;&lt;p&gt;После этого в меню появится новый пункт &lt;i&gt;GameObject -&amp;gt; Create Other -&amp;gt; Custom Plane...&lt;/i&gt; Создадим новую плоскость с такими параметрами:
&lt;br&gt;
&lt;img src=&quot;https://gamedev.ru/files/images/83353_1354204803_2.png&quot; alt=&quot;2 | Реализуем multi-touch жесты самостоятельно&quot; title=&quot;2&quot;&gt;
&lt;br&gt;
На экране появится розовый квадрат - это наш нетекстурированный объект.&lt;/p&gt;&lt;p&gt;Добавим какую-нибудь картинку в папку Assets, создадим для него новый материал и установим для него текстурой только что добавленное изображение. Для материала поставим шейдер Unlit/Texture:&lt;/p&gt;
 &lt;div id=&quot;spoilerHead12&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(12, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler12&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(12, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/83354_1354204818_3.png&quot; alt=&quot;3 | Реализуем multi-touch жесты самостоятельно&quot; title=&quot;3&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;/p&gt;&lt;p&gt;Наконец, установим материал для объекта:&lt;/p&gt;
 &lt;div id=&quot;spoilerHead13&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(13, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler13&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(13, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/83355_1354204839_4.png&quot; alt=&quot;4 | Реализуем multi-touch жесты самостоятельно&quot; title=&quot;4&quot; loading=&quot;lazy&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;p&gt;Теперь у нас есть неподвижный объект на экране, и нам нужно написать код, который будет двигать камеру.&lt;/p&gt;
&lt;br&gt;
==Часть вторая. Код==&lt;p&gt;Создадим новый файл с текстом скрипта под названием CameraScript и добавим его к MainCamera (перетянем в окно Hierarchy на MainCamera). Затем запишем в него такой текст:&lt;/p&gt;
 &lt;div id=&quot;spoilerHead14&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(14, 1)&quot;&gt;+  Показать&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;spoiler14&quot; style=&quot;display:none;&quot;&gt;&lt;div class=&quot;bound overflow&quot;&gt;&lt;span class=&quot;splink&quot; onclick=&quot;javascript:showSpoiler(14, 0)&quot;&gt;&amp;minus; Скрыть&lt;/span&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; UnityEngine;
&lt;span class=&quot;key&quot;&gt;using&lt;/span&gt; System.Collections;

&lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;class&lt;/span&gt; CameraScript : MonoBehaviour
{
    &lt;span class=&quot;comment&quot;&gt;// двигаем камеру&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; drag = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
    &lt;span class=&quot;comment&quot;&gt;// масштабируем&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; zoom = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;

    &lt;span class=&quot;comment&quot;&gt;// экранные координаты начальной точки касания&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 initialTouchPosition;
    &lt;span class=&quot;comment&quot;&gt;// мировые координаты камеры при инициировании&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// перемещения/масштабирования&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 initialCameraPosition;

    &lt;span class=&quot;comment&quot;&gt;// экранные координаты начальной точки первого касания&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 initialTouch0Position;
    &lt;span class=&quot;comment&quot;&gt;// экранные координаты начальной точки второго касания&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 initialTouch1Position;
    &lt;span class=&quot;comment&quot;&gt;// средняя точка между начальными координатами касаний&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; Vector3 initialMidPointScreen;
    &lt;span class=&quot;comment&quot;&gt;// ортогональный размер камеры на момент начала масштабирования&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; initialOrthographicSize;

    &lt;span class=&quot;comment&quot;&gt;// Use this for initialization&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Start&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {

    }

    &lt;span class=&quot;comment&quot;&gt;// Update is called once per frame&lt;/span&gt;
    &lt;span class=&quot;key&quot;&gt;void&lt;/span&gt; Update&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {
        &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.touchCount == &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
        {
            zoom = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
            Touch touch0 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;IsTouching&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
            {
                &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!drag&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
                {
                    initialTouchPosition = touch0.position;
                    initialCameraPosition = &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position;

                    drag = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
                }
                &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;
                {
                    Vector2 delta = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; - 
                                    camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialTouchPosition&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

                    Vector3 newPos = initialCameraPosition;
                    newPos.x -= delta.x;
                    newPos.y -= delta.y;

                    &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = newPos;
                }
            }

            &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!IsTouching&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
            {
                drag = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
            }
        }
        &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;
        {
            drag = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
        }

        &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.touchCount == &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
        {
            drag = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;

            Touch touch0 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
            Touch touch1 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

            &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!zoom&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
            {
                initialTouch0Position = touch0.position;
                initialTouch1Position = touch1.position;
                initialCameraPosition = &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position;
                initialOrthographicSize = Camera.main.orthographicSize;
                initialMidPointScreen = &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position + touch1.position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; / &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt;;

                zoom = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
            }
            &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;
            {
                &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = initialCameraPosition;
                camera.orthographicSize = initialOrthographicSize;

                &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; scaleFactor = GetScaleFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position,
                                                   touch1.position,
                                                   initialTouch0Position,
                                                   initialTouch1Position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

                Vector2 currentMidPoint = &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position + touch1.position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; / &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt;;
                Vector3 initialPointWorldBeforeZoom = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

                Camera.main.orthographicSize = initialOrthographicSize / scaleFactor;

                Vector3 initialPointWorldAfterZoom = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
                Vector2 initialPointDelta = initialPointWorldBeforeZoom - initialPointWorldAfterZoom;

                Vector2 oldAndNewPointDelta =
                    camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;currentMidPoint&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; -
                    camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

                Vector3 newPos = initialCameraPosition;
                newPos.x -= oldAndNewPointDelta.x - initialPointDelta.x;
                newPos.y -= oldAndNewPointDelta.y - initialPointDelta.y;

                &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = newPos;
            }
        }
        &lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;
        {
            zoom = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
        }
    }

    &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;bool&lt;/span&gt; IsTouching&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Touch touch&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {
        &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; touch.phase == TouchPhase.Began ||
                touch.phase == TouchPhase.Moved ||
                touch.phase == TouchPhase.Stationary;
    }

    &lt;span class=&quot;key&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; GetScaleFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Vector2 position1, Vector2 position2, Vector2 oldPosition1, Vector2 oldPosition2&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
    {
        &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; distance = Vector2.Distance&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;position1, position2&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
        &lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; oldDistance = Vector2.Distance&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;oldPosition1, oldPosition2&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

        &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;oldDistance == &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt; || distance == &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
        {
            &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;digit&quot;&gt;1.0&lt;/span&gt;f;
        }

        &lt;span class=&quot;key&quot;&gt;return&lt;/span&gt; distance / oldDistance;
    }
}&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;bound overflow&quot;&gt;&lt;/p&gt;
&lt;br&gt;
===Перемещение===&lt;p&gt;Сначала рассмотрим вариант с перемещением. Для него нам нужен:
&lt;br&gt;
а) 1 флаг &lt;b&gt;drag&lt;/b&gt; типа boolean, который будет говорить, что мы находимся в режиме перемещения камеры
&lt;br&gt;
б) 1 точка касания &lt;b&gt;initialTouchPosition&lt;/b&gt;, которая хранит начальные экранные координаты точки касания экрана пальцем
&lt;br&gt;
в) 1 мировые координаты камеры &lt;b&gt;initialCameraPosition&lt;/b&gt; в момент первоначального касания пальцем экрана&lt;/p&gt;&lt;p&gt;Код для перемещения камеры одним пальцем очень прост. Для начала проверяем, что экрана касается только один палец:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.touchCount == &lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
{
    ...
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Внутри первым делом отменяем операцию масштабирования, чтобы она не мешала коду перемещения, и получаем экранные координаты точки касания:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;zoom = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;
Touch touch0 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если это первый кадр, который инициирует перемещение, то устанавливаем соответствующий флаг и запоминаем координаты точки касания и текущих координаты камеры:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;!drag&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
{
    initialTouchPosition = touch0.position;
    initialCameraPosition = &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position;
    drag = &lt;span class=&quot;key&quot;&gt;true&lt;/span&gt;;
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если же это последующие после начала касания кадры, то мы определяем, насколько сместился палец относительно начальной точки касания и перемещаем камеру в обратную сторону, чтобы скомпенсировать это движение, и объект под пальцем остался в той же точке:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;
{
    Vector2 delta = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; -
                              camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialTouchPosition&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

    Vector3 newPos = initialCameraPosition;
    newPos.x -= delta.x;
    newPos.y -= delta.y;

    &lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = newPos;
}&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;br&gt;
===Масштабирование===&lt;p&gt;Код для двуточечного масштабирования немного сложнее. Как и в прошлый раз, сначала проверяем, что экрана касается 2 пальца:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.touchCount == &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;
{
    ...
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Затем отменяем операцию перемещения и получаем экранные координаты обеих точек касания:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;drag = &lt;span class=&quot;key&quot;&gt;false&lt;/span&gt;;

Touch touch0 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
Touch touch1 = Input.GetTouch&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для первого кадра, в котором экрана касаются оба пальца одновременно, нужно запомнить 5 переменных: 
&lt;br&gt;
а) 2 точки касания &lt;b&gt;initialTouch0Position&lt;/b&gt; и &lt;b&gt;initialTouch1Position&lt;/b&gt;
&lt;br&gt;
б) 1 мировые координаты камеры &lt;b&gt;initialCameraPosition&lt;/b&gt; в момент первоначального касания пальцев экрана
&lt;br&gt;
в) 1 размер половины видимого объёма камеры &lt;b&gt;initialOrthographicSize&lt;/b&gt;
&lt;br&gt;
г) 1 координаты точки &lt;b&gt;initialMidPointScreen&lt;/b&gt; посередине точек касания&lt;/p&gt;&lt;p&gt;Каждый последующий кадр сбрасываем координаты камеры и её &amp;quot;размер&amp;quot; к значениям на момент первоначального касания обеих пальцев экрана для того, чтобы каждый следующий кадр не накапливались изменения в положении/размере камеры.&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = initialCameraPosition;
camera.orthographicSize = initialOrthographicSize;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Затем находим, во сколько раз увеличивать или уменьшать размер области вывода камеры. Это значение есть отношение длины отрезка, образованного расположением пальцев на текущем кадре, к длине отрезка, образованного расположением пальцев на первом кадре.&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt; scaleFactor = GetScaleFactor&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position,
                                            touch1.position,
                                            initialTouch0Position,
                                            initialTouch1Position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Далее находим среднюю точку между двумя пальцами на текущем кадре:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;Vector2 currentMidPoint = &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;touch0.position + touch1.position&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; / &lt;span class=&quot;digit&quot;&gt;2&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Следующие 4 строки вычисляют разницу в мировых координатах средней точки касания после изменения размера камеры, так как при изменении размера экранные координаты средней точки остаются неизменными, а мировые могут поменяться, так как масштабирование камеры всегда происходит относительно центра экрана:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;Vector3 initialMidPointWorldBeforeZoom = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
Camera.main.orthographicSize = initialOrthographicSize / scaleFactor;
Vector3 initialMidPointWorldAfterZoom = camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
Vector2 initialMidPointDelta = initialMidPointWorldBeforeZoom - initialMidPointWorldAfterZoom;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;bound&quot;&gt;
&lt;p&gt;Наконец, вычисляем между мировыми координатами средней точки на текущем и первом
&lt;br&gt;
кадре:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;Vector2 oldAndNewMidPointDelta =
                    camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;currentMidPoint&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; -
                    camera.ScreenToWorldPoint&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;initialMidPointScreen&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;и корректируем положение камеры, учитывая все поправки:&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;Vector3 newPos = initialCameraPosition;
newPos.x -= oldAndNewMidPointDelta.x - initialMidPointDelta.x;
newPos.y -= oldAndNewMidPointDelta.y - initialMidPointDelta.y;

&lt;span class=&quot;key&quot;&gt;this&lt;/span&gt;.transform.position = newPos;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;В качестве побочного эффекта код двуточечного масштабирования отвечает так же и за перемещение камеры, и, если расстояние между пальцами не меняется, сводится к коду для перемещения камеры одним пальцем.&lt;/p&gt;&lt;p&gt;Хотя код прверялся только на Android, он должен работать без изменений и на iOS.&lt;/p&gt;&lt;p&gt;Исходный код можно скачать по адресу &lt;a href=&quot;https://github.com/Skybladev2/Unity3D-pan-and-zoom-example&quot;&gt;&lt;a href=&quot;https://github.com/Skybladev2/Unity3D-pan-and-zoom-example&quot;&gt;https://github.com/Skybladev2/Unity3D-pan-and-zoom-example&lt;/a&gt;&lt;/a&gt;
&lt;br&gt;
Готовое приложение для Android можно скачать отсюда: &lt;a href=&quot;http://rghost.ru/41537997&quot;&gt;&lt;a href=&quot;http://rghost.ru/41537997&quot;&gt;http://rghost.ru/41537997&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/unity3dshaman</guid>
  <pubDate>Mon, 27 Aug 2012 18:45:59 GMT</pubDate>
  <title>Заповеди разработчика Unity3D 3.5.X</title>
  <link>https://gamedev.ru/unity/unity3dshaman</link>
  <comments>https://gamedev.ru/unity/forum/?id=166397</comments>
  <category>необъяснимое</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Начал работать с редактором Unity3D (версии 3.5.Х). Приходится привыкать делать множество необъяснимых вещей, поэтому решил собрать личный опыт вот в такой списочек. Надеюсь, что они всё же имеют под собой какие-то объективные обоснования, и я просто тупой.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Заповеди разработчика Unity3D&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;1) Запускай редактор только под админом. Иначе не сможешь скомпилировать проект. Особенно под андроид.
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;2) Не доверяй автоматическому открытию последнего проекта при старте редактора. Всегда открывай проект ещё раз вручную, иначе опять не сможешь его скомпилировать. 
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;3) Если из сцены пропадают объекты при открытии проекта на другом комьютере/аккаунте - открой её вручную.
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP, Windows 7&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;4) При первой компиляции под андроид удали последний символ в &amp;quot;Bundle Identifier&amp;quot;. Иначе не сможешь скомпилировать проект. 
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;5) Явно указывай Background для любых стилей контрола, кроме Normal, чтобы они работали. Цвет текста для состояния Normal применяется даже при Background = None (Texture 2D). 
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP, Windows 7&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;6) Перед программным изменением текстуры при помощи &lt;a href=&quot;http://docs.unity3d.com/Documentation/ScriptReference/Texture2D.SetPixel.html&quot;&gt;Texture2D.SetPixel&lt;/a&gt; установи в инспекторе цвет текстуры в 255, 255, 255,255 (RGBA), так как вместо замещения цвета SetPixel использует в качестве возможного диапазона значений цвет текстуры.
&lt;br&gt;
&lt;i&gt;Наблюдалось в Windows XP, Windows 7&lt;/i&gt;&lt;/p&gt;</description>
</item>
<item>
  <guid isPermaLink="true">https://gamedev.ru/unity/articles/unity_wheels</guid>
  <pubDate>Tue, 19 Jul 2011 08:50:57 GMT</pubDate>
  <title>Простая машинка в Unity</title>
  <link>https://gamedev.ru/unity/articles/unity_wheels</link>
  <comments>https://gamedev.ru/unity/forum/?id=150204</comments>
  <category>Программирование</category>
  <category>Unity</category>
  <description>
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Урок по созданию простой машинки в среде &lt;a href=&quot;https://gamedev.ru/code/terms/Unity&quot; title=&quot;Unity (Unity3D)&quot;&gt;Unity&lt;/a&gt; при помощи компонента &lt;i&gt;WheelCollider&lt;/i&gt;. Описаны базовые настройки этого компонента. Для понимания урока необходимы базовые знания среды Unity.&lt;/p&gt;
&lt;p&gt;==Введение==&lt;/p&gt;

&lt;p&gt;С английским у меня неплохо, поэтому когда понадобилось сделать машинку в проекте, хватило стандартного &lt;a href=&quot;http://unity3d.com/support/resources/tutorials/car-tutorial&quot;&gt;примера&lt;/a&gt;, однако, хоть и написано, что он &amp;laquo;аркадный&amp;raquo;, все же пример затрагивает гораздо больше вопросов, и один только pdf документ весит 11 мб, а сам пример 212 мб.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68301_1308045006_08.jpg&quot; alt=&quot;08 | Простая машинка в Unity&quot; title=&quot;08&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;От того примера сложилось впечатление, будто нам дали готовый проект, рассказали как менять параметры, чтобы можно было поиграться, но не стали особо объяснять, как его сделали шаг за шагом. Кроме того, он на английском, отсюда родился порыв сделать урок для GameDev.ru.&lt;/p&gt;

&lt;p&gt;Я хочу описать самый примитивный вариант создания машинки, управляемой с клавиатуры, освоив который уже стоит браться за более сложные примеры.&lt;/p&gt;

&lt;p&gt;==Создаем по шагам==&lt;/p&gt;

&lt;p&gt;&lt;b&gt;1.&lt;/b&gt; Итак создадим четыре пустых объекта для удобной иерархии в нашем объекте:&lt;/p&gt;

&lt;ul&gt;&lt;b&gt;carRoot&lt;/b&gt; &amp;mdash; контейнер&amp;nbsp; для всех элементов машины (геометрия, свет, камеры, объекты для скриптов)
&lt;br&gt;
&lt;b&gt;car&lt;/b&gt; &amp;mdash; контейнер для объектов именно машины,
&lt;br&gt;
&lt;b&gt;physic&lt;/b&gt; &amp;mdash; контейнер для физических моделей колес,
&lt;br&gt;
&lt;b&gt;graphic&lt;/b&gt; &amp;mdash; графические объекты.&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68302_1308045047_01.jpg&quot; alt=&quot;01 | Простая машинка в Unity&quot; title=&quot;01&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;b&gt;2.&lt;/b&gt; Добавим в &lt;b&gt;carRoot&lt;/b&gt; камеру и закинем на нее стандартный скрипт &lt;i&gt;SmoothFollow&lt;/i&gt;, однако на 32 строке добавим или внешнюю переменную &lt;i&gt;angle&lt;/i&gt;, или просто добавим поворот на 90 градусов, так как стандартный скрипт заставляет камеру быть сбоку от объекта :&lt;/p&gt;
&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;wantedRotationAngle = target.eulerAngles.y+&lt;span class=&quot;digit&quot;&gt;90&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68303_1308045105_02.jpg&quot; alt=&quot;02 | Простая машинка в Unity&quot; title=&quot;02&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;b&gt;3. &lt;/b&gt;в car добавляем компонент &lt;i&gt;RigidBody&lt;/i&gt;, его масса &amp;mdash; это масса корпуса машины, она будет влиять на устойчивость и на то, насколько сильно он прижимает колеса. &lt;/p&gt;

&lt;p&gt;Для того чтобы на поворотах нас не сильно заносило, и при падении машина возвращалась в правильное положение, а не переворачивалась, нам потребуется два изменения &amp;mdash; компонент &lt;i&gt;ConstantForce&lt;/i&gt; в car, который будет прижимать машину на поворотах, кроме того, переместим центр массы машины вниз, добавив на нее скрипт &lt;i&gt;carControl&lt;/i&gt; с кодом вроде этого:&lt;/p&gt;
&lt;div class=&quot;overflow full&quot;&gt;&lt;pre&gt;gameObject.GetComponent&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Rigidbody&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.centerOfMass = Vector3 &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, -высота_машинки*масштаб, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68304_1308045148_03.jpg&quot; alt=&quot;03 | Простая машинка в Unity&quot; title=&quot;03&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;b&gt;4. &lt;/b&gt;В physic создаем пустой объект &lt;b&gt;body&lt;/b&gt; с компонентом &lt;i&gt;CapsuleCollider&lt;/i&gt; &amp;mdash; это будет простой корпус машины, обтекаемый и не дающий застревать в стенках.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68305_1308045219_04.jpg&quot; alt=&quot;04 | Простая машинка в Unity&quot; title=&quot;04&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;b&gt;5. &lt;/b&gt;создаем пустой объект &lt;b&gt;wheel_BL&lt;/b&gt; и добавляем ему компонент &lt;i&gt;WheelCollider&lt;/i&gt;, и настраиваем его:&lt;/p&gt;

&lt;ul&gt;&lt;i&gt;Center&lt;/i&gt; &amp;mdash; расположение колеса,
&lt;br&gt;
&lt;i&gt;Radius&lt;/i&gt; &amp;mdash; радиус колеса,
&lt;br&gt;
&lt;i&gt;Suspension Distance &lt;/i&gt; &amp;mdash; длинна пружин подвески.
&lt;br&gt;
 
&lt;br&gt;
&lt;i&gt;Suspension Spring : Spring &lt;/i&gt; &amp;mdash; сила пружины подвески, чем выше тем сильнее пружинит,
&lt;br&gt;
&lt;i&gt;Suspension Spring : Damper&lt;/i&gt; &amp;mdash; сила амортизатора, делает более плавным движение пружины.
&lt;p&gt;&lt;i&gt;Mass&lt;/i&gt; &amp;mdash; масса колес, опять же влияет на поведение на заносах, прыжках.&lt;/p&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68307_1308045284_06.jpg&quot; alt=&quot;06 | Простая машинка в Unity&quot; title=&quot;06&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Теперь добавим в объект &lt;b&gt;wheel_BL&lt;/b&gt; простой цилиндрический меш, чтобы так сказать, иметь отладочную версию, впрочем, можно ограничится тем, что Unity нам и так предоставляет.&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68306_1308045252_05.jpg&quot; alt=&quot;05 | Простая машинка в Unity&quot; title=&quot;05&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;&lt;b&gt;6. &lt;/b&gt;Раскопируем наше колесо до нужного числа, например, 4 &amp;mdash; в принципе, машинка уже готова. &lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;full&quot;&gt;&lt;img src=&quot;https://gamedev.ru/files/images/68308_1308045318_07.jpg&quot; alt=&quot;07 | Простая машинка в Unity&quot; title=&quot;07&quot;&gt;&lt;/div&gt;&lt;!--full end--&gt;
&lt;div class=&quot;bound overflow&quot;&gt;
&lt;p&gt;Теперь нам нужно только заставить колеса вращаться с ускорением по нажатию клавиш и поворачиваться, что можно сделать например так :&lt;/p&gt;

&lt;div class=&quot;overflow pre&quot;&gt;&lt;pre&gt;var WheelRight:GameObject;
var WheelLeft:GameObject;
var BackWheels:boolean;
var BackFront:boolean;
var WheelSpeed:&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt;;
var WheelRpmMax:&lt;span class=&quot;key&quot;&gt;float&lt;/span&gt;;
&lt;span class=&quot;key&quot;&gt;private&lt;/span&gt; var WheelRotateAngle = &lt;span class=&quot;digit&quot;&gt;20&lt;/span&gt;;
gameObject.GetComponent&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Rigidbody&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.centerOfMass = Vector3 &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;, -&lt;span class=&quot;digit&quot;&gt;0.6&lt;/span&gt;*&lt;span class=&quot;digit&quot;&gt;2.3&lt;/span&gt;, &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;

function FixedUpdate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; {

  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyDown &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
      WheelRight.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = WheelSpeed;
      WheelLeft.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = WheelSpeed;
  }
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyUp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
    WheelRight.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
    WheelLeft.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
  }  
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyDown &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
    WheelRight.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = -WheelSpeed;
    WheelLeft.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = -WheelSpeed;
  
  }
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyUp&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
    WheelRight.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
    WheelLeft.GetComponent &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;WheelCollider&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;.motorTorque = &lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;;
  }  
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyDown &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
      &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;BackWheels&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;{
      WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }&lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;{
       WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }
  }
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyUp &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
      &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;BackWheels&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;{
      WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }&lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;{
       WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }
  }
  
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyDown &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
     &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;BackWheels&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;{
      WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }&lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;{
       WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }
  }
  &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;Input.GetKeyUp &lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt; { 
      &lt;span class=&quot;key&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;BackWheels&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;{
      WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,-WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }&lt;span class=&quot;key&quot;&gt;else&lt;/span&gt;{
       WheelRight.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
      WheelLeft.transform.Rotate&lt;span class=&quot;bracket&quot;&gt;(&lt;wbr&gt;&lt;/span&gt;&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;,WheelRotateAngle,&lt;span class=&quot;digit&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;bracket&quot;&gt;)&lt;/span&gt;;
     }
  }  
}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;==Заключение==&lt;/p&gt;

&lt;p&gt;Собственно, теперь вы можете соорудить любое нечто на колесах, можно прикрепить две группы (подвески) по два колеса друг к другу джоинтами (как в этом уроке) или сделать вращение колес при развороте, как у танка. Широкий простор для креатива.&lt;/p&gt;

&lt;p&gt;==Файлы==&lt;/p&gt;

&lt;p&gt;Готовый пакет с префабом, описанной в уроке машинки (чтобы заработали фары машинки, нужно подключить пакет Projectors, но это не обязательно, можно и избавиться от того что у меня в графической части): &lt;b&gt;&lt;a href=&quot;https://gamedev.ru/files/?id=68309&quot;&gt;CarSimple.unitypackage&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
</item>
</channel>
</rss>
