<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Vyacheslav Valerievich</title><subtitle>Sic parvis magna</subtitle><author><name>Vyacheslav Valerievich</name></author><id>https://teletype.in/atom/holyslav</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/holyslav?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@holyslav?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=holyslav"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/holyslav?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-05-01T14:03:17.838Z</updated><entry><id>holyslav:Otsechenie-perevodov-05-18</id><link rel="alternate" type="text/html" href="https://teletype.in/@holyslav/Otsechenie-perevodov-05-18?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=holyslav"></link><title>Отсечение переводов</title><published>2021-06-17T11:00:03.469Z</published><updated>2021-06-17T13:03:03.369Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/eb/23/eb236628-b023-490b-a532-5b2fc607ecd4.png"></media:thumbnail><category term="zametki-po-godot-engine" label="Заметки по GodotEngine"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/a2/46/a246cffa-56c5-4a3c-9897-86f86f49d656.png&quot;&gt;Всем привет, эта заметка будет посвящена хранению файлов переводов в удобном для чтения формате и в удалённом хранилище, для этого идеально подходит гитхаб, публичный репозиторий это всё, что нужно.</summary><content type="html">
  &lt;p&gt;Всем привет, эта заметка будет посвящена хранению файлов переводов в удобном для чтения формате и в удалённом хранилище, для этого идеально подходит гитхаб, публичный репозиторий это всё, что нужно.&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/a2/46/a246cffa-56c5-4a3c-9897-86f86f49d656.png&quot; width=&quot;1319&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Создаём сцену, накидываем небольшую вёрстку с Label и OptionButton, плюс TextureRect с иконкой годо и пивот поинтом в центре картинки, через Layout центруем картинку.&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/fc/ba/fcbaddc1-bfc1-4ac4-bd40-081daf145480.png&quot; width=&quot;913&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Так же создаём GDScript &amp;quot;boot.gd&amp;quot;, и кидаем его в автозагрузку, если вкратце, скрипт загружает сначала locales.json с гита, в этом файле хранится список локалей в виде массива, далее скрипт проходя по массиву загружает каждую локаль с гита, конвертирует в словарь, создаёт объект перевода, заполняет его словами и добавляет сервер переводов. Ссылка на репозиторий будет в конце заметки.&lt;/p&gt;
  &lt;p&gt;Далее скрипт сцены, т.к. интернет не обладает безграничной скоростью будет некоторая задержка при загрузке локалей, нам нужно как-то показать пользователю, что приложение не зависло, а чё-то делает, самый простой вариант это отображать какую-то анимацию загрузки, я решил сделать простую анимацию через Tween с вращением картинки пока происходит инициализация локалей.&lt;/p&gt;
  &lt;p&gt;Когда инициализация приложения завершена мы твином уменьшаем прозрачность текстовых контейнера с лейблами и увеличиваем прозрачность картинку загрузки, загружаем в опшион список загруженных локалей, выделаем в опшионе системную локаль и подключаем сигнал смены локали.&lt;/p&gt;
  &lt;p&gt;В этом проекте использовался новый элемент &amp;quot;Tween&amp;quot;, более подробно будет в следующих статьях описана работа с ними.&lt;/p&gt;
  &lt;p&gt;Исходный код:&lt;/p&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/holyslav/RemoteLocalizeGodot&quot; target=&quot;_blank&quot;&gt;https://github.com/holyslav/RemoteLocalizeGodot&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>holyslav:Sila-tyagi-05-16</id><link rel="alternate" type="text/html" href="https://teletype.in/@holyslav/Sila-tyagi-05-16?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=holyslav"></link><title>Сила тяги</title><published>2021-06-17T10:59:54.736Z</published><updated>2021-06-17T13:02:50.381Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/6c/5d/6c5d7d29-95e5-4229-ac42-303de830dd17.jpeg"></media:thumbnail><category term="razrabotka-igry" label="Разработка игры"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/22/a3/22a33bbb-7f76-46f6-86a5-5f28488e65da.jpeg&quot;&gt;Ещё на сегодня небольшая заметка: вчера вечером решил переделать шкалу мощности двигателя.</summary><content type="html">
  &lt;p&gt;Ещё на сегодня небольшая заметка: вчера вечером решил переделать шкалу мощности двигателя.&lt;/p&gt;
  &lt;p&gt;Нашёл в сети такой вот sci-fi интерфейс:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/22/a3/22a33bbb-7f76-46f6-86a5-5f28488e65da.jpeg&quot; width=&quot;626&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Решил взять из него, но сложность в том, что у него есть дополнительный 3 элемент - указатель текущего положения, прогресс бар нам из-за этого не подходит, попоробовал разные варианты, думал уже делать на шейдере, но потом допёр, что можно проще, но чуть замороченее, нам понадобятся:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ba/73/ba7308a9-6031-4538-a4a4-bc34ea3de2a4.png&quot; width=&quot;284&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Аж целых 3 элемента, плюс нам нужно будет вырезать из картинки отдельно фон, отдельно белое заполнение шкалы поверх фона и отдельно указатель, указатель нужно засунуть в слайдер&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/d5/55/d555cf4a-5102-4657-8ae2-1c2fed57083b.png&quot; width=&quot;276&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;В TextureRect - файл с фоном, в TextureProgress - заполнение бара, вот так они выглядят по отдельности:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/76/f9/76f906b3-0f01-483b-bc22-6ea34693b598.png&quot; width=&quot;261&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/58/ea/58ea2939-01ca-4178-802a-20e3d6d74d58.png&quot; width=&quot;274&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/1d/3f/1d3fbfc9-f9da-4d38-9786-a04834a17574.png&quot; width=&quot;288&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Значение слайдера и прогресс бара максимальные.&lt;/p&gt;
  &lt;p&gt;Осталось последнее, подогнать размеры слайдера и прогресс бара так, чтобы они были синхронизированы, плюс наложение заполнения на фон тоже должно быть корректным, далее одновременно изменяем значение слайдера и прогресс бара и должно получится что-то такое:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;video src=&quot;/file/b9af00e3e8a0ee80fac54.mp4&quot; controls=&quot;&quot;&gt;&lt;/video&gt;
  &lt;/figure&gt;
  &lt;p&gt;Благодарю за внимание!&lt;/p&gt;

</content></entry><entry><id>holyslav:Odnoj-lish-myshkoj-04-28</id><link rel="alternate" type="text/html" href="https://teletype.in/@holyslav/Odnoj-lish-myshkoj-04-28?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=holyslav"></link><title>Одной лишь мышкой</title><published>2021-06-17T10:59:46.953Z</published><updated>2021-06-17T13:02:34.739Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/ad/dc/addc0875-a73f-4077-8674-b47d9eebd16f.png"></media:thumbnail><category term="zametki-po-godot-engine" label="Заметки по GodotEngine"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/e5/df/e5dfb268-16d1-401e-a6a7-e271d1b6118a.png&quot;&gt;Кнопки, всё о чем вы хотели, но боялись спросить</summary><content type="html">
  &lt;p&gt;Кнопки, всё о чем вы хотели, но боялись спросить&lt;/p&gt;
  &lt;p&gt;Всем привет, сразу к делу, а почему бы нам сделать инвентарь с Drag&amp;amp;Drop&amp;#x60;ом и бонусом от меня?&lt;/p&gt;
  &lt;p&gt;Начнём) Я не дизайнер, поэтому будет функциональный вариант, задизайните потом сами)&lt;/p&gt;
  &lt;p&gt;Сначала создам проект и накидаю необходимые для работы ноды в минимальном варианте&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/e5/df/e5dfb268-16d1-401e-a6a7-e271d1b6118a.png&quot; width=&quot;209&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;В контрол кидаем PanelContainer, его через кнопку Layout(Вид) растягиваем по всему контролу и сразу накидываем флаги на расширение по высоте и ширине&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ec/7a/ec7aebe6-cb27-4928-b00a-589052d36185.png&quot; width=&quot;187&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Чилдом кидаем ГридКонтейнер(сетка), в неё мы уже будем кидать наши элементы, так же для удобства отладки добавим кнопку “поднятия” предмета, она будет генерировать рандомный элемент с рандомным кол-вом.&lt;/p&gt;
  &lt;p&gt;У нас будет 8 столбцов в инвентаре и 4 строчки, для необходимого разнообразия подготовил иконки итемов.&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c0/6e/c06ede47-78ef-4f47-b8b5-6a7b3dede20a.png&quot; width=&quot;513&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Скачаем с гугла шрифт и закинем его в контрол, чтобы мы могли менять размер шрифта&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/9f/ed/9fed9026-5453-4525-adf3-98cd3cd3c4ee.png&quot; width=&quot;1337&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Далее чуть стилизуем, чтобы это больше было похоже на инвентарь, создаём один слот, и сохраняем его в отдельный файл, т.к. мы его будем динамически создавать слоты&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/74/95/7495bac3-e092-4a09-b267-15c4b4d7c7d8.png&quot; width=&quot;1366&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Далее закидываем в главную сцену следующий скрипт:&lt;/p&gt;
  &lt;pre&gt;extends Control

export (int, 1, 20) var columns = 8
export (int, 1, 20) var rows = 4

onready var inv = $InvContainer/InvContent

const slot_scene = preload(&amp;quot;res://Slot.tscn&amp;quot;)

func _ready():
 inv.columns = columns
 for i in range(columns*rows):
  var slot = slot_scene.instance()
  inv.add_child(slot)
&lt;/pre&gt;
  &lt;p&gt;Промежуточный вариант примерно такой&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/dc/ea/dcea7790-96e8-452c-ae64-936933cb5ff9.png&quot; width=&quot;598&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Открываем сцену слота, добавляем туда ещё одну панель, добавляем ей пустой стиль, в неё TextureRect для иконки и Label для кол-ва элементов&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c5/03/c5039834-c8ee-45c8-bc85-164dbdd6f208.png&quot; width=&quot;1358&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Ставим для Иконки такие параметры, если кому интересно, напишите в комментариях, я подробнее расскажу про все параметры, которые использовал в статье&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/3e/af/3eafd9b5-e160-49e3-9bc4-c3782ae77bfe.png&quot; width=&quot;287&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Для текста похожие параметры:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/0f/d3/0fd339cc-4a79-469b-8dce-b07c5db02d09.png&quot; width=&quot;281&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;в Slot создаём скрипт, и кидаем тестовый код&lt;/p&gt;
  &lt;pre&gt;extends PanelContainer

onready var item = $Item
onready var icon = $Item/Icon
onready var count = $Item/Count

var item_type = null
var item_count = 0

func _ready():
 update_data({&amp;quot;type&amp;quot;: &amp;quot;item_type_1&amp;quot;, &amp;quot;count&amp;quot;: 0})
 
func update_data(data = null):
 item.visible = data != null
 if data:
  icon.texture = load(&amp;quot;res://graphics/%s.png&amp;quot; % data.type) #Динамическая загрузка иконки
  count.text = str(data.count)
&lt;/pre&gt;
  &lt;p&gt;Получаем такую картину:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/21/13/2113e51b-b8d2-498b-8be9-a330c320eba3.png&quot; width=&quot;773&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Теперь займёмся кнопкой очистки:&lt;/p&gt;
  &lt;p&gt;Изменяем главный скрипт:&lt;/p&gt;
  &lt;pre&gt;extends Control

export (int, 1, 20) var columns = 8
export (int, 1, 20) var rows = 4

onready var inv = $InvContainer/InvContent

const slot_scene = preload(&amp;quot;res://Slot.tscn&amp;quot;)

func _ready():
 $InvContainer/HBoxContainer/Clear.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;clear_inventory&amp;quot;)
 inv.columns = columns
 for i in range(columns*rows):
  var slot = slot_scene.instance()
  inv.add_child(slot)

func clear_inventory():
 for child in inv.get_children(): #Пробегаем по чилдам инвентаря
  child.update_data() #делаем апдейт без параметров
&lt;/pre&gt;
  &lt;p&gt;Очистка очень простая, коннектимся к сигналу кнопки и функцией из цикла с одной строчкой очищаем инвентарь.&lt;/p&gt;
  &lt;p&gt;Далее кнопка рандомного добавления:&lt;/p&gt;
  &lt;p&gt;Для начала в скрипт изменим так:&lt;/p&gt;
  &lt;pre&gt;extends PanelContainer

onready var item = $Item
onready var icon = $Item/Icon
onready var count = $Item/Count

var item_data = null

func _ready():
 update_data()#{&amp;quot;type&amp;quot;: &amp;quot;item_type_1&amp;quot;, &amp;quot;count&amp;quot;: 123})
 
func empty():
 return item_data == null
 
func update_data(data = null):
 item.visible = data != null
 item_data = data
 if item:
  icon.texture = load(&amp;quot;res://graphics/%s.png&amp;quot; % item_data.type) #Динамическая загрузка иконки
  count.text = str(item_data.count)
 return true
&lt;/pre&gt;
  &lt;p&gt;Закидываем в главный скрипт новые функции:&lt;/p&gt;
  &lt;pre&gt; func has_empty_slot(): #Метод проверки наличия хотя бы одной пустой ячеки
 for child in inv.get_children(): #Пробегаем по чилдам инвентаря
  if child.empty():
   return true
 return false

func get_empty_slot(): #Метод получения случайной пустой ячеки
 var slot = null
 if has_empty_slot(): 
  #Обязательно нужно проверить, что у нас есть пустые ячейки
  #Иначе при полном инвентаре будет бесконечный цикл при полном инвентаре и игра зависнет
  while slot == null: #Ищем случайную пустую ячейку, пока не найдём
   var temp_slot = inv.get_child(rng.randi_range(0, columns*rows-1))
   if temp_slot.empty():
    slot = temp_slot
    break
 return slot

func add_item(): #Слот добавления случайного предмета, который подключен к кнопке
 var slot = get_empty_slot()
 if slot:
  var data = {&amp;quot;type&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;count&amp;quot;: 0}
  data.type = &amp;quot;item_type_&amp;quot; + str(rng.randi_range(1, 8))
  data.count = rng.randi_range(1, 999)
  slot.update_data(data)
&lt;/pre&gt;
  &lt;p&gt;И не забудь подключить сигнал кнопки в методу “add_item”, и всё заработает.&lt;/p&gt;
  &lt;p&gt;*Видео*&lt;/p&gt;
  &lt;p&gt;Следующим шагом реализация D&amp;amp;D(Drag&amp;amp;Drop).&lt;/p&gt;
  &lt;p&gt;Для начала, нужно создать отдельную сцену итема, т.к. нам нужен в двух местах.&lt;/p&gt;
  &lt;p&gt;Выглядит дерево примерно так:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/48/09/4809718e-680c-4151-82c4-cb4c7e4efa25.png&quot; width=&quot;430&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Сразу создадим внутренний скрипт для итема, он простой, чисто устанавливает значение.&lt;/p&gt;
  &lt;pre&gt;extends PanelContainer

onready var icon = $Icon
onready var count = $Count

const path_to_items_icons = &amp;quot;res://graphics/%s.png&amp;quot;

func set_data(item_data):
 icon.texture = load(path_to_items_icons % item_data.type) #Динамическая загрузка иконки
 count.text = str(item_data.count)
&lt;/pre&gt;
  &lt;p&gt;Далее приступаем к слоту:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ec/a5/eca557ae-86f7-4e2c-9f95-aae03ff5e132.png&quot; width=&quot;645&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Сюда мы закинули нашу сцену с итемом, плюс добавился лейбл “Num”, в нём лежит номер слота, я его использовал для отладки, вы можете просто скрыть его или удалить из сцены и из скрипта главной сцены. Кстати о главной сцене, в ней тоже произошли изменения:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ac/d4/acd40e64-6c27-4291-83e8-b125cb59735b.png&quot; width=&quot;285&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Добавился как раз наш итем, координатно ни к чему не привязанный (без контейнеров), а зачем читайте дальше)&lt;/p&gt;
  &lt;p&gt;Теперь самое сложное, это скрипт главной сцены, там произошло куча изменений, в общем смотрите:&lt;/p&gt;
  &lt;pre&gt;extends Control

export (int, 1, 20) var columns = 8 #кол-во столбцов инвентаря
export (int, 1, 20) var rows = 4 #кол-во строчек инвентаря

const slot_scene = preload(&amp;quot;res://Slot.tscn&amp;quot;) #Подгружаем при компиляции сцену слота

onready var inv = $InvContainer/InvContent #Хранилище слотов
onready var titem = $TempItem #Это как раз наш временный итем, он нужен для отображения перетаскивания

onready var rng = RandomNumberGenerator.new() #Инициализация объекта класса рандомайзера

onready var item_dragging = null #Здесь хранится итем при перетаскивании
onready var prev_slot = null #Слот из которого мы перетаскиваем итем

func _ready():
 titem.visible = false #скрываем временный итем
 rng.randomize() #запускаем рандомайзер
 $InvContainer/HBoxContainer/Clear.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;clear_inventory&amp;quot;)
 $InvContainer/HBoxContainer/Add.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;add_item&amp;quot;)
 inv.columns = columns #ограничиваем кол-во слолбцов отображения
 for i in range(columns*rows): #Цикл создания слотов
  var slot = slot_scene.instance() #Создаём объект слота
  slot.name = &amp;quot;Slot_%d&amp;quot; % i #Задаём ему имя, в целом не обязательное действия, но для отладки удобно
  slot.get_node(&amp;quot;Num&amp;quot;).text = str(i) #Как раз тот самый номер слота, если удаляете из сцены слота
  # текстовое поле, то эту строчку тоже нужно удалить
  inv.add_child(slot) #Добавление слота в хранилище

func clear_inventory(): #Функция очистки хранилища
 for child in inv.get_children(): #Пробегаем по чилдам инвентаря
  child.update_data() #делаем апдейт без параметров

func has_empty_slot(): #Метод проверки наличия хотя бы одной пустой ячеки
 for child in inv.get_children(): #Пробегаем по чилдам инвентаря
  if child.empty():
   return true
 return false

func get_empty_slot(): #Метод получения случайной пустой ячеки
 var slot = null
 if has_empty_slot(): 
  #Обязательно нужно проверить, что у нас есть пустые ячейки
  #Иначе при полном инвентаре будет бесконечный цикл при полном инвентаре и игра зависнет
  while slot == null: #Ищем случайную пустую ячейку, пока не найдём
   var temp_slot = inv.get_child(rng.randi_range(0, columns*rows-1))
   if temp_slot.empty():
    slot = temp_slot
    break
 return slot

func add_item(): #Слот добавления случайного предмета, который подключен к кнопке
 var slot = get_empty_slot()
 if slot:
  var data = {&amp;quot;type&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;count&amp;quot;: 0}
  data.type = &amp;quot;item_type_&amp;quot; + str(rng.randi_range(1, 8))
  data.count = rng.randi_range(1, 999)
  slot.update_data(data)

func find_slot(pos:Vector2, need_data = false): #Метод поиска слота по координатам
 #второй параметр - необязательный, он говорит функции искать в позиции слот с итемом или нет
 for c in inv.get_children(): #Пробегаем по чилдам инвентаря
  if (need_data and not c.empty()) or (not need_data):
   if Rect2(c.rect_position, c.rect_size).has_point(pos):
    #Создаём прямоугольник из координат слота и его размеров, чтобы 
    #легко одним методом проверить находится ли точка в этом прямоугольнике
    return c
 return null

func _process(delta):
 var mouse_pos = get_viewport().get_mouse_position() #Получаем позицию мышки
 if Input.get_mouse_button_mask() == BUTTON_LEFT: #Проверяем нажата ли левая кнопка мыши
  if not item_dragging: #если мы уже не тащим элемент
   var slot = find_slot(mouse_pos, true)#ищем под курсором слот с итемом
   if slot: #если слот найден
    item_dragging = slot.item_data #сохраняем в хранилище данные итема
    titem.set_data(item_dragging) #во временнный итем пихаем данные
    titem.visible = true #показываем временный итем
    titem.rect_position = slot.rect_position #перемещаем временный итем в координаты слота
    prev_slot = slot #сохраняем слот из которого будем тащить итем
    slot.update_data() #очищаем слот из которого тащим
  else: #если мы уже тащим итем, то перемещаем временный итем под курсор, со смещением от половины размера итема(чтобы центр итема был под курсором)
   titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)

 else: #если кнопка отпущена
  if item_dragging: #если у нас в хранилище есть итем
   var slot = find_slot(mouse_pos, false) #Ищет слот под курсором
   
   if slot: #если он есть, то пытаемся закинуть в слот данные
    if not slot.update_data(item_dragging): #если не получилось, то возвращаем итем обратно
     prev_slot.update_data(item_dragging)
     
    prev_slot = null #очищаем ссылку на старый слот
    item_dragging = null #сбрасываем хранилище итема
    titem.visible = false #скрываем временный итем
&lt;/pre&gt;
  &lt;p&gt;Я постарался и прокомментировал практически каждую строчку, и получаем такой результат:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;video src=&quot;/file/df864e94ed75e641362a8.mp4&quot; controls=&quot;&quot;&gt;&lt;/video&gt;
  &lt;/figure&gt;
  &lt;p&gt;Чтобы нам ещё хотелось ? Я бы сделал обмен между слотами, мусорку и в конце будет ещё бонус)&lt;/p&gt;
  &lt;p&gt;Для начала дополним и чуть изменим скрипт слота&lt;/p&gt;
  &lt;pre&gt;func check_data(data):
 return &amp;quot;all&amp;quot; in available_types or data.type in available_types

func update_data(data = null):
 item.visible = data != null
 item_data = data
 if item_data:
  if check_data(data):
   item.set_data(item_data)
   return true
  return false
 return true
&lt;/pre&gt;
  &lt;p&gt;Теперь главный скрипт, в нём нужно поменять лишь функцию _process:&lt;/p&gt;
  &lt;pre&gt;func _process(delta):
 var mouse_pos = get_viewport().get_mouse_position() #Получаем позицию мышки
 if Input.get_mouse_button_mask() == BUTTON_LEFT: #Проверяем нажата ли левая кнопка мыши
  if not item_dragging: #если мы уже не тащим элемент
   var slot = find_slot(mouse_pos, true)#ищем под курсором слот с итемом
   if slot: #если слот найден
    item_dragging = slot.item_data #сохраняем в хранилище данные итема
    titem.set_data(item_dragging) #во временнный итем пихаем данные
    titem.visible = true #показываем временный итем
    titem.rect_position = slot.rect_position #перемещаем временный итем в координаты слота
    prev_slot = slot #сохраняем слот из которого будем тащить итем
    slot.update_data() #очищаем слот из которого тащим
  else: #если мы уже тащим итем, то перемещаем временный итем под курсор, со смещением от половины размера итема(чтобы центр итема был под курсором)
   titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)

 else: #если кнопка отпущена
  if item_dragging: #если у нас в хранилище есть итем
   var slot = find_slot(mouse_pos) #Ищет слот под курсором
# Вариант №1
#   if slot: #если он есть, то пытаемся закинуть в слот данные
#    if slot.empty(): #если в слот пустой
#     if slot.check_data(item_dragging): #подходит ли данные к слоту, то обновляем данные
#      slot.update_data(item_dragging)
#     else: #если нет, то возвращаем итем обратно
#      prev_slot.update_data(item_dragging)
#    else: #если слот не пустой, то проверяем подходят ли данные для обмена, если подходят меняем местами
#     if slot.check_data(item_dragging) and prev_slot.check_data(slot.item_data):
#      prev_slot.update_data(slot.item_data)
#      slot.update_data(item_dragging)
#     else: #если нет, то возвращаем обратно
#      prev_slot.update_data(item_dragging)

# Вариант №2
   if slot: #если слот найден
    if slot.check_data(item_dragging): #сразу проверям подходит ли к новому слоту данные, тобишь имеет ли смысл делать проверки дальше
     if slot.empty(): #если в слот пустой
      slot.update_data(item_dragging)
     else: #если слот не пустой, то проверяем подходят ли данные найденного слота для предыдущего
      if prev_slot.check_data(slot.item_data): #если подходит, то обновляем
       prev_slot.update_data(slot.item_data)
       slot.update_data(item_dragging)
    else:
     prev_slot.update_data(item_dragging)

      
    prev_slot = null #очищаем ссылку на старый слот
    item_dragging = null #сбрасываем хранилище итема
    titem.visible = false #скрываем временный итем
&lt;/pre&gt;
  &lt;p&gt;Думаю дополнительное объяснение излишне, единственное хотел бы пояснить зачем два варианта блока условий, оба выполняют одну и ту же задачу, работают одинаково верно, но оцените читаемость первого и второго, сначала мой на скорую руку был набросан первый вариант, задачу выполнял, но читаемость были никакая, написал я его вчера, а сегодня, когда дописывал статью не смог сразу понять чё там происходит, так же и в реальном продакшен коде, зачастую попадаются именно такие куски кода, где без 100 грамм не разберёшься, поэтому бесплатный совет, пишите так, чтобы ваш код понял даже медведь, не говоря уже о возможном психопате после вас, который знает ваш адрес)&lt;/p&gt;
  &lt;p&gt;Это был обмен, теперь мусорка, я решил сделать у слота специальный мета-тип, который будет определять алгоритм работы слота, если бы в годо было адекватное объектно- ориентированное программирование, тогда бы можно было просто наследоваться от класса слота и переопределить методы принятия данных и проверки данных, но нам придётся лепить условия.&lt;/p&gt;
  &lt;p&gt;Подправляем скрипт слота:&lt;/p&gt;
  &lt;pre&gt;extends PanelContainer

signal dropped(data)

export (Array) var available_types = [&amp;quot;all&amp;quot;] 
#массив для ограничения доступности типов предметов для этой ячейки

enum Actions {NONE, TRASH} #Перечисление с допустимиы действиями слота
var cur_act = Actions.NONE #установка переменной действия слота в стандартное положение

onready var item = $Item

var item_data = null #Здесь будет словарь с данными предмета

func _ready():
 update_data()

func set_action(new_value):
 cur_act = new_value
 
 $Item.visible = false
 $Trash.visible = false
 
 match cur_act:
  Actions.NONE:
   $Item.visible = true
  Actions.TRASH:
   $Trash.visible = true

func empty():
 return item_data == null

func check_data(data):
 if cur_act:
  return true
 return &amp;quot;all&amp;quot; in available_types or data.type in available_types

func update_data(data = null):
 if data and cur_act:
  emit_signal(&amp;quot;dropped&amp;quot;, data)
  return true
 item.visible = data != null
 item_data = data
 if item_data:
  if check_data(data):
   item.set_data(item_data)
   return true
  return false
 return true
&lt;/pre&gt;
  &lt;p&gt;И подправляем главный скрипт:&lt;/p&gt;
  &lt;pre&gt;func _ready():
 titem.visible = false #скрываем временный итем
 rng.randomize() #запускаем рандомайзер
 $InvContainer/HBoxContainer/Clear.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;clear_inventory&amp;quot;)
 $InvContainer/HBoxContainer/Add.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;add_item&amp;quot;)
 inv.columns = columns #ограничиваем кол-во слолбцов отображения
 for i in range(columns*rows): #Цикл создания слотов
  var slot = slot_scene.instance() #Создаём объект слота
  slot.name = &amp;quot;Slot_%d&amp;quot; % i #Задаём ему имя, в целом не обязательное действия, но для отладки удобно
  slot.get_node(&amp;quot;Num&amp;quot;).text = str(i) #Как раз тот самый номер слота, если удаляете из сцены слота
  # текстовое поле, то эту строчку тоже нужно удалить
  slot.set_action(slot.Actions.NONE)
  if i == columns*rows-1:
   slot.set_action(slot.Actions.TRASH)
   slot.connect(&amp;quot;dropped&amp;quot;, self, &amp;quot;trash_dropped&amp;quot;)
  inv.add_child(slot) #Добавление слота в хранилище

func trash_dropped(data):
 print(&amp;quot;dropped &amp;quot;, data)
&lt;/pre&gt;
  &lt;p&gt;Мы изменили цикл создания слотов в _ready, плюс добавили новую функцию дропа итема, на случай если вы захотите сделать в игре выброс предмета в мир.&lt;/p&gt;
  &lt;p&gt;Ну а теперь бонус, сделаем полноценный инвентарь игрока.&lt;/p&gt;
  &lt;p&gt;Добавляем доп панель для инвентаря и накидываем ещё слотов:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/9e/99/9e993b40-3d3a-4e56-8612-88b623552f6d.png&quot; width=&quot;948&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Helmet и другие это тоже слоты, как и те, которые мы генерируем.&lt;/p&gt;
  &lt;p&gt;В скрипте слота нужно чутка дополнить:&lt;/p&gt;
  &lt;pre&gt;extends PanelContainer

signal dropped(path, data) #Сигнал помещения итема в корзину
signal accepted(path, data) #Сигнал помещения итема в слот

export (Array) var available_types = [&amp;quot;all&amp;quot;] 
#массив для ограничения доступности типов предметов для этой ячейки

enum Actions {NONE, TRASH} #Перечисление с допустимиы действиями слота
var cur_act = Actions.NONE #установка переменной действия слота в стандартное положение

onready var item = $Item

var item_data = null #Здесь будет словарь с данными предмета

func _ready():
 set_action()
 update_data()

func set_action(new_value = Actions.NONE):
 cur_act = new_value
 
 $Item.visible = false
 $Trash.visible = false
 $Num.visible = false
 
 match cur_act:
  Actions.NONE:
   $Item.visible = true
#   $Num.visible = true
  Actions.TRASH:
   $Trash.visible = true

func empty():
 return item_data == null

func check_data(data):
 if cur_act:
  return true
 return &amp;quot;all&amp;quot; in available_types or data.type in available_types

func update_data(data = null):
 if data and cur_act:
  emit_signal(&amp;quot;dropped&amp;quot;, get_path(), data)
  return true
 item.visible = data != null
 item_data = data
 if item_data:
  if check_data(data):
   item.set_data(item_data)
   emit_signal(&amp;quot;accepted&amp;quot;, get_path(), data)
   return true
  return false
 return true
&lt;/pre&gt;
  &lt;p&gt;Ну и теперь самое главное, скрипт главной цены:&lt;/p&gt;
  &lt;pre&gt;extends Control

export (int, 1, 20) var columns = 8 #кол-во столбцов инвентаря
export (int, 1, 20) var rows = 4 #кол-во строчек инвентаря
export (Array, NodePath) var slots_containers # Экспортная переменная с массивом хранилищ слотов

onready var slots = [] #Массив слотов

const slot_scene = preload(&amp;quot;res://scenes/Slot.tscn&amp;quot;) #Подгружаем при компиляции сцену слота

onready var inv = $PlayerInv/Inv/InvContent #Хранилище слотов
onready var titem = $TempItem #Это как раз наш временный итем, он нужен для отображения перетаскивания
onready var clearButton = $PlayerInv/Inv/Button/Clear
onready var addButton = $PlayerInv/Inv/Button/Add

onready var rng = RandomNumberGenerator.new() #Инициализация объекта класса рандомайзера

onready var item_dragging = null #Здесь хранится итем при перетаскивании
onready var prev_slot = null #Слот из которого мы перетаскиваем итем

func _ready():
 titem.visible = false #скрываем временный итем
 rng.randomize() #запускаем рандомайзер
 clearButton.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;clear_inventory&amp;quot;)
 addButton.connect(&amp;quot;pressed&amp;quot;, self, &amp;quot;add_item&amp;quot;)
 inv.columns = columns #ограничиваем кол-во слолбцов отображения
 for i in range(columns*rows): #Цикл создания слотов
  var slot = slot_scene.instance() #Создаём объект слота
  slot.name = &amp;quot;Slot_%d&amp;quot; % i #Задаём ему имя, в целом не обязательное действия, но для отладки удобно
  slot.get_node(&amp;quot;Num&amp;quot;).text = str(i) #Как раз тот самый номер слота, если удаляете из сцены слота
  # текстовое поле, то эту строчку тоже нужно удалить
  inv.add_child(slot) #Добавление слота в хранилище
  if i == columns*rows-1:
   slot.set_action(slot.Actions.TRASH)
  slots.push_back(slot)
 
 for slots_node in slots_containers: #Массив для перебора всех хранилищ слотов и помещении их в массив для удобства дальнейшего взаимодействия
  for slot in get_node(slots_node).get_children():
   slots.push_back(slot)
 
 for slot in slots:
  slot.connect(&amp;quot;accepted&amp;quot;, self, &amp;quot;slot_accepted&amp;quot;)
  slot.connect(&amp;quot;dropped&amp;quot;, self, &amp;quot;trash_dropped&amp;quot;)
   
func slot_accepted(path, data):
 print(&amp;quot;accepted &amp;quot;, path, &amp;quot; &amp;quot;, data)

func trash_dropped(path, data):
 print(&amp;quot;dropped &amp;quot;, path, &amp;quot; &amp;quot;, data)

func clear_inventory(): #Функция очистки хранилища
 for child in slots: #Пробегаем по всем слотам доступным
  child.update_data() #делаем апдейт без параметров

func has_empty_slot(): #Метод проверки наличия хотя бы одной пустой ячеки
 for child in slots: #Пробегаем по всем слотам доступным
  if child.empty() and child.cur_act != child.Actions.TRASH:
   return true
 return false

func get_empty_slot(): #Метод получения случайной пустой ячеки
 var rand_slot = null
 if has_empty_slot(): 
  var empty_slots = [] #Массив пустых слотов
  for slot in slots: #Перебираем все слоты и ищем пустые и слоты с недопустимыми экшенами
   if slot.empty() and slot.cur_act != slot.Actions.TRASH:
    empty_slots.push_back(slot)
  rand_slot = empty_slots[(rng.randi_range(0, empty_slots.size()-1))] #выбираем случайный слот из пустых
 return rand_slot

func add_item(): #Слот добавления случайного предмета, который подключен к кнопке
 var slot = get_empty_slot()
 if slot:
  var data = {&amp;quot;type&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;count&amp;quot;: 0}
  data.type = &amp;quot;item_type_&amp;quot; + str(rng.randi_range(1, 8))
  data.count = rng.randi_range(1, 999)
  slot.update_data(data)

func find_slot(pos:Vector2, need_data = false): #Метод поиска слота по координатам
 #второй параметр - необязательный, он говорит функции искать в позиции слот с итемом или нет
 for c in slots: #Пробегаем по чилдам инвентаря
  if (need_data and not c.empty()) or (not need_data):
   if c.get_global_rect().has_point(pos):
    #Создаём прямоугольник из координат слота и его размеров, чтобы 
    #легко одним методом проверить находится ли точка в этом прямоугольнике
    return c
 return null

func _process(delta):
 var mouse_pos = get_viewport().get_mouse_position() #Получаем позицию мышки
 if Input.get_mouse_button_mask() == BUTTON_LEFT: #Проверяем нажата ли левая кнопка мыши
  if not item_dragging: #если мы уже не тащим элемент
   var slot = find_slot(mouse_pos, true)#ищем под курсором слот с итемом
   if slot: #если слот найден
    item_dragging = slot.item_data #сохраняем в хранилище данные итема
    titem.set_data(item_dragging) #во временнный итем пихаем данные
    titem.visible = true #показываем временный итем
    titem.rect_position = slot.get_global_rect().position #перемещаем временный итем в координаты слота
    prev_slot = slot #сохраняем слот из которого будем тащить итем
    slot.update_data() #очищаем слот из которого тащим
  else: #если мы уже тащим итем, то перемещаем временный итем под курсор, со смещением от половины размера итема(чтобы центр итема был под курсором)
   titem.rect_position = lerp(titem.rect_position, mouse_pos - titem.rect_size/2, 0.3)

 else: #если кнопка отпущена
  if item_dragging: #если у нас в хранилище есть итем
   var slot = find_slot(mouse_pos) #Ищет слот под курсором

   if slot: #если слот найден
    if slot.check_data(item_dragging): #сразу проверям подходит ли к новому слоту данные, тобишь имеет ли смысл делать проверки дальше
     if slot.empty(): #если в слот пустой
      slot.update_data(item_dragging)
     else: #если слот не пустой, то проверяем подходят ли данные найденного слота для предыдущего
      if prev_slot.check_data(slot.item_data): #если подходит, то обновляем
       prev_slot.update_data(slot.item_data)
       slot.update_data(item_dragging)
    else:
     prev_slot.update_data(item_dragging)
      
    prev_slot = null #очищаем ссылку на старый слот
    item_dragging = null #сбрасываем хранилище итема
&lt;/pre&gt;
  &lt;p&gt;На самом деле здесь есть ещё что дорабатывать, можно было бы отказаться от массива слотов и сделать всё через встроенное в Годо средство, но об этом в одной из следующих статей.&lt;/p&gt;
  &lt;p&gt;Полный листинг в моём гитхаб репозитории: &lt;a href=&quot;https://github.com/holyslav/InventoryGodot&quot; target=&quot;_blank&quot;&gt;https://github.com/holyslav/InventoryGodot&lt;/a&gt;&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;video src=&quot;/file/00b85fbabcfd65343e41a.mp4&quot; controls=&quot;&quot;&gt;&lt;/video&gt;
  &lt;/figure&gt;
  &lt;p&gt;UPD: Подправил функцию &lt;code&gt;get_empty_slot&lt;/code&gt; в последнем листинге, чтобы убрать возможность попадания в бесконечный цикл. в гите так же обновлено.&lt;/p&gt;

</content></entry><entry><id>holyslav:S-zhyostkogo-na-myagkoe-04-25</id><link rel="alternate" type="text/html" href="https://teletype.in/@holyslav/S-zhyostkogo-na-myagkoe-04-25?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=holyslav"></link><title>С жёсткого на мягкое</title><published>2021-06-17T10:59:31.885Z</published><updated>2021-06-17T13:02:03.437Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/27/44/27445e91-47eb-4e64-879f-14a795d73770.png"></media:thumbnail><category term="razrabotka-igry" label="Разработка игры"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/c0/02/c00218d8-8742-4411-b6cb-870ab6666d62.png&quot;&gt;Всем привет, пора вернуться на путь геймдева, теперь у нас есть всё, чтобы можно было удобно вести разработку и отправлять тестировщиками билды.</summary><content type="html">
  &lt;p&gt;Всем привет, пора вернуться на путь геймдева, теперь у нас есть всё, чтобы можно было удобно вести разработку и отправлять тестировщиками билды.&lt;/p&gt;
  &lt;p&gt;Итак, интерполированная камера в годо, на самом деле механизм очень простой, мы копируем текущее положение корабля и применяем его к камере с интерполяцией(сглаживанием перемещения) новой позиции.&lt;/p&gt;
  &lt;p&gt;Мы имеем:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c0/02/c00218d8-8742-4411-b6cb-870ab6666d62.png&quot; width=&quot;413&quot; /&gt;
    &lt;figcaption&gt;Камера&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c4/86/c48688ab-daba-4738-9f31-993f0722f8d9.png&quot; width=&quot;587&quot; /&gt;
    &lt;figcaption&gt;Корабль&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Но странность, с которой я столкнулся, камера повернута назад(в сторону уменьшения координаты Z), а корабль повернут вперед(что правильно), поэтому сначала я пытался повернуть базис камеры на 180 градусов по оси Y, что дало инвертирование положение камеры и корабля.&lt;/p&gt;
  &lt;p&gt;Как исправить? А исправить достаточно просто, нужно пойти от обратного, убрать поворот базиса и просто повернуть саму меш модель корабля на 180, и чтобы исправить косяк с тем, что корабль летит в минусовые координаты нужно ещё сам объект игрока повернуть тоже на 180.&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ae/0c/ae0c1e3f-bf87-4c73-8c2c-8202620b7af7.png&quot; width=&quot;511&quot; /&gt;
    &lt;figcaption&gt;Меш&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/2f/d4/2fd4a2fe-e668-42cd-90b9-579183e788aa.png&quot; width=&quot;509&quot; /&gt;
    &lt;figcaption&gt;Игрок&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Потом закидываем в скрипт камеры следующий код:&lt;/p&gt;
  &lt;pre&gt;extends Camera

export (Vector3) var shift = Vector3(0, 3, 6) #Сдвиг камеры(я сделал вид чуть назад и над кораблём
export (int) var smooth = 4 #С какой скоростью будет происходит возврат в заданное положение камеры, относительно корабля

onready var player = $&amp;quot;../Player&amp;quot; #Ссылка на объект игрока

func _ready():
 make_current() #Делаем камеру текущий(у меня есть камера для вида от 1 лица чуть в объекте игрока)
 global_transform = get_new_trans() #Применяем заданное положение, если этого не сделать, то при запуске игры камера будет из нулевых координат перемещаться к заданным

  func get_new_trans(): #Для удобства модификации сделал отдельную функцию получения координат игрока
    return player.global_transform.translated(shift) #Получем глобальные координаты игрока и сдвигаем их

  func _process(delta):
 global_transform = global_transform.interpolate_with(get_new_trans(), delta*smooth)
 #Применяем к текущим координатам камеры интерполированное значение конечной точки, 
 #и движок сам нам расчитает где нужно находится камере, исходя их текущего положения и необходимого, учитывая скорость
&lt;/pre&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;video src=&quot;/file/5c44e0a8e68d00f3bffde.mp4&quot; controls=&quot;&quot;&gt;&lt;/video&gt;
    &lt;figcaption&gt;Результат&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;И всё, и получаем результат, который нас устраивает. В годо всё делается просто, а если кажется, что сложно, то это временно)&lt;/p&gt;
  &lt;p&gt;Задавайте вопросы, предлагайте темы для статей, удачного геймдева!&lt;/p&gt;

</content></entry></feed>