<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Pavel Busovikov</title><generator>teletype.in</generator><description><![CDATA[Educational self reflexia of software developer]]></description><image><url>https://img1.teletype.in/files/cf/f1/cff1bc2b-ce11-43ca-9628-4be430a709ef.jpeg</url><title>Pavel Busovikov</title><link>https://teletype.in/@busovikov</link></image><link>https://teletype.in/@busovikov?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/busovikov?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/busovikov?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 25 May 2026 18:32:11 GMT</pubDate><lastBuildDate>Mon, 25 May 2026 18:32:11 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@busovikov/24AZ_rZ3-Ng</guid><link>https://teletype.in/@busovikov/24AZ_rZ3-Ng?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/24AZ_rZ3-Ng?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Задний фон для игры с помощью шейдера на Defold</title><pubDate>Mon, 01 May 2023 11:19:06 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/00/35/003561c6-dce5-47e1-bff8-1d5f149f8c1f.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/d1/dc/d1dc9b43-ac3e-48c4-82c2-7b4b7a445149.png"></img>В качестве заднего фона для своей игры я решил использовать паттерн из игровых персонажей, то есть переиспользовать текстуры. Это позволило мне сэкономить на размере картинки (да мало, но все же), избавило от мук рисования или лицензирования изображения. А еще я получил новый навык.]]></description><content:encoded><![CDATA[
  <p id="5kgW">В качестве заднего фона для своей игры я решил использовать паттерн из игровых персонажей, то есть переиспользовать текстуры. Это позволило мне сэкономить на размере картинки (да мало, но все же), избавило от мук рисования или лицензирования изображения. А еще я получил новый навык.</p>
  <figure id="ml97" class="m_column">
    <img src="https://img2.teletype.in/files/d1/dc/d1dc9b43-ac3e-48c4-82c2-7b4b7a445149.png" width="1280" />
  </figure>
  <h2 id="1C8K">Подготовка проекта.</h2>
  <figure id="E4sz" class="m_column">
    <img src="https://img1.teletype.in/files/0c/53/0c53f9fa-7a17-4714-a658-e7acc5a598d5.png" width="1506" />
  </figure>
  <p id="97L3">Для демонстрации создадим новый проект и выполним следующие шаги:</p>
  <ol id="0toF">
    <li id="aDOm">Скопировать текстуры в папку images.</li>
    <li id="If8a">Скопировать встроенные файлы sprite.material, sprite.fp (fragment program), sprite.vp (vertex program) в папку materials из buildins/materials</li>
    <li id="HNVg">Добавить game object и model в главную коллекцию </li>
    <li id="gVP5">Выбрать текстуры для модели. </li>
  </ol>
  <p id="Bvsf">Для того чтобы свойства модели отображали текстуры, нужно изменить материал, выбрав скопированные ранее sprite.fp, sprite.vp и добавить 5 семплеров</p>
  <figure id="SBh4" class="m_original">
    <img src="https://img1.teletype.in/files/8a/f3/8af36468-6f4e-49e7-8b30-ca2642121ca8.png" width="672" />
  </figure>
  <p id="Uq9v">Поставим для go размеры экрана и разместим по середине</p>
  <figure id="w2RE" class="m_column">
    <img src="https://img2.teletype.in/files/14/65/14658b28-7330-4ba1-bc3a-6caaab8e384e.png" width="1648" />
  </figure>
  <h2 id="Juc0">Шейдер</h2>
  <p id="nQA6">Откроем sprite.fp<br />Шейдер рисует пиксели выбирая их из текстуры <strong>texture_sampler </strong>по координатам <strong>var_texcoord0.xy, </strong>каждый пиксель будет умножен на <strong>tint </strong>для получения цвета и уровня прозрачности.</p>
  <figure id="yp14" class="m_column">
    <img src="https://img4.teletype.in/files/fb/a3/fba397ef-e6d0-4dc1-8b01-f1ffd9b48fd1.png" width="978" />
  </figure>
  <p id="lMH0">Для того чтобы замостить текстурой всю поверхность несколько раз, нужно раздробить поверхность на секции, умножив <strong>var_texcoord0 </strong>на желаемое количество по горизонтали и вертикали: </p>
  <p id="6ExF"><code>vec2 uv = var_texcoord0.xy * 3.0;<br />gl_FragColor = texture2D(texture_sampler, uv) * tint_pm;</code></p>
  <figure id="XoF5" class="m_column">
    <img src="https://img4.teletype.in/files/f0/69/f069597f-1ea2-4114-b6be-b55956265c54.png" width="1197" />
  </figure>
  <p id="Qh5E">Это не тот результат, которого мы хотим добиться, потому что мы проходим по координатам uv за пределы текстуры. Для того, чтобы возвращаться в начало, нужно брать значение по модулю. Для этого подходит функция<strong> fract()</strong>, которая отсекает целую часть.</p>
  <p id="WUT8"><code>gl_FragColor = texture2D(texture_sampler, fract(uv)) * tint_pm;</code></p>
  <figure id="FkQa" class="m_column">
    <img src="https://img2.teletype.in/files/16/d2/16d244af-bbff-4c68-bfe9-8fcf592f46dd.png" width="1202" />
  </figure>
  <p id="GeDy">Эти мордашки довольно растянутые, потому что размеры экрана 960х640, разделены на 3 части с сохранением пропорций экрана, но без учета размера текстуры. Зная размеры экрана и желаемый размер текстуры, можно получить количество секций, на которое следует делить поверхность.</p>
  <p id="qGzg"><code>lowp float pixels_per_unit = 300;<br />lowp vec2 resolution = vec2(960,640);<br />vec2 uv = vec2(var_texcoord0.x * resolution.x / pixels_per_unit, var_texcoord0.y * resolution.y / pixels_per_unit);</code></p>
  <figure id="Gy4O" class="m_column">
    <img src="https://img3.teletype.in/files/e7/88/e788c72e-eeee-44f8-83f9-a4d335071991.png" width="1201" />
  </figure>
  <p id="FCL4">Теперь выберем текстуры в зависимости от позиции в колонке и ряду</p>
  <p id="8xEo"><code>vec4 random_texture(in vec2 uv, in vec3 resolution)<br />{<br />    vec2 id = floor(uv);<br />    int index = int(id.x + id.y * resolution.x / resolution.z) % 5;</code></p>
  <p id="zd9z"><code>    vec4 col = vec4(0);</code></p>
  <p id="gaX2"><code>    if (index == 0) <br />    col = texture2D(tex1,uv);<br />    else if (index == 1) <br />    col = texture2D(tex2,uv);<br />    else if (index == 2) <br />    col = texture2D(tex3,uv);<br />    else if (index == 3) <br />    col = texture2D(tex4,uv);<br />    else<br />    col = texture2D(tex5,uv);<br /><br />    return col;<br />}</code></p>
  <p id="TWxn">Использование функции</p>
  <p id="IyNz"><code>gl_FragColor = random_texture(uv, vec3(resolution, pixels_per_unit)) * tint_pm;</code></p>
  <p id="coq2"></p>
  <figure id="I9FB" class="m_column">
    <img src="https://img4.teletype.in/files/ff/69/ff696819-a020-4d32-b66c-1d610ef80b57.png" width="1202" />
  </figure>
  <p id="AVxN">Теперь можно изменить индивидуальный размер текстуры, для того чтобы между ними появилось пространство. </p>
  <p id="kVWn">Матрица трансформации масштаба:</p>
  <p id="MPMh"><code>mat2 scale(vec2 _scale)<br />{<br />    return mat2(_scale.x,0.0,0.0,_scale.y);<br />}</code></p>
  <p id="rkPZ">Масштаб применяется следующим образом:<br /><code>uv = fract(uv) - vec2(0.5);<br />uv = scale( vec2(_scale) ) * uv;<br />uv = fract(uv) + vec2(0.5);</code></p>
  <figure id="3b0j" class="m_column">
    <img src="https://img1.teletype.in/files/c5/0c/c50cde35-84b3-4f36-ab23-744adbe6ea9c.png" width="1203" />
  </figure>
  <p id="5cPZ">Мы уменьшили размер изображения, но видим соседние части замощенной текстуры. Для того, чтобы оставить только нужное, можно создать маску.</p>
  <p id="azkL">Подойдет обычный квадрат, который должен быть также масштабирован</p>
  <p id="YCzT"><code>float box(vec2 _st, vec2 _size){<br />    _size = vec2(0.5)-_size*0.5;<br />    vec2 uv = step(_size,_st);<br />    uv *= step(_size,vec2(1.0)-_st);<br />    return uv.x*uv.y;<br />}</code></p>
  <p id="X4WP">Там, где квадратная область возвращает цвет, альфа будет 1</p>
  <p id="YyZU"><code>vec2 box_uv = fract(uv) - vec2(0.5);<br />box_uv = scale( vec2(_scale) ) * box_uv;<br />box_uv = box_uv + vec2(0.5);<br />float alpha = box(box_uv,vec2(1));</code></p>
  <p id="gCiV">Перед тем как вернуть текстуру, нужно применить к ней эту прозрачность</p>
  <p id="DwBA"><code>col.a *= alpha;</code></p>
  <figure id="MuEg" class="m_column">
    <img src="https://img2.teletype.in/files/d1/e9/d1e9c2d3-5d36-4c30-b50a-2ea51103be09.png" width="1200" />
  </figure>
  <p id="Zqqy">Время <s>приключений</s> случайных вращений! Функция вращения выглядит следующим образом:</p>
  <p id="TaFz"><code>vec2 rotate2D(vec2 _st, float _angle){<br />	_st -= 0.5;<br />	_st =  mat2(cos(_angle),-sin(_angle),<br />	sin(_angle),cos(_angle)) * _st;<br />	_st += 0.5;<br />	return _st;<br />}</code></p>
  <p id="40Ii">Установим случайный угол</p>
  <p id="kKpl"><code>float rot = PI*random(id);</code></p>
  <p id="DQEm">Функция рандом возвращает псевдослучайное значение</p>
  <p id="ha9H"><code>float random(vec2 st)<br />{<br />    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);<br />}</code></p>
  <p id="CIUX">Вращать нужно как саму текстуру, так и маску</p>
  <p id="BR7T"><code>float alpha = box(rotate2D(box_uv, rot),vec2(1));<br />uv = rotate2D(fract(uv),rot);</code></p>
  <figure id="J2KJ" class="m_column">
    <img src="https://img2.teletype.in/files/57/e2/57e21814-4711-4b8b-ab27-cf68a194c092.png" width="1201" />
  </figure>
  <p id="xbXG">Вращать можно не только отдельно текстуру, но и всю поверхность целиком, если трансформировать координаты сразу после разделения на секции.</p>
  <p id="znn0"><code>vec2 uv = vec2(<br />var_texcoord0.x * resolution.x / pixels_per_unit, <br />var_texcoord0.y * resolution.y / pixels_per_unit );<br />uv = rotate2D(uv,PI*0.2);</code></p>
  <figure id="sJc9" class="m_column">
    <img src="https://img2.teletype.in/files/d5/fa/d5fa5a4e-12e8-448b-b2b3-6ba0b144c28e.png" width="1201" />
  </figure>
  <p id="CJn4">Осталось раскрасить в любимые цвета</p>
  <p id="KGlI">Цвет фона:</p>
  <figure id="gcFx" class="m_original">
    <img src="https://img1.teletype.in/files/03/71/03716c76-a45f-4dd3-9ca6-c9bc5950332d.png" width="547" />
  </figure>
  <p id="ZuC3">Цвет для зверьков:</p>
  <figure id="ajvP" class="m_original">
    <img src="https://img2.teletype.in/files/1b/df/1bdfb365-b94a-42c1-987e-680a694f83fc.png" width="542" />
  </figure>
  <p id="uQPM"><code>vec4 bg = vec4(0.21, 0.09, 0.41, 1);<br />vec4 mc = vec4(0.33, 0.08, 0.54, 1);</code></p>
  <p id="eTQS"><code>vec4 col = random_texture(uv, vec3(resolution, pixels_per_unit), 1.4);<br />if (col.a &gt; 0)<br />    gl_FragColor =  col * mc * tint_pm;<br />else<br />    gl_FragColor = bg * tint_pm;</code></p>
  <figure id="xgfD" class="m_column">
    <img src="https://img2.teletype.in/files/9d/19/9d1909ca-5ce1-4211-927e-65ebdd1aefc0.png" width="1201" />
  </figure>
  <h2 id="ppZ3">Параметры шейдера в defold</h2>
  <p id="1M83">Для того, чтобы передавать значения цвета, размера текстуры и размера экрана в шейдер, можно добавить переменные в материал, объявив их как uniform в коде<br /><br /><code>uniform lowp vec4 resolution;<br />uniform lowp vec4 bg;<br />uniform lowp vec4 mc;</code><br /></p>
  <figure id="18iB" class="m_column">
    <img src="https://img2.teletype.in/files/18/43/1843ad0a-dcef-48c3-9ee0-9672255a3f48.png" width="760" />
  </figure>
  <p id="y4lE">В game object, где лежит model можно добавить скрипт и менять параметры шейдера в runtime. На практике это полезно при изменении размера экрана.</p>
  <figure id="7ExM" class="m_original">
    <img src="https://img1.teletype.in/files/05/56/0556e643-4f9d-426d-83c4-dd45eeb72caa.png" width="347" />
  </figure>
  <figure id="S4KV" class="m_original">
    <img src="https://img1.teletype.in/files/84/8e/848e2d28-99c0-4a43-93a7-378a3aae1d2a.png" width="518" />
  </figure>
  <p id="wwp2">Код fp целиком</p>
  <pre id="0d8z">varying mediump vec2 var_texcoord0;

uniform lowp sampler2D tex1;
uniform lowp sampler2D tex2;
uniform lowp sampler2D tex3;
uniform lowp sampler2D tex4;
uniform lowp sampler2D tex5;
uniform lowp vec4 tint;
uniform lowp vec4 resolution;
uniform lowp vec4 bg;
uniform lowp vec4 mc;

#define PI 3.14159265358979323846

float random(vec2 st)
{
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

vec2 rotate2D(vec2 _st, float _angle){
    _st -= 0.5;
    _st =  mat2(cos(_angle),-sin(_angle),
    sin(_angle),cos(_angle)) * _st;
    _st += 0.5;
    return _st;
}

mat2 scale(vec2 _scale)
{
    return mat2(_scale.x,0.0,0.0,_scale.y);
}

float box(vec2 _st, vec2 _size){
    _size = vec2(0.5)-_size*0.5;
    vec2 uv = step(_size,_st);
    uv *= step(_size,vec2(1.0)-_st);
    return uv.x*uv.y;
}

vec4 random_texture(in vec2 uv, in vec3 resolution, in float _scale)
{
    vec2 id = floor(uv);
    float rot = PI*random(id);
    int index = int(id.x + id.y * resolution.x / resolution.z) % 5;

    vec4 col = vec4(0);

    vec2 box_uv = fract(uv) - vec2(0.5);
    box_uv = scale( vec2(_scale) ) * box_uv;
    box_uv = box_uv + vec2(0.5);

    float alpha = box(rotate2D(box_uv, rot),vec2(1));
    uv = rotate2D(fract(uv),rot);

    uv = fract(uv) - vec2(0.5);
    uv = scale( vec2(_scale) ) * uv;
    uv = fract(uv) + vec2(0.5);

    if (index == 0) 
    col = texture2D(tex1,uv);
    else if (index == 1) 
    col = texture2D(tex2,uv);
    else if (index == 2) 
    col = texture2D(tex3,uv);
    else if (index == 3) 
    col = texture2D(tex4,uv);
    else
    col = texture2D(tex5,uv);

    col.a *= alpha;
    
    return col;
}

void main()
{
    vec2 uv = vec2(var_texcoord0.x * resolution.x / resolution.z, var_texcoord0.y * resolution.y / resolution.z);
    uv = rotate2D(uv,PI*0.2);
    lowp vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);

    vec4 col = random_texture(uv, vec3(resolution.xy, resolution.z), 1.4);
    if (col.a &gt; 0)
    gl_FragColor =  col * mc * tint_pm;
    else
    gl_FragColor = bg * tint_pm; //chess(botom_color, top_color, uv);
}
</pre>
  <p id="60Qq"></p>
  <p id="cLw2">Проект целиком: <a href="https://github.com/busovikov/Shader-Showroom" target="_blank">https://github.com/busovikov/Shader-Showroom</a></p>
  <p id="fND0">Мой телеграм канал: <a href="https://t.me/pasha_gamedev" target="_blank">https://t.me/pasha_gamedev</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/4PeFPDi_lUq</guid><link>https://teletype.in/@busovikov/4PeFPDi_lUq?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/4PeFPDi_lUq?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Использовал нейросеть для создания игрового персонажа.</title><pubDate>Tue, 07 Mar 2023 20:45:16 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/1d/f6/1df61042-2705-4f69-a9d4-85e89e1fc324.png"></media:content><category>GameDev</category><description><![CDATA[<img src="https://img2.teletype.in/files/96/37/963763b7-f169-491a-a8fe-cbfaeebc6d18.png"></img>На Яндекс играх вышла моя игра.]]></description><content:encoded><![CDATA[
  <p id="8g7E">На Яндекс играх вышла моя игра.</p>
  <p id="i9DO"><a href="https://yandex.ru/games/app/216779?lang=ru" target="_blank">https://yandex.ru/games/app/216779?lang=ru</a></p>
  <p id="Qd3l">Следуя трендам, персонажей, конечно, сгенерировал с помощью нейросети. У меня были клипарты из предыдущей игры, которых я нарисовал еще год назад. </p>
  <figure id="D7qM" class="m_column">
    <img src="https://img2.teletype.in/files/96/37/963763b7-f169-491a-a8fe-cbfaeebc6d18.png" width="1086" />
  </figure>
  <p id="uUGW">Я уже пробовал проделать этот трюк, но ни Dall-e ни Stable Diffusion не давали нужного результата. Если коротко, то результат получался хуже, чем исходник. Да, нужна тонкая настройка и возня с промтом. Но мне не хотелось инвестировать много времени в персонажа для игры, на создание которой я суммарно потратил 3 дня.</p>
  <figure id="ibHF" class="m_column">
    <img src="https://img4.teletype.in/files/f5/3c/f53c0451-cf43-4dd1-a955-feda0f17a6c0.png" width="1640" />
  </figure>
  <p id="2Gfs">Совсем по-другому себя показала Lexica Aperture v2. Эта модель как будто поняла контекст рисунка и не изменяя пропорции начала подставлять разные варианты глаз, ушей, носов и т. д.</p>
  <figure id="wP57" class="m_column">
    <img src="https://img4.teletype.in/files/b0/88/b0884564-3049-4663-8639-5e6d5dfc1b38.png" width="896" />
  </figure>
  <p id="E941">Несколько прогонов дали результаты, которые можно смело нарезать на куски в ФШ и комбинировать для разных персонажей. Я же, для быстроты, просто выбрал те, что мне понравились и лишь вырезав фон сразу вставил в игру.</p>
  <figure id="CanW" class="m_column">
    <img src="https://img3.teletype.in/files/a9/27/a927c3a2-6294-46fb-8ecb-57c4048d33aa.png" width="887" />
  </figure>
  <p id="j50j">В итоге:</p>
  <p id="zXwA">Я не потратил много времени на создание персонажей (если не считать моих художеств год назад) и получил довольно симпатичный результат. Результат можно подрихтовать в ФШ в дальнейшем, но если игра не взлетит, то и беспокоиться не о чем.</p>
  <figure id="L64y" class="m_column">
    <img src="https://img3.teletype.in/files/a4/fd/a4fdb4fe-df4f-4159-93b4-7bbc488d5da6.png" width="808" />
  </figure>
  <p id="5XRp">Посмотрите на игру в действии:<br /><a href="https://yandex.ru/games/app/216779?lang=ru" target="_blank">https://yandex.ru/games/app/216779?lang=ru</a><br /><br />Подпишитесь на мой телеграм канал:</p>
  <p id="I8P6"><a href="https://t.me/pasha_gamedev" target="_blank">https://t.me/pasha_gamedev</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/yandex-games-porting-bubble-beasts</guid><link>https://teletype.in/@busovikov/yandex-games-porting-bubble-beasts?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/yandex-games-porting-bubble-beasts?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Yandex Игры. Опыт портирования существующей игры</title><pubDate>Thu, 23 Feb 2023 22:47:27 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/75/7d/757d59ad-59e2-4c32-a372-1bc6a9e9bf3d.png"></media:content><category>GameDev</category><description><![CDATA[<img src="https://img4.teletype.in/files/71/01/710129fb-ef24-4081-8d23-30d9f34042f1.png"></img>Яндекс игры - это платформа браузерных игр HTML5, JS, WebGL и т.д. Яндекс активно развивается, улучшая свою платформу для разработчиков и пользователей. У меня есть готовая игра на Unity и портировал ее под эту платформу, чтобы сравнить с Google Play.]]></description><content:encoded><![CDATA[
  <p id="mcj0">Яндекс игры - это платформа браузерных игр HTML5, JS, WebGL и т.д. Яндекс активно развивается, улучшая свою платформу для разработчиков и пользователей. У меня есть готовая игра на Unity и портировал ее под эту платформу, чтобы сравнить с Google Play.</p>
  <figure id="lzYz" class="m_original">
    <img src="https://img4.teletype.in/files/71/01/710129fb-ef24-4081-8d23-30d9f34042f1.png" width="776" />
    <figcaption><a href="https://yandex.ru/games/app/203027?lang=ru" target="_blank">Зверьки Пузырьки: Три в ряд — играть онлайн бесплатно на сервисе Яндекс Игры (yandex.ru)</a></figcaption>
  </figure>
  <h2 id="WHM3">Отличия от Google Play</h2>
  <ul id="EqtD">
    <li id="LMU4">Браузерные игры не требуют установки. Это значит доступер более широкий пул устройств (iPhone, iPad, Android телефоны и планшеты, телевизоры на Android, компьютер под любой операционной системой)</li>
    <li id="pena">Яндекс молодая платформа. Здесь гораздо меньше пользователей (в основном это СНГ), но также гораздо меньше разработчиков. Это значит, что здесь больше органического трафика и меньшая конкуренция за внимание пользователей.</li>
    <li id="yNYg">Консоль разработчика в Яндекс Играх уступает в функциональности. В Google Play разрабочику доступны A/B тесты, экперименты с оформлением, аналитика.</li>
    <li id="aLhC">Яндекс SDK прост в использовании. Подключение межстраничной рекламы, рекламы за вознаграждение, внутриигровых покупок осуществляется в консоли разработчика и лиш требует вызова функции SDK. Тестирование происходит на реальной рекламе.</li>
    <li id="4FP0">Поддержка отвечает в течении нескольких минут! Чат находится прямо в консоли разработки, он также интегрирован с другими сервисами яндекса (Метрика и РСЯ). В отличие от Google Play, AdMob, AdSense, Google Ads все это разные сервисы, с разной поддержкой, до которой очень трудно добраться. </li>
    <li id="Syl5">Модерация проходит в течении 5 дней. Быстро, но с развитием платформы время модерации увеличивается. Также модераторы в отличие от Google Play гораздо серьезней относятся к оформлению и соответсвию заявленым функциям игры. Выступают в роли тестировщиков и временами находят баги  в геймплее подробно описывая проблему.</li>
  </ul>
  <h2 id="r6i8">Из Bubble Beasts в Зверьки Пузырьки</h2>
  <p id="zzpW">Я взял <a href="https://play.google.com/store/apps/details?id=com.BusovikovGames.BubbleBeasts" target="_blank">игру </a>из <a href="https://teletype.in/@busovikov/bgAAgNSEtKJ" target="_blank">предыдущего поста</a>. Она уже довольно давно лежит на Google Play и было бы интересно сравнить результаты на одинаковом продукте.</p>
  <figure id="rAXA" class="m_original">
    <img src="https://img1.teletype.in/files/0b/bd/0bbd7336-64c4-4d78-ba62-d92e6fa3d912.png" width="1185" />
    <figcaption><a href="https://yandex.ru/games/app/203027?lang=ru" target="_blank">Зверьки Пузырьки: Три в ряд — играть онлайн бесплатно на сервисе Яндекс Игры (yandex.ru)</a></figcaption>
  </figure>
  <ul id="prdv">
    <li id="9qtx">Пришлось переделать интерфейс и ориентацию экрана на альбомную, чтобы уменьшить разницу в отображении на различных устройствах.</li>
    <li id="qndj">Создание билда WebGL делается простым переключением платформы в Unity</li>
    <li id="EOLe">Интеграция SDK добавлением одной строчки в index файл. Также есть <a href="https://t.me/yandexgame_plugin" target="_blank">прекрасный плагин</a> для Unity, которым я не пользовался в этот раз, но активно использовал в последующих играх.</li>
    <li id="01u2">Игровые покупки подключаются в консоли в пару кликов, но также следует отправить письмо в поддержку и подписать присланный шаблон акта приема-передачи неисключительных прав на использование игры.</li>
    <li id="Z06V">Платформа имеет широкое распространение в СНГ странах, поэтому я добавил локализацию на Русский язык, а также по рекомендации Яндекса на Турецкий.</li>
    <li id="kHzw">Облачные сохранения добавляют асинхронности в архитектуру игры, поэтому пришлось изменить подход в загрузке и сохранении.</li>
    <li id="nzaD">Таблица лидеров доступна в окне игры на Яндекс, я не стал добавлять таблицу в интерфейс самой игры.</li>
  </ul>
  <p id="fXVM"><a href="https://yandex.ru/games/app/203027?lang=ru" target="_blank">Зверьки Пузырьки: Три в ряд — играть онлайн бесплатно на сервисе Яндекс Игры (yandex.ru)</a></p>
  <h2 id="npQP">Факты</h2>
  <ul id="Sdx5">
    <li id="KAvO"><a href="https://play.google.com/store/apps/details?id=com.BusovikovGames.BubbleBeasts" target="_blank">Игра на Google Play</a> за год своего существования получила 50+ установок. Можно взглянуть на графики установок и конверсии в предыдущем посте.</li>
    <li id="ae7R"><a href="https://yandex.ru/games/app/203027?lang=ru" target="_blank">Игра на Яндекс играх</a> за 2 месяца нахождения на Яндекс играх набрала 1000+ новых пользователей.</li>
    <li id="eERG">За 2 недели, находясь в категории &quot;новые&quot;, игра набрала 600+ пользователей. Сейчас приток новых пользователей более чем скромный.</li>
  </ul>
  <figure id="DMzf" class="m_original">
    <img src="https://img1.teletype.in/files/07/43/0743d474-c9fe-4259-b7cc-b4828450e794.png" width="1067" />
  </figure>
  <h2 id="OAKd">Выводы</h2>
  <ul id="AzRk">
    <li id="IWKZ">К сожалению, в Яндекс играх сейчас отсутствует возможность проводить A/B тестирование. Поэтому экспериментировать с оформлением смысла нет.</li>
    <li id="a5l6">Я не использовал все доступные символы в описании игры, что могло бы помочь при индексации</li>
    <li id="UuAZ">Также можно добавить рекламный ролик. Яндекс сам занимается распространением.</li>
    <li id="Of5N">При всей простоте SDK, были допущены архитектурные ошибки, в следующих проектах я использовал <a href="https://t.me/yandexgame_plugin" target="_blank">плагин</a>, что существенно ускоряет разработку.</li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/bgAAgNSEtKJ</guid><link>https://teletype.in/@busovikov/bgAAgNSEtKJ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/bgAAgNSEtKJ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Первая игра на Google Play - Год спустя</title><pubDate>Thu, 23 Feb 2023 19:47:54 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/58/44/584485cb-2ccb-42e3-a039-59da25b7b05a.png"></media:content><category>GameDev</category><description><![CDATA[<img src="https://lh3.googleusercontent.com/csKoTpb84_7gpk4H-oAs3RT-OZWF66ynvGQ6lIHweq28CnBnkt9lLaT0Mo7roEjJg6Y_"></img>В декабре 2021 года моя первая игра, сделанная на Unity, появилась на Google Play. Я не настраивал рекламную кампанию, лишь скинул ссылку в пару чатов и на своей LinkedIn странице.]]></description><content:encoded><![CDATA[
  <p id="jWZX">В декабре 2021 года <a href="https://play.google.com/store/apps/details?id=com.BusovikovGames.BubbleBeasts&pli=1" target="_blank">моя первая игра</a>, сделанная на Unity, появилась на Google Play. Я не настраивал рекламную кампанию, лишь скинул ссылку в пару чатов и на своей <a href="https://www.linkedin.com/in/pavelbusovikov/" target="_blank">LinkedIn </a>странице.</p>
  <figure id="uPud" class="m_original">
    <img src="https://lh3.googleusercontent.com/csKoTpb84_7gpk4H-oAs3RT-OZWF66ynvGQ6lIHweq28CnBnkt9lLaT0Mo7roEjJg6Y_" width="512" />
  </figure>
  <h2 id="tZLv">Вводные данные</h2>
  <ul id="kQpu">
    <li id="oKWr">Это MVP - Minimal Viable Product. Два режима игры, бесконечная прогрессия. </li>
    <li id="YnqT">Я не прибегал к помощи художников и не использовал готовые ассеты. </li>
    <li id="PBrE">Промо-материалы (Иконка, Обложка, Трейлер) были сделаны самостоятельно. </li>
    <li id="mewB">Как и большинство начинающих разработчиков игр, я ничего не смыслил в ASO оптимизации, упаковке продукта, A/B тестировании и т.д.</li>
  </ul>
  <h2 id="xc1V">Каков результат?</h2>
  <p id="ZO4q"><br />На графике посещений за год можно наблюдать небольшой рост с середины сентября по конец октября - это Хэллоуин. Игра сделана в этой тематике, также содержит слово &quot;Хэллоуин&quot; в названии и описании. Тем не менее, было не больше 20 посещений.</p>
  <figure id="IiMJ" class="m_original">
    <img src="https://img4.teletype.in/files/39/fe/39feb8fe-8a9b-44a8-9e77-f45ed2f3eef7.png" width="1462" />
  </figure>
  <p id="XOXD">График установок за год<br /></p>
  <figure id="9bS8" class="m_original">
    <img src="https://img2.teletype.in/files/5d/b1/5db1dedc-e408-4ba8-be00-b5f6e5bb6d07.png" width="1457" />
  </figure>
  <p id="VzlP">Конверсия</p>
  <p id="AKc2">На таких маленьких цифрах смотреть эти данные не очень репрезентативно, но все равно можно заметить, что игра привлекает больше посетителей из Юго-Восточной Азии. Возможно, способствует стилистика рисовки.<br /></p>
  <figure id="ouHs" class="m_original">
    <img src="https://img4.teletype.in/files/71/76/71763a42-3227-49ff-afd5-cd87bba83a97.png" width="1507" />
  </figure>
  <h2 id="acDi">Выводы</h2>
  <ul id="FEcd">
    <li id="1NMK">Стоит ли делать пробную закупку трафика? Думаю, нет.</li>
    <li id="F9sw">Стоит разобраться с ASO оптимизацией и попробовать увеличить количество посетителей. При большем количестве посетителей можно провести A/B тестирование иконки, обложки.</li>
    <li id="m2fT">Увеличив конверсию, можно делать доработки по игре, чтобы увеличить удержание. Например, добавить туториал, супер-тайлы, как в прочих match3 играх, компанию или мета-игру.</li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/Privacy_Policy_Bubble_Beasts_Match3_Halloween</guid><link>https://teletype.in/@busovikov/Privacy_Policy_Bubble_Beasts_Match3_Halloween?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/Privacy_Policy_Bubble_Beasts_Match3_Halloween?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Privacy Policy for Bubble Beasts Match 3 Halloween</title><pubDate>Thu, 18 Nov 2021 22:16:24 GMT</pubDate><description><![CDATA[Last updated: March 08, 2024]]></description><content:encoded><![CDATA[
  <h1 id="F8Ux">Privacy Policy</h1>
  <p id="CyId">Last updated: March 08, 2024</p>
  <p id="1tuy">This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.</p>
  <p id="uUTn">We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. </p>
  <h1 id="xSN4">Interpretation and Definitions</h1>
  <h2 id="1Q4b">Interpretation</h2>
  <p id="WKcY">The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.</p>
  <h2 id="kXJY">Definitions</h2>
  <p id="jBH1">For the purposes of this Privacy Policy:</p>
  <ul id="rn9x">
    <li id="u0ZM"><strong>Account</strong> means a unique account created for You to access our Service or parts of our Service.</li>
    <li id="Y614"><strong>Affiliate</strong> means an entity that controls, is controlled by or is under common control with a party, where &quot;control&quot; means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.</li>
    <li id="FZcH"><strong>Application</strong> means the software program provided by the Company downloaded by You on any electronic device, named Bubble Beasts Match 3</li>
    <li id="4a4j"><strong>Company</strong> (referred to as either &quot;the Company&quot;, &quot;We&quot;, &quot;Us&quot; or &quot;Our&quot; in this Agreement) refers to Bubble Beasts Match 3.</li>
    <li id="FLQ2"><strong>Country</strong> refers to: Russia</li>
    <li id="6X0e"><strong>Device</strong> means any device that can access the Service such as a computer, a cellphone or a digital tablet.</li>
    <li id="nffV"><strong>Personal Data</strong> is any information that relates to an identified or identifiable individual.</li>
    <li id="qtNo"><strong>Service</strong> refers to the Application.</li>
    <li id="EYKy"><strong>Service Provider</strong> means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used.</li>
    <li id="40VK"><strong>Usage Data</strong> refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).</li>
    <li id="tUHU"><strong>You</strong> means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.</li>
  </ul>
  <h1 id="xpDl">Collecting and Using Your Personal Data</h1>
  <h2 id="Qq43">Types of Data Collected</h2>
  <h3 id="l6QB">Personal Data</h3>
  <p id="J9k4">While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:</p>
  <ul id="pJ0h">
    <li id="0Cmn">Usage Data</li>
  </ul>
  <h3 id="X1jL">Usage Data</h3>
  <p id="g2Ud">Usage Data is collected automatically when using the Service.</p>
  <p id="C0VV">Usage Data may include information such as Your Device&#x27;s Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data.</p>
  <p id="3eqM">When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data.</p>
  <p id="ibYP">We may also collect information that Your browser sends whenever You visit our Service or when You access the Service by or through a mobile device.</p>
  <h2 id="XpsP">Use of Your Personal Data</h2>
  <p id="JOCE">The Company may use Personal Data for the following purposes:</p>
  <ul id="BMhl">
    <li id="5bzS"><strong>To provide and maintain our Service</strong>, including to monitor the usage of our Service.</li>
    <li id="94LF"><strong>To manage Your Account:</strong> to manage Your registration as a user of the Service. The Personal Data You provide can give You access to different functionalities of the Service that are available to You as a registered user.</li>
    <li id="SaGj"><strong>For the performance of a contract:</strong> the development, compliance and undertaking of the purchase contract for the products, items or services You have purchased or of any other contract with Us through the Service.</li>
    <li id="WOzF"><strong>To contact You:</strong> To contact You by email, telephone calls, SMS, or other equivalent forms of electronic communication, such as a mobile application&#x27;s push notifications regarding updates or informative communications related to the functionalities, products or contracted services, including the security updates, when necessary or reasonable for their implementation.</li>
    <li id="9dzL"><strong>To provide You</strong> with news, special offers and general information about other goods, services and events which we offer that are similar to those that you have already purchased or enquired about unless You have opted not to receive such information.</li>
    <li id="Xd8B"><strong>To manage Your requests:</strong> To attend and manage Your requests to Us.</li>
    <li id="6cUb"><strong>For business transfers:</strong> We may use Your information to evaluate or conduct a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which Personal Data held by Us about our Service users is among the assets transferred.</li>
    <li id="dV9K"><strong>For other purposes</strong>: We may use Your information for other purposes, such as data analysis, identifying usage trends, determining the effectiveness of our promotional campaigns and to evaluate and improve our Service, products, services, marketing and your experience.</li>
  </ul>
  <p id="Ljvx">We may share Your personal information in the following situations:</p>
  <ul id="zDK2">
    <li id="OJfv"><strong>With Service Providers:</strong> We may share Your personal information with Service Providers to monitor and analyze the use of our Service, to contact You.</li>
    <li id="1HXa"><strong>For business transfers:</strong> We may share or transfer Your personal information in connection with, or during negotiations of, any merger, sale of Company assets, financing, or acquisition of all or a portion of Our business to another company.</li>
    <li id="bBAu"><strong>With Affiliates:</strong> We may share Your information with Our affiliates, in which case we will require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and any other subsidiaries, joint venture partners or other companies that We control or that are under common control with Us.</li>
    <li id="p34M"><strong>With business partners:</strong> We may share Your information with Our business partners to offer You certain products, services or promotions.</li>
    <li id="9Plf"><strong>With other users:</strong> when You share personal information or otherwise interact in the public areas with other users, such information may be viewed by all users and may be publicly distributed outside.</li>
    <li id="ZvjJ"><strong>With Your consent</strong>: We may disclose Your personal information for any other purpose with Your consent.</li>
  </ul>
  <h2 id="S3zu">Retention of Your Personal Data</h2>
  <p id="ZhGe">The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies.</p>
  <p id="OYzf">The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally retained for a shorter period of time, except when this data is used to strengthen the security or to improve the functionality of Our Service, or We are legally obligated to retain this data for longer time periods.</p>
  <h2 id="Z1PA">Transfer of Your Personal Data</h2>
  <p id="eXeB">Your information, including Personal Data, is processed at the Company&#x27;s operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from Your jurisdiction.</p>
  <p id="9z3o">Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to that transfer.</p>
  <p id="TQSp">The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information.</p>
  <h2 id="1fG1">Disclosure of Your Personal Data</h2>
  <h3 id="qeVB">Business Transactions</h3>
  <p id="MKL3">If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy.</p>
  <h3 id="OVPY">Law enforcement</h3>
  <p id="BUAV">Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency).</p>
  <h3 id="3aIS">Other legal requirements</h3>
  <p id="8Sde">The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:</p>
  <ul id="Mki6">
    <li id="j5fN">Comply with a legal obligation</li>
    <li id="8SdC">Protect and defend the rights or property of the Company</li>
    <li id="Y9im">Prevent or investigate possible wrongdoing in connection with the Service</li>
    <li id="egYz">Protect the personal safety of Users of the Service or the public</li>
    <li id="43fD">Protect against legal liability</li>
  </ul>
  <h2 id="vQF5">Security of Your Personal Data</h2>
  <p id="tORT">The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially acceptable means to protect Your Personal Data, We cannot guarantee its absolute security.</p>
  <h1 id="HL32">Children&#x27;s Privacy</h1>
  <p id="Vcup">Our Service does not address anyone under the age of 13. We do not knowingly collect personally identifiable information from anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 13 without verification of parental consent, We take steps to remove that information from Our servers.</p>
  <p id="4ynN">If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent&#x27;s consent before We collect and use that information.</p>
  <h1 id="wBIj">Links to Other Websites</h1>
  <p id="HDup">Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party&#x27;s site. We strongly advise You to review the Privacy Policy of every site You visit.</p>
  <p id="Un2U">We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.</p>
  <h1 id="Fxqe">Changes to this Privacy Policy</h1>
  <p id="t1ug">We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page.</p>
  <p id="6WWm">We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the &quot;Last updated&quot; date at the top of this Privacy Policy.</p>
  <p id="XQiP">You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.</p>
  <h1 id="g2Uw">Contact Us</h1>
  <p id="sd59">If you have any questions about this Privacy Policy, You can contact us:</p>
  <ul id="3Gn9">
    <li id="eiRr">By email: pavel.busovikov@gmail.com</li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/ogre-next-basic-app</guid><link>https://teletype.in/@busovikov/ogre-next-basic-app?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/ogre-next-basic-app?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Ogre-next [Tutorial]: basic application</title><pubDate>Thu, 08 Jul 2021 14:42:52 GMT</pubDate><category>Ogre</category><description><![CDATA[I am a newbie in 3d, graphics, gamedev, etc. This tutorial is my way of learning of these things.]]></description><content:encoded><![CDATA[
  <h3>Disclaimer</h3>
  <p>I am a newbie in 3d, graphics, gamedev, etc. This tutorial is my way of learning of these things.</p>
  <h3>Intro</h3>
  <p><a href="https://www.ogre3d.org" target="_blank">The Ogre</a> is 3d rendering engine. This article is dedicated to Ogre-next (2.x version) which is more powerful than first version but has less learning resources found online. In fact, there are <a href="https://ogrecave.github.io/ogre-next/api/2.1/" target="_blank">API Reference</a> and code samples located in source code: </p>
  <ul>
    <li>ogre-next\Samples\2.0\Tutorials</li>
  </ul>
  <p>Even a basic app that shows an empty scene requires relatively complex setup.</p>
  <p>Here we will cover how to build a prototype using CMake which could be used as a template for future projects.</p>
  <p>The example is for Windows, but it is not so different for Linux. BTW Macoy Madson has similar <a href="https://macoy.me/blog/programming/Ogre2Setup" target="_blank">blog post</a> making it for Linux. He avoids using CMake, but goes far beyond basic set up. </p>
  <p>I also assume you have installed CMake and compiler and git</p>
  <h3>Build or Install ogre-next</h3>
  <p>You can start from <a href="https://www.ogre3d.org/download/sdk/sdk-ogre-next" target="_blank">here.</a> Just download and unpack SDK or build it using scripts provided or manually:</p>
  <pre data-lang="shell">git clone --recurse-submodules https://github.com/OGRECave/ogre-next.git</pre>
  <p>Go to <strong>ogre-next-deps</strong> and build it</p>
  <pre data-lang="powershell">mkdir build
cd build
cmake -G &quot;Visual Studio 16 2019&quot; -A x64 ..
cmake --build . --config Debug
cmake --build . --target install --config Debug
cmake --build . --config Release
cmake --build . --target install --config Release</pre>
  <p>Go to <strong>ogre-next</strong> folder and make link to <strong>build\ogredeps</strong></p>
  <pre data-lang="shell">mkdir build
cd build
cmake -D OGRE_USE_BOOST=0 -D OGRE_CONFIG_THREAD_PROVIDER=0 -D OGRE_CONFIG_THREADS=0 -D OGRE_BUILD_COMPONENT_SCENE_FORMAT=1 -D OGRE_BUILD_SAMPLES2=1 -D OGRE_BUILD_TESTS=1 -D OGRE_DEPENDENCIES_DIR=..\..\ogre-next-deps\build\ogredeps -G &quot;Visual Studio 16 2019&quot; -A x64 ..
cmake --build . --config Debug
cmake --build . --target install --config Debug
cmake --build . --config Release
cmake --build . --target install --config Release</pre>
  <p><strong>Note: </strong>use the latest version of CMake as there is a <a href="https://github.com/OGRECave/ogre-next/issues/206" target="_blank">bug</a> in configuration.</p>
  <p>At this point you should have SDK. It is located in <strong>ogre-next\build\sdk</strong></p>
  <h3>CMake</h3>
  <p>Create CMake project using your IDE or create a folder with <strong>CMakeLists.txt</strong> in it.</p>
  <pre data-lang="coffeescript">cmake_minimum_required(VERSION 3.8.0)
project(Test VERSION 0.1.0)
add_executable(${PROJECT_NAME} main.cpp)</pre>
  <p>Using Ogre is not that different from using other libraries in your projects. It has <strong>FindOGRE.cmake</strong> so we can use find_package to get all includes, links, etc. All we need is to forward SDK path to CMake like this: </p>
  <p>-DOGRE_SDK=F:\Libs\Ogre\ogre-next\build\sdk</p>
  <pre data-lang="coffeescript">if(NOT OGRE_SDK)    
  message(FATAL_ERROR &quot;OGRE SDK is not found!&quot;)
endif()
list(APPEND CMAKE_MODULE_PATH &quot;${OGRE_SDK}/CMake&quot;)
find_package(OGRE)</pre>
  <p>Manual says we need:</p>
  <ul>
    <li>Include OgreMain/include</li>
    <li>Include Components/Hlms/Common/include</li>
    <li>Include Components/Hlms/Pbs/include</li>
    <li>Include Components/Hlms/Unlit/include</li>
    <li>Include build/Release/include (that&#x27;s where OgreBuildSettings.h is)</li>
    <li>Add the link path build/Release/lib/</li>
    <li>Link against OgreHlmsPbs.lib</li>
    <li>Link against OgreHlmsUnlit.lib</li>
    <li>Link against OgreMain.lib</li>
    <li>Bundle the data files in Samples/Media/Hlms/Common with your application</li>
    <li>Bundle the data files in Samples/Media/Hlms/Pbs with your application</li>
    <li>Bundle the data files in Samples/Media/Hlms/Unlit with your application</li>
  </ul>
  <p>A brief examination of <strong>FindOGRE.cmake</strong> shows that the OGRE include directories are defined by <strong>OGRE_INCLUDE_DIRS </strong>and <strong>OGRE_${COMPONENT}_INCLUDE_DIRS</strong></p>
  <pre data-lang="coffeescript">set(PROJECT_INCLUDES    
  &quot;${OGRE_INCLUDE_DIRS}&quot;    
  &quot;${OGRE_HlmsPbs_INCLUDE_DIRS}&quot;    
  &quot;${OGRE_HlmsUnlit_INCLUDE_DIRS}&quot;    
  &quot;${OGRE_HlmsUnlit_INCLUDE_DIRS}/../Common&quot; )</pre>
  <p><strong>Note: </strong>for some reason there is no variable for <strong>Hlms/common</strong> so add it manually. </p>
  <p>Same logic for libraries. </p>
  <pre data-lang="coffeescript">set(PROJECT_LIBS     
  &quot;${OGRE_LIBRARIES}&quot;     
  &quot;${OGRE_HlmsPbs_LIBRARIES}&quot;     
  &quot;${OGRE_HlmsUnlit_LIBRARIES}&quot; )</pre>
  <p>Add it to the target.</p>
  <pre data-lang="coffeescript">add_executable (${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDES})
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})</pre>
  <h3>HLMS</h3>
  <blockquote>HLMS stands for &quot;High Level Material System&quot;, because for the user, the HLMS means just define the material and start looking at it (no need for coding or shader knowledge!).</blockquote>
  <p>There are two options:</p>
  <ul>
    <li><strong>PBS</strong>: Stands for Physically Based Shading. The most common for most of your material needs.</li>
    <li><strong>Unlit</strong>: Basic system with no lighting or skeleton skinning. Great for GUI, billboards, particle FXs. Supports multiple texture layering with photoshop-like blending modes.</li>
  </ul>
  <p>It seems to make a &quot;basic&quot; demo app we do not need Hlms at all. But to make something more complicated than an empty window we have to initialize it. So, in order Hlms to work it has to load some data files which are conveniently located in <strong>sdk\Media\Hlms. </strong>All these share common hierarchy that we will define in configuration file. It can be generated directly in CMake</p>
  <p>Create <strong>Resources.cfg.in</strong></p>
  <pre data-lang="gitignore">[Essential]
Zip=DebugPack.zip

[Popular]
FileSystem=./
FileSystem=Materials/Common
FileSystem=Materials/Common/GLSL
FileSystem=Materials/Common/HLSL
FileSystem=Materials/Common/Metal

# Do not load this as a resource. It&#x27;s here merely to tell the code where
# the Hlms templates are located
[Hlms]
DoNotUseAsResource=./</pre>
  <p>This file will be copied as is. Doing this we leave us a space for future modifications.</p>
  <pre data-lang="coffeescript">set(DATA_DIR &quot;${CMAKE_BINARY_DIR}/data&quot;)
set(RESOURCES_CFG &quot;Resources.cfg&quot;)

configure_file(&quot;${RESOURCES_CFG}.in&quot; &quot;${DATA_DIR}/${RESOURCES_CFG}&quot;)

file( COPY &quot;${OGRE_MEDIA_DIR}/Hlms/Common&quot;  DESTINATION &quot;${DATA_DIR}/Hlms&quot; )
file( COPY &quot;${OGRE_MEDIA_DIR}/Hlms/Pbs&quot;     DESTINATION &quot;${DATA_DIR}/Hlms&quot; )
file( COPY &quot;${OGRE_MEDIA_DIR}/Hlms/Unlit&quot;   DESTINATION &quot;${DATA_DIR}/Hlms&quot; )
file( COPY &quot;${OGRE_MEDIA_DIR}/2.0/scripts/materials/Common&quot; 
      DESTINATION &quot;${DATA_DIR}/Materials&quot; )</pre>
  <p><strong>Note:</strong> <strong>RESOURCES_CFG</strong> can be also used as a parameter for a header file template. This allows you avoid hardcoded literals as I will do later in source code.</p>
  <h3>Plug-ins and dynamic libraries</h3>
  <p>Unless <a href="https://ogrecave.github.io/ogre-next/api/2.3/namespace_ogre.html" target="_blank">Ogre</a> was build with <strong>OGRE_STATIC_LIB</strong>, you will have a plug-in for every render system. It means rendering backend will be loaded at runtime. Happily for us all plug-ins are registred automaticaly by providing configuration file to <a href="https://ogrecave.github.io/ogre-next/api/2.3/class_ogre_1_1_root.html" target="_blank">Root.</a></p>
  <p>The same <strong>*.cfg.in</strong> approch is applicable to plug-ins configuration. Keep in mind that we have two sets of binaries: Debug and Release. Let&#x27;s make configuration file for each.</p>
  <p><strong>Plugins.cfg.in</strong></p>
  <pre data-lang="gitignore"># Defines plugins to load
# Define plugin folder
PluginFolder=@OGRE_PLUGIN_DIR_REL@

# Define plugins
Plugin=RenderSystem_Direct3D11
Plugin=Plugin_ParticleFX</pre>
  <p><strong>Plugins_d.cfg.in </strong></p>
  <pre data-lang="gitignore"># Defines plugins to load
# Define plugin folder
PluginFolder=@OGRE_PLUGIN_DIR_DBG@

# Define plugins
Plugin=RenderSystem_Direct3D11_d
Plugin=Plugin_ParticleFX_d</pre>
  <p>Now we need to copy cfg and plug-ins according to build configuration. Let&#x27;s go further and define also a list of dynamic libraries to be shipped with executable.</p>
  <pre data-lang="coffeescript">if(CMAKE_BUILD_TYPE STREQUAL  &quot;Debug&quot;)    
  set(DEBUG_POSTFIX &quot;_d&quot;)    
  set(OGRE_DLLS         
    &quot;${OGRE_BINARY_DBG}&quot;        
    &quot;${OGRE_HlmsPbs_BINARY_DBG}&quot;         
    &quot;${OGRE_HlmsUnlit_BINARY_DBG}&quot;
    &quot;${OGRE_SDK}/bin/Debug/amd_ags_x64.dll&quot;)
else()    
  set(OGRE_DLLS         
    &quot;${OGRE_BINARY_REL}&quot;        
    &quot;${OGRE_HlmsPbs_BINARY_REL}&quot;        
    &quot;${OGRE_HlmsUnlit_BINARY_REL}&quot;        
    &quot;${OGRE_SDK}/bin/Release/amd_ags_x64.dll&quot;)
endif()</pre>
  <p><strong>Note: </strong><a href="https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order" target="_blank">Windows documentation</a> says</p>
  <blockquote>If a DLL has dependencies, the system searches for the dependent DLLs as if they were loaded with just their module names. This is true even if the first DLL was loaded by specifying a full path. </blockquote>
  <p>Sadly, this is the case for <strong>amd_ags_x64.dll</strong> as it is a dependency for <strong>RenderSystem_Direct3D11.dll. </strong>So even having a full path for plug-in itself it could not be loaded because of its dependency located in same directory but unseen for loading function. Moreover the Ogre source code gives as a hint that we can not specify our own search path</p>
  <pre data-lang="cpp"> #define DYNLIB_LOAD( a ) LoadLibraryEx( a, NULL, 0) 
// we can not use LOAD_WITH_ALTERED_SEARCH_PATH with relative paths</pre>
  <p>For now, it is enough to put all of necessary DLLs to executable location. Let&#x27;s finally generate cfg and copy them. </p>
  <pre data-lang="coffeescript">set(PLUGINS_CFG &quot;Plugins${DEBUG_POSTFIX}.cfg&quot;)
configure_file(&quot;${PLUGINS_CFG}.in&quot; &quot;${DATA_DIR}/${PLUGINS_CFG}&quot;)

foreach(DLL_NAME ${OGRE_DLLS})    
  file( COPY &quot;${DLL_NAME}&quot;    DESTINATION &quot;${CMAKE_BINARY_DIR}&quot; )
endforeach()</pre>
  <p>That&#x27;s pretty much it. After CMake is done with your project you will see all necessary DLLs and data folder in building directory. </p>
  <h3>Main.cpp</h3>
  <p>For me the starting point was <strong>ogre-next\Samples\2.0\Tutorials\Tutorial00_Basic</strong></p>
  <p>It is a bit messy but completely suits us as an example. I moved Hlms part away and removed all platform specific defines to focus solely on Windows.</p>
  <pre data-lang="cpp">#include &quot;OgreCamera.h&quot;
#include &quot;OgreRoot.h&quot;
#include &quot;OgreWindow.h&quot;
#include &quot;Compositor/OgreCompositorManager2.h&quot;
#include &quot;OgreWindowEventUtilities.h&quot;

class MyWindowEventListener : public Ogre::WindowEventListener
{    
  bool mQuit;
public:    
  MyWindowEventListener() : mQuit(false) {}    
  virtual void windowClosed(Ogre::Window *rw) { mQuit = true; }
  bool getQuit(void) const { return mQuit; }
};</pre>
  <p>It is just helper class, notifies us when window is closed.</p>
  <pre data-lang="cpp">int main(int argc, const char *argv[])
{    
  using namespace Ogre;
  const String pluginsFolder = &quot;./data/&quot;;    
  const String writeAccessFolder = &quot;./&quot;;
  
#ifndef OGRE_STATIC_LIB
#if OGRE_DEBUG_MODE    
  const char *pluginsFile = &quot;plugins_d.cfg&quot;;
#else    
  const char *pluginsFile = &quot;plugins.cfg&quot;;
#endif
#endif    
  Root *root = OGRE_NEW Root(pluginsFolder + pluginsFile,
                             writeAccessFolder + &quot;ogre.cfg&quot;,  
                             writeAccessFolder + &quot;Ogre.log&quot;);</pre>
  <p>Here we are using <strong>plugins.cfg</strong> generated by CMake. Note that there is also <strong>ogre.cfg</strong>, it would not be created unless you call <strong>saveConfig</strong>. The example code uses dialog window to choose a rendering system. We already know which to choose, let’s hardcode it as well.</p>
  <pre data-lang="cpp">Ogre::RenderSystem *renderSystem = 
  root-&gt;getRenderSystemByName(&quot;Direct3D11 Rendering Subsystem&quot;);    
if (!(renderSystem))    
{        
  printf(&quot;Render system not found!\n&quot;);        
  return -1;    
}
root-&gt;setRenderSystem(renderSystem);</pre>
  <p>You can set up it using <strong>ogre.cfg </strong>or manually.</p>
  <pre data-lang="cpp">root-&gt;getRenderSystem()-&gt;setConfigOption(&quot;sRGB Gamma Conversion&quot;, &quot;Yes&quot;);
root-&gt;getRenderSystem()-&gt;setConfigOption(&quot;Full Screen&quot;, &quot;No&quot;);</pre>
  <p>Create window.</p>
  <pre data-lang="cpp">Window *window = root-&gt;initialise(true, &quot;Tutorial 00: Basic&quot;);
MyWindowEventListener eventListener;    
WindowEventUtilities::addWindowEventListener(window, &amp;eventListener);</pre>
  <p>Create SceneManager.</p>
  <pre data-lang="cpp">const size_t numThreads = 1u;    
SceneManager *sceneManager = 
  root-&gt;createSceneManager(ST_GENERIC, numThreads, &quot;ExampleSMInstance&quot;);</pre>
  <p>Create camera and position it at 500 in Z direction looking back along -Z.</p>
  <pre data-lang="cpp">Camera *camera = sceneManager-&gt;createCamera(&quot;Main Camera&quot;);
camera-&gt;setPosition(Vector3(0, 5, 15)); 
camera-&gt;lookAt(Vector3(0, 0, 0));    
camera-&gt;setNearClipDistance(0.2f);    
camera-&gt;setFarClipDistance(1000.0f);    
camera-&gt;setAutoAspectRatio(true);</pre>
  <p>Setup a basic <a href="https://ogrecave.github.io/ogre-next/api/2.1/compositor.html" target="_blank">compositor</a> with a blue clear colour.</p>
  <pre data-lang="cpp">CompositorManager2 *compositorManager = root-&gt;getCompositorManager2();    
const String workspaceName(&quot;Demo Workspace&quot;);    
const ColourValue backgroundColour(0.2f, 0.4f, 0.6f);    
compositorManager-&gt;createBasicWorkspaceDef(workspaceName, 
                                           backgroundColour, 
                                           IdString());    
compositorManager-&gt;addWorkspace(sceneManager, 
                                window-&gt;getTexture(), 
                                camera, 
                                workspaceName, 
                                true);</pre>
  <p>Main loop that just waiting window to be closed.</p>
  <pre data-lang="cpp">bool bQuit = false;
while (!bQuit)    
{        
  WindowEventUtilities::messagePump();        
  bQuit = myWindowEventListener.getQuit() || !root-&gt;renderOneFrame();    
}</pre>
  <p>Clean up.</p>
  <pre data-lang="cpp">WindowEventUtilities::removeWindowEventListener(window, &amp;eventListener);
OGRE_DELETE root;    
root = 0;</pre>
  <p>If you build and run, you will see the window filled blue.</p>
  <h3>HLMS</h3>
  <p>Though there is nothing we can use material system for, let&#x27;s initialize it. Add <strong>Hlms.h</strong> </p>
  <pre data-lang="cpp">#include &quot;OgreConfigFile.h&quot;
#include &quot;OgreArchiveManager.h&quot;
#include &quot;OgreHlmsManager.h&quot;
#include &quot;OgreHlmsPbs.h&quot;
#include &quot;OgreHlmsUnlit.h&quot;

void registerHlms( void )
{    
  using namespace Ogre;
  String resourcePath = &quot;data/&quot;;
  ConfigFile cf;    
  cf.load( resourcePath + &quot;Resources.cfg&quot; );    
  String rootHlmsFolder = 
    resourcePath + cf.getSetting( &quot;DoNotUseAsResource&quot;, &quot;Hlms&quot;, &quot;&quot; );
  if( rootHlmsFolder.empty() )        
    rootHlmsFolder = &quot;./&quot;;    
  else if( *( rootHlmsFolder.end() - 1 ) != &#x27;/&#x27; )        
    rootHlmsFolder += &quot;/&quot;;

  auto mng = Root::getSingleton().getHlmsManager();
  mng-&gt;registerHlms( CreateHlms&lt;HlmsUnlit&gt;(rootHlmsFolder) );    
  mng-&gt;registerHlms( CreateHlms&lt;HlmsPbs&gt;(rootHlmsFolder) );
}</pre>
  <p>Create an instance of Hlms, specifying the location of the shader templates.</p>
  <pre data-lang="cpp">template&lt;class T&gt;
T* CreateHlms(const Ogre::String&amp; rootFolder)
{    
  using namespace Ogre;

  String mainFolderPath;    
  StringVector libraryFoldersPaths;    
  StringVector::const_iterator libraryFolderPathIt;    
  StringVector::const_iterator libraryFolderPathEn;
  ArchiveManager &amp;archiveManager = ArchiveManager::getSingleton();
  T::getDefaultPaths( mainFolderPath, libraryFoldersPaths );    
  ArchiveVec archiveLibraryFolders;    
  Archive *archive = archiveManager.load( rootFolder + mainFolderPath, 
                                          &quot;FileSystem&quot;, true );    
  libraryFolderPathIt = libraryFoldersPaths.begin();    
  libraryFolderPathEn = libraryFoldersPaths.end();    
  while( libraryFolderPathIt != libraryFolderPathEn )    
  {        
    Archive *archiveLibrary = 
      archiveManager.load( rootFolder + *libraryFolderPathIt, 
                           &quot;FileSystem&quot;, true );        
    archiveLibraryFolders.push_back( archiveLibrary );        
    ++libraryFolderPathIt;    
  }
  T* hlms = OGRE_NEW T( archive, &amp;archiveLibraryFolders );
    
  RenderSystem *renderSystem = Root::getSingletonPtr()-&gt;getRenderSystem();
  if( renderSystem-&gt;getName() == &quot;Direct3D11 Rendering Subsystem&quot; ) 
  {       
    bool supportsNoOverwriteOnTextureBuffers;        
    renderSystem-&gt;getCustomAttribute(&quot;MapNoOverwriteOnDynamicBufferSRV&quot;,
                                     &amp;supportsNoOverwriteOnTextureBuffers);
    if( !supportsNoOverwriteOnTextureBuffers )        
      hlms-&gt;setTextureBufferDefaultSize( 512 * 1024 );
   } 
   return hlms;
}</pre>
  <p><strong>Note:</strong> That <strong>setTextureBufferDefaultSize </strong>is only for Direct3D11 and if <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_feature_data_d3d11_options" target="_blank">MapNoOverwriteOnDynamicBufferSRV</a> is not supported. Comment from samples says:</p>
  <blockquote>Set lower limits 512kb instead of the default 4MB per Hlms in D3D 11.0<br />and below to avoid saturating AMD&#x27;s discard limit (8MB) or<br />saturate the PCIE bus in some low end machines.</blockquote>
  <p>I did not found any clarification on this matter, but it seems a common practise.</p>
  <h3>Last words</h3>
  <p>Afterward it is not so hard. Now I am planning to add some proper rendering loop, play around with some meshes and materials. I will definitely describe my experience in next article.</p>
  <p>If you find any misprints, errors or have any thoughts, please let me know.<br />Thank you.</p>
  <p>You can find whole project on <a href="https://github.com/busovikov/OgreDemoApp" target="_blank">GitHub</a>. each tutorial chapter will have a <a href="https://github.com/busovikov/OgreDemoApp/tags" target="_blank">tag</a>.</p>
  <p>© 2021 Pavel Busovikov.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@busovikov/xQE9N9IO7bX</guid><link>https://teletype.in/@busovikov/xQE9N9IO7bX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov</link><comments>https://teletype.in/@busovikov/xQE9N9IO7bX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=busovikov#comments</comments><dc:creator>busovikov</dc:creator><title>Hey teletype Pavel here</title><pubDate>Tue, 06 Jul 2021 11:36:45 GMT</pubDate><description><![CDATA[I am a software engineer since 2013 ]]></description><content:encoded><![CDATA[
  <p>I am a software engineer since 2013 </p>
  <p>Here I am going to share my experience in IT industry and programming and show how I learn a new technologies </p>

]]></content:encoded></item></channel></rss>