tag:blogger.com,1999:blog-19045096020566526922024-03-13T03:08:06.119-07:00Blog by Flogger-K. 3D и не толькоFlogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.comBlogger110125tag:blogger.com,1999:blog-1904509602056652692.post-49882009901477256862022-11-08T20:56:00.000-08:002022-11-08T20:56:51.539-08:00"Перехват"<p>Данная статья
рассказывает о первом личном опыте
работы в игровом движке <span lang="en-US">GODOT.
</span>Имея некоторый опыт работы в ныне
уже мертвом движке <span lang="en-US">Blender Game
Engine (BGE), </span>долгое время искал ему
замену. По ряду причин меня не устраивали
ни <span lang="en-US">Unity, </span>ни <span lang="en-US">Unreal
Engine </span>(ограниченность размеров игрового
мира, явная перенасыщенность меню этих
движков, сложность освоения — это прежде
всего к <span lang="en-US">UE, </span>да и политическая
ситуация ныне нездоровая, достаточно
посмотреть на Олимпийское движение с
его «спорт вне политики»).</p><p>Сам проект можно скачать здесь: https://cloud.mail.ru/public/UZ1V/4Sa2ShCMt</p><p>20 МБ. Версия GODOT 3.5.1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">По ряду причин,
прежде всего, технических (старый
компьютер с маленьким объемом ОЗУ), я
вынужден был отказаться (надеюсь,
временно), от освоения российского
движка <span lang="en-US">UNIGINE, </span>хотя и считаю
его весьма перспективным, по некоторым
причинам затормозилось освоение другогог
российскогог движка <span lang="en-US">NeoAxis
</span>(прежде всего из-за запутанности
интерфейса и некоторых вещей, вроде
импорта моделей), но потом я еще попробую
вернуться к этой теме, если что-то будет
получаться.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Фактически
требовалась замена <span lang="en-US">BGE,
</span>желательно <span lang="en-US">open-source, </span>не
перетяжеленный интерфейс, язык
программирования — желательно <span lang="en-US">Python,
</span>хотя и <span lang="en-US">C# </span>подошел бы,
да и С++, если сильно будет нужно, можно
начать изучать на практике.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">В <span lang="en-US">GODOT
</span>используется собственный язык
скриптования, очень похожий на Питон,
есть правда, свои нюансы, самый важный
из которых — невозможность прицепить
к одному объекту несколько скриптов.
Но в принципе, для быстрого старта и
изучения движка на практике, это не
критично.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Первым проектом,
который был доведен до ума и который
можно назвать завершенным, является
небольшая двумерная игра «Перехват»,
созданная по мотивам советского игрового
автомата, на котором когда-то довелось
поиграть в детстве (во второй половине
1980-х годов).</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Скрин стартовой сцены.</p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAPz24terhiHyIgWuFy0TqIK_1oIcfw_MmkJSy7Sd24SkwDydiQG2s5drTwV6wNvz_vJ2s8z2acRdDHWXoTI7gGtucEPJicKd6shzUq_t9UnVdvBadQnDoDoCfy85xymiqJ2BZMaAhbBMaOtpYe7o4xg_cjC_adYFRJJGyiCd0JLHacLbx_lsV_R7J/s1220/vlcsnap-2022-11-08-16h58m51s222.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1220" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAPz24terhiHyIgWuFy0TqIK_1oIcfw_MmkJSy7Sd24SkwDydiQG2s5drTwV6wNvz_vJ2s8z2acRdDHWXoTI7gGtucEPJicKd6shzUq_t9UnVdvBadQnDoDoCfy85xymiqJ2BZMaAhbBMaOtpYe7o4xg_cjC_adYFRJJGyiCd0JLHacLbx_lsV_R7J/s320/vlcsnap-2022-11-08-16h58m51s222.png" width="320" /></a></div><br /><p lang="ru-RU" style="margin-bottom: 0cm;"><br /></p><p lang="ru-RU" style="margin-bottom: 0cm;">Скрин. Перехватчик ведет огонь.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbtz4oU8zAtgbvDHkRDR3lOBamAq_cli4wmozWfHeN-XCGLtA_Wk5JY0cTzu8rcMftTnt3Hqmdz-IcIx1Pk2AA-r3z0CkROhraKb1CqqG7jyfy4x9FYJek2CE768gBg9L3lHwl3tzcCNS6H4EhDl0xkssGipNope1XfEQRnUFUpNfzMT7I6p0JfAYC/s1220/vlcsnap-2022-11-08-17h01m46s264.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1220" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbtz4oU8zAtgbvDHkRDR3lOBamAq_cli4wmozWfHeN-XCGLtA_Wk5JY0cTzu8rcMftTnt3Hqmdz-IcIx1Pk2AA-r3z0CkROhraKb1CqqG7jyfy4x9FYJek2CE768gBg9L3lHwl3tzcCNS6H4EhDl0xkssGipNope1XfEQRnUFUpNfzMT7I6p0JfAYC/s320/vlcsnap-2022-11-08-17h01m46s264.png" width="320" /></a></div><br /><p lang="ru-RU" style="margin-bottom: 0cm;"><br /></p><p lang="ru-RU" style="margin-bottom: 0cm;">Скрин. Взрыв "наглотавшейся" снарядов цели.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS6_9umMnuehLq7mFNaA3WbE4DQobBEL5ldIU8gr5BFP3cBf581YGD13hP7ercBskx6ZFriGLrxuq7mAi4-qRwxQ8_BB-40PoFvcKwfRmVp26wX6UGk16laPyN61wykEU24FiPfygHbbTvuzaM169OBCWRFlFmv31-m9cB86rXUp0RH0RPG-cAO4Uz/s1220/vlcsnap-2022-11-08-17h02m18s454.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1220" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS6_9umMnuehLq7mFNaA3WbE4DQobBEL5ldIU8gr5BFP3cBf581YGD13hP7ercBskx6ZFriGLrxuq7mAi4-qRwxQ8_BB-40PoFvcKwfRmVp26wX6UGk16laPyN61wykEU24FiPfygHbbTvuzaM169OBCWRFlFmv31-m9cB86rXUp0RH0RPG-cAO4Uz/s320/vlcsnap-2022-11-08-17h02m18s454.png" width="320" /></a></div><br /><p lang="ru-RU" style="margin-bottom: 0cm;"><br /></p><p lang="ru-RU" style="margin-bottom: 0cm;">Скрин итоговой сцены.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaiCEfX9jixr-rC1jCQ1-goW-LwUTmSOetgq-ZFz8OgMtgh_2CymTWXsOpC6Xu7m9g2Klf1joAO3ACc6nqruIJ_2G184Xrq4orS1P0m90RraGK5BWsF15K6Bf3bKYHaRngy1z4VFubRRUDGNkhV2lb_3CTIAjTM7hyaGKbA0BCj7WJJH17kWOV7icx/s1220/vlcsnap-2022-11-08-17h02m54s498.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1220" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaiCEfX9jixr-rC1jCQ1-goW-LwUTmSOetgq-ZFz8OgMtgh_2CymTWXsOpC6Xu7m9g2Klf1joAO3ACc6nqruIJ_2G184Xrq4orS1P0m90RraGK5BWsF15K6Bf3bKYHaRngy1z4VFubRRUDGNkhV2lb_3CTIAjTM7hyaGKbA0BCj7WJJH17kWOV7icx/s320/vlcsnap-2022-11-08-17h02m54s498.png" width="320" /></a></div><br /><p lang="ru-RU" style="margin-bottom: 0cm;"><br /></p><p lang="ru-RU" style="margin-bottom: 0cm;">Сама игра
предельно проста — небольшой экран, на
котором изображается небо с плывущими
по нему облаками, создающее иллюзию
полета. В нижней части экрана влево-вправо
перемещается с помощью ручки управления
силуэт перехватчика, навстречу которому
по экрану сверху вниз пролетают самолеты
противника. За время игрового сеанса
игрок должен сбить как можно больше
«вражеских» самолетов, нажимая на кнопку
на ручке управления, имитрующую гашетку
авиациооной пушки. Имеется табло
подсчета уничтоженных целей, и таймер.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">При создании
игры я работал именно «по мотивам», не
копируя все из оригинальной версии
«Перехвата», поэтому все, что можно
увидеть на экране — отличается и довольно
сильно. Сама игра состоит из трех основных
сцен — стартовой, собственно игровой
и итоговой. На игровой сцене происходят
основные события и на нее же подгружаются
сцены элементов игры. <span lang="en-US">GODOT
</span>предусматривает создание некоего
конструктора из множества сцен, на
которых размещаются элементы самого
проекта, которые могут подгружаться в
основную сцену по мере надобности. Это
очень удобно, почти точно так же работал
и почивший ныне <span lang="en-US">BGE.</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Итак, переходим,
собственно к описанию проекта, можно
даже сказать, к его технической
документации.
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Стартовая сцена,
она же <span lang="en-US">StartGameScene. </span>Она довольно
проста и состоит всего из 5 элементов,
как их называют в <span lang="en-US">GODOT – </span>нод,
узлов. Одна из них является корнем,
родителем для всех остальных. В сцене
имеются две кнопки «Старт» и «Выход»,
фоновая каринка и нода фоновой музыки.
Картинка была создана в ГИМП, на ней
фото самого игрового автомата и
сопроводительный текст, в котором заодно
указаны клавиши управления, их всего
три — Пробел, Левая Стрелка и Правая
Стрелка. Поверх картинки и размещаются
две кнопки. Фоном играет музыка,
представляющая из себя мелодию формата
<span lang="en-US">ogg. </span>Здесь следует отметить,
что если вам надо непрерывно проигрывающийся
звук (циклическое воспроизведение), то
надо использовать именно формат <span lang="en-US">OGG.
</span>При его добавлении на ноду
<span lang="en-US">AudioStreamPlayer </span>он автоматически
зацикливается и вам не нужны дополнительные
манипуляции. Только надо не забыть
поставить галочку в графу <span lang="en-US">Autoplay
</span>в Инспекторе меню (справа, верхняя
часть меню). В свое время я долго не ммог
понять, почему у меня не происходит
циклического проигрывания звука, который
был в формате <span lang="en-US">wav, </span>так вот
причина крылась именно в формате —
нужен был <span lang="en-US">ogg.</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Скриптов в первой
сцене немного — всего-то один. И он
расположен на корневой ноде.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Node2D</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Это
переменная таймера кнопки «старт»</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var timerStart = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Эта
функция выполняется РАЗОВО</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Установка
сигналов на нажатие кнопок, и названия
функций по которым должна идти
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>отработка
функций после выдачи сигнала. Сигнал
стандартный «кнопка нажата», а
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>функции
разные.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
$GameStart.connect("button_down", self, "_click1")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $GameEnd.connect("button_down",
self, "_click2")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Эта
функция запускает таймер кнопки «Старт»</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _click1():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> timerStart += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Стандартная
функция </span>GODOT, <span lang="ru-RU">работать
начинает сразу после _</span>ready <span lang="ru-RU">и
работает непреывно.</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Если
таймер кнопки стартовал и не равне нулю,
он непрерывно увеливает свое
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>значение,
пока не достигнет некоего предела.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if timerStart!= 0:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> timerStart+= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Все
это нужно, чтобы игрок увидел реакцию
кноки, иначе будет мгновенно
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#
</span>загружена игровая сцена, будет не
слишком красиво.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if timerStart > 10:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>Кнопка нажалась, теперь
можно перейти на игровую сцену</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_tree().change_scene("res://Scenes/GeneralGameScene.tscn")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>Эта
функция просто выключает игру </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _click2():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_tree().quit()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">А далее мы
попадаем на игровую сцену. На ней имеются:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Фон однотонного
цвета</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Черная полоска
с иконками и цифрами внизу</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Перехватчик,
перемещающийся влево-вправо</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Проплывающие
по экрану сверху вниз облака</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Самолеты и
иракеты противника, также плывущие
сверху вниз</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Трассера
перехватчика игрока</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Взрывы при
попаданиях в цели</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Сама сцена
называется <span lang="en-US">GeneralGameScene, </span>это
имя носит корневая нода, к которой
подсоединены все выше перечисленные
вещи.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Фон сцены —
просто цветовая нода <span lang="en-US">ColorRect,
</span>которая задает цвет фону.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Черная полоска
с иконками снарядов, взрывов, уничтоженных
целей и «проскочивших» целей также
выполнена в ГИМПЕ — это просто рисунок
с прозрачным слоем, в формате <span lang="en-US">png.</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Цифры же
представляют из себя ноды типа <span lang="en-US">Label,
</span>то есть «метка» с текстом в виде
цифр, причем все ноды переименованы
так, чтобы можно было с ходу понять, кто
и за что отвечает.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">А вот дальше уже
идут подгружаемые объекты — перехватчик,
облака и цели...</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Сначал скрипт
корневой ноды <span lang="en-US">GeneralGameScene.</span></p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">extends Node2D</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>Переменная
таймера облаков</p>
<p lang="en-US" style="margin-bottom: 0cm;">var randomClouds = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>Словарь
данных игры — текущие данные и данные
за лучший сеанс
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Уничтоженные
цели, Кол-во попаданий, Пропущенные цели</span></p>
<p lang="en-US" style="margin-bottom: 0cm;">var DictSave = {"Frag":0,
"Victories":0, "Survives":0, "BestFrag":0,
"BestVictories":0, "BestSurvives":0}</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Таймер
целей</span></p>
<p lang="en-US" style="margin-bottom: 0cm;">var TargetGen = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>Боекомплект
(БК), попадания, проскочившие цели,
уничтоженные цели</p>
<p lang="en-US" style="margin-bottom: 0cm;">var BK = 500</p>
<p lang="en-US" style="margin-bottom: 0cm;">var Frag = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;">var Survives = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;">var Victories = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US"> #Добавление
сцены перехватчика игрока, </span>срабатывает
ОДИН раз</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var Interceptor =
preload("res://Scenes/Interceptor.tscn").instance()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> add_child(Interceptor)</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Очень
важная вещь — сохранение данных после
игры в файл формата </span>json</p>
<p lang="en-US" style="margin-bottom: 0cm;">func jsonJobWrite():</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var fileSave = File.new()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> fileSave.open("res://Scripts/DictSave.json",
File.WRITE)</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Ключеовая
строчка — запись обновленного словаря
скрипта в <span lang="en-US">json</span></p>
<p lang="en-US" style="margin-bottom: 0cm;"> fileSave.store_string(to_json(DictSave))</p>
<p lang="en-US" style="margin-bottom: 0cm;"> fileSave.close()</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>Здесь
возможна некоторая путаница, дело в
том, что сначала открывается файл <span lang="en-US">json
</span>с
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>данными
за предыдущую игру и эти данные пишутся
в словарь этого скрипта. Причем</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>по
окончании сеанса происходит исправление
части данных словаря — а именно, текущих</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>достижений,
достижения лучшего сеанса (ключи словаря
с приставкой <span lang="en-US">Best </span>не
меняются),</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US">#</span>и
уже пеотом, при переходе на итоговую
сцену, обновленный словарь вновь пишется
в <span lang="en-US">json. </span></p>
<p lang="en-US" style="margin-bottom: 0cm;">func jsonJob():</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var LoadFile = File.new()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Проверка,
есть ли такой файл</p>
<p lang="en-US" style="margin-bottom: 0cm;"> if not
LoadFile.file_exists("res://Scripts/DictSave.json"):</p>
<p lang="en-US" style="margin-bottom: 0cm;"> return</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Если
есть, то открываем, читаем данные и
записываем их в словарь скрипта</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Что-то
вроде временной копии.</p>
<p lang="en-US" style="margin-bottom: 0cm;"> else:</p>
<p lang="en-US" style="margin-bottom: 0cm;"> LoadFile.open("res://Scripts/DictSave.json",
File.READ)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var temp =
parse_json(LoadFile.get_line())</p>
<p lang="en-US" style="margin-bottom: 0cm;">
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>Это как раз чтение и
запись в словарь скрипта</p>
<p lang="en-US" style="margin-bottom: 0cm;"> DictSave = temp</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>А
вот это уже пошли исправления —
учитываются текущие результаты</p>
<p lang="en-US" style="margin-bottom: 0cm;"> DictSave["Victories"]
= Victories</p>
<p lang="en-US" style="margin-bottom: 0cm;"> DictSave["Frag"]
= Frag</p>
<p lang="en-US" style="margin-bottom: 0cm;"> DictSave["Survives"]
= Survives</p>
<p lang="en-US" style="margin-bottom: 0cm;"> LoadFile.close()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#
</span>А вот теперь обновленный словарь
можно и записать — перед закрытием
игровой
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>сцены
и уходом на итоговую (см. функцию выше)<span lang="en-US"> </span></p>
<p lang="en-US" style="margin-bottom: 0cm;"> jsonJobWrite()</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Блок
случайности пояления облаков, работает
таймер облаков</p>
<p lang="en-US" style="margin-bottom: 0cm;"> randomize()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> randomClouds =
rand_range(0, 10)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> if randomClouds >
9.98:</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var Clouds =
preload("res://Scenes/Cloud.tscn").instance()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> add_child(Clouds)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Выход
из игры происходит при исчерпании БК
либо по нажатии <span lang="en-US">Esc </span></p>
<p lang="en-US" style="margin-bottom: 0cm;"> if
Input.is_action_pressed("ui_cancel") or BK == 0:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>Сначал обязательно
обрабатываются данные текущего сеанса,
и только</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>потом идет переход на
итоговую сцену</p>
<p lang="en-US" style="margin-bottom: 0cm;"> jsonJob()</p>
<p lang="en-US" style="margin-bottom: 0cm;"> get_tree().change_scene("res://Scenes/EndGame.tscn")</p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Работа
генератора целей, а также контроль
расхода БК, попаданий и т.п.
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Используется
текст дочерних нод <span lang="en-US">Label</span></p>
<p lang="en-US" style="margin-bottom: 0cm;"> TargetGen += 1
</p>
<p lang="en-US" style="margin-bottom: 0cm;"> get_parent().get_node("BK").text
= str(BK)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> get_parent().get_node("Frag").text
= str(Frag)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> get_parent().get_node("Survives").text
= str(Survives)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> get_parent().get_node("Victories").text
= str(Victories)</p>
<p lang="en-US" style="margin-bottom: 0cm;">
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><span lang="en-US"> #</span>Появление
новой цели на экране</p>
<p lang="en-US" style="margin-bottom: 0cm;"> if TargetGen == 300:</p>
<p lang="en-US" style="margin-bottom: 0cm;"> var Target =
preload("res://Scenes/Target.tscn").instance();</p>
<p lang="en-US" style="margin-bottom: 0cm;"> add_child(Target)</p>
<p lang="en-US" style="margin-bottom: 0cm;"> TargetGen = 0</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Скрипт относительно
длинный, но много места в нем занимает
работа по контролю и сохранению данных.
А теперь перейдем к подгружаемым
объектам.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Сначала облака
— это чисто декоративные объекты, их
задача медленно и величественно проплыть
по экрану, иногда мешая, иногда помогая
игроку. Скрипт прост.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Sprite</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>При
появлении облака происходят три случайные
вещи. Задается положение</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>размер
и форма (через изменение текстуры вида
облака)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> randomize()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var coordinateX =
rand_range(40,980)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var scaleCloud =
rand_range(0.1, 0.6)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var textureNum =
rand_range(0,1)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Смена
текстуры, просто выбирается одна картинка
из трех</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if textureNum < 0.66
and textureNum > 0.33:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Cloud2.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum >
0.66:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Cloud3.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Случайное
место появления — только по горизонтали,
по вертикали облако ВСЕГДА</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>ставится
за пределами экран </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.x = coordinateX</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y = -20</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> scale =
Vector2(scaleCloud, scaleCloud)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Дальше
оно просто плывет сверху вниз, при выходе
за пределы видимости</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>убирается,
чтобы не захламлять экран и память</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y += 0.5</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Это
как раз егог исчезновение</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if position.y > 700:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> self.queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">А вот теперь
переходим уже к перезватчику, трассерам,
противникам и взрывам...</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Скрипт перехватчика:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Sprite</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Таймер
стрельбы</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var shoot = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>При
своем появлении перехватчик устанавливается
в правом нижнем углу экрана</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.x = 980</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y = 500</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Для
разнообразия можно сменить текстуру
вида перехватчика случайным образом</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Их
три штуки, довольно сильно отличающиеся,
но только видом</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> randomize()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var textureNum =
rand_range(0,1)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if textureNum < 0.66
and textureNum > 0.33:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Interceptor2.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum >
0.66:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Interceptor1.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Управление
— вправо-влево и стрельба, перемещение
ограничено в координатах,
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>чтобы
не «уезжать» за пределы экрана</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if
Input.is_action_pressed("ui_right") and position.x <
980:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.x +=8</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif
Input.is_action_pressed("ui_left") and position.x > 40:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.x -=8</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if
Input.is_key_pressed(KEY_SPACE):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if
get_parent().get_parent().BK > 0:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> shoot += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if shoot == 7:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>Это и есть
стрельба — добавление сцены трассера
раз в 8
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>тиков
таймера, чтобы темп был не слишком высок</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var Trasser =
preload("res://Scenes/Trasser.tscn").instance();</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> add_child(Trasser)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> shoot = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Дальше смотрим
скрипт трассера-снаряда. Его задача —
пролететь по экрану вверх, если он не
встретился с целью, то просто исчезнуть,
если было пересечение — вызвать взрыв,
сообщить о своем попадании «счетчику»
и исчезнуть.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Sprite</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>В
этом блоке разово выставляется размер
трассера, отыскивается корневой элемент
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>элемент
сцены со счетчиком и включается нода
отслеживания столкновения с целью</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#Area2D,
</span>которая должна подать сигнал всем
«заинтересованным лицам»</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_parent().get_parent().get_parent().get_parent().BK
-= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $AudioStreamPlayer.play()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $Area2D.connect("area_entered",
self, "hit")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> scale = Vector2(1.5, 1.5)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Это
просто полет трассера вверх по экрану
и его «самоликвидация» при выходе
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>за
пределы оного</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y -= 100</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if position.y < -2500:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> self.queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func hit(object):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>В
случае попадания следует сообщение для
«счетчика», смотрим скрипт главной</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>игровой
сцены <span lang="en-US">GeneralGameScene, </span>там есть
переменная <span lang="en-US">Frag</span>
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_parent().get_parent().get_parent().get_parent().Frag
+= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Все,
сообщение передано, убираем ненужный
трассер</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Все, что происходит
с целью — описано в скрипте <span lang="en-US">Target,
</span>в котором описано ее поведение.
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Sprite</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Переменные
урона и «порога стойкости»</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var healthAnti = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var limitLife = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Появление
цели по горизонтали случайно, но она
всегда появляется за верхним</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>обрезом
экрана</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> randomize()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var numX =
rand_range(40,980)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Выставляем
«порог стойкости», то есть сколько
попаданий выдержит цель</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>стартовые
координаты, и включаем сигнал столкновения
со снарядом</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> limitLife =
rand_range(0,5)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.x = numX</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y = -20</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $Area2D.connect("area_entered",
self, "hit")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var textureNum =
rand_range(0,1)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Это
длинный и однообразный блок для смены
текстур вида цели, выбирается аж</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>девять
картинок</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if textureNum < 0.3
and textureNum > 0.15:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target1.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum < 0.45
and textureNum > 0.3:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target2.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum < 0.6
and textureNum > 0.45:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target3.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum < 0.75
and textureNum > 0.6:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target4.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum < 0.9
and textureNum > 0.75:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target5.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif textureNum > 0.9:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> set_texture(load("res://GamePics/Target6.png"))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>А
дальше цель плывет вниз, и исчезает,
если в нее не попали, только и всего</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> position.y += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> #print(get_parent().get_parent().name)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if position.y > 700:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_parent().get_parent().Survives
+= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> self.queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func hit(object):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>А
вот если попали, идет добавление урона,
и как только цель «наглотается» снарядов</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>следует
взрыв и сопутствующая этому возня с
картинками, звуками и подсчетами</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> healthAnti += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if healthAnti >
limitLife:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> deadTarget()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Основная
возня описана здесь</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func deadTarget():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Сначала
подгружаем сцену взрыва</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var Explode =
preload("res://Scenes/Explode.tscn").instance();</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Сообщение
об уничтожении цели в ноду <span lang="en-US">GeneralGameScene</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_parent().get_parent().Victories
+= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Этот
блок нужен для правильного расположения
места взрыва, иначе он появится в
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#
</span>левом верхнем углу, в нулевой точке,
надо переместить спрайт взрыва в точку
своих</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>последних
координат, да еще отсоединить от себя,
чтобы не убрать вместе с собой, то</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>есть
приходится менять родительский объект...</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var generalNode =
get_tree().get_root().get_child(0)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> add_child(Explode)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> self.remove_child(Explode)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> generalNode.add_child(Explode)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> Explode.position =
self.position</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Вот
теперь все, и цель может быть убрана с
экрана после своего уничтожения</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Рассмотрим
скрипт взрыва снаряда, который выдает
картинку вспышки (анимированную) и звук,
кстати, звук выстрела присутствует на
сцене трассера, добавляясь вместе с
ним. Так и звук взрыва тоже добавляется
вместе со сценой взрыва и также
проигрывается автоматически один раз
при появлении.
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends AnimatedSprite</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var timerLife = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Ради
эксперимента здесь выставлено проигрывание
звука через код</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $AudioStreamPlayer.play()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Чтобы
анимация спрайта проигрывалась, ее надо
создать и дать ей название</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>а
уже потом ее можно проиграть одной
строчкой</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> play("Explode")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>А
это просто задержка исчезновения
ненужной после анимации и звука сцены,
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Чтобы
звук не «обрезался»</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> timerLife += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if timerLife == 100:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_parent().queue_free()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Собственно, на
этом все с осноной игровой сценой. Теперь
остается итоговая. Она тоже относительно
проста, как и стартовая, только кнопок
там три, и функции у них немного другие.
Также присутствует текстура фона с
текстом и зацикленная музыка.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Скрипт <span lang="en-US">EndGame
</span>проводит подведение итогов, он тоже
длинный.</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">extends Node2D</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Таймер
для кнопки, чтобы была небольшая задержка
(см. самый первый скрипт)</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var timerStart = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Все
уже знакомо — словарь сохранения
результатов, переменные для попаданий,
побед и пр</span></p>
<p lang="en-US" style="margin-bottom: 0cm;">#<span lang="ru-RU">Плюс
лучшие достижения</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var DictSave = {"Frag":0,
"Victories":0, "Survives":0, "BestFrag":0,
"BestVictories":0, "BestSurvives":0}</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var CurrentVictories = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var CurrentBullet = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var CurrentLoose = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var BestVictories = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var BestBullet = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">var BestLoose = 0</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _ready():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>И
снова загрузка и чтение фала <span lang="en-US">json
</span>после только что проведенного
игрового сеанса</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var LoadFile = File.new()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if not
LoadFile.file_exists("res://Scripts/DictSave.json"):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> return</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> else:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> LoadFile.open("res://Scripts/DictSave.json",
File.READ)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var temp =
parse_json(LoadFile.get_line())</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> #print(temp)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> CurrentVictories =
temp["Victories"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> CurrentBullet =
temp["Frag"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> CurrentLoose =
temp["Survives"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> BestVictories =
temp["BestVictories"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> BestBullet =
temp["BestFrag"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> BestLoose =
temp["BestSurvives"]</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">
<span lang="en-US">#</span>Меняем текст дочерних
нод типа <span lang="en-US">Labael </span>на итоговом
меню</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $CurrentBullet.text =
str(temp["Frag"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $CurrentVictories.text =
str(temp["Victories"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $CurrentLoose.text =
str(temp["Survives"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $BestBullet.text =
str(temp["BestFrag"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $BestVictories.text =
str(temp["BestVictories"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $BestLoose.text =
str(temp["BestSurvives"])</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Файлы
<span lang="en-US">json </span>после использования
надо обязательно закрывать</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> LoadFile.close()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Три
кнопки с сигналами при их нажатии -
«Выход», «Новая игра», «Сброс» </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $ButtonReset.connect("button_down",
self, "_click1")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $ButtonNewGame.connect("button_down",
self, "_click2")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> $ButtonQuit.connect("button_down",
self, "_click3")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _process(delta):</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Снова
задержка действия, чтобы кнопка успела
показать свое нажатие</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if timerStart!= 0:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> timerStart+= 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if timerStart > 10:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> Resultat()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_tree().change_scene("res://Scenes/GeneralGameScene.tscn")</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _click1():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Для
кнопки «Сброс» - просто обнуляет все
значения для словаря, игра начинается</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#
</span>с нулевой точки, все старые достижения
стерты</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave = {"Frag":0,
"Victories":0, "Survives":0, "BestFrag":0,
"BestVictories":0, "BestSurvives":0}</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var fileSave = File.new()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.open("res://Scripts/DictSave.json",
File.WRITE)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.store_string(to_json(DictSave))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.close()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _click2():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Новая
игра</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> Resultat()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> timerStart += 1</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func _click3():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#”</span>Выход»
- просто выключение игры</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> get_tree().quit()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;">func Resultat():</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Идет
сравнение только что проведенного
игрового сеанса с лучщими достижениями</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Если
результат лучше, то старые достижения
сменяются новыми</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if CurrentVictories >
BestVictories:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestVictories"]
= CurrentVictories</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestFrag"]
= CurrentBullet</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestSurvives"]
= CurrentLoose</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif CurrentVictories ==
BestVictories:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if CurrentBullet <
BestBullet:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestVictories"]
= CurrentVictories</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestFrag"]
= CurrentBullet</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestSurvives"]
= CurrentLoose</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> elif CurrentBullet ==
BestBullet:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> if CurrentLoose <
BestLoose:</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestVictories"]
= CurrentVictories</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestFrag"]
= CurrentBullet</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["BestSurvives"]
= CurrentLoose</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> </p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["Victories"]
= CurrentVictories</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["Frag"]
= CurrentBullet</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> DictSave["Survives"]
= CurrentLoose</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> <span lang="en-US">#</span>Запись
обновленного словаря в файл <span lang="en-US">json</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> var fileSave = File.new()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.open("res://Scripts/DictSave.json",
File.WRITE)</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.store_string(to_json(DictSave))</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"> fileSave.close()</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Надеюсь, было
более-менее понятно. Следует учесть,
что далеко не все возможности и нюансы
<span lang="en-US">GODOT </span>были мною освоены,
очень возмржно, что в некоторых местах
код может быть более лаконичным, возможно,
что-то было наворочено лишнего, но проект
был доведен до рабочего состояния, был
успешно экспортирован в приложение
типа <span lang="en-US">exe </span>для Виндовс, может
быть существенно доработан, к примеру,
при увеличении числа побед увеличивается
частота появления целей и их скорость
и скорость перехватчика, можно добавлять
бонусы в виде дополнительного боезапаса,
ограничить время игры по таймеру,
включить новые виды оружия типа
самонаводящихся ракет, постановку
помех, и так далее (но это уже ближе к
авиасимулятору будет).
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Для освоения
<span lang="en-US">GODOT </span>использовались примеры
из блога Дзен;</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><a href="https://dzen.ru/godotengine">https://dzen.ru/godotengine</a></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Официальная
документация <span lang="en-US">GODOT</span></p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><a href="https://godot-ru.readthedocs.io/ru/latest/"><span lang="en-US">https://godot-ru.readthedocs.io/ru/latest/</span></a></p>
<p lang="ru-RU" style="margin-bottom: 0cm;">Музыка взята с
сайта <span lang="en-US">Opengameart</span></p>
<p lang="en-US" style="margin-bottom: 0cm;">https://opengameart.org/</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="ru-RU" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;"><br />
</p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>
<p lang="en-US" style="margin-bottom: 0cm;"> </p>Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-71043185469210833172021-07-24T01:58:00.005-07:002021-07-24T01:58:49.496-07:00Игра "Морской бой. След торпеды" Альфа-версия.<p> В другой жизни и другой стране, в детстве довелось мне несколько раз поиграть в существовавшие тогда игровые автоматы. И больше всего меня тог8да впечатлил именно автомат "Торпедная атака". Смотришь себе в окуляры с рукоятками, поворачиваешь их влево-вправо и видишь, как из перископа подолдки морскую даль, бегающие туда-сюда кораблики, жмешь кнопочку и следишь, как светлячок торпеды движется к цели...</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="541" src="https://www.youtube.com/embed/SnnzQadOnoQ" width="650" youtube-src-id="SnnzQadOnoQ"></iframe></div><br /><p>И вот подумалось, а почему бы не попробовать сделать то же самое в БГЕ. Пусть и мертв ныне этот движок, в смысле, не занимаются им больше создатели Блендера, но уж такое-то должен он потянуть.</p><p>Сказано- сделано. Альфа версия</p><p><br /></p><p><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Простенькая игра. "Морской бой. след торпеды" Создана в BGE, для работы игры испольуйте Blender 2.79b.Управление - перископ влево-вправо - стрелочки влево-вправо, выстрел - - пробел. При переходе на сцену подведения итогов - выход из игры - стрелочка вниз, новый круг - стрелочка вверх.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Длительность игрового сеанса - 3 минуты, боезапас - 20 торпед. После первого сеанса в разделе "Лучший результат будут нули, затем идет сравнение и результат улучшается в первую очередь по количеству побед за сеанс, во-вторых, если количество текущих побед совпало с лучшим результатом, идет сравнение с количеством израсходованного боезапаса, чем меньше израсходовано торпед на одну цель, тем лучше.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Можете поэкспериментировать с дальностью до генерируемых кораблей - открывайте скрипт Submarine_Control.py и ищите строчку почти в самом конце randomXY = random.randrange(200, 1400). В скобках эта самая дальность, можете расширить ее пределы, но не более 4500, хотя сомневаюсь, что на такой дистанции удастся вообще когда-нибудь попасть. А вот уменьшить, скажем до 800-1000 - улучшит количество попаданий. Отрицательные цифры ставить нельзя, и меньше 100 тоже.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Объем упакованного файла - 49 Мб, распакованного проекта - 74. Запускаемый файл - бленд SeaBattle>blend. Скрипт, о котором я говорил, находится в нем же.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Альфа версия. Только один тип мишени - незатекстуренный, тонет при попадании без всяких дополнительных эффектов, движение слева-направо. Предполагаю следующие изменения/дополнения:</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">1) Еще пара-тройка моделей кораблей.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">2) Все модели с текстурами.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">3) Смена текстуры неба случайным образом при старте (для разнообразия).</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">4) Возможность появления кораблей с другой стороны, но движение все равно будет только в одну сторону (типа конвой в море)</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">5) Если БГЕ не будет сильно возмущаться - добавить эффект разрушения - пожар на тонущем корабле с дымом.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><span style="background-color: #fafafa; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; text-align: justify;">Возможно, что при первом запуске будет "осекаться", у меня так бывало, при повторном запуске все нормально (может у меня просто другие программы мешали, их много было, запущенных). Возможно, будет нестандартное срабатывание - взрыв есть, но корабль не тонет, причем сработка происходит на самом "кончике" мишени, видимо, надо еще помудрить с настройками сенсора Неар. Но происходит это крайне редко - меньше, чем пальцев на руке при где-то сотне запусков и тестовых прогонах.</span><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><a href="https://drive.google.com/file/d/1cfOjeJLvPIUFGBjxt1e19u2ubN6V5XBh/view?usp=sharing" style="background-color: #fafafa; box-sizing: border-box; color: #518473; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify; text-decoration-line: none;">https://drive.google.com/file/d/1cfOjeJLvPIUFGBjxt1e19u2ubN6V5XBh/view?usp=sharing</a><br style="background-color: #fafafa; box-sizing: border-box; color: #333333; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify;" /><a href="https://yadi.sk/d/nqPDWcWfxCe93Q" style="background-color: #fafafa; box-sizing: border-box; color: #518473; font-family: Roboto, sans-serif; font-size: 15px; margin: 0px; padding: 0px; text-align: justify; text-decoration-line: none;">https://yadi.sk/d/nqPDWcWfxCe93Q</a></p><p><br /></p><p>Скрины:</p><p>Старт<a href="https://1.bp.blogspot.com/-KBhJvtxWVJo/YPvVCmbqrQI/AAAAAAAAAjs/SifnNaZicFQVocIlcV0RaHsuUj7DOwjHwCLcBGAsYHQ/s1244/vlcsnap-2021-07-24-09h29m08s896.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="720" data-original-width="1244" height="366" src="https://1.bp.blogspot.com/-KBhJvtxWVJo/YPvVCmbqrQI/AAAAAAAAAjs/SifnNaZicFQVocIlcV0RaHsuUj7DOwjHwCLcBGAsYHQ/w632-h366/vlcsnap-2021-07-24-09h29m08s896.png" width="632" /></a></p><div><br /></div>Сама игра.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-pP2XFKgASTE/YPvVClwOrNI/AAAAAAAAAjw/7hWFHe0-8zQXPuW4VkF7sqsmODmOxaXOACLcBGAsYHQ/s1244/vlcsnap-2021-07-24-09h31m17s698.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1244" height="416" src="https://1.bp.blogspot.com/-pP2XFKgASTE/YPvVClwOrNI/AAAAAAAAAjw/7hWFHe0-8zQXPuW4VkF7sqsmODmOxaXOACLcBGAsYHQ/w718-h416/vlcsnap-2021-07-24-09h31m17s698.png" width="718" /></a></div><div><br /></div>Подведение итогов<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-XT51MA3JneA/YPvVCqeyS0I/AAAAAAAAAjo/M7ZC-vY84ngTQiCQu_-iuff6PyZ2F0rXgCLcBGAsYHQ/s1244/vlcsnap-2021-07-24-09h31m38s196.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1244" height="411" src="https://1.bp.blogspot.com/-XT51MA3JneA/YPvVCqeyS0I/AAAAAAAAAjo/M7ZC-vY84ngTQiCQu_-iuff6PyZ2F0rXgCLcBGAsYHQ/w711-h411/vlcsnap-2021-07-24-09h31m38s196.png" width="711" /></a></div><br /><p><br /></p><p><br /></p>Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-91092196373751222202020-07-28T00:26:00.000-07:002020-07-28T00:26:51.235-07:00Снова о ландшафтах и квадратах, то есть блоках.<div dir="ltr" style="text-align: left;" trbidi="on">
Чтение учебной литературы по разным темам отнимает много времени, но работа над проектом все ще продолжается, хотя и непонятно, чем все это закончится и закончится ли вообще. Но и сидеть, ничего не делая, тоже неохота.<br />
Недавно отработал самонаведение ракеты в Unity, отыскал аналогичный урок для UE4, провел массовое обновление ПО - Юнити и UE4, плюс VisualStudio, обновил Блендер несколько раз, пробовал UPBGE для EEVEE. Увы, для последнего альфа сборка - это альфа и есть. Категорически не хочет работать подгрузка бленд-файлов через LibLoad - UPBGE мгновенно вылетает. Смотрел и Armory3D, но толком не занимался. Просмотрел и прослушал курсы по Блендеру от А. Слаквы, для Блендер 2.8, пытаюсь в нем работать - с непривычки тяжело.<br />
Есть мысли по поводу упрощения и сокращения проектов в Юнити и БГЕ, касающиеся прежде всего моделей оружия и их json файлов. Если вкратце - стоит провести объединение в одном файле вариантов подвески для ракет однотипного семейства, например AIM-120, AIM-7, Р-3/13, Р-23/24, Р-27 и так далее. Все дело в том, что для нескольких файлов json имеются одинаковые координаты и углы поворота ракет для подвески, отличаются они лишь наименованиями самих ракет - всего-то пара слов, даже не строчек. Поэтому имеет смысл просто перечислить в отдельной строке меши ракет, которые надо найти для подгрузки, а вместо названий ракет в словах проставить missileStr, а не R-23R, которые будут указаны выше.<br />
<br />
{<br />
<br />
"objList":["R-23R", "R-23T", "R-24R", "R-24T", "R-24RM"],<br />
<br />
"obves":{"FLG_APU23_|":{"parentObj":"CntAircraft","locObj":[0.0,0.0,0.0],"rotObj":[0.0,0.0,0.0]},<br />
"missileStr|1":{"parentObj":"CntAircraft","locObj":[-1.428,-2.0,0.03],"rotObj":[-0.035,0.0,0.0],"weapon":1,"CatapultSbros":0},<br />
"missileStr|2":{"parentObj":"CntAircraft","locObj":[1.428,-2.0,0.03],"rotObj":[-0.035,0.0,0.0],"weapon":1,"CatapultSbros":0}<br />
}<br />
}<br />
<br />
Поскольку у меня отлажена система поиска файлов с разным расширением, то поисковик-скрипт отыщет и подгрузит все нужное. А вместо пяти файлов для ракет можно получить один. Это для Р-23 и Р-24, а для aIM-120 вместо 8 будет 1 и для "Спэрроу" вместо 12 - 1, столько же для "Сайдуиндеров"...<br />
Есть еще один нюанс. Для ракет типа "Спэрроу" и "АМРААМ модели внешне почти неотличимы или совсем неотличимы, поэтому в файле ТТХ ракеты надо просто перечислить названия моделей ракет например Sparrow_II и скоратить число блендов. А сами ТТХ ракет объединить в json - поисковик все найдет...<br />
<br />
Но все это по ракетам, а есть еще ландшафт. Тут тоже есть новые задумки. Ландшафт разбивается на квадраты типа А1, Б4 и так далее. Все эти квадраты упакованы в отдельные бленды, и снабжены json с перечислениями стоящих на них объектов. В зависимости от положения активной камеры сначала грузятся стартовый квадрат и 8 квадратов вокруг него. В адльнейшем идет отслеживание положения камеры и "догрузка", если надо, но происходить это будет редко. Фактически, выбирается один квадрат, что-то вроде "центра мира" и вокруг него выстраивается "периферия". Но и это еще не все. Новый "центр мира" "оттаскивается" в нулевое исходное положение, вместе с ним на ту же величину переносятся и ранее сгенеренные "квадраты" со всем их содержимым. Плюс юниты игры также сменяют сове положение на величину "единицы" ландшафта. А создавалась эта система с прицелом на Юнити. Большой ландшафт единым кусокм делать неудобно, плюс говорилось, что координаты больше 100 тысяч единипц приводят к некорректной работе и тормозам, значит, надо уменьшать масштаб самих юнитов, ну раз в 10. Тогда надо учесть, что и их скорости и величина ускорения свободного падения и размеры статических объектов (деревья, здания, дороги) надо также отмасшатбировать, уменьшив в 10 раз.<br />
Возвращаясь к ландшафтуи отрабатываемой сейчас системой его "постройки", скажу, что с "передвиганиями" юнитов, по идее, должно получиться "удерживать" юнит игрока внутри некоторого предела, да и остальные юниты, в общем-то тоже.<br />
<br />
Чкрипт на данный момент:<br />
import bge<br />
scene = bge.logic.getCurrentScene()<br />
<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
<br />
bge.logic.globalDict["nameBlock"] = "D4"<br />
scaleBlock = 2.5<br />
<br />
#Конфигурация расположения блоков террайна - вложенные списки<br />
configBlock = [<br />
["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"],<br />
["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"],<br />
["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"],<br />
["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8"],<br />
["E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8"],<br />
["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"],<br />
["G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8"],<br />
["H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"] <br />
]<br />
<br />
def BlockTerrain():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner <br />
#Индексы списка блоков - внешний и вложенный<br />
x = 0<br />
y = 0<br />
#Величина перемещения блоков и их направление<br />
posX = 0.0<br />
posY = 0.0<br />
<br />
nameBlock = bge.logic.globalDict["nameBlock"]<br />
<br />
#Список элементов, содержащих информацию о блоках и их смещении при появлении<br />
listIndex = []<br />
#Список уже имеющихся блоков террайна<br />
terrainList = []<br />
<br />
#Сначала ищем в общем списке вложенный с наименованием "центра", вокруг которого<br />
# выстраиваются еще 8 дополнительных блоков террайна <br />
for listObj in configBlock:<br />
for obj in listObj:<br />
#После нахождения "центра мира" в списке блоков внутри общего списка блоков террайна<br />
if nameBlock == obj:<br />
#Заносим его в список индексов, это обязательно, смещение для "центра" нулевое<br />
listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj) ) + "|" + "0.0" + "_" + "0.0" )<br />
#Загоняем в список блоки "перед" и "позади" "центра мира", учитывая пределы индексов списка<br />
if listObj.index(obj)-1 > -1:<br />
listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj)-1 ) + "|" + "0.0" + "_" + str(-scaleBlock) )<br />
if listObj.index(obj)+1 < len(listObj):<br />
listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj)+1 ) + "|" + "0.0" + "_" + str(scaleBlock) )<br />
<br />
#А теперь осматриваем вложенные списки "выше" и "ниже" найденного, опять учитываем пределы индексов<br />
#Если такие списки есть, то заносим в listIndex информацию о блоках с индеками "центра мира" и плюс-минус 1<br />
if configBlock.index(listObj)-1 > -1: <br />
listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj) ) + "|" + str(scaleBlock) + "_" + "0.0" )<br />
if listObj.index(obj)-1 > -1:<br />
listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj)-1 ) + "|" + str(scaleBlock) + "_" + str(-scaleBlock) )<br />
if listObj.index(obj)+1 < len(listObj):<br />
listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj)+1 ) + "|" + str(scaleBlock) + "_" + str(scaleBlock) )<br />
<br />
if configBlock.index(listObj)+1 < len(configBlock):<br />
listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj) ) + "|" + str(-scaleBlock) + "_" + "0.0" )<br />
if listObj.index(obj)-1 > -1:<br />
listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj)-1 ) + "|" + str(-scaleBlock) + "_" + str(-scaleBlock) )<br />
if listObj.index(obj)+1 < len(listObj): <br />
listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj)+1 ) + "|" + str(-scaleBlock) + "_" + str(scaleBlock) )<br />
<br />
#Проверка на нличие блоков террайна в сцене<br />
for obj in scene.objects:<br />
if "Terrain" in obj.name:<br />
terrainList.append(obj.name)<br />
<br />
#По окончании составления списка препарируем каждый его элемент типа 3_2|2.5_-2.5 <br />
for element in listIndex:<br />
#Перед "|" указаны индексы общего списка и вложенного списка, они дают выход на элемент "D3" в данном случае<br />
x = int( element.split("|")[0].split("_")[0] )<br />
y = int( element.split("|")[0].split("_")[1] )<br />
if "TerrainQuad_" + configBlock[x][y] not in terrainList:<br />
#Добавляем блок террайна и препарируем элементы после "|"<br />
BlockTerrain = scene.addObject("TerrainQuad_" + configBlock[x][y], own)<br />
#Получаем смещение ОТНОСИТЕЛЬНО "ЦЕНТРАЛЬНОГО" блока, весь отсчет идет относительно него<br />
posX = float( element.split("|")[1].split("_")[1] )<br />
posY = float( element.split("|")[1].split("_")[0] )<br />
#Смещаем только что добавленный блок и переходим к следующему - и так до конца списка<br />
BlockTerrain.worldPosition[0] += posX<br />
BlockTerrain.worldPosition[1] += posY<br />
<br />
def control():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
cam = scene.objects["Camera"]<br />
deltaX = 0.0<br />
deltaY = 0.0<br />
nameBlock = ""<br />
if cam.worldPosition[0] < -scaleBlock or cam.worldPosition[1] < -scaleBlock or cam.worldPosition[1] > scaleBlock or cam.worldPosition[0] > scaleBlock:<br />
if cam.worldPosition[0] < -scaleBlock:<br />
deltaX = -scaleBlock<br />
elif cam.worldPosition[0] > scaleBlock:<br />
deltaX = scaleBlock<br />
elif cam.worldPosition[1] < -scaleBlock:<br />
deltaY = -scaleBlock<br />
elif cam.worldPosition[1] > scaleBlock:<br />
deltaY = scaleBlock<br />
<br />
for obj in scene.objects:<br />
if "Terrain" in obj.name:<br />
obj.worldPosition[0] -= deltaX<br />
obj.worldPosition[1] -= deltaY<br />
<br />
if -scaleBlock * 0.1 < obj.worldPosition[0] < scaleBlock * 0.1 and -scaleBlock * 0.1 < obj.worldPosition[1] < scaleBlock * 0.1:<br />
bge.logic.globalDict["nameBlock"] = obj.name.split("_")[1]<br />
<br />
cam.worldPosition[0] -= deltaX<br />
cam.worldPosition[1] -= deltaY<br />
BlockTerrain()<br />
<br />
#Первый стартовый запуск функции генерации и расстановки блоков террайна<br />
BlockTerrain()<br />
<div>
<br /></div>
<div>
Думаю, комментарии делают этот код понятным... Надеюсь, во всяком случае. )))</div>
<div>
Сам же террайн в БГЕ (или УПБГЕ, когда его отладят) планируется рскрасить по способу denis8424 - с разделением материалов по высоте, но с одним дополнением. Для каждого блока террайна провести смешивание текстур через маски, правда, это уж как получится. Там надо большое разрешение масок, все же даже масштабированные блоки - это 10 км, но можно попробовать маски при наложении дублировать - при больших размерах повторяемость не будет сильно бросаться в глаза, плюс для разных блоков маски будут разными, а число блоков в сумме - не слишком велико, вряд ли больше 100 (уж точно не 2500).</div>
<div>
<br /></div>
<div>
<br /></div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-63771798998708410492020-05-11T22:58:00.000-07:002020-05-11T23:02:09.140-07:00Новое меню и несколько туманные перспективы...<div dir="ltr" style="text-align: left;" trbidi="on">
После очень долгого перерыва решил все же написать очередной пост. Поскольку за это время произошло довольно мн6ого событий в мире 3D, а многие так и довольно давно, но мой консерватизм вкупе с известными проблемами не давал возможности заняться новым вплотную...<br />
Итак, БГЕ - все. Обновлять его не будут, из версии 2.8 он выпилен, это и так многие знают. Остается, правда, UPBGE, да время от времени всплывают слухи, что в Блендер все же вставят что-то вроде игрового движка, но что это будет за новый "БГЕ" и будет ли он вообще - неясно.<br />
Версия 2.8 уверенно развивается, 2.83 уже почему-то обозвали 2.9 - во всяком случае, при распаковке 2.9 в названии присутствует.<br />
Следовательно, рано или поздно, но пришлось бы осваивать 2.8, с его новым интерфейсом, и новыми фишками, в которых автор этих строк пока не слишком (он и в 2.7 не слишком-то - имеется в виду рендер и использование всех возможностей программы).<br />
За это время я принялся изучать Cycles и делать по урокам материалы (уроки Striver), а также читать, смотреть видеоуроки (которые перед этим скачивал ночью, по причине огромного трафика), и вообще пытаться больше понять 3D, его терминологию и возможности. И не только Блендер, но и Юнити и Анрил Энжин (с последним все же сильно не очень, потому как в Юнити куда как больше справочных материалов).<br />
Но бросать свой бге-ешный проект не хотелось и работа продолжилась. Итогом стал очередной погром, который, похоже, выльется в переход на UPBGE с новым, еще большим погромом. Все дело в том, что два с половиной года, отнятые у меня катарактой и сопутствующими ей "прелестями" привели к тому, что многое из того, что делалось, забылось, и просто-напросто устарело. Фактически проект будет делаться заново, правда в нем будут использоваться огромные куски кода, которые не забыты и могут быть использованы. Помимо этого необходимо освоить PBR-материалы, рендер EEVEE, заодно до кучи продолжить изучение Юнити и Cycles, а также заняться еще кое-чем, что непосредственно к 3D отношения не имеет и здесь поэтому перечисляться не будет. Как говорил Ильич: "Учиться, учиться и еще раз учиться" (Я помню этот лозунг, сложенный из вырезанных из пенопласта букв, прикленных к стене нашего школьного класса под потолком рядом с портретом вождя мирового пролетариата).<br />
Некоторое время назад я занялся меню игры, которое было собрано наспех и кое-как. Сделать его удалось, хотя и было в итоге завалено дело с загрузкой объектов, но меню тут ни при чем, все дело было в загрузочном скрипте игровой сцены, которое было чрезмерно раздуто и усложнено. Само же меню прекрасно работает (хотя и не доделано по причине провала с загрузкой, но, судя по распечаткам консоли, все что нужно, оно делало, а редактор миссий и кампаний делать пока бессмысленно).<br />
Меню позволяет переключаться между разделами, выбирать одиночную миссию, переходить на игровую сцену (где все и рвется), вызывает справку, меняет язык интерфейса. В общем, все работает, кроме редактора миссий и кампаний.<br />
Ниже приводится текст скрипта меню, для которого используется логика лишь на одном объекте стартовой сцены-меню, причем логических кирпичей используется только 4. В ранней версии их было втрое больше, плюс имелась логика на других объектах. Само меню перед стартом подгружает бленд с нужными объектами, изначально в сцене только камера с логикой и плейн со стартовым черным фоном.<br />
Итак, скрипт:<br />
<br />
import bge<br />
import sys<br />
import os<br />
import json<br />
scene = bge.logic.getCurrentScene()<br />
<br />
#Обьявление объекта сцены курсора<br />
CursorMenu = scene.objects["CursorMenu"]<br />
<br />
#Загрузка элементов меню из папки с компонентами<br />
bge.logic.LibLoad("//Menu/Blend_Folder/Menu_Component.blend", 'Scene', load_actions = True)<br />
<br />
pathJSON = ""<br />
pathFon = ""<br />
bge.logic.globalDict["bglText"] = ""<br />
bge.logic.globalDict["DayNight"] = 1.3<br />
bge.logic.globalDict["textGame"] = "Rus"<br />
<br />
#Далее идет блок клавиатурных команд<br />
keyboard = bge.logic.keyboard<br />
JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED<br />
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED<br />
INPUT_ACTIVE = bge.logic.KX_INPUT_ACTIVE<br />
<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
<br />
#Сенсоры для управления меню<br />
mouseMotion = cont.sensors["MouseMotion"]<br />
mouseClick = cont.sensors["MouseClick"]<br />
<br />
#Звуковой актуатор щелчка по кнопке<br />
audioTemp = cont.actuators['SoundClick']<br />
<br />
#Стартовые значения для меню - миссия по умолчанию<br />
bge.logic.globalDict['currentMission'] = "MiG-23MF_vs_F-5E"<br />
bge.logic.globalDict["dictUnit"] = []<br />
bge.logic.globalDict["listUnit"] = []<br />
bge.logic.globalDict["listEnemy"] = [ [[],[]], [[],[]] ]<br />
<br />
scaleButtonStandart = {<br />
"ButtonShort":[5.775, 1.5, 0.0],<br />
"ButtonLong":[8.49, 1.5, 0.0],<br />
"ButtonExit":[1.5, 1.5, 0.0],<br />
"ButtonUp":[1.5, 1.5, 0.0],<br />
"ButtonDown":[1.5, 1.5, 0.0],<br />
#"ButtonUniversal":[1.5, 1.5, 0.0],<br />
#"ButtonBig":[5.775, 4.12, 0.0],<br />
"ButtonForward":[1.5, 1.5, 0.0],<br />
"ButtonBack":[1.5, 1.5, 0.0]<br />
}<br />
<br />
#Создание проперти объекта для положения курсора<br />
if "cursorPos" not in own:<br />
own["cursorPos"] = {"addSceneGame":0,<br />
"tempX":0.0,<br />
"tempY":0.0,<br />
"language":"",<br />
"listButton":[],<br />
"animationButton":0,<br />
"indexButton":"",<br />
"oldPathFon":"",<br />
"newPathFon":"",<br />
"TimerColor":0,<br />
"AlphaColor":0.0,<br />
"newPatchMenu":"//Menu/JSON_Folder/Menu_Start.json",<br />
"oldPatchMenu":"",<br />
"PathFon":"",<br />
"PathFonMonitor":"",<br />
"limitXscroll":0,<br />
"limitYscroll":0,<br />
"valueButtonCoord":[0.0, 0.0, 0.0],<br />
"pathButtonTXT":{},<br />
"pathNameMission":{}<br />
}<br />
<br />
#Функция создания файла json с путями к файлам всего проекта<br />
def startPath():<br />
cont = bge.logic.getCurrentController()<br />
scene = bge.logic.getCurrentScene()<br />
own = cont.owner<br />
<br />
#Создаем переменные - списки путей блендов и json плюс пустую строку для путей<br />
k = ""<br />
endStrPy = ""<br />
pathGeneral = []<br />
pathBlend = []<br />
pathJSON = []<br />
pathPy = []<br />
<br />
#Циклом перебираем файлы, директории и папки, создаем и сшиваем строки в пути<br />
for d, dirs, files in os.walk(bge.logic.expandPath("//")):<br />
for f in files:<br />
<br />
k = os.path.join(d,f)<br />
<br />
if ".blend1" in k:<br />
os.remove(k)<br />
<br />
#Питон-файлы добавляем в sys.path, остальные в список путей<br />
if ".py" not in k:<br />
k = k.split("BlendSim_2.1")[1]<br />
k = k.replace("\\", "/")<br />
if ".blend" in k:<br />
if ".blend1" not in k:<br />
pathGeneral.append("/"+k)<br />
if ".json" in k:<br />
pathGeneral.append("/"+k)<br />
<br />
elif ".py" in k:<br />
if "__pycache__" not in k:<br />
endStrPy = k.split("\\")[-1]<br />
k = k.replace(endStrPy, "")<br />
sys.path.append(k)<br />
pathPy.append(k)<br />
<br />
#Открываем файл json с путями (если его нет, то он создается) и сбрасываем туда список путей<br />
with open(bge.logic.expandPath('//NodePath.json'), 'w') as NODEPATH:<br />
json.dump(pathGeneral, NODEPATH)<br />
<br />
#Функция работы курсора меню и всех его компонентов - УЗЛОВАЯ ФУНКЦИЯ для GeneratorMenuObj, buttonJob и on_click<br />
def cursorPos():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
<br />
#Сработка псевдоанимации при старте игры<br />
if mouseMotion.positive:<br />
if own["cursorPos"]["addSceneGame"] == 0:<br />
#Работет таймер<br />
if own["cursorPos"]["TimerColor"] < 600:<br />
own["cursorPos"]["TimerColor"] += 1<br />
<br />
#Сначала меняем меш курсора на логотип и медленно увеличиваем его "видимость"<br />
if own["cursorPos"]["TimerColor"] == 1:<br />
CursorMenu.replaceMesh("Logo", True, False)<br />
CursorMenu.color = [1.0, 1.0, 1.0, 0.0]<br />
<br />
if 1 < own["cursorPos"]["TimerColor"] < 200:<br />
if own["cursorPos"]["AlphaColor"] < 1.0:<br />
own["cursorPos"]["AlphaColor"] += 0.005<br />
<br />
#Затем логотип медленно истаивает - увеличивается прозрачность<br />
if 200 < own["cursorPos"]["TimerColor"] < 400:<br />
if own["cursorPos"]["AlphaColor"] > 0.0:<br />
own["cursorPos"]["AlphaColor"] -= 0.005<br />
<br />
#Теперь меняем меш логотипа обратно на меш курсора<br />
if own["cursorPos"]["TimerColor"] == 401:<br />
CursorMenu.replaceMesh("CursorMenu", True, False)<br />
<br />
#И заставляем курсор постепенно "проступать" на фоне меню<br />
if 400 < own["cursorPos"]["TimerColor"] < 600:<br />
if own["cursorPos"]["AlphaColor"] < 1.0:<br />
own["cursorPos"]["AlphaColor"] += 0.005<br />
<br />
#За все время работы таймера управление прозрачностью курсора идет через словарь свойств<br />
CursorMenu.color = [1.0, 1.0, 1.0, own["cursorPos"]["AlphaColor"]]<br />
#И местоположение курсора задется жестко - в центре меню<br />
CursorMenu.worldPosition = [0.0, 0.0, 0.5]<br />
<br />
#И только когда таймер прекратил работу - начинает работать перемещение и клики курсором<br />
if own["cursorPos"]["TimerColor"] == 600:<br />
if mouseMotion.positive:<br />
if own["cursorPos"]["tempX"] != mouseMotion.position[0]:<br />
CursorMenu.worldPosition[0] += (mouseMotion.position[0] - own["cursorPos"]["tempX"])/4<br />
own["cursorPos"]["tempX"] = mouseMotion.position[0]<br />
if own["cursorPos"]["tempY"] != mouseMotion.position[1]:<br />
CursorMenu.worldPosition[1] -= (mouseMotion.position[1] - own["cursorPos"]["tempY"])/4<br />
own["cursorPos"]["tempY"] = mouseMotion.position[1]<br />
<br />
if CursorMenu.worldPosition[0] > 43.0:<br />
CursorMenu.worldPosition[0] = 43.0<br />
if CursorMenu.worldPosition[0] < -45.0:<br />
CursorMenu.worldPosition[0] = -45.0<br />
<br />
if CursorMenu.worldPosition[1] > 26.0:<br />
CursorMenu.worldPosition[1] = 26.0<br />
if CursorMenu.worldPosition[1] < -24.0:<br />
CursorMenu.worldPosition[1] = -24.0<br />
<br />
if mouseClick.positive:<br />
on_click(own)<br />
if own["cursorPos"]["oldPathFon"] != own["cursorPos"]["newPathFon"]:<br />
FonChanging(own)<br />
<br />
if own["cursorPos"]["animationButton"] > 0:<br />
buttonJob(own)<br />
<br />
#Эта часть кода отвечает за смену языка - при нажатии кнопок с флажками производится выбор и замена текста <br />
if own["cursorPos"]["language"] != bge.logic.globalDict["textGame"]:<br />
for buttonObj in own["cursorPos"]["listButton"]:<br />
#Выбираются только кнопки с текстом<br />
if "IDtext" in buttonObj:<br />
if bge.logic.globalDict["textGame"] == "Eng":<br />
scene.objects.from_id(int(buttonObj["IDtext"]))["Text"] = buttonObj["textObj"]<br />
elif bge.logic.globalDict["textGame"] == "Rus":<br />
scene.objects.from_id(int(buttonObj["IDtext"]))["Text"] = buttonObj["textRus"]<br />
#После замены текста исправляется значение в словаре, чтобы не проверять понапрасну настройки языка по многу раз<br />
own["cursorPos"]["language"] = bge.logic.globalDict["textGame"]<br />
<br />
#Переустановка кнопок и прочего при смене пути к файлу json для данного раздела меню <br />
if own["cursorPos"]["newPatchMenu"] != own["cursorPos"]["oldPatchMenu"]:<br />
GeneratorMenuObj(own)<br />
<br />
#СТАРТ ИГРЫ! - переход на игровую сцену <br />
if own["cursorPos"]["addSceneGame"] != 0:<br />
own["cursorPos"]["addSceneGame"] += 1<br />
if own["cursorPos"]["addSceneGame"] == 20:<br />
cont.activate(cont.actuators['Scene'])<br />
own["cursorPos"]["addSceneGame"] = 0<br />
<br />
#Функция расстановки кнопок и текста, фона раздела меню и так далее - УЗЛОВАЯ ФУНКЦИЯ для FonChanging<br />
def GeneratorMenuObj(own):<br />
FonObj = None<br />
Monitor = None<br />
#Местотоположение курсора задется жестко - в центре меню<br />
CursorMenu.worldPosition = [0.0, 0.0, 0.5]<br />
<br />
#Прежде чем добавлять новые кнопки, удалим старые<br />
for oldButton in own["cursorPos"]["listButton"]:<br />
oldButton.endObject()<br />
own["cursorPos"]["listButton"].clear()<br />
<br />
for textObj in scene.objects:<br />
if textObj.name == "TextMenu":<br />
textObj.endObject()<br />
<br />
#После очистки экрана от старыхобъектов начинаем создавать новый вид меню, открыв нужный json<br />
with open(bge.logic.expandPath(own["cursorPos"]["newPatchMenu"]), 'r', encoding = 'utf-8') as directMenu:<br />
JSONmenu = json.load(directMenu)<br />
<br />
if own["cursorPos"]["TimerColor"] == 600:<br />
for obj in JSONmenu["Buttons"]: <br />
newSceneObject = scene.addObject(obj.split('|')[0],own)<br />
newSceneObject.worldPosition = JSONmenu["Buttons"][obj]["coordObj"]<br />
newSceneObject.color = JSONmenu["Buttons"][obj]["colorObj"]<br />
own["cursorPos"]["listButton"].append(newSceneObject)<br />
if "meshButton" in JSONmenu["Buttons"][obj]:<br />
newSceneObject.replaceMesh(JSONmenu["Buttons"][obj]["meshButton"],True,False)<br />
if "scaleObj" in JSONmenu["Buttons"][obj]:<br />
newSceneObject.worldScale = JSONmenu["Buttons"][obj]["scaleObj"]<br />
if newSceneObject.name in scaleButtonStandart:<br />
newSceneObject.worldScale = scaleButtonStandart[newSceneObject.name]<br />
<br />
#Иногда нужен текст без кнопки, который еще надо прокручивать <br />
if newSceneObject.name == "TextMenu":<br />
#Печатаем текст из txt файла, находя его по нужному пути<br />
if "TextPath" in JSONmenu["Buttons"][obj]:<br />
newSceneObject["Text"] = ""<br />
<br />
#Задаем кнопке проперти (для прокрутки или других изменений объект помечается тегом "objTag"<br />
#Кнопка, которая должна управлять изиенениями остальных объектов получает ключ "tagObj", что<br />
#позволяет ей "опознать" объекты, необходимые для воздействия на них<br />
if "propObj" in JSONmenu["Buttons"][obj]:<br />
for key in JSONmenu["Buttons"][obj]["propObj"]:<br />
if key not in newSceneObject:<br />
newSceneObject[key] = JSONmenu["Buttons"][obj]["propObj"][key]<br />
<br />
if "childObj" in JSONmenu["Buttons"][obj]:<br />
#Если надо добавляем текст, парентим его к кнопке, выставляем его размер<br />
newObj = scene.addObject(JSONmenu["Buttons"][obj]["childObj"], newSceneObject)<br />
newObj.worldPosition[2] = newSceneObject.worldPosition[2] + 0.02<br />
newObj.worldScale = newSceneObject.worldScale<br />
newObj.setParent(newSceneObject, False, False)<br />
if "childObjScale" in JSONmenu["Buttons"][obj]:<br />
newObj.worldScale = JSONmenu["Buttons"][obj]["childObjScale"]<br />
<br />
#Текст на кнопке<br />
if "textObj" in JSONmenu["Buttons"][obj]:<br />
newSceneObject["textObj"] = JSONmenu["Buttons"][obj]["textObj"]<br />
newSceneObject["textRus"] = JSONmenu["Buttons"][obj]["textRus"]<br />
#Если надо добавляем текст, парентим его к кнопке, выставляем его размер<br />
newTextObj = scene.addObject("TextMenu", newSceneObject)<br />
if "IDtext" not in newSceneObject:<br />
newSceneObject["IDtext"] = str(id(newTextObj))<br />
newTextObj.worldScale = [ newSceneObject.worldScale[1], newSceneObject.worldScale[1], newSceneObject.worldScale[1] ]<br />
#Выставляем локальные координаты текста<br />
newTextObj.worldPosition[0] -= 0.8*newSceneObject.worldScale[0]<br />
newTextObj.worldPosition[1] -= 0.3*newSceneObject.worldScale[1]<br />
newTextObj.worldPosition[2] += 0.01<br />
#Печатаем текст на кнопке<br />
if bge.logic.globalDict["textGame"] == "Rus":<br />
newTextObj["Text"] = JSONmenu["Buttons"][obj]["textRus"]<br />
elif bge.logic.globalDict["textGame"] == "Eng":<br />
newTextObj["Text"] = JSONmenu["Buttons"][obj]["textObj"]<br />
<br />
#В этом блоке содержатся инструкции по замене мешей и текстур фоновых объектов<br />
if "meshFonObj" in JSONmenu:<br />
scene.objects["FonObj"].replaceMesh(JSONmenu["meshFonObj"],True,False)<br />
#Для монитора (плоскости под фоном) можно задать свой цвет, меш, и координаты<br />
if "meshMonitor" in JSONmenu:<br />
scene.objects["Monitor"].replaceMesh(JSONmenu["meshMonitor"],True,False)<br />
if "coordMonitor" in JSONmenu:<br />
scene.objects["Monitor"].worldPosition = JSONmenu["coordMonitor"]<br />
if "scaleMonitor" in JSONmenu:<br />
scene.objects["Monitor"].worldScale = JSONmenu["scaleMonitor"]<br />
if "colorMonitor" in JSONmenu:<br />
scene.objects["Monitor"].color = JSONmenu["colorMonitor"]<br />
#Замена текстур фоновой плоскости и монитора (необходим для отображения карты террайна и закрытия отверстий в плоскости общего фона) <br />
if "pathFonObjTexture" in JSONmenu:<br />
FonObj = scene.objects["FonObj"]<br />
own["cursorPos"]["PathFon"] = JSONmenu["pathFonObjTexture"]<br />
FonChanging(own, FonObj)<br />
if "pathMonitorTexture" in JSONmenu:<br />
Monitor = scene.objects["Monitor"]<br />
own["cursorPos"]["PathFonMonitor"] = JSONmenu["pathMonitorTexture"]<br />
FonChanging(own, FonObj)<br />
<br />
own["cursorPos"]["newPatchMenu"] = own["cursorPos"]["oldPatchMenu"]<br />
<br />
#Функция смены текстуры фона меню<br />
def FonChanging(own, FonObj):<br />
pathFon = own["cursorPos"]["PathFon"]<br />
if FonObj.name == "Monitor": <br />
pathFon = own["cursorPos"]["PathFonMonitor"]<br />
ID = bge.texture.materialID(FonObj, "IM" + FonObj.name + ".jpg")<br />
object_texture = bge.texture.Texture(FonObj, ID)<br />
url = bge.logic.expandPath(pathFon)<br />
new_source = bge.texture.ImageFFmpeg(url)<br />
bge.logic.texture = object_texture<br />
bge.logic.texture.source = new_source<br />
bge.logic.texture.refresh(False)<br />
FonObj[FonObj.name] = new_source<br />
<br />
#Функция сработки клика по кнопке - УЗЛОВАЯ ФУНКЦИЯ для buttonMove<br />
def on_click(own):<br />
#Сработка кнопки идет, когда курсор находится в определенной области, с заданными координатами,<br />
#проверка которых идет в зависимости от размера кнопки и ее центра, с координатами, уже данными<br />
#в списке при загрузке и расстановке деталей меню<br />
for button in own["cursorPos"]["listButton"]:<br />
if button.worldPosition[0]-button.worldScale[0] < CursorMenu.worldPosition[0] < button.worldPosition[0]+button.worldScale[0]:<br />
if button.worldPosition[1]-button.worldScale[1] < CursorMenu.worldPosition[1] < button.worldPosition[1]+button.worldScale[1]:<br />
own["cursorPos"]["indexButton"] = str(id(button))<br />
own["cursorPos"]["animationButton"] = 1<br />
buttonMove(own, button)<br />
<br />
#Фунция работы кнопки при ее нажатии, действия, направленные на смену разделов или объектов емню, старт или выход из игры <br />
def buttonJob(own):<br />
own["cursorPos"]["animationButton"] += 1<br />
buttonText = None<br />
<br />
try: <br />
#Находим кнопку под курсором - область координат, ограниченная размером кнопки<br />
button = scene.objects.from_id(int(own["cursorPos"]["indexButton"]))<br />
<br />
#Находим надписи (если есть) и окрашиваем и нажатую кнопку и ее надпись<br />
if "IDtext" in button:<br />
buttonText = scene.objects.from_id(int(button["IDtext"]))<br />
if own["cursorPos"]["animationButton"] == 2:<br />
button.color = [1.0, 0.0, 1.0, 1.0]<br />
if buttonText != None:<br />
buttonText.color = [1.0, 0.0, 1.0, 1.0]<br />
<br />
#Окрашиваем незадействованные кнопки в исходный цвет вместе с надписями (если имеются)<br />
for obj in own["cursorPos"]["listButton"]:<br />
if obj != button:<br />
obj.color = [1.0, 1.0, 1.0, 1.0]<br />
if "IDtext" in obj:<br />
scene.objects.from_id(int(obj["IDtext"])).color = [1.0, 1.0, 1.0, 1.0]<br />
<br />
#Двигаем кнопку, обнаруживаем и используем ее свойства для смены меню, картинок и сцен <br />
if 1 < own["cursorPos"]["animationButton"] < 3:<br />
button.applyMovement([0.2,-0.2,0.0],True)<br />
if buttonText != None:<br />
buttonText.applyMovement([0.2,-0.2,0.0],True)<br />
if 3 < own["cursorPos"]["animationButton"] < 5:<br />
button.applyMovement([-0.2, 0.2, 0.0],True)<br />
if buttonText != None:<br />
buttonText.applyMovement([-0.2,0.2,0.0],True)<br />
if own["cursorPos"]["animationButton"] > 4:<br />
own["cursorPos"]["indexButton"] = ""<br />
own["cursorPos"]["animationButton"] = 0<br />
if "GAME_body" in button:<br />
#Блок для пути к файлу миссии при нажатии кнопки выбора миссии<br />
if "BGE_globalDict" in button["GAME_body"]:<br />
if button["GAME_body"]["BGE_globalDict"] == "pathNameMission":<br />
bge.logic.globalDict['currentMission'] = own["cursorPos"]["pathNameMission"]["key_" + str(id(button))]<br />
#Кнопка выхода из игры<br />
if button["GAME_body"] == "Stop_Game":<br />
bge.logic.endGame()<br />
#Кнопка перехода на игровую сцену<br />
if button["GAME_body"] == "Start_Game":<br />
own["cursorPos"]["addSceneGame"] = 1<br />
#Кнопка выбора языка игры<br />
if "languageGame" in button["GAME_body"]:<br />
bge.logic.globalDict["textGame"] = button["GAME_body"]["languageGame"]<br />
#Это смена пути к файлу раздела меню - меняется само меню, его види кнопки, для кнопки "Назад" или кнопок ивыбора других разделов<br />
#вроде "Миссии", "Кампании", "Справки" и тому подобных, при котором не происходит выхода из игры<br />
if "pathJSON" in button["GAME_body"]:<br />
own["cursorPos"]["TimerColor"] = 595<br />
own["cursorPos"]["newPatchMenu"] = button["GAME_body"]["pathJSON"]<br />
<br />
#Многоступенчатый процесс "опознания" и сравнения тэгов кнопок и текста для их передвижения<br />
if "tagObj" in button["GAME_body"]:<br />
for scrollObj in own["cursorPos"]["listButton"]:<br />
if "GAME_body" in scrollObj:<br />
#Проверяем совпадение тэгов после их обнаружения<br />
if "objTag" in scrollObj["GAME_body"]:<br />
#Теперь передвигаем найденный объект<br />
if scrollObj["GAME_body"]["objTag"] == button["GAME_body"]["tagObj"]:<br />
if "scrollTagObj" in button["GAME_body"]:<br />
scrollObj.worldPosition[0] += button["GAME_body"]["scrollTagObj"][0]<br />
scrollObj.worldPosition[1] += button["GAME_body"]["scrollTagObj"][1]<br />
<br />
if "nullCoordX" in scrollObj["GAME_body"]:<br />
if scrollObj.worldPosition[0] < scrollObj["GAME_body"]["nullCoordX"]:<br />
scrollObj.worldPosition[0] = scrollObj["GAME_body"]["nullCoordX"]<br />
if scrollObj.worldPosition[0] > own["cursorPos"]["limitXscroll"]:<br />
scrollObj.worldPosition[0] -= scrollObj["Game_body"]["limitXscroll"]<br />
if "listDriverObj" in scrollObj["GAME_body"]:<br />
for scrollChild in scrollObj["GAME_body"]["listDriverObj"]:<br />
scrollChild.worldPosition[0] += scrollObj["GAME_body"]["scaleAddObj"][0]*button["GAME_body"]["valueButton"] # own["cursorPos"]["limitXscroll"]<br />
<br />
if "nullCoordY" in scrollObj["GAME_body"]:<br />
#print(scrollObj.name)<br />
if scrollObj.worldPosition[1] < scrollObj["GAME_body"]["nullCoordY"]:<br />
scrollObj.worldPosition[1] = scrollObj["GAME_body"]["nullCoordY"]<br />
if scrollObj.worldPosition[1] > scrollObj["Game_body"]["limitYscroll"]:<br />
scrollObj.worldPosition[1] -= own["cursorPos"]["limitYscroll"]<br />
if "listDriverObj" in scrollObj["GAME_body"]:<br />
for scrollChild in scrollObj["GAME_body"]["listDriverObj"]:<br />
scrollChild.worldPosition[1] += scrollObj["GAME_body"]["scaleAddObj"][1]*button["GAME_body"]["valueButton"] #own["cursorPos"]["limitYscroll"]<br />
<br />
#Изменение текста на объекте TextMenu, например при смене текста справки для видов юнитов<br />
if "changTextTagObj" in button["GAME_body"]:<br />
if "pathButtonTXT" not in button["GAME_body"]["changTextTagObj"]:<br />
with open(bge.logic.expandPath(button["GAME_body"]["changTextTagObj"]+bge.logic.globalDict["textGame"]+".txt"), 'r', encoding = 'utf-8') as MenuHelpTXT:<br />
scrollObj["Text"] = MenuHelpTXT.read()<br />
#print(scrollObj.name)<br />
#with open(bge.logic.expandPath(button["GAME_body"]["changTextTagObj"]+bge.logic.globalDict["textGame"]+".txt"), 'r', encoding = 'utf-8').readlines() as changText:<br />
#own["cursorPos"]["limitYscroll"] = scrollObj.worldScale[1]*len(changText)<br />
# scrollObj["Game_body"]["limitYscroll"] = scrollObj.worldScale[1]*len(changText)<br />
else:<br />
with open(bge.logic.expandPath(own["cursorPos"]["pathButtonTXT"]["key_"+str(id(button))]+bge.logic.globalDict["textGame"]+".txt"), 'r', encoding = 'utf-8') as MenuHelpTXT:<br />
scrollObj["Text"] = MenuHelpTXT.read()<br />
<br />
if "changButtonTagObj" in button["GAME_body"]:<br />
if "listDriverObj" in scrollObj["GAME_body"]:<br />
own["cursorPos"]["pathButtonTXT"] = {}<br />
#Убираем кнопки из списка драйвера объекта<br />
if len(scrollObj["GAME_body"]["listDriverObj"]) != 0:<br />
for objOLD in scrollObj["GAME_body"]["listDriverObj"]:<br />
if objOLD in own["cursorPos"]["listButton"]:<br />
own["cursorPos"]["listButton"].remove(objOLD)<br />
#scrollObj["GAME_body"]["listDriverObj"].remove(objOLD)<br />
objOLD.endObject()<br />
own["cursorPos"]["valueButtonCoord"] = [0.0,0.0,0.0]<br />
scrollObj["GAME_body"]["listDriverObj"] = []<br />
<br />
with open(bge.logic.expandPath('//NodePath.json'), 'r', encoding = 'utf-8') as NODEPATH:<br />
jsonButtonPath = json.load(NODEPATH)<br />
for pathButton in jsonButtonPath:<br />
if button["GAME_body"]["changButtonTagObj"] in pathButton:<br />
if ".json" in pathButton:<br />
newObjGen = scene.addObject(scrollObj["GAME_body"]["addObjName"], scrollObj)<br />
scrollObj["GAME_body"]["listDriverObj"].append(newObjGen)<br />
own["cursorPos"]["listButton"].append(newObjGen)<br />
newObjGen.replaceMesh(scrollObj["GAME_body"]["meshAddObj"], True, False)<br />
newObjGen.worldScale = scrollObj["GAME_body"]["scaleAddObj"]<br />
own["cursorPos"]["valueButtonCoord"][0] += scrollObj["GAME_body"]["valueCoord"][0]<br />
own["cursorPos"]["valueButtonCoord"][1] += scrollObj["GAME_body"]["valueCoord"][1]<br />
own["cursorPos"]["valueButtonCoord"][2] += scrollObj["GAME_body"]["valueCoord"][2]<br />
newObjGen.worldPosition[0] += own["cursorPos"]["valueButtonCoord"][0]<br />
newObjGen.worldPosition[1] += own["cursorPos"]["valueButtonCoord"][1]<br />
newObjGen.worldPosition[2] += own["cursorPos"]["valueButtonCoord"][2]<br />
<br />
if "textChild" in scrollObj["GAME_body"]:<br />
with open(bge.logic.expandPath(pathButton), 'r', encoding = 'utf-8') as textButton:<br />
jsonTextButton = json.load(textButton)<br />
newTextGen = scene.addObject("TextMenu", newObjGen)<br />
scrollObj["GAME_body"]["listDriverObj"].append(newTextGen)<br />
own["cursorPos"]["listButton"].append(newTextGen)<br />
newTextGen.worldScale = scrollObj["GAME_body"]["textChild"]<br />
newTextGen.color = scrollObj["GAME_body"]["textChildColor"]<br />
if bge.logic.globalDict["textGame"] == "Rus":<br />
newTextGen["Text"] = jsonTextButton["textRus"]<br />
elif bge.logic.globalDict["textGame"] == "Eng":<br />
newTextGen["Text"] = jsonTextButton["textEng"]<br />
#Выставляем локальные координаты текста<br />
newTextGen.worldPosition[0] -= 0.8*newObjGen.worldScale[0]<br />
newTextGen.worldPosition[1] -= 0.15*newObjGen.worldScale[1]<br />
<br />
if "addObjGAME_body" in scrollObj["GAME_body"]:<br />
newObjGen["GAME_body"] = scrollObj["GAME_body"]["addObjGAME_body"]<br />
if "BGE_globalDict" in newObjGen["GAME_body"]:<br />
if newObjGen["GAME_body"]["BGE_globalDict"] == "pathNameMission":<br />
if "key_" + str(id(newObjGen)) not in own["cursorPos"]["pathNameMission"]:<br />
own["cursorPos"]["pathNameMission"]["key_" + str(id(newObjGen))] = pathButton<br />
#print(own["cursorPos"]["pathNameMission"]) <br />
if "key_" + str(id(newObjGen)) not in own["cursorPos"]["pathButtonTXT"]:<br />
own["cursorPos"]["pathButtonTXT"]["key_" + str(id(newObjGen))] = pathButton.split(".json")[0] + "_"<br />
<br />
except:<br />
print(error)<br />
own["cursorPos"]["indexButton"] = ""<br />
<br />
#Функция звукового сопровождения и выдачи на кнопку свойств проперти<br />
def buttonMove(own, button):<br />
cont = bge.logic.getCurrentController()<br />
audioTemp = cont.actuators['SoundClick']<br />
<br />
if "GAME_body" in button:<br />
if "scrollTagObj" not in button["GAME_body"]:<br />
cont.activate(audioTemp)<br />
<br />
if "GLOBAL_DICT_name" in button:<br />
if "GLOBAL_DICT_body" in button:<br />
if button["GLOBAL_DICT_name"] not in bge.logic.globalDict:<br />
bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]<br />
else:<br />
bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]<br />
<br />
#Поиск путей к файлам, выстраивание списков, ОЧЕНЬ ВАЖНОЕ СТАРТОВОЕ ДЕЙСТВИЕ<br />
startPath()<br />
<br />
Функция startPath срабатывает всегда только один раз, отыскивая пути и файлы и добавляя их в список файла json NodePath. Это - ключ ко всему. Там расположены пути к файлам blend и файлы json с описаниями объектов. Из-за этого файла я и решил в итоге фактически все начать с нуля. Пусть объекты сами ищут пути к нужным данным, вскрывают нужные файлы и действуют по заданным инструкциям. Раньше надо было это делать, ну да ладно, опыт сын ошибок трудных...<br />
Фото были взяты из Сети, временно, потом будут заменены на скрины из самой игры.<br />
<div class="" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-_S9rzTmM6cA/Xro5S4DQg_I/AAAAAAAAAeQ/-nwUsmqDxVoML37jgxPeq0N2maa2O0VNQCLcBGAsYHQ/s1600/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_00_44_272.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="1261" height="374" src="https://1.bp.blogspot.com/-_S9rzTmM6cA/Xro5S4DQg_I/AAAAAAAAAeQ/-nwUsmqDxVoML37jgxPeq0N2maa2O0VNQCLcBGAsYHQ/s640/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_00_44_272.png" width="640" /></a><a href="https://1.bp.blogspot.com/-urvOO-hQJmA/Xro5S1eoW3I/AAAAAAAAAeM/YuGdozZO1G40Xdgd0ZOc9ozzxhI2ivlpgCLcBGAsYHQ/s1600/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_00_11_120.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="1261" height="374" src="https://1.bp.blogspot.com/-urvOO-hQJmA/Xro5S1eoW3I/AAAAAAAAAeM/YuGdozZO1G40Xdgd0ZOc9ozzxhI2ivlpgCLcBGAsYHQ/s640/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_00_11_120.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Меню справки для юнитов разных "сред". Вверху - меню выбора миссии с коротким описанием оной.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-4DhFUbuVeBw/Xro5UnYkT7I/AAAAAAAAAeY/NkxZY7LyvpwA-GIrxNnYOEremoeIldOPgCLcBGAsYHQ/s1600/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_01_20_604.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="1261" height="374" src="https://1.bp.blogspot.com/-4DhFUbuVeBw/Xro5UnYkT7I/AAAAAAAAAeY/NkxZY7LyvpwA-GIrxNnYOEremoeIldOPgCLcBGAsYHQ/s640/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_15_01_20_604.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Так пока и не доделанное меню редактора.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-S9-N-A2oKe0/Xro5TavpnpI/AAAAAAAAAeU/gixGuhKiMuA84vuJra2JyIkr0rM2mrISwCLcBGAsYHQ/s1600/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_14_59_37_102.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="1261" height="374" src="https://1.bp.blogspot.com/-S9-N-A2oKe0/Xro5TavpnpI/AAAAAAAAAeU/gixGuhKiMuA84vuJra2JyIkr0rM2mrISwCLcBGAsYHQ/s640/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA_2020_04_30_14_59_37_102.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Стартовое меню.</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-61548986783738555472019-12-14T22:51:00.000-08:002019-12-14T22:51:54.289-08:00Дорогу осилит ползущий...<div dir="ltr" style="text-align: left;" trbidi="on">
Долгое время не писал. Особо не о ч ем было. Как-тог подзабросил дела - очередное лечение с его порцией уколов в глаза, виски и прочие места, чтение накопившихся книг, ползанья по Сети в поисках учебников, чтение оных же, поиск информации и примеров. Да и лень-матушка, куда ж без нее...<br />
На днях выправил ситуацию с неправильной индикацией выбранного оружия на подвесках самолета игрока. Как оказалось, задача была не такой страшной, как виделось. Для этого понадобилось ввести в стартовый json летательного аппарата еще одну переменную, раскомментировать строку в одном скрипте, добавить пару строк в другом. В итоге теперь информация с названием и оружия и его БК послушно высвечивается при перебре оружия и гаснет при выключении системы управления вооружением. Вот так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-6FtKyjIg7Tw/XfXWqFrtWMI/AAAAAAAAAdM/w7TKWXEbI8o06yrtq08FZaSJIqL-hQ3gACEwYBhgL/s1600/BlendSim30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="856" data-original-width="1452" height="376" src="https://1.bp.blogspot.com/-6FtKyjIg7Tw/XfXWqFrtWMI/AAAAAAAAAdM/w7TKWXEbI8o06yrtq08FZaSJIqL-hQ3gACEwYBhgL/s640/BlendSim30.png" width="640" /></a>Цель - F-16C ВВС США, удаление 24 км, тип оружия - УРСД Р-23Р, БК-2, режим - РЛС с дальностью обнаружения - 60 км.</div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com1tag:blogger.com,1999:blog-1904509602056652692.post-43711716178780051972019-08-04T23:01:00.000-07:002019-08-04T23:14:51.614-07:00Четвертая операция.<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="font-family: arial, verdana, sans-serif; font-size: 12px;">
<span style="color: #eeeeee;">Почти ровно четыре недели тому назад прошла четвертая для моего правого галаз операция - дисцизия вторичной катаракты. Если перевести на обычный язык, то лазером рассекли постепенно густеющую пленку позади искусственного хрусталика. Делали ее у нас, никуда ехать не пришлось, все прошло быстро и безболезненно. </span></div>
<div style="font-family: arial, verdana, sans-serif; font-size: 12px;">
<span style="color: #eeeeee;">Согбственно, речь в этом посте пойдет не столько об операции (которая выступает в роли очередной вехи на жизенном пути), сколько о том, чего удалось сделать за время, прошедшее со дня моего предыдущего поста, то бишь продолжение большого апдейта.</span></div>
<div style="font-family: arial, verdana, sans-serif; font-size: 12px;">
<span style="color: #eeeeee;">На некоторое время я резко затормозил, бывает со мной такое - внутреннее сопротивление к работе, которое надо рано или поздно давить. После преодоления нежелания работать,дело опять потихоньку стронулось с мертвой точки. Работа теперь идет по двум направлениям - апдейт БГЕ и освоение Юнити. Так, вчера умудрился понять принцип создания деревьев - вроде бы и несложно, но раньше я тупил с добавлением материалов на ветки и листья. Все-таки в Юнити рабочий процесс сильно завязан на интерфейс, и временами таскание переменных туда-сюда начинает раздражать - там ведь еще надо не промахнуться мимо довольно маленькой графы. Иногда проще набить ручками с клавиатуры. По-хорошему, стоило бы ввести поиск перемнных по набираемым символам - например, начинаешь вводить, а тебе в выпадающем меню высвечивается список возможных вариантов переменной. А может, он в Юнити уже и есть, проверить просто надо? </span></div>
<div style="font-family: arial, verdana, sans-serif; font-size: 12px;">
<span style="color: #eeeeee;">Ну, кроме брюзжания (возможно, что и незаслуженного), удалось в юнити написать серию скриптов на С#, обеспечивающих работу анимации подвижных частей самолетов. Собственно говоря, как таковой анимации в моем проекте пока нет - идет просто вращение или передвижение нужных деталей-потомков в нужном направлении скриптоами. Вводятся строгие ограничения по скоростям и границы перемещений. Все это, хоть не сразу, но заработало, и из всех нужных скриптов остался недоведенным только скрипт для флаперонов. Скрипты для элеронов, интерцепторов, стабилизаторов, рулей направления, подвижных консолей крыла, закрылков, воздушных тормозов и фонарей кабин были закончены. Как-нибудь потом распишу. Также с подсказкой моего более продвинутого в программировании коллеги удалось заставить скрипт читать файл json.</span></div>
<div style="font-family: arial, verdana, sans-serif; font-size: 12px;">
<span style="color: #eeeeee;">В Блендер-проекте идет пока отладка скрипта Питона для загрузки нужных данных и моделей через чтение все того же json. Модуль загрузки разбит на несколько более мелких и простых функций, но одна сильно большая, многоступенчатая и несколько запутанная, хотя и работает. Модуль загрузки можно счиать готовым процентов на 90, надо только решить, как реализовать погодные условия и время суток. Наверное, придется отказаться от сложных и сильно затратных шейдеров, а также от масштабных батальных сцен, потому что у БГЕ есть свой предел и через него не перепрыгнешь, хотя и стоит максимально постараться оптимизировать работу игры. Локальные конфликты малой интенсивности - вот на что будет способен проект. Ну и ладно. Лишь бы работало. Да и сам проект я хочу закончить скорее из-за чисто иррационального упрямства - жалко бросать так далеко продвинувшееся дело...</span></div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-17102065533531677112019-04-01T07:44:00.000-07:002019-04-01T07:44:57.037-07:00Большой Апдейт. И прочие новости...<div dir="ltr" style="text-align: left;" trbidi="on">
Не писал я довольно давно. Тут причин мног7о, но главная, просто не знал, о чем писать...<br />
БГЕ таки мертв, будут ли из него делать форки, помимо известных мне upBGE, неясно... С другой стороны, мне все ж стало жалко своих трудов на ниве быдлокодерства, и, помявшись, походив вокруг да около, я все же решил начать Большой Апдейт. Так сказать, прощальный аккорд для БГЕ. Плюс, таким образом я рассчитываю не забыть совсем уж навыки работы с Питоном, а заодно отработать кое-какие приемчики, могущие мне пригодиться в работе уже с Юнити (о нем чуть позже).<br />
Правда, я не знаю, к чему и когда все это приведет, потому как назревает уже четвертая по счету операция для моего глаза (вполне ожидаемая вторичная катаракта), на какое время и когда мне придется "заткнуть свой фонтан" (С), пока непонятн6о, но и сидеть, сложа ручки, тоже неохота, а водку и прочие средства расширения сознания я не уважаю категорически.<br />
Данный пост должен сыграть (и я надеюсь, сыграет), роль своеобразного пинка самому себе, дабы подьянуть расщатавшуюся за последнее время самодисциплину и хотя бы частично прогнать лень.<br />
Итак.<br />
В чем сущность Большого Апдейта (данное понятие следует рассматривать сугубо с иронической точки зрения)?<br />
Сущность его в том, чтобы довести проект до хоть какой-то завершенности. Здесь основной задачей является упорядочивание структуры проекта и обретения новых навыков работы с файлами. А именно - новые методы поиска и загрузки моделей в игру, создание редактора миссий и кампаний и введения в состав доступных игроку юнитов зенитно-ракетных комплексов, вертолетов и танков. Хотя бы и в единственном экземпляре, причем необходимо повысить гибкость проекта и сделать его открытым для добавления новых юнитов (точнее, сохранить открытость). Это основная задача.<br />
Вспомогательная задача - избавиться от завышенных ожиданий, ввести ограничения в проект, превращающий его в, гм... в симулятор конфликтов малой интенсивности (поскольку БГЕ не любит большого количества объектов). Например, для редакторов миссий и кампаний ограничить число одновременно находящихся в воздухе самолетов и вертолетов, скажем, не больше 20 с обеих сторон (в "Ил-2", кстати, ЕМНИП максимум можно было выставить 32 самолета с разных сторон). Скорее всего, онлайн-версию делать не буду (а так, не знаю, что там получится, зарекаться не буду). Приведу в порядок код, в соответствии с новыми знаниями, почерпнутыми мною из всевозможных наставлений и инструкций. Пейзаж покрасивше сделаю (кроме БГЕ, у меня есть и другие планы, но о них я пока помолчу ввиду неопределенности с кое чем - см. выше).<br />
<br />
Теперь немного кода. Меня весьма раздражала выстроенная мною система подгрузки объектов в игру из блендов и необходимость долго и нудно расставлять разного рода слеши и двоеточия в файлах json. Почитав учебники и форумы, а также примеры по Питону и воспользовавшись подсказкой dron-a, я наваял очередной блок кода, в котором происходит поиск и установление всех путей ко всем файлам проекта и их сортировка. Пути к файлам Питона добавляются в json файл, лежащий рядом с пусковым блендом проекта. По задумке, игра должна сама смотреть нужные ей пути и тащить в себя необходимые бленды, а не пользоваться готовеньким и разжеванным заранее, что должно сильно сократить как сам код загрузки объектов, так и объем файлов json, в которых не надо будет прописывать пути.<br />
<br />
import bge<br />
import sys<br />
import os<br />
import json<br />
<br />
#Функция создания файла json с путями к файлам всего проекта<br />
def startPath():<br />
cont = bge.logic.getCurrentController()<br />
scene = bge.logic.getCurrentScene()<br />
own = cont.owner<br />
<br />
#Создаем переменные - список путей и пустую строку для путей<br />
pathList = []<br />
k = ""<br />
<br />
#Циклом перебираем файлы, директории и папки, создаем и сшиваем строки в пути<br />
for d, dirs, files in os.walk(bge.logic.expandPath("//")):<br />
for f in files:<br />
<br />
#Обрабатываем вид путей, убирая слеши и название стартовой директории<br />
k = os.path.join(d,f)<br />
k = k.split("BlendSim_2.1")[1]<br />
k = k.replace("\\", "/")<br />
<br />
#Питон-файлы добавляем в sys.path, остальные в список путей<br />
if ".py" not in k:<br />
pathList.append("/"+k)<br />
else:<br />
sys.path.append("/"+k)<br />
<br />
#Открываем файл json с путями (если его нет, то он создается) и сбрасываем туда список путей<br />
with open(bge.logic.expandPath('//NodePath.json'), 'w') as NODEPATH:<br />
json.dump(pathList, NODEPATH)<br />
<br />
#Это просто проверка <br />
proverka(own, scene)<br />
<br />
def proverka(own, scene):<br />
missile = None<br />
<br />
with open(bge.logic.expandPath('//NodePath.json'), 'r') as READPATH:<br />
JSONREADPATH = json.load(READPATH)<br />
<br />
for ob in JSONREADPATH:<br />
if "R-24R.blend" in ob:<br />
print(ob)<br />
bge.logic.LibLoad(bge.logic.expandPath(ob), 'Scene', load_actions = True)<br />
missile = scene.addObject("R-24R", own)<br />
<br />
Ну, я думаю, с комментариями будет все ясно, для чего все это надо. Проверка в виде появления на сцене ракеты Р-24Р прошла успешно и неоднократно...<br />
<br />
Теперь о Юнити. В свое время я скачал и Юнити и Анрил Энжин 4. Второй движок ощутимо мощнее и совершеннее первого, да вот беда, пользоваться им смогут не все, я - пока точно нет. Его недостатком является отсутствие большого количества документации, в отличие от Юнити, к тому же, на форуме b3d.ua мне много раз говорили, что Си Шарп относительно простой в освоении язык (но, к сожалению, я тугодум и с налету его понять не могу, изображая обезьяну, повторяющую поведение человека, а именно, создавая код по примерам и образцам, не понимая, для чего и ка, впрочем, при освоении Питона начиналось так же). В Юнити для меня еще остались кое-какие нюансы, лежащие за пределами моего понимания, например, как присвоить материал дереву и его листьям (перетаскивание порой сильно раздражает, проше было бы набрать на клавиатуре). Но все-таки, я добавил в Юнити многострадального подопытного кролика - МиГ-23МФ из моего же бленд-проекта и попробовал для начала помахать крылышками. Не сразу, но это получилось. Код, наверное, я пока приводить не буду - он состоит из занудного объявления публичных переменных и всего-то из десятка строчек кода для крыльев. Пока я не столько работаю в Юнити, сколько пытаюсь уложить в свою голову хотя бы азы Сишарпа. И прежде всего - взаимодействие скриптов между собой, минуя Инспектор Юнити. У меня он ассоциируется с логическими кирпичами БГЕ, которые необходимо использовать только там, где это действительно нужно. А многократно переделывать еще и переменные в инспекторе - нет уж. Лучше сразу научиться хотя бы по минимуму работать с кодом, а не таскать переменные и оббъекты с одно края экрана на другой (да и окошечки там маленькие, с моим зрением сейчас...)<br />
Ну ладно, будем считать, что стартовый шаг сделан (точнее, самопинок) и блогспот не накроется медным тазом, как Гуглплюс (ходили т акие слухи).</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-33851705743204350422018-09-24T22:47:00.000-07:002018-09-24T22:55:22.338-07:00Прерывая вынужденное долгое молчание...<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
<span style="font-family: "verdana";"><span style="background-color: black; color: white; font-size: 13.3333px;">Пришлось надолго прервать свою "блогерскую" деятельность. Причиной тому были проблемы со зрением. В течение полутора лет медленно, но верно прогрессировала катаракта, которая в моем случае являлась довольно сложным случаем. Настолько сложным, что наши, местные, врачи-офтальмологи, хорошо меня знающие, как постоянного клиента соответствующего лечебного учреждения, не рискнули проводить операцию имеющимися у них средствами, посчитав это слишком опасным - вероятность полдной потери зрения была ненулевой. Так что направили меня в Москву, в МНТК имени академика Федорова. Самым сложным из всей эпопеи с документами, необходимыми для поездки, оказалось попасть на прием к нашему областному офтальмологу. Регистратура в нашей областной клинике работает по принципу "правая рука не знает, что делает левая", так что первый раз записали на прием в 18:57, разумеется, рабочий день уже закончился, и пришлось переться на прием второй раз, да еще и вроде как она собралась в отпуск... Записались на 7:00, пришлось сначала ждать до 12:00, как всем сказали, но пришла врач только в 16:00... Особо ее винить в этом не стогит, у нее, между прочим, своих пациентов хватает, да и считается она хорошим хирургом, и операции делает, и весьма сложные. Просто система такая у нас теперь - все квоты на бесплатные высокотехнологичные оепрации теперь сосредоточены в одних руках, поэтому и на прием надо пробиваться. Ну ладно, это понятно. Но была в очереди одна тетка, возмущавшаяся "палачами в белых халатах", которая будучи второй, все же проперлась впереди нас, как танк (наверное, потенциальный правозащитник, не иначе). Пришлось ждать, когда она свалит, наконец из кабинета офтальмолога. Потом уже меня приняли, вместе с мамой, которая последние лет двадцать выступает в качестве моего сопровождающего везде, кроме дома и собственного двора (где я все и так наизусть знаю и даже в темноте или с закрытыми глазами могу перемщаться). Врач и ее ассистент не очерствели, сразу бросились помогать найти стул и сесть перед прибором. Посмотрели, затем офтальмолог выписала кучу направлений на анализы и снимки, проинструктировала, что и как нужно сделать, предупредила, что если что понадобится, то обратились к ее заму и тот ее из отпуска вызовет, если что понадобится. Дальше наша бюрократическая машина закрутилась с бешеной скоростью - анализы и снимки были сделаны очень быстро, в Москву ушел запрос, а ответ пришел уже на следующий день.Как-то так вышло, что мама взяла билеты в Москву очень удачно - мы приехали туда за два дня до операции, остановились в гостинице при МНТК, взяв заранее забронированный номер (бронирование осущетсвил мой друг и тезка, которому за это отдельное спасибо - он сейчас в Москве работает). Там и дождались госпитализации. Вышла, правда заминка - не было анализа крови на сифилис(есть подозрение, что тамошняя лаборатория таким нехитрым образом немного подрабатывает - деньги за нализ небольшие, но капают регулярно, а с другой стороны, зная, как просела медицина в бывших советских республиках после ухода "русских дармоедов и оккупатнов", а потом обретшие "долгожданную независимость" рванули на заработки в страну-"оккупанта и агрессора", в основном ту же Москву...), но и с этим тоже справились и в "последней волне" - около 17:00 мы отправились в больничные коридоры. Толпа страждущих и сопровождающих идет за санитаркой, как стадо слонов со своими рюкзаками и сумками. Встречные обитатели жмутся к стенам, а проходящая медсестра, под общий хохот спрашивает у нашей вожатой: "Где ты их столько нбрала?!" Определили нас с мамой в отдельный бокс - две койки - для больного и его сопровождающего, небольшой, но по-своему уютный, тем же вечером к нам заглянула ассистент хирурга, представилась, сказала, что меня прооперируют после обеда, после чего я поставил свю кракозябру под согласием на лечение и стали ждать. Оперировали меня раньше - около 11 часов дня - врач пришел гораздо раньше и откладывать дело в долгий ящик не стал. Группу больных и меня в том числе привели в оперционный корпус (или как он там нзывается), и стали одного за другим заводить в операционные. Сначала - закапывание в глаз обезболивающего, затем укол в морду возле уха анестетиком (у меня богатая практика получения уколов в глаза и виски, поэтому особых эмоций это не вызвало). Потом дождался своей очереди и лег на операционный стол. Зафиксировали веки, в глаз ударил яркий белый свет, который потом стал желтым (лазер? - наверное...) и глазу стало сильно горячо, но терпимо. Потом все исчезло и осталась только ослепительно белая пелена - будто все поле зрения занял лист чистой бумаги. Затем на ее фоне проявилось нечто - это если бы на лист бумаги положили бы, ну скажем, точилку для карандашей и стали ею двигать в разные стороны (ну как-то так, в 1996 году я видел картинку и пожестче - скальпель, входящий в мой глаз - это как бы кто-то сует палку в чистую воду, а ты, значит, находишься под пленкой воды и это целится тебе точно в лицо - как-то так, да еще два столба из воды торчат, то бишь из твоего галаза - какой-то еще инструмент тогда применялся). Предмет то появлялся, то исчезал, я слышал, как работает что-то вроде микромотора, но могу и ошибаться - там всяких звуков хватает... Затем в глаз начало что-то влезать - больно, но не сильно, И я стал видеть. Уже перебираясь на каталку, я слышал, как хирург подробно объясняет своей ассистентке, как надо работать с оборудованием - готовит молодое поколение, так сказать... Мама потом мне говорила, что когда мне открыли склеившиеся веки на следующий день при осмотре, что глаз у меня был абсолютно чистый и совсем не походил на тот, каким он был после операции в 1996 - подозреваю, что тогда с пол-мордой в зеленке и кроваво-красным белком оперированного глаза я сильно смахивал на вурдалака... Но что делать - лазера тогда не было и много чег7о не было. После осмотра на следующий день после операции врач констатировал, что осложнений нет и нас выпишут на следующий день. А мама взяла обратные билеты как раз на этот день (кто-то наверху подсказал, не иначе). Так и отбыли обратно... В Москве я был в 1995 году и тоже по офтальмолшогическим делам - потерял зрение на левый глаз из-за отслойки сетчатки и тогда я ездил с моим, к сожалению, ныне покойным папой, и запомнилась мне Моска тогдашняя неустроенностью, суетой, кучами мусора и всеобщим бардаком... Сейчас это другая Москва - чистая, опрятная, ярко освещенная, с ровными дорогами, где дети не боятся играть на улицах после часа ночи... Да и наш родной город все же прибавил, пусть и не так сильно - Россия выкарабкивается из той ямы, в которую мы угодили почти тридцать лет назад. Да и народ изменился - в лучшую сторону (за исключением отдельных индивидуумов - смотрии выше, но мама была свидетелем того, как в нашей поликлинике очередь дружно пропустила вперед женщину на инвалидной коляске, так что...). Такие дела.</span></span></div>
<div style="text-align: justify;">
<br /></div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-63516392273567670602018-04-02T00:25:00.000-07:002018-04-02T00:25:19.751-07:00Ведущий-ведомый. Эксперименты в коде.<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; margin-bottom: 7px; padding: 0px; text-align: justify;">
Копия. Оригинал: <a href="https://www.liveinternet.ru/users/flogger-k/post432889601/">https://www.liveinternet.ru/users/flogger-k/post432889601/</a></div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; margin-bottom: 7px; padding: 0px; text-align: justify;">
Итак, задача. Провести перестроение пары ведущий-ведомый в БГЕ из строя "правый пеленг" в "левый пеленг". Задача решалась в БГЕ, объекты имели тип физики "но коллижн", то есть физика отсутствовала. Ведомый при получении команды немного "приотставал", разворачивался по отношению к ведущему под углом, после чего занимал свое место с противоположной стороны и, догнав "командира" уравнивал с ним скорость. Активно использовался метод getVectTo. В итоге, после опробования различных вариантов кода, получилось следующее:import bge</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
scene = bge.logic.getCurrentScene()</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
def Wingman():</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_own_x = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_x = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_own_y = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_y = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_own_z = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z = 0.0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Wingman = scene.objects["MiG21Wingman"]</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
cont = bge.logic.getCurrentController()</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
own = cont.owner</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if own["prop"] == 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
#Перестройка в пеленг</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
elif own["prop"] == 1:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if own.getVectTo(Wingman)[2][0] > 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own += 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman -= 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if abs(Wingman.getVectTo(own)[2][0]) < 0.5 and own.getVectTo(Wingman)[2][0] > 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z += 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
elif abs(Wingman.getVectTo(own)[2][0]) > 0.5 and own.getVectTo(Wingman)[2][0] < 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z -= 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] > 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z -= 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z += 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own -= 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman += 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if Wingman.worldOrientation[2] != own.worldOrientation[2]:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Wingman.worldOrientation[2] = own.worldOrientation[2]</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if own.getDistanceTo(Wingman) > V_own*60:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman += 0.1 </div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
own["prop"] == 0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3 </div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
elif own["prop"] == 2:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if own.getVectTo(Wingman)[2][0] < 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own += 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman -= 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if abs(Wingman.getVectTo(own)[2][0] > 0.5) and own.getVectTo(Wingman)[2][0] > 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z += 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
elif abs(Wingman.getVectTo(own)[2][0] < 0.5) and own.getVectTo(Wingman)[2][0] < 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z -= 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] < 0:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z += 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
R_wingman_z -= 0.01</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own -= 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman += 0.05</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if Wingman.worldOrientation[2] != own.worldOrientation[2]:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Wingman.worldOrientation[2] = own.worldOrientation[2]</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
if own.getDistanceTo(Wingman) > V_own*60:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman += 0.1 </div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
else:</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
own["prop"] == 0</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_own = 0.3</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
V_wingman = 0.3 </div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
own.applyMovement([0.0, V_own, 0.0], True)</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Wingman.applyMovement([0.0, V_wingman, 0.0], True)</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
own.applyRotation([R_own_x, R_own_y, R_own_z], True)</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Wingman.applyRotation([R_wingman_x, R_wingman_y, R_wingman_z], True)</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
Параллельно с написанием пробного скрипта перестроения пары самолетов пришлось составлять таблийу для значений векторов метода getVectTo[2] . Если попытаться представить это в трехмерном пространстве, то получается куб, в центра которого находится ведущий, а в углах, центрах граней и ребер - ведомый. Условно говоря, конечно. То есть ведомый может идти параллельно ведущему рядом с ним. Или с отставанием справа и сверху или с опережением слева по центру и так далее. В итоге табличку я сюда солью, может потом пригодиться (хотя бы и самому себе).</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
</div>
<div style="background-color: white; font-family: Verdana; font-size: 13.3333px; text-align: justify;">
<div>
НА ОДНОМ УРОВНЕ</div>
<div>
Спереди по центру [0.0, 1.0, 0.0]</div>
<div>
Сзади по центру [0.0, -1.0, 0.0]</div>
<div>
Слева спереди [-0.7071, 0.7071, 0.0]</div>
<div>
Справа спереди [0.7071, 0.7071, 0.0]</div>
<div>
Слева сзади [-0.7071, -0.7071, 0.0]</div>
<div>
Справа сзади [0.7071, -0.7071, 0.0]</div>
<div>
Справа по центру [1.0, 0.0, 0.0]</div>
<div>
Слева по центру [-1.0, 0.0, 0.0]</div>
<div>
</div>
<div>
ВЫШЕ</div>
<div>
Выше по центру [0.0, 0.0, 1.0]</div>
<div>
Спереди по центру [0.0, 0.7071, 0.7071]</div>
<div>
Сзади по центру [0.0, -0.7071, 0.7071]</div>
<div>
Слева спереди [-0.5774, 0.5774, 0.5774]</div>
<div>
Справа спереди [0.5774, 0.5774, 0.5774]</div>
<div>
Слева сзади [-0.5774, -0.5774, 0.5774]</div>
<div>
Справа сзади [0.5774, -0.5774, 0.5774]</div>
<div>
Справа по центру [0.7071, 0.0, 0.7071]</div>
<div>
Слева по центру [-0.7071, 0.0, 0.7071]</div>
<div>
</div>
<div>
НИЖЕ</div>
<div>
Ниже по центру [0.0, 0.0, -1.0]</div>
<div>
Спереди по центру [0.0, 0.7071, -0.7071]</div>
<div>
Сзади по центру [0.0, -0.7071, -0.7071]</div>
<div>
Слева спереди [-0.5774, 0.5774, -0.5774]</div>
<div>
Справа спереди [0.5774, 0.5774, -0.5774]</div>
<div>
Слева сзади [-0.5774, -0.5774, -0.5774]</div>
<div>
Справа сзади [0.5774, -0.5774, -0.5774]</div>
<div>
Справа по центру [0.7071, 0.0, -0.7071]</div>
<div>
Слева по центру [-0.7071, 0.0, -0.7071]</div>
<div>
</div>
<div>
Потом, когда все это было закончено, я попробовал сделать немного по-другому. А именно - сделать так, чтобы ведущий постоянно вычислял координаты ведомого относительно себя, любимого, чтобы подчиненный просто занимал эту область, не заморачиваясь с векторами. Тем более, что гораздо раньше у меня уже был отработан метод наведения ботов на цель с помощью крена, тангажа или рыска по отдельности или сразу всего одновременно. Никаких проблем с наведением ведомого на эту самую искомую точку быть не должно. Однако вот тут я застрял. Как поется в одной старой песне из кинофильма: "Уж я к ней и так и этак, со словами и без слов. Обломал немало веток, наломал немало дров". В итоге все же удалось решить проблему со строем "фронт" - когда ведомый и ведущий выстроены в одну линию параллельно друг другу и строем "колонна", когда ведомый держится строго позади ведущего. А вот со строем "пеленг" пока облом. Там нужно реализовать отставание ведомого от ведущего, либо его опережение. Ну, хоть что-то получилось, а с пеленгом я все равно как-нибудь разберусь. Код написан уже для объектов с физикой типа Dynamics.</div>
<div>
</div>
<div>
<div>
port bge</div>
<div>
import math</div>
<div>
scene = bge.logic.getCurrentScene()</div>
<div>
</div>
<div>
def Wingman():</div>
<div>
cont = bge.logic.getCurrentController()</div>
<div>
own = cont.owner</div>
<div>
#Задание объекта-цели</div>
<div>
target = scene.objects["wingman"]</div>
<div>
target.worldOrientation = own.worldOrientation</div>
<div>
#target.worldPosition = front(own, target)</div>
<div>
target.worldPosition = peleng(own, target)</div>
<div>
#target.worldPosition =colonna(own, target)</div>
<div>
</div>
<div>
def colonna(own, target):</div>
<div>
targetX = own.worldPosition[0] - 3*own.worldLinearVelocity[0]</div>
<div>
targetY = own.worldPosition[1] - 3*own.worldLinearVelocity[1]</div>
<div>
targetZ = own.worldPosition[2] - 3*own.worldLinearVelocity[2]</div>
<div>
listCoord = [targetX, targetY, targetZ]</div>
<div>
return listCoord</div>
<div>
</div>
<div>
def front(own, target):</div>
<div>
LeftRightX = 0</div>
<div>
LeftRightY = 0</div>
<div>
</div>
<div>
kY = own.worldLinearVelocity[0]/own.localLinearVelocity[1]</div>
<div>
kX = own.worldLinearVelocity[1]/own.localLinearVelocity[1]</div>
<div>
kZ = own.worldLinearVelocity[2]/own.localLinearVelocity[1]</div>
<div>
</div>
<div>
PRAVOLEVO = 1</div>
<div>
</div>
<div>
if kX > 0 and kY > 0:</div>
<div>
LeftRightX = 1</div>
<div>
LeftRightY = -1</div>
<div>
elif kX < 0 and kY > 0:</div>
<div>
LeftRightX = 1</div>
<div>
LeftRightY = -1</div>
<div>
elif kX > 0 and kY < 0:</div>
<div>
LeftRightX = 1</div>
<div>
LeftRightY = -1</div>
<div>
elif kX < 0 and kY < 0:</div>
<div>
LeftRightX = 1</div>
<div>
LeftRightY = -1</div>
<div>
</div>
<div>
ownX = own.worldPosition[0]</div>
<div>
ownY = own.worldPosition[1]</div>
<div>
ownZ = own.worldPosition[2]</div>
<div>
</div>
<div>
DIST = 20.0</div>
<div>
</div>
<div>
targetX = ownX + DIST*kX*LeftRightX*PRAVOLEVO</div>
<div>
targetY = ownY + DIST*kY*LeftRightY*PRAVOLEVO</div>
<div>
targetZ = ownZ + DIST*kZ</div>
<div>
</div>
<div>
listCoord = [targetX, targetY, targetZ]</div>
<div>
return listCoord</div>
<div>
</div>
<div>
В функциях colonna и front идет как раз вычисление и выдача искомых координат ведомого. Комментариев в коде почти нет, потому как работа над этим делом еще не закончена. Получится ли из этого что-нибудь, пока неясно. Во всяком случае, в основном коде проекта включить их можно хоть сейчас, но хотелось бы добить строй "пеленг"...</div>
</div>
</div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-40422475847931896202018-03-15T22:55:00.000-07:002018-03-16T04:12:38.555-07:00Будь готов! Всегда готов!<div dir="ltr" style="text-align: left;" trbidi="on">
Этот девиз помнят те, кто успел стать пионером во времена СССР. Я был одним из последних пионеров Советского Союза. Положа руку на сердце, скажу, что в общем-то ничего членство в пионерской организации мне не дало. К концу 80-х все это уже давно превратилось в формальность для галочки, набор ритуалов, смысл которых временами был совсем непонятен. Сама-то идея пионерской организации была неплоха, но, как всегда, подвела реализация. На этом нудное вступление считаю законченным и переходим к делу.<br />
Пост будет коротким, не о Блендере, не об авиации, а о политике (вынужденно).<br />
На данный момент давление на нас со стороны сами знаете кого продолжает нарастать. При этом звучат обвинения буквально во всем и не только высшего руководства страны, но и рядовых русских. Пока еще прямо не говорят в том, что мы виноваты в том, что вообще живем на этой планете, но все к тому и идет. Припоминается высказывание госпожи Тэтчер насчет того, что территория России экономически обоснована для проживания 12-15 миллионов человек. С явным намеком на судьбк остальных...<br />
Как обычно, раз в сто лет у наших западных соседей начинается обострение. Ныне времена другие, а сущность та же.<br />
Высказывать свои политические взгляды, клеймит Запад и влезать в политические споры мне влом. Тем более, что переубедить собеседника в Сети невозможно- ты просто потеряешь время. Да у меня есть дела поважнее.<br />
Учитывая сложившуюся ситуацию с ограничениями, накладываемыми на русских пользователей в том же Фейсбуке (на котором у меня нет аккаунта), а также дискриминацию русских же в том же Стиме (кому интересно, поищите информацию об игре Syrian Warfire, кажется, так она называлась), а также раздающиеся время от временеи призывы отключить Россию от мирового Интеренета, придется принять меры.<br />
Недавно Герман Клименко, создатель сети Live Internet заявил, что Россия к отключению от мировой Сети готова. Что ж, я переношу "основную деятельность" на ЛиРу. С Гугла уходить я не буду, пока они сами не прикроют лавочку. А судя по нарастающему безумию наших соседей все к тому и идет.<br />
<b><a href="https://www.liveinternet.ru/users/flogger-k/post416416506/">https://www.liveinternet.ru/users/flogger-k/post416416506/</a></b><br />
Пока там только переводы статей. Да и создавался дневник как резервная площадка на "всякий пожарный и при условии, что". Теперь пора преодолевать леностьи печатать свои опусы сначала там, а потом уже дублировать здесь. Да еще наверное и на компе сохранять. Мало ли...<br />
В общем, если что, если кому-то мое творчество еще интересно - вы знаете, где меня найти в условиях Малого Песца. При Большом Песце (который, будем надеяться не придет совсем или придет через пару тысяч лет) все это уже не будет иметь никакогог значения.<br />
P.S. А мы, оказывается, террористы, даже если не травили. https://aftershock.news/?q=node/627005<br />
No comments</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-70881036437541003872018-03-13T23:42:00.000-07:002018-03-13T23:42:04.438-07:00"Новые птички" и и их искусственный интеллект.<div dir="ltr" style="text-align: left;" trbidi="on">
Затянувшаяся "перестройка", по-видимому, имеет все шансы не превратиться в "катастройку", как это получилось в реальном мире. Несмотря на все трудности и препятствия, процесс ползет вперед. И подтверждением тому стали две новые "птички", оживленные и подключенные к игровому процессу. Хотя, если честно, "новыми" их можно назвать именно так - в кавычках. Речь идет об F-5E Tiger и МиГ-23МС Flogger-E.<br />
"Тайгер" был задействован еще в первой версии еще двухгодичной давности, но в данной ситуации его ведение в строй все время откладывалось на потом. Но, в конце концов, когда возниклас нужда в легком истребителе второго поколения в качестве оппонента упрощенной версии МиГ-23, я принялся спешно чинить эту машину и подключать ее к игре. Не сразу, но получилось.<br />
Что касаемо МиГ-23МС, то это не что иное, как сильно упрощенная версия МиГ-23МФ, с авионикой от МиГ-21МФ, поставлявшаяся развивающимся странам. Тогда, в середине 70-х, промышленность еще не справлялась с массовой поставкой в войска МиГ-23М, а МФ был еще только в проекте, но советским союзникам новый самолет был нужен еще вчера. В итоге и появился на свет упрощенный экспортный вариант со слабым, но отработанным БРЭО, увеличенным аж на полтонны запасом топлива (по причине свободного от оборудования места в закабинном отсеке), но не способный вести бой ракетами средней дальности, чье управляемое оружие ограничивалось только ракетами Р-3Р/С и Р-23М.<br />
Отношение к МиГ-23МС довольно противоречивое - советские летчики, изображавшие на учениях "агрессора", любили этот самолет за надежность, увеличенную дальность полета и меньший, по сравнению с МиГ-23М, вес. А вот арабские пилоты, которым и приходилось на нем воевать, эту модификацию не любили за крайне слабое бортовое оборудование и отсутствие ракет средней дальности.<br />
Но тут следует помнить, что даже хорошее оружие в неумелых руках автоматически не дает победы тому, кто воевать не умеет. Опытные летчики и на МиГ-23МС одерживали победы. Так, по сирийским данным, за все время противостояния с ВВС Израиля МиГ-23МС сбили 4 F-4E и 2 А-4, потеряв при этом 7 МиГов (1 из этих семи был сбит собственным ЗРК, два - пушечным огнем в ближнем бою с F-16, остальные - ракетами с F-15). Израильтяне яростно отрицают факты потерь, но за последнее время, глядя на все более сбоящую и стремительно глупеющую работу западных СМИ, в отсутствии потерь начинаешь сомневаться... Во время ирано-иракской войны МиГ-23МС, по данным иракцев, сбили 13 иранских машин, при собственных потерях в 12 МиГов. Правда, многие исследователи той войны считают, что на счет МиГ-23МС следует записать 5-7 побед, максимум - 9. Да и потери МиГов завышены и составляют 6 машин. А вот в Ливии МиГ-23МС себя не проявили вообще никак. Во время египетско-ливийской войны в бою с египетскими МиГ-21МФ один ливийский самолет был сбит, спустя пару летв приграничной стычке был потерян еще один МиГ-23МС. Оба раза бой вели пары и оба раза отличился один и тот же египетский летчик - полковник Сал Мухаммед, имевший немалый боевой опыт, дважды горевший в сбитом МиГ-21 в войне Судного Дня. Еще один МиГ-23МС был сбит из ПЗРК в Чаде при проведении поисково-спасательной операции после сбития и тоже из ПЗРК ливийскогог Су-22. Еще два МиГ-23МС были сбиты 4 января 1989 года американскими палубными истребителями F-14A, причем американцы упорно утверждают, что это были МиГ-23МФ с ракетами Р-23Р. На возражения, что МиГ-23МФ не смогли бы совершить столь длительный полет и вернуться, американцы отвечают, что "это были смертники", которых режим Каддафи специально послал на провокацию. Насчет провокации, пожалуй, верно, насчет смертников - вряд ли. Кстати, ливийские МиГ-23МС и БН, имевшие большой внутренний запас топлива, активно работали над Средиземным морем, и даже были случаи дезертирства в Грецию. Еще один МиГ-23МС был потерян при весьма странных обстоятельствах в Италии, летом 1980 года.<br />
Самолеты F-5E весьма активно участвовали в ирано-иракской войне, на их счету много сбитых иракских самолетов, но их потери оказались выше. В основном, этот самолет использовался в качестве истребителя-бомбардировщика, но применялся и как охотник на вертолеты, а один раз даже сбил МиГ-25ПД. В тот раз иракский летчик сосредоточил свое внимание на паре "Томкэтов" и пропустил атаку "Тайгера" в хвост - после пушечной очереди иранский летчик увидел мощный выброс пламени- не менее 15 метров из пробитого топливного бака, после чего пилот сбитого МиГа катапультировался. Горящий самолет пролетел еще довольно большое расстояние и упал на территории Турции. Утверждается, что в Сети есть фото обломков этого самолета, но найти егл не удалось. Во время войны в Йемене один МиГ-29 южан, похоже, был сбит именно "Тайгером" северян, но информация об этом крайне скудна. Еще один интерсный штрих к судьбе F-5E - этот самолет едва не стал основной боевой машиной ВВС Югославии - но в конечном итоге, конкурс выиграл МиГ-21. Воевали "Тайгеры" в составе ВВС Эфиопии в Огадене и не их счету есть МиГи сомалийцев.<br />
Согласно реестру МиГ-23, в 2014 один МиГ-23МС был сбит террористами над Сирией, на момент потери машине было уже 40 лет - самолет был 1974 года выпуска. Недавно сняла с вооружения свои F-5E Мексика, фактически оставшись без авиации.<br />
Но довольно экскурса в прошлое, вернемся в настоящее, виртуальное, правда... Я решил немного сжульничать и заодно проверить выстраиваемую мною архитектуру - в качестве кабины МиГ-23МС поставил кокпит от МиГ-23МФ, обозвав его в соответствие с моделью. Жульство прошло - кабина встала на место, как родная, Это внушает оптимизм - следовательно, при соблюдении некоторых правил, кабины в будущем легко менять, модернизировать и добавлять.<br />
А потом пошла работа над ИИ ботов. Я условно разделил манеру поведения ботов на дальний воздушный и ближний воздушный бой - ДВБ и БВБ. Поведение бота в ДВБ предсказуемо - он выравнивается по крену в глобальных осях и направляет свой нос на цель - идет выравнивание на цель по рыску и тангажу, плюс поиск выгодной позиции для пуска ракеты. Пока нет разрешения на пуск, бот сближается с целью на форсаже, при разрешении пуска он "притормаживает" з все равно надо "подсвечивать" ракету, да и нет резона самому входить в зону пуска оппонента...<br />
Что касаемо БВБ, то сейчас идет работа по отработке маневра "страхивания с хвоста" и создания видимости сообразительности бота - его поведение надо сделать достаточно непредсказуемым, но не пергнуть при этом палку - там еще много работы.<br />
Ну, и в завершение - скрины - МиГ-23МС и F-5E.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-SU-j07RbgBM/WqjCXy6mjtI/AAAAAAAAAZw/DyWYDiT4q48U-n7K6DcDn5LG4tGXtjz4QCLcBGAs/s1600/BlendSim42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1518" height="368" src="https://4.bp.blogspot.com/-SU-j07RbgBM/WqjCXy6mjtI/AAAAAAAAAZw/DyWYDiT4q48U-n7K6DcDn5LG4tGXtjz4QCLcBGAs/s640/BlendSim42.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-uvBoXIAqpuw/WqjCX-xxJ3I/AAAAAAAAAZs/0mKKoij9BV8jy0bws0JQm7Gf9CXNPbuBACLcBGAs/s1600/BlendSim44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1518" height="368" src="https://4.bp.blogspot.com/-uvBoXIAqpuw/WqjCX-xxJ3I/AAAAAAAAAZs/0mKKoij9BV8jy0bws0JQm7Gf9CXNPbuBACLcBGAs/s640/BlendSim44.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-HQZudaz1arE/WqjCXvM9cFI/AAAAAAAAAZo/HqYNzM6ZhnAJzpaVlZzEV5e1Q-moAr1yACLcBGAs/s1600/BlendSim45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1518" height="368" src="https://1.bp.blogspot.com/-HQZudaz1arE/WqjCXvM9cFI/AAAAAAAAAZo/HqYNzM6ZhnAJzpaVlZzEV5e1Q-moAr1yACLcBGAs/s640/BlendSim45.png" width="640" /></a></div>
</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-38518912865960038672018-03-04T01:12:00.000-08:002018-03-04T01:12:39.162-08:00Доводка классов. Прячем все.<div dir="ltr" style="text-align: left;" trbidi="on">
Вполне закономерным итогом работы с классами стал полный переход на "внутриклассовую" работу скриптов... В конце концов, под зонтик класса перешла и работа уровней детализации и расчет поправочного коэффициента для выпуска-уборки шасси, тормозов и крыла изменяемой стреловидности. Приводить здесь скрипт класса того же МиГ-23, как представителя юнитов, у которого присутствует все вышеперчисленное, не стоит. Он довольно длинный и особо ничем не отличается от приведенных ранее. Ну, добавилось в метод движения расчет положения крыла, ну появился в классе новый метод для расчета уровня детализации. Все это чисто рабочие моменты...<br />
Главное, что все это работает - и слава Богу, без сбоев. Пока, во всяком случае.<br />
А потом пришел черед выполнить еще один пункт - добавить в классы оружия метод движения. И поведения заодно. Как водится первой жертвой исследования стала ракета Р-27Р. Сам ее класс был слегка модифицирован - всего с полдюжины строчек, но работа себя оправдала. Даже на фоне того, что пришлось аврально вносить коррективы во все остальные классы управляемых ракет. Заодно, кстати, дополнительно "раздробил" скрипт движения оружия, выделив из негог первый тип - ракета. Он характерен для всех типов ракет, кроме НАР. Причина - ракеты такого типа летают не по прямой, поэтому для них невозможно сделать дым одной частицей. О частицах чуть позже. Итак. Скрипт движения ракеты.<br />
<br />
import mathutils<br />
import random<br />
import bge<br />
scene = bge.logic.getCurrentScene()<br />
<br />
def control(self):<br />
cont = bge.logic.getCurrentController()<br />
own = self<br />
if own.engineWeapon == 'CatapultWeapon':<br />
CatapultWeapon(own)<br />
else:<br />
Missile(own)<br />
<span style="white-space: pre;"> </span> <br />
<br />
#Полет ракеты<br />
def CatapultWeapon(own):<br />
if own.vzryvatel == 10:<br />
own.suspendDynamics<br />
own.engineWeapon = "Missile"<br />
<br />
#Полет ракеты<br />
def Missile(own):<br />
<br />
own.applyMovement([0.0,own.speed/60,0.0],True)<br />
own.timerAmountFire += 1<br />
if own.amountFire > own.timerAmountFire:<br />
#Срабатывание добавления частиц дыма от ракет<br />
own.timerSmoke += 1<br />
if own.timerSmoke == 1:<br />
#Дым снаряда<br />
smoke = scene.addObject('ParticleUniversal', own)<br />
smoke.replaceMesh("SmokeLong", True, False)<br />
smoke.setParent(own, False, False)<br />
if own.timerSmoke == 3:<br />
if 'ParticleUniversal' in own.childrenRecursive:<br />
own.childrenRecursive['ParticleUniversal'].scaleX = own.speed/360<br />
own.childrenRecursive['ParticleUniversal'].scaleY = own.speed/12<br />
own.childrenRecursive['ParticleUniversal'].scaleZ = own.speed/360<br />
own.childrenRecursive['ParticleUniversal'].Delta_scaleX = 0.1<br />
own.childrenRecursive['ParticleUniversal'].Delta_scaleY = 0.1<br />
own.childrenRecursive['ParticleUniversal'].Delta_scaleZ = 0.1<br />
own.childrenRecursive['ParticleUniversal'].Delta_colorAlpha = -0.0005<br />
own.childrenRecursive['ParticleUniversal'].colorAlpha = 0.2<br />
own.childrenRecursive['ParticleUniversal'].removeParent()<br />
if own.timerSmoke == 5:<br />
own.timerSmoke = 0<br />
<br />
<br />
<br />
А теперь скрипт класса Р-27Р, который вызывает этот самый скрипт и скрипт головки самонаведения.<br />
<br />
import bge<br />
<br />
from ClassWeapon import typeWeapon<br />
<br />
class R27R(typeWeapon):<br />
<br />
import Weapon_Missile as __Weapon_Missile<br />
import Weapon_GSN as __Weapon_GSN<br />
<br />
def __init__(self, old_owner):<br />
typeWeapon.__init__(self, old_owner)<br />
<br />
def engine(self):<br />
self.__Weapon_GSN.GSN(self)<br />
self.__Weapon_Missile.control(self)<br />
<br />
def mutate(cont):<br />
R27R(cont.owner)<br />
<br />
<br />
Ну, и собственно, функция работы оружия в игровом файле.<br />
<br />
#Функция эффекта взрывов<br />
def controlWeapon():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
own.engine()<br />
<br />
<br />
Как видно, опять "матрешка". Так сказать, в прямом смысле - в собранной матрешке не видно внутренних составляющих. Но они есть. Как тот суслик из фильма ДМБ".<br />
За это время я восстановил работу РЭБ, восстановил катапультирование из сбитого самолета, и, наконец, добавил огонь для сбитого. Псевдочастицами-плейнами.<br />
А теперь по поводу частиц. К сожалению, БГЕ имеет ограничения и довольно существенные, как раз в этой области, поэтому эти меры - паллиатив. Правда, есть возможность отобрать у БГЕ почти все функции, оставив только загрузку окна и еще что-то по мелочи. Но для этого нужен другой уровень программирования, мой очень сильно недотягивает. Хотя, повторяю, часть функций БГЕ можно передать другим программам. Поэтому извращаться по поводу частиц огня, дыма, облаков и прочего я прекращаю и сосредотачиваюсь на ИИ, оружии, систем противодействия и защиты, выстраивания иерархии ботов, поиска путей и так далее. А еще, наверное, я радикально сокращу количество блоков ландшафта. Раз в 10. Схема вполне работоспособна, но ее можно и нужно оптимизировать.<br />
Все же приведу скрипт работы частиц огня. Суть в том, что их число растет, пока не достигает определенного предела. Все частицы являются потомками и занимают свое место в списке childrenRecursive. Они расставляются на удалении от родителя согласно своему номеру в списке - чем он больше, тем дальше от родителя частица. При этом присутствует некий элемент случайности. По координатам и размерам, но в некоем диапазоне.<br />
<br />
import random<br />
scene = bge.logic.getCurrentScene()<br />
<br />
#Эта переменная определяет длину "хвоса" огня<br />
fireLong = random.randrange(8, 20)<br />
<br />
def FireAir():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
<br />
#Длина списка потомков<br />
particleLen = len(own.childrenRecursive)<br />
<br />
#Это просто списко для ускорения добавления частиц - удвоение или утроение за один проход в тик<br />
listObj = ["num0"]<br />
#listObj = ["num0","num1"]<br />
<br />
#Характеристики частицы - локация, размер и цыкт с прозрачностью<br />
partColor = random.randrange(2, 10)/10<br />
partLoc = random.randrange(particleLen, particleLen+2)<br />
partScale = random.randrange(particleLen, (particleLen+2)*2)/(particleLen+2)*4<br />
<br />
#Эта переменная используется для отслеживания индекса потомка в списке<br />
indexObj = 0<br />
<br />
#Этап1 - добавляем, парентим и раставляем частицы, отслеживая длину списка потомков<br />
if particleLen < fireLong:<br />
for newParticle in listObj:<br />
newParticle = scene.addObject('Plane', own)<br />
newParticle.setParent(own, False, False)<br />
newParticle.replaceMesh("FireAir_", True, False)<br />
newParticle.localPosition = [partLoc/10, -partLoc, partLoc/10]<br />
newParticle.worldScale = [partScale, partScale, partScale]<br />
<br />
#Этап 2 - работаем с тем, что есть, больше ничего не добавляем<br />
else:<br />
for obj in own.childrenRecursive:<br />
#Здесь переменные локации и размера используются в качестве эталона,<br />
#к которому подтягиваются ТТХ частиц<br />
indexObj = own.childrenRecursive.index(obj)<br />
#partLoc = random.randrange(indexObj, indexObj*indexObj*indexObj+2)<br />
partLoc = random.randrange(indexObj, indexObj+2)<br />
partScale = random.randrange(indexObj, (indexObj+2)*2)/(indexObj+2)*3<br />
<br />
#Можно поиграться с цветом и прозрачностью<br />
obj.color = [1.0, partColor, partColor, partColor]<br />
<br />
#Хвост пламени - локальные координаты Х<br />
if obj.localPosition[1] < -partLoc:<br />
obj.localPosition[1] += partLoc/50<br />
elif obj.localPosition[1] > -partLoc:<br />
obj.localPosition[1] -= partLoc/50 <br />
#Размазанность и толщина хвоста пламени - координаты Икс и Зет<br />
obj.localPosition[0] = partLoc/4<br />
obj.localPosition[2] = partLoc/4<br />
<br />
<br />
Вот так. Хотя, все это - костыли. Пусть и работающие. Подожду пока прояснения обстоятельств с перхватом рендера и управления БГЕ. Если обстоятельства позволят, то откроются новые перспективы.<br />
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-2316269872221224122018-02-23T22:13:00.000-08:002018-02-23T22:13:06.423-08:00Прячься, кто может! Инкапсуляция в классах. Кажется, выхожу на оперативный простор...<div dir="ltr" style="text-align: left;" trbidi="on">
На довольно долгое время я застрял. Хорошо так застрял, давно со мной такого не было. Причиной этому стала "размазанная" по нескольким скриптам ошибка в иде недостающих аргументов для функций. А учитывая то, что функции вызывались через try-except с перхватом исключения, и внешне все выглядело благообразно... Едиснтвенно, что выглядело безобразно- так это целераспределение для ботов. Боты считали противниками своих собратьев и не стреляли по своим только потому, что в условие пуска был заложен запрет на стрельбу по объекту с одинаковыми для бота "цветами". Это выглядело смешно, если бы не отняло у меня три недели. Правда, насчет "отняли", я погорячился. По-всякому измываясь над собственным кодом, я научился с подсказки dron-a проводить инкапсуляцию внутри кода. Кода класса, я имею в виду. Теперь, значит, кроме наследования, еще и инкапсуляция... Собственно говоря, ошибка лежала в несколько другой плоскости, но это именно тот случай, когда не было бы счасться, да несчастья помогло. Ее устранение свелось всего-начсего к тщательной проверке "шапок" модулей и наборам переменных в скобках после названий функций...<br />
Инкапсуляция - это другое дело. Она позвоялет исключить "накладок" в поеведении объектов, "пряча" данные внутри класса от внешнего мира. Вообще-то до них все равно добраться можно, но сделать это гораздо сложнее. Основательной переделке подверглись все классы боевых самолетов, причем делалось это неоднократно, по мере выяснения свойств инкапсуляции и осознания области ее применения. Ниже приводится класс для МиГ-23БН. Мне влом его резать, проще клавишами копировать-вставить. Да и образцом в случае чего послужит.<br />
<br />
import bge<br />
#import UnitAir_engine<br />
<br />
from ClassUnitAir import UnitAir<br />
<br />
class MiG23BN_Libya_(UnitAir):<br />
import UnitAir_engine as __UnitAir_engine<br />
<br />
def __init__(self, old_owner):<br />
UnitAir.__init__(self, old_owner)<br />
<br />
__air = True<br />
__airtype = "JET"<br />
__dron = False<br />
__multiengine = False<br />
__airbrake = True<br />
__slats = True<br />
__flaps = True<br />
__swingwing = True<br />
__canopy = True<br />
__chassy = True<br />
__yaw = True<br />
<br />
#Летательный аппарат<br />
def is_AIR(self):<br />
return self.__air<br />
#Подтип ЛA - вертолет, самолет (реактивный или внитовой)<br />
def is_AIRTYPE(self):<br />
return self.__airtype<br />
#Пилотируемый или БПЛА<br />
def is_DRON(self):<br />
return self.__dron<br />
def is_FLAPS(self):<br />
return self.__flaps<br />
def is_SLATS(self):<br />
return self.__slats<br />
def is_SWINGWING(self):<br />
return self.__swingwing<br />
def is_AIRBRAKE(self):<br />
return self.__airbrake<br />
#Одно- или многодвигательный<br />
def is_MULTIENGINE(self):<br />
return self.__multiengine<br />
def is_CHASSY(self):<br />
return self.__chassy<br />
def is_CANOPY(self):<br />
return self.__canopy<br />
def is_YAW(self):<br />
return self.__yaw<br />
<br />
<br />
#######################################<br />
def is_CANOPY_Device(self):<br />
CANOPY_Device = [<br />
["Cnp_",[0.0087,0.0,0.0],"rotat"]<br />
]<br />
return CANOPY_Device<br />
def is_POINT_CANOPY(self):<br />
reper = [0, 114]<br />
return reper<br />
########################################<br />
def is_FLAPS_Device(self):<br />
FLAPS_Device = [<br />
["FlpR_",[-0.0087,0.0,0.0],"rotat"],<br />
["FlpL_",[-0.0087,0.0,0.0],"rotat"]<br />
]<br />
return FLAPS_Device<br />
def is_POINT_FLAPS(self):<br />
reper = [-50, 0, 100]<br />
return reper<br />
<br />
<br />
######################################<br />
#Наличие-отсутствие предкрылков<br />
def is_SLATS_Device(self):<br />
SLATS_Device = [<br />
["SltR_",[-0.0087,0.0,0.0],"rotat"],<br />
["SltL_",[-0.0087,0.0,0.0],"rotat"]<br />
]<br />
return SLATS_Device<br />
def is_POINT_SLATS(self):<br />
reper = [0, 40]<br />
return reper<br />
<br />
#####################################<br />
#Наличие-отсутствие крыла изменяемой стреловидности<br />
def is_POINT_WINGS(self):<br />
reper = [0, 300, 550]<br />
return reper<br />
<br />
def is_WINGS_Device(self):<br />
WINGS_Device = [<br />
["WngR_",[0.0,0.0,-0.00174],"rotat"],<br />
["WngL_",[0.0,0.0,0.00174],"rotat"]<br />
]<br />
return WINGS_Device<br />
<br />
####################################<br />
#Наличие-отсутствие воздушных тормозов<br />
def is_AIRBRAKE_Device(self):<br />
AIRBRAKE_Device = [<br />
["ArbUR_",[-0.00783,0.0,0.0],"rotat"],<br />
["ArbUL_",[-0.00783,0.0,0.0],"rotat"],<br />
["ArbDR_",[0.00696,0.0,0.0],"rotat"],<br />
["ArbDL_",[0.00696,0.0,0.0],"rotat"]<br />
]<br />
return AIRBRAKE_Device<br />
<br />
def is_POINT_AIRBRAKE(self):<br />
reper = [0, 100]<br />
return reper<br />
<br />
####################################<br />
#Наличие-отсутствие рулей направления<br />
def is_YAW_Device(self):<br />
YAW_Device = [<br />
["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",-1],<br />
["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",1]<br />
]<br />
return YAW_Device<br />
def is_POINT_YAW(self):<br />
reper = [-25, 25]<br />
return reper<br />
<br />
####################################<br />
#Наличие-отсутствие стабилизатора<br />
def is_ROLL(self):<br />
return True<br />
def is_ROLL_Device(self):<br />
ROLL_Device = [<br />
["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],<br />
["ElvR_",[0.0087,0.0,0.0],"rotat",-1],<br />
["ElvL_",[-0.0087,0.0,0.0],"rotat",1],<br />
["ElvR_",[0.0087,0.0,0.0],"rotat",1]<br />
]<br />
return ROLL_Device<br />
def is_POINT_ROLL(self):<br />
reper = [-20, 20]<br />
if self.WINGS > 300:<br />
reper = [-13, 13]<br />
#print(reper)<br />
return reper<br />
#Наличие-отсутствие стабилизатора<br />
def is_ROLLwings(self):<br />
return True<br />
def is_ROLLwings_Device(self):<br />
ROLLwings_Device = [<br />
["InpR_",[0.0,0.0,0.0],"rotat",0],<br />
["InpL_",[0.0,0.0,0.0],"rotat",0]<br />
]<br />
<br />
if self.ROLLwings < 0 and self.rollSelf == -1:<br />
ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",-1]]<br />
elif self.ROLLwings > 0 and self.rollSelf == 1:<br />
ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",1]]<br />
elif self.ROLLwings < 0 and self.rollSelf == 0:<br />
ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]<br />
elif self.ROLLwings > 0 and self.rollSelf == 0:<br />
ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]<br />
elif self.ROLLwings < 0 and self.rollSelf == 1:<br />
ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]<br />
elif self.ROLLwings > 0 and self.rollSelf == -1:<br />
ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]<br />
<br />
#print(self.ROLLwings,self.rotatY)<br />
return ROLLwings_Device<br />
def is_POINT_ROLLwings(self):<br />
reper = [-45, 45]<br />
if 299 < self.WINGS < 549:<br />
reper = [-30, 30]<br />
elif self.WINGS > 549:<br />
reper = [0, 0]<br />
#print(reper)<br />
return reper<br />
<br />
####################################<br />
#Наличие-отсутствие стабилизатора<br />
def is_PITCH(self):<br />
return True<br />
def is_PITCH_Device(self):<br />
PITCH_Device = [<br />
["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],<br />
["ElvR_",[-0.0087,0.0,0.0],"rotat",-1],<br />
["ElvL_",[-0.0087,0.0,0.0],"rotat",1],<br />
["ElvR_",[-0.0087,0.0,0.0],"rotat",1]<br />
]<br />
return PITCH_Device<br />
def is_POINT_PITCH(self):<br />
reper = [-17, 49]<br />
return reper <br />
<br />
#################################################<br />
#Методы движения, работы ЛОД и прочего<br />
def engine(self):<br />
self.__UnitAir_engine.engine(self)<br />
<br />
def mutate(cont):<br />
MiG23BN_Libya_(cont.owner)<br />
<br />
Самая вкусная строчка почти в самом низу:<br />
self.__UnitAir_engine.engine(self)<br />
Символ __ означает как раз эту самую инкапсуляцию. Вызов идет изнутри самого класса и других объектов не касается. Если посмотреть на класс с методами, то там тоже можно встретить переменные вида __air. Это специфические переменные, сообщающие, например, что у МиГ-23БН один двигатель, крыло с изменяемой стреловидностью, есть тормоза, закрылки и прочее. Это чисто его внутреннее дело.<br />
Теперь же движение объекта из логического кирпича в пусковом файле выглядит так:<br />
ge<br />
import json<br />
import sys<br />
import random<br />
<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
scene = bge.logic.getCurrentScene()<br />
import sys<br />
pathFolder = own.unitName + own.unitNation + "/folderPy"<br />
sys.path.append(bge.logic.expandPath("//Aircraft/" + pathFolder))<br />
<br />
unitmodule = own.unitNode + own.unitNation + "NodeScript"<br />
unit_module = __import__(unitmodule)<br />
<br />
UNITCLASS = own.unitNode + own.unitNation<br />
UNIT_CLASS = __import__(UNITCLASS)<br />
<br />
<br />
#Импорт из папки для юнитов ЛА модклей<br />
import UnitAir_engine<br />
import UnitAir_LODes<br />
import UnitAir_shoot<br />
import CONTROL_Operations<br />
import mathutils<br />
<br />
import UnitGround_Sensores<br />
import UnitGround_Artillery<br />
import UnitGround_LODes<br />
<br />
import Weapon<br />
ArbitrGame = scene.objects["ArbitrGame"]<br />
<br />
#############################################<br />
#############################################<br />
def UnitAir():<br />
cont = bge.logic.getCurrentController()<br />
own = cont.owner<br />
<br />
#if len(own.idTarget) > 0:<br />
# print(own.unitName, scene.objects.from_id(int(own.idTarget[0])).unitName) <br />
<br />
<br />
if own.controlUnit != "Statist":<br />
own.globalTargetList = ArbitrGame.UNITS[0][own.targetType][own.target]<br />
#UnitAir_engine.engine(own, scene)<br />
<br />
own.engine()<br />
<br />
unit_module.correctData(own)<br />
<br />
<br />
#Работа уровня детализации идет ВСЕГДА<br />
UnitAir_LODes.LODes(own)<br />
<br />
<br />
Думаю, извивы моей мысли понятны - импорттируем класс по названию - MiG-23BN_Libya_.py, а из него тащим метод движения, который, в свою очередь, дергает скрипт движения. Если посмотреть класс, то в начале можно найти строку import UnitAir_engine. Это он и есть...<br />
Теперь подобные вещи придется делать для оружия, от греха подальше. А то вдруг нацелятся все ракеты типа Р-23Р на один "Фантом" (который еще делать надо).<br />
Простор для инкапсуляции еще есть, но не будем пока о грустном. После отлова ошибки и доводки сриптов, наконец, боты стали вести себя правильно. А именно - разворачиваться в сторону противника, сканировать местность, сбрасывать баки, разгоняться и маневрировать, и даже стрелять... Вновь ожила СПО, заработали звуковые маркеры. Короче, жизнь стала налаживаться.<br />
<br />
<br />
<br />
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-40777394945216997632018-02-02T21:47:00.000-08:002018-02-02T21:47:38.980-08:00Классы-наследники для оружия. Доводка напильником.<div dir="ltr" style="text-align: left;" trbidi="on">
На некоторое время работа над прпоектом как бы подзависла. Причиной тому было продолжавшееся"дробление" скриптов, кстановление взаимосвязи между ними, шлифовка кода и его "притирка". Долгое время никак не удавалось отработать поражающее действие оружия на юнитах - из-за того, что когда я ломал старую схему, в импортируемом новом коде сдублировал функции. После чего корректная работа кода нарушилась. Также я долго и безуспешно пытался понять причину странного поведения ракет Р-27Р - после пуска с МиГ-29 одна обязательно взрывалась почти сразу (самоликвидация), а вторая шла в цель. Позже выяснилось, что причина просто в некорректном разрешении на пуск - олно выдавалось за пределами дальности, поэтому придется еще пошерстить скрипт сеносрра юнитов, чтобы этот баг устранить.<br />
Но сначала я грешил на сам класс оружия и такие основания у меня были. Хотя я вроде как и освоил классы, и даже классы наследники, но не все так просто. Когда идет мутация объекта - при создании класса, то данные в самом классе меняются для всех объектов, которые ранее этот самый классс использовали. Иначе я никак не могу объяснить феномен поиска деталей МиГ-29 у Ф-15 (а именно такую ошибку мне выадвала консоль) или деталей МиГ-23 у Ф-16. Это безобразие продолжалось ровно до тех пор, пока я не ввел наследование классов для юнитов. В итоге подобные вещи прекратились, потому что классы МиГ-29 и Ф-15, а также МиГ-23 и Ф-16 более не пересекаются. Это отдельные классы, хотя и имеющие схожую структуру.<br />
Весьма раздраженный поведением Р-27Р и еще не ведая об истинных причинах, я приступил к созданию классов наследников для оружия. По-видимому, я все же набил руку, потому что сделал это практически с первого залпа. Отработав технику на Р-27Р, принялся терпеливо дописывать json с данными для оружия и для оружия же писать скрипты класса. Папок было довольно много, поэтому делал я это в несколько приемов, затратив пару дней. Несложно, но одноообразно и надо быть внимательным, чтобы не проскочили символы типа плюс-минус в заголовке класса - консоль тут же выразит свое возмущение...<br />
Теперь превращение 30-мм снаряда в 152-мм во время выстрела и его полета к цели исключено. Схема выглядит так.<br />
Запускаем скрипт в игровом файле - тащим из нужной папки класс оружия, название питоновского файла обычно выглядит так - R27R.py.<br />
<br />
def Weapon(cont):<br />
own = cont.owner<br />
<br />
nameWeapon = ""<br />
nameGeneral = own.parent.name<br />
nameSysPath = ""<br />
#print(own.parent, own.childrenRecursive)<br />
if hasattr(own.parent, "dictWeapon") == True:<br />
if "WPU" in own.parent.dictWeapon:<br />
nameGeneral = own.parent.dictWeapon["WPU"]["gunBullet"]<br />
if hasattr(own.parent, "parent") == True:<br />
#print(own.parent.parent)<br />
if hasattr(own.parent.parent, "parent") == True:<br />
#print(own.parent.parent.parent)<br />
if hasattr(own.parent.parent.parent, "dictWeapon") == True:<br />
if "bulletGen" in own.parent.parent.parent.dictWeapon:<br />
#print("True")<br />
nameGeneral = own.parent.parent.parent.dictWeapon["bulletGen"]<br />
#print(nameGeneral)<br />
<br />
nameSysPath = "//Weapon/"+nameGeneral <br />
<br />
sys.path.append(bge.logic.expandPath(nameSysPath))<br />
with open(bge.logic.expandPath("//Weapon/"+nameGeneral+"/"+nameGeneral+".json"), 'r') as directWeaponClass:<br />
JSONweaponClass = json.load(directWeaponClass)<br />
nameWeapon = JSONweaponClass["ownClass"]<br />
<br />
unit_module = __import__(nameWeapon)<br />
#print(nameWeapon)<br />
unit_class = getattr(unit_module, nameWeapon)<br />
#print(nameGeneral)<br />
x = unit_class(own)<br />
<br />
#import ClassWeapon<br />
#own = cont.owner<br />
#x = ClassWeapon.typeWeapon(own)<br />
<br />
<br />
А потом уже тащим класс-наследник. Он совсем маленький.<br />
<br />
import bge<br />
<br />
from ClassWeapon import typeWeapon<br />
<br />
class R27R(typeWeapon):<br />
def __init__(self, old_owner):<br />
typeWeapon.__init__(self, old_owner)<br />
<br />
def mutate(cont):<br />
R27R(cont.owner)<br />
<br />
А этот класс сначаа тащит стандартный класс оружия, который лежит в папке со скриптами для оружия.<br />
<br />
import bge<br />
import json<br />
<br />
with open(bge.logic.expandPath('//Weapon/WEAPON_CLASS.json'), 'r') as directWeapon:<br />
JSONweapon = json.load(directWeapon)<br />
<br />
class typeWeapon(bge.types.KX_GameObject): <br />
<br />
def __init__(self, old_owner):<br />
self.__dict__.update(JSONweapon)<br />
<br />
def mutate(cont):<br />
typeWeapon(cont.owner)<br />
<div>
<br /></div>
<div>
<br /></div>
<div>
Тоже ничего особенного. Опять матрешка - отыскивается json со стартовыми данными и идет присвоение атрибутов.</div>
<div>
Потом все это подвергается доводке другим скриптом - функцией сартовой инициализации - я об этом раньше писал - просто обновляется словарь класса оружия на основе json той же Р-27Р и все.</div>
<div>
По-хорошему, наверное, следовало бы прямо в класс вписать эту самую инициализацию, навеное. Но сделаю это чуть позже. Там свои нюансы, и опять придется перебирать все скрипты ручками...</div>
<div>
Поражающее действие ракет я таки отработал и теперь полра шлифовать модель повреждений, а также ИИ ботов, до которого все ника не доходят руки. Что касаемо модели повреждений, то тут меня душит жаба. БГЕ сильно не любит большого количества объектов, а с огнем и дымом именно так дело и обстоит. Очевидно, придется жульничать. Бывает, что сбитые машины падают либо относительно целые (заглох двигатель), либо слабо дымя. Или просто взрываются в воздухе. Вообще же, Евгений Пепеляев, воевавший в Корее, в своей книге "МиГи против Сейбров" писал, что реактивные самолеты горят плохо, в отличие от поршневых, что ж приму его слова в качестве рекомендации и излоупотреблять красивым огненным хвостом не буду. </div>
<br />
<br />
<br />
<br />
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-52801918606766571192018-01-09T00:17:00.000-08:002018-01-09T00:17:49.188-08:00Размышления на тему игрового ИИ.<div dir="ltr" style="text-align: left;" trbidi="on">
Всех с прошедшими праздниками, которые вновь сумели пережить... правда, новгодние каникулы закончились не для всех, и явно еще не израсъодована пиротехника, заботливо приберегаемая для встречи Сатрого Нового Года...<br />
Пока что успешно завершена война с анимацией подвижных частей авиатехники - ее последним аккордом стала борьба с элеронами, которая отрабатывалась уже на МиГ-29. Как обычно, казавшаяся простой и легкорешаемая задача сожрала больше времени, чем та, которая признавалась сложной и запутанной. Это я про интерцепторы. Затем последовала стандартизация уже собранных ранее классов самолетов, которая пока не коснулась Ф-16 и Су-25. У Ф-16, кстати, как и у Су-27 имеются "интерцепоторы наоборот" - флапероны, так что там придется менять знаки перед углами отклонения - интерцепторы поднимаются, а флапероны опускаются...<br />
Подошла очередь искусственного интеллекта. Скрипт этого имитатора интеллекта, будем уж честными до конца, у меня насчитывал уже больше пары тысяч строк и отыскивать в нем нужное место для внесения изменений становилось все сложнее... Поскольку метод дробления больших скриптов на узкоспециализированные модули уже отработан, то супер-скрипт ИИ (в смысле размеров) постигла та же участь. Он раздробился примерно на дюжину модулей, относительно небольших. Пока раздробление прошло начерно - я еще не приступал к отработке связей между отдельными модулями. Но примерная схема уже вырисовывается. Модули можно разделить на несколько категорий.<br />
1. Стандартные модули разворотов юнита и его ориентаций.<br />
-Модуль стандартного движения при крене-тангаже-рыске -приложение сил в определенном направлении.<br />
-Модуль ориентации в пространстве для горизонтального полета. Выравнивание по крену и тангажу, чтоб самолет летел прямо с выдерживанием нужной высоты. Также в этом модуле есть набор высоты и снижение. Некоторые машины имеют ограничения по крену и тангажу (это характерно для тяжелых машин, таких, как транспортники, хотя были уникумы, крутившие "бочку" на Ту-16, но для тяжелых бомберов такая вещь ни к чему, на мой взглядд), так что в этом модуле они так же соблюдаются.<br />
-Модуль с ориентацией на цель - противника или точку маршрута. Ту, думаю, понятно.<br />
-Модуль выдерживания строя. Пока в природе не существует - только в моей голове и весьма смутно.<br />
2. Модули сканирования и выбора сенсоров и вооружения.<br />
-Пока один модуль - в нем есть функции выбора отимального вооружения (подальнобойнее) и подбора к нему сенсора. Также в этом модуле идет сканирование окружающего мира на предмет бодания лбом земли и наличия угроз - например летящей к боту ракеты.<br />
3. Стандартные модели поведения ботов на земле и в полете. Их много, этих модулей...<br />
-Модуль руления на взлет.<br />
-Модуль руления после посадки.<br />
-Модуль взлета.<br />
-Модуль посадки.<br />
-Модуль полета по маршруту.<br />
-Модуль уклонения от атаки. Включает в себя выбор варианта уклоонения от атаки при помощи сочетания маневров типа "горка" с "размезанно бочкой" и тд. А также постановку активных и пассивных помех.<br />
-Модуль атаки цели. в нем имеются подварианты - атака наземной или воздушной цели. и этот модуль будет потихоньку разрастаться по мере наработки опыта.<br />
-Модуль уклонения от столкновения с землей.<br />
-Модуль набора энергии после маневра. Как бы ни был самолет маневрен, но он может потерять скорость на том же вираже и сорваться в штопор, чего допускать нельзя. Поэтому самолет надлежит аккуратно выровнять, врубить форсаж и снова разогнать.<br />
<br />
вот пока примерно так.Есть еще любопытные мысли на этот счет. А именно - по поводу сканирования встречи сземлей. Пока у меня имеется один террайн из 2500 блоков. На мой взгляд, это все же жирно, поэтому лучше сделать из пары сотен блоков. Или 400 - макимум. Причем для террайна ввести свой json, в котором можно перечислить положение блока в прсотранстве, его высоту, отметить координаты горных районов и тд. Зачем? А вот зачем...<br />
Как известно, модули стандартных функций БГЕ типа луча много кушают, поэтому лучше лишний раз их не трогать. А зачем врубать этот самый луч, если самолет находится над блоком, самая высокая вершина которого имеет 500 метров, а высотв полета самолета - 2000? Незачем... Вот для этого и нужны данные по высоте блока. Но это еще не все. В горных районах ботам следует соблюдать осторожность и идти с огибанием рельефа - тут пригодится список маршрутных точек в этом районе. Чтобы не впечататься в стену ущелья, например. Там еще придется вводить алгоритм постройки маршрута по этим точкам, но суть, я думаю, понятна.<br />
В свое время denis8424 продемонстрировал в своем блоге приер движения бота по земле без использования физики. Но при этом бот плавно повторяет все неровности ландшафта - используется словарь координат вершин. он генерится после появления ландшафта, но можно попытаться забить этот словарь в json блока террайна. Но тут еще думать надо.<br />
Пока с ИИ придется слегка притормозить - надо раздробить хотя бы наскоро скрипт оружия - он тоже здоровенный и с ним тоже надо решать кое-какие набившие оскомину вопросы...</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-89639984222356422612017-12-28T22:17:00.000-08:002017-12-28T22:17:57.614-08:00Дао скриптов-2 Анимация и лазейки для ИИ..<div dir="ltr" style="text-align: left;" trbidi="on">
()Официальным голосомЖ "Всех с наступающим Новым Годом, товарищи! Пусть Новый Год будет лучше прошедшего (для меня это болеее, чем актуально), но хуже идущего за ним 9это, наверное, актуально для всех)."<br />
С поздравлениями закончили, переходим к делу. Только что, минут десять назад, подопытный кролик (а точнее, уже Очень Опытный Кролик) МиГ-23БН выпустил и убрал шасси. Кк положено, с характерным стоном гидравлики, открытием и закрытием створок, вращением и передвижением многочисленных деталей. На этом, похоже, эпопея с анимациями завершается. В очередной раз, надо заметить.<br />
Постепенно проявилась схема работы скриптов, которая сменит предыдущую. Как я уже писал выше, теперь скрипты в подавляющем большинстве своем раскиданы по тематическим папкам, многие из них раздроблены для удобства на более мелкие модули, занимающиеся исключительно своим делом и не обращающие внимания на соседей.<br />
Прежде чем я добил шасси, мне пришлось изрядно повозиться с анимацией крена. Анимацию тангажа я сделал с ходу, практически без проблем, как оно выглядит, смотрите в предыдущих постах, там должно быть про анимацию рыска yaw, то жесамое, только используются другие обозначения и и детали самолета. Не оставило особой проблемы справиться и с анимацией стабилизаторов при крене, даже с учетом хитрого механизма отклонения для МиГ-23. У этой машины при стреловидности до 72 половины стабилизаторов работают в режиме "ножницы" с углами отклонения плюс-минус десять градусов. А при максимальной стреловидности они отклоняются на угол шесть с половиной. Решилась эта проблема так...<br />
#Наличие-отсутствие стабилизатора<br /> def is_ROLL(self):<br /> return True<br /> def is_ROLL_Device(self):<br /> ROLL_Device = [<br /> ["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],<br /> ["ElvR_",[0.0087,0.0,0.0],"rotat",-1],<br /> ["ElvL_",[-0.0087,0.0,0.0],"rotat",1],<br /> ["ElvR_",[0.0087,0.0,0.0],"rotat",1]<br /> ]<br /> return ROLL_Device<br /> def is_POINT_ROLL(self):<br /> reper = [-20, 20]<br /> if self.WINGS > 300:<br /> reper = [-13, 13]<br /> #print(reper)<br />
return reper<br />
<br />
<br />
Этот код находится внутри класса-наследника самолета. Из него можно узнать имена деталей. скорость их отклонения за один тик, а также ограничения по отклонению. Первая функция говорит о том, есть ли такие детали в принципе. Втораяя перечисляет детали и их параметры, а вот третья... Как видно из кода, она способна выдать ограничения на угол отклонения. Это, на мой взгляд, проще, чем менять списки для деталей во второй функции. Да и скорость отклонения не меняется.<br />
А вот далее пошла война с интерцепторами. Засада заключалась в том, что рабоать должен ТОЛЬКО один интерцептор, но НЕ ОБА ОДНОВРЕМЕННО. Для элеронов подобной проблемы нет. Они работают аналогично стабилизаторам - смотрим выше.<br />
В общем, перпробовав уйму вариантов, я решение все-таки нашел. Пока один интерцептор не займет нулевое положение, второй работать не будет. Учитываются направление крена или его отсутствие...<br />
if self.ROLLwings < 0 and self.rollSelf == -1:<br /> ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",-1]] <br /> elif self.ROLLwings > 0 and self.rollSelf == 1:<br /> ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",1]]<br /> elif self.ROLLwings < 0 and self.rollSelf == 0:<br /> ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]] <br /> elif self.ROLLwings > 0 and self.rollSelf == 0:<br /> ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]] <br /> elif self.ROLLwings < 0 and self.rollSelf == 1:<br /> ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]] <br /> elif self.ROLLwings > 0 and self.rollSelf == -1:<br /> ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]] <br /> <br /> #print(self.ROLLwings,self.rotatY)<br /> return ROLLwings_Device<br /> def is_POINT_ROLLwings(self):<br /> reper = [-45, 45]<br /> if 299 < self.WINGS < 549:<br /> reper = [-30, 30]<br /> elif self.WINGS > 549:<br /> reper = [0, 0]<br /> #print(reper)<br /> return reper<br />
<br />
Из последней функции можно заметить отключение интерцепторов на максимальном угле стреловидности. Так и должно быть. Хотя можно и просто влепить выдачу False в первой функции. Может, так и сделаю...<br />
Ладно, с анимацией все, переходим к шасси и лазейкам для ИИ.<br />
import bge<br />#print("animat")<br />cont = bge.logic.getCurrentController()<br />own = cont.owner<br />#Импортируем модуль юнита<br />#unit_module = __import__(own.unitModule)<br />scene = bge.logic.getCurrentScene()<br /><br />import sys<br />pathFolder = own.unitName + own.unitNation + "/folderPy"<br />sys.path.append(bge.logic.expandPath("//Aircraft/" + pathFolder))<br />unitmodule = own.unitNode + own.unitNation + "NodeScript"<br />unit_module = __import__(unitmodule) <br /><br /><br />def animat(own):<br /> audioProp = "NULL"<br /> <br /> ########################<br /> #Анимации механики юнита<br /> <br /> #Перекладка крыла<br /> if own.Temp_WINGS != own.WINGS:<br /> wings(own)<br /> <br /> #Шасси<br /> if own.Temp_CHASSY != own.CHASSY:<br /> #Звук<br /> audioProp = "GEAR"<br /> if own.Temp_CHASSY > own.CHASSY and own.CHASSY == 98:<br /> audioGen(own, audioProp)<br /> elif own.Temp_CHASSY < own.CHASSY and own.CHASSY == 2:<br /> audioGen(own, audioProp)<br /> unit_module.CHASSY(<br /> <br />Это часть кода из скрипта анимаций самолета. Он работает для всех машин. Изюминка здесь в том, что отыскивается и импортируется модуль с анимацией шасс по названию, указанному в атрибутах юнита. Смотрим строчки с sys.path и ниже. В импортированном модуле есть не только шасси, но есть и функция корректировки поведения самолета, КОНКРЕТНОГО самолета, в зависимости от разных условий. Смотрим код из этого "нодового" скрипта.<br />
<br /> import bge<br />scene = bge.logic.getCurrentScene()<br /><br />def correctData(own): <br /> <br /> Airbrake = 0.35*own.AIRBRAKE/100<br /> Chassy = 0.25*own.CHASSY/100<br /> own.correctSpeed = 1.0 - Airbrake - Chassy<br /> <br /> if own.Temp_WINGS != own.WINGS: <br /> #Изменения в поведении самолета<br /> changeWings(own)<br /> #Сброс подкрыльевых ьаков, если они есть<br /> if own.WINGS == 2: <br /> sbrosPTB(own)<br />
<br />
Ничего не мешает вставить и сюда в "шапку" импорт модуля искусственного интеллекта из этой же папки. Или более тонко смоделировать поведение КОНКРЕТНО этого типа самолета. Смоделировать что-то вроде национального характера пведения юнитов (есть такая поговорка - есть бойцы, есть военнослужащие, а есть арабы - наши советники на Ближнем Востоке навидались).<br />
В общем, пока чистая бюрократия, картинок новых, извиняюсь, нет. Надеюсь, в Новом году будут.<br />
Еще раз всех с Новым Годом!</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-65150140695944856252017-12-18T22:04:00.000-08:002017-12-18T22:04:13.104-08:00Дао скриптов. Перманентный погром, как оптимизация.<div dir="ltr" style="text-align: left;" trbidi="on">
"Лев Революции", товарищ Троцкий (ставший потом Иудушкой после изменения политичекой еонъюктуры) как-то провозгласил: "Есть у революции начало и нет у революции конца". Это изречение вполне применимо к моему проекту, хотя, если честно, мне гораздо больше нпавится другое его изречение: "И пусть наши враги знают - на всякую принципиальность с их стороны мы ответим абсолютной беспринципностью!" Но что есть - то есть.<br />
В прошлом посте я писал, по поводу "дробления" огромных кусков кода на удобочитаемые скрипты относительно небольшого объема. Что ж, схема обретает свои очертания. Итак, сначала мы используем священное заклинание import sys. А потом перечисляем требующиеся нам пути к папкам с новыми скриптами.<br />
import bge<br /><br />import sys<br />for pathFolder in ["Scene_Scripts","UnitAir_Scripts", "UnitGround_Scripts", "Weapon_Scripts"]:<br /> sys.path.append(bge.logic.expandPath("//Scripts/" + pathFolder))<br />
<br />
Всего-то три строчки привели прямо-таки к тектоническим сдвигам... Постепенно стали исчезать скрипты из пускового бленда, а оставшиеся стали "съехиваться" в размерах, порой всего-то то десятка-другого строчек. А огромные куски кода стали распадаться на мелкие скрипты и скриптики, узкоспециализированные, не интересующиеся жизнью соседей и занимающихся исключительно своим делом.<br />
Сама схема теперь выглядит примерно так. Смотрим по уровням.<br />
<br />
1. Первый уровень, видимый в пусковом бленде и там же находящийся. Я его обозвал скрипт-нод. Например CONTROL_UnitAir. Он отвечает за поведение летательного аппарата, безразлично от того, летит ли он или стоит на аэродроме, изображая мишень. Теперь от огромного скрипта остались только пара десятков строчек. Непрервыно в этом скрипте работает лишь система детализации. При условии, что ЛА летит, из папки со скриптами вызывается скрипт UnitAir_engine.<br />
<br />
def UnitAir():<br /> cont = bge.logic.getCurrentController()<br /> own = cont.owner<br /> <br /> if own.controlUnit != "Statist":<br /> UnitAir_engine.engine(own)<br /> <br /> #Работа уровня детализации идет ВСЕГДА<br /> UnitAir_LODes.LODes(own)<br />
<br />
2. Уровень два. Включает два скрипта. Первый - уровни детализации - смена объектов потомков или замена их мешей в зависмости от расстояния. Все то же, о чем раньше писал, просто вынесено в отдельный скрипт. И второй скрипт - описание поведения ЛА. А вот здесь кроется еще одна ниточка, ведущая на третий уровень. Это приводит к тому, что в начале скрипта взываются модкли анимации, модели сенсоров, модкли ИИ или модуль управления игроком. так сказать, "смерть кощея": "Игла в яйце, яйцо в утке, утка в зайце, заяц в шоке". Ну, или в сундуке, который висит на огромном дубе за тридевять земель...<br />
<br />
3. Уровень три. так сказать, уровень нюансов. Здесь скриптов гораздо больше и лни помельче. Это, к примеру, описание работы сенсоров самолета (РЛС, теплопеленгатор и тд), описание анимации подвижных деталей (рули высоты, элероны и тд), описание поведения самолета при штопоре, модель разрушения и так далее. В свою очередь, некоторые из этих скриптов ведут все дальге и вниз. На четвертый уровень, если надо.<br />
<br />
Вообще главное в этом деле, - точно прописать в вызываемых модулях в "шапке" перед функциями импортирование нужных именно на этом этапе модулей. А поскольку на самом первом этапе мы уже перечислили, какие папки нам нужны, можно обойтись без прописания строчек типа бгеЭспандПатч(чего-то-там). Для профессиональных программистов ничего нового в этом нет, но я-то всего лишь самоучка и пишу эти строчки для таких же, как и я. Ну или позабавить более продвинутых своими героическими штурмами открытх дверей, которых надо просто потянуть на себя, а не пытаться вышибать с налету.<br />
<br />
Тем временем, похоже идет к концу разрешение проблемы с анимацией всех без исклюяения подвижных объектов, за исключением шасси. Мне ранее удалось сделать универсальную функцию работы рулей направления, перекладки крыла, открытия фонаря, выпуска-уборки закрылков и предкрылков.. Вроде я об этом писал, потом напишу еще для элеронов, интерцепторов и рулей высоты. Проблема крылась в том, что у многих машин оно отклоняются на больший или меньший угол в зависмости от скорости или стреловидности. Кажется, теперь понятно, как этого добиться, не сильно напрягая мозг большим количеством строчек кода. Но пока данное "изобретение" нуждается в проверке и об этом напишу в другой раз.<br />
На данный момент неторопливо и осторожно я "растаскиваю" скрипты, не делая слишком резких движений, чтобы не обвалить работающую конструкцию. Надеюсь, в пусковом бленде останется совсем мало скриптов, а все остальное будет лежать в папках, и вытаскиваться не все подряд, а только то, что нужно в данный момент.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-54296484784942418702017-12-12T21:27:00.000-08:002017-12-12T21:27:26.661-08:00Прерву несколько затянувшееся молчание...<div dir="ltr" style="text-align: left;" trbidi="on">
Некоторое время не писал по причине разом свалившихся проблем, как чисто технических, так и отсутствия вдохновения (бывает со мной такое). Сначала пришлось чистить комп от скопившихся файлов - как дублей, так и устаревших и ставших мусором. Потом воевать с модемом от Билайна - складывается стойкое подозрение, что доблестные связисты зажрались, обленились и перестали ловить мышей - хронически слабый сигнал, но трафик тот же и денбги те же. Только качество стало хуже. А я еще на ни ив чем не повинный модем грешил...<br />
Как всегда, как только я начинаю осваивать новые методы программирования, Вечно Голубое Небо посылает конкретный намек: "Верной дорогой идешь, товарищ!", через наших невероятно трудолюбивых энергетиков, которые проводят "плановые реконструкционно-восстановительные работы на объектах энергообеспечения". Для этого им нужен именно декабрь... Впрочем, см. чуть выше. Два вторника подряд нашу улицу ы числе прочих отключали на весь день. Хотелось бы послущать, что об энергетиках в это время говорили, хотя и так понятно...<br />
Тем временем, проект со страшным скрипом опять сдвинулся с места и пополз вперед. Но для начала мне пришлось опять откатиться назад с шейдерами неба. Причина нетривиальная - перестали нормально работать текстовые маркеры объектов - они стали появляться только в верхней половине экрана. После возврашения старого шейдера неба все опять заработало. Ну ладно, в конце концов небо все равно придется переделывать под свой код...<br />
А затем пришло время новшеств. А именно - назрела необходимость "дробления" скриптов и их вызова из папок. Это позволит разгрузить пусковой файл, сделать скрипты более компактными и понятными, правда, их станет больше. Но все-таки можно теперь не крутить скрипт из полторы тыщи строк, отыскивая нужную функцию и корректируя строчки именно в ней. Особено это касается ИИ ботов. Сурипт раздут очень сильно и его придется разбить на множество "типов поведения" - полет по маршруту, ведение боя, взлет, посадка, уклонение от препятствия и так далее.<br />
Пока что не привожу ни картинок, ни кода, поскольку работа в процессе и код меняется очень быстро, скажу лишь, что с подсказки dron-а используется sys.path(путь к папкам). Этот метод планируется запустить в самом начале при старте игры и в него загонять все нужные пути к папкам со скриптами. Прикол еще в том, что теперь "раздробленные" скрипты при вызове сами вызывают дополнительные надстройки - еще дополнительные модули, к примеру скрипт для движения юнита тащит за собой модуль анимации подвижных частей, модули управления 9бот или игрок) и модуль сенсоров.<br />
По идее, в самом пусковом файле останутся "узловые" скрипты, которые и будут дкргать цепочки нужных модулей и не все подряд, а только те, что необходимы. Придется, конечно, повозиться, выстраивая новую систему связей, но оно того стоит...<br />
А, да, сделал было приборные панели МиГ-21, но в кабину пока не вставил - см. выше.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-91969299362526562352017-10-20T06:29:00.000-07:002017-10-20T06:29:08.872-07:00Опытный кролик-2. Новые методы использования классов.<div dir="ltr" style="text-align: left;" trbidi="on">
В прошлом посте я писал о своих опытах с МиГ-23БН, который был моим подопытным кроликом в деле отработки новых методов использования классов.<br />
Тогда речь шла всего-навсего о создании класса-наследника с целью "развести" и не допустить "перекрещивания" данных при создании юнитов.<br />
однако совет dron-a по поводу использования методов в самом классе заставил меня задуматься... когда я только начинал осваивать классы, я уже, сам того не зная, писал в них методы, но не знал, как правильно их использовать. видимо, для осознания новых возможностей и способов требуется время и с "лету" схватывать я таки не способен. Но все-таки медленно, но учусь. Для начала приведу пример с использованием методов теле класса на примере класса уже очень опытного виртуального колика - многострадального МиГ-23БН.<br />
<br />
import bge<br /><br />from ClassUnitAir import UnitAir<br /><br />class MiG23BN_Libya_(UnitAir):<br /> def __init__(self, old_owner):<br /> UnitAir.__init__(self, old_owner)<br /> #Летательный аппарат<br /> def is_AIR(self):<br /> return True<br /> #Подтип ЛA - вртолет, самолет (реактивный или внитовой)<br /> def is_AIRTYPE(self):<br /> return "JET"<br /> #Пилотируемый или БПЛА<br /> def is_DRON(self):<br /> return False<br /> <br /> #Одно- или многодвигательный<br /> def is_MULTIENGINE(self):<br /> return False<br /> def is_CHASSY(self):<br /> return True<br /> <br /> #######################################<br /> def is_CANOPY(self):<br /> return True<br /> def is_CANOPY_Device(self):<br /> CANOPY_Device = [<br /> ["Cnp_",[0.0087,0.0,0.0],"rotat"]<br /> ]<br /> return CANOPY_Device<br /> def is_POINT_CANOPY(self):<br /> reper = [0, 114]<br /> return reper<br /> ########################################<br /> def is_FLAPS(self):<br /> return True<br /> def is_FLAPS_Device(self):<br /> FLAPS_Device = [<br /> ["FlpR_",[-0.0087,0.0,0.0],"rotat"],<br /> ["FlpL_",[-0.0087,0.0,0.0],"rotat"]<br /> ]<br /> return FLAPS_Device<br /> def is_POINT_FLAPS(self):<br /> reper = [-50, 0, 100]<br /> return reper<br /> <br /> <br /> ######################################<br /> #Наличие-отсутствие предкрылков<br /> def is_SLATS(self):<br /> return True<br /> def is_SLATS_Device(self):<br /> SLATS_Device = [<br /> ["SltR_",[-0.0087,0.0,0.0],"rotat"],<br /> ["SltL_",[-0.0087,0.0,0.0],"rotat"]<br /> ]<br /> return SLATS_Device<br /> def is_POINT_SLATS(self):<br /> reper = [0, 40]<br /> return reper<br /> <br /> #####################################<br /> #Наличие-отсутствие крыла изменяемой стреловидности<br /> def is_SWINGWING(self):<br /> return True<br /> def is_POINT_WINGS(self):<br /> reper = [0, 300, 550]<br /> return reper<br /> <br /> def is_WINGS_Device(self):<br /> WINGS_Device = [<br /> ["WngR_",[0.0,0.0,-0.00174],"rotat"],<br /> ["WngL_",[0.0,0.0,0.00174],"rotat"]<br /> ]<br /> return WINGS_Device<br /> <br /> ####################################<br /> #Наличие-отсутствие воздушных тормозов<br /> def is_AIRBRAKE(self):<br /> return True<br /> def is_AIRBRAKE_Device(self):<br /> AIRBRAKE_Device = [<br /> ["ArbUR_",[-0.00783,0.0,0.0],"rotat"],<br /> ["ArbUL_",[-0.00783,0.0,0.0],"rotat"],<br /> ["ArbDR_",[0.00696,0.0,0.0],"rotat"],<br /> ["ArbDL_",[0.00696,0.0,0.0],"rotat"]<br /> ]<br /> return AIRBRAKE_Device<br /> <br /> def is_POINT_AIRBRAKE(self):<br /> reper = [0, 100]<br /> return reper<br /><br /> <br />def mutate(cont):<br /> MiG23BN_Libya_(cont.owner)<br />
<br />
Смотрим на функции типа def is_чегго-то-там. Это не что иное, как некие константы - например наличие или отсутствие крыла изменяемой стреловидности, принадлежности к воздушным юнитам, но эти константы, например, могут нести такую ценную информацию, как скорость поворота крыла и его название. Первоначально я хотел ставить не многомерные списки, а словари. Однако, после многократной ругани со стороны консоли БГЕ о невозможности прочесть из функции класса метод с этим самым словарем, решил все же поставить многомерные списки. Посмотрим, мрожет потом я все-таки пойму, где ошибался и поставлю словари.<br />
Что это дает? Это дакт возможность пока для стандартных анимаций (точнее псевдоанимаций, потому как идет просто движение деталей через скрипт) загнать их работу в единый скрипт, избавив бленды моделей от лишнего кода по тем же перемещениям КИС для МиГ-23. Экономия существенная.<br />
Посмотрим, как это работает. Для начала команда на перекладку крыла из скрипта для игрока CONTROL_gamer.<br />
<br />
#Смена стреловидности - для самолетов с КИС<br /> if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED or keyboard.events[bge.events.TABKEY] == JUST_ACTIVATED:<br /> #Проперти wing у самолетов с КИС имеет минимальное значение 0. При смене стреловидности вызывается класс юнита<br /> #и проводится смена значений максимальных значений скорости и маневренности<br /> if own.FLAPS == 0: <br /> if own.Temp_WINGS == own.WINGS:<br /> if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED:<br /> if own.is_SWINGWING():<br /> own.WINGS += 1<br />
<br />
Сначала при нажатии клавиши идет проверка наличия крыла изменяемой стреловидности у данного юнита. смотрим is_SWINGWING в коде выше.Если там стоит True, то команда проходит и начинается самое интересное. Функция перекладки в скрипте CONTTROL_UnitAir универсальна для всех типов самолетов с КИС. Вообще всех. Такова особенность построения моделей и использования в них названий.<br />
#Псевдоанимации крыльев<br />def wings(own): <br /> #Направоение перекладки крыла<br /> wings = 0<br /> <br /> #Смена стреловидности возможна лишь при убранных закрылках<br /> if own.WINGS > own.is_POINT_WINGS()[-1]: <br /> own.WINGS = own.is_POINT_WINGS()[-1]<br /> elif own.WINGS < 0: <br /> own.WINGS = 0<br /> <br /> #Выставление напрaвления перекладки и убывания-возрастания проперти<br /> if own.Temp_WINGS < own.WINGS: <br /> wings = 1<br /> if own.WINGS-own.Temp_WINGS == 5: <br /> AudioEmitter = scene.addObject('AudioEmitter', own, 220)<br /> AudioEmitter.setParent(own, False, False)<br /> AudioEmitter["audioProp"] = "FLAPS" <br /> <br /> own.WINGS += 1<br /> <br /> elif own.Temp_WINGS > own.WINGS: <br /> wings = -1<br /> own.WINGS -= 1<br /> if own.WINGS-own.Temp_WINGS == -5: <br /> AudioEmitter = scene.addObject('AudioEmitter', own, 220)<br /> AudioEmitter.setParent(own, False, False)<br /> AudioEmitter["audioProp"] = "FLAPS"<br /> <br /> #Собственно, движение крыльев - это действие не зависит от уровня детализации<br /> for key in own.is_WINGS_Device():<br /> if key[2] == "rotat":<br /> own.childrenRecursive[key[0]].applyRotation([key[1][0]*wings,<br /> key[1][1]*wings,<br /> key[1][2]*wings<br /> ],True)<br /> <br /> #Остановка псевдоанимации на кадрах со значением 0,300,550 <br /> if own.WINGS in own.is_POINT_WINGS():<br /> own.Temp_WINGS = own.WINGS<br />
<br />
Здесь используются данные о реперных точках положения крыла - там , где должна прекратиться анимация и жестко выставлены ограничения слева и справа. Это is_POINT_WINGS. А еще используются данные из многомерного списка. я думаю, понятно, как - название детали, скорость поворота, флаг - "поворот".<br />
<br />
Остается добавить, что подобные вещи можно проделать с фонарями кабин, щитками тормозов, закрылками и предкрылками. Потому что все это вещи стандартные и неизменяемые. Они либо есть, либо их нет.полагаю, что можно в общем-то все данные по всем деталям загнать в класс и их использовать. Но пока ограничился вышеперчисленным.<br />
Наверняка, в класс можно вогнать и данные по сенсорам, встроенным пушкам, отстреливаемым ловушкам и прочему. Это дело будущего. По идее, все это приведет к сокращению кода. И упрощению кода тоже.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-49566245138051188572017-10-13T00:59:00.000-07:002017-10-13T01:03:43.883-07:00Опытный кролик и наследство. Классы и новый (небольшой) погром.<div dir="ltr" style="text-align: left;" trbidi="on">
Наконец, я добрался до файлов Упитиса и честнго прочитал их. И даже сумел кое-что понять. Но все это касалось скрипта lum.py, а еще надо было разбираться с другими скриптами - для неба и земли, воды и еще мелочью вроде шумов и тп.<br />
Но тут застарелая проблема все-таки выползла наружу и заявила о себе в полный голос.<br />
Да, я вроде как научился создавать экземпляры классов и более-менее вменяемо могу ответить, зачем - для придания игровым объектам свойств, с помощью которых они реагируют на события. В более широком смысле - создания больших групп объектов, со своими схожими атрибутами (вроде полета) и своими ТТХ (скорость, потолок).<br />
Закавыка, однако, заключалась в том, что класс один на все самолеты и вертолеты, артиллерию, танки и так далее. Юниты отличаются лишь своими ТТХ внутри группы.<br />
Видимо, ручки у меня все же не совсем прямые, потому что время от времени происходило странное "перекрещивание". Например ф-15 у себя искал детали МиГ-29, если верить распечатке консоли. Причем этот прикол начинался после внесения изменений и сохранения пускового файла. Стало ясно, что с этим надо что-то делатью Окончательно мое терпение лопнуло после того, как в миссии с МиГ-23 и Ф-16 "двадцатьтретий" отказался менять стреловидность, а его атрибут WINGS стал равен минус 1, как у Ф-16, хотя в json прописан ноль.<br />
И решил я попробовать наследование классов. Спасибо подсказкам dron-a, вроде получилось.<br />
Для начала я создал еще один json и вынес его в папку с самолетами - это был общий файл для всех летательных аппратов. Приводить я его не буду, нет смысла - нудное перечисление через запятую ключей и их нулевых значений. Но все это привело к резкому сокращению скрипта класса ЛА. За счет выкидывания из него этих ключей - идет чтение json.<br />
<br />
import bge<br />
import json<br />
<br />
with open(bge.logic.expandPath('//Aircraft/AIRCRAFT_CLASS.json'), 'r') as directAircraft:<br />
JSONaircraft = json.load(directAircraft)<br />
<br />
class UnitAir(bge.types.KX_GameObject):<br />
<br />
def __init__(self, old_owner):<br />
self.__dict__.update(JSONaircraft)<br />
<br />
def mutate(cont):<br />
UnitAir(cont.owner)<br />
<br />
Этот скрипт висит в пусковом файле и выдает создание класса для ЛА. А запускает его функция из другого скрипта, в том же бленде.<br />
<br />
def UnitAir(cont):<br />
own = cont.owner<br />
nameGeneral = own.childrenRecursive["confaUnit"]["confaUnit"]["unitClass"]<br />
#unit_module = importlib.import_module(nameGeneral)<br />
unit_module = __import__(nameGeneral)<br />
unit_class = getattr(unit_module, nameGeneral)<br />
x = unit_class(own)<br />
<br />
Тут была возня с поиском и импортом нужного скрипта класса-наследника из другого бленда (который загружается при запуске игры - файл модели). К моему удивлению импортлиб скрипт не нашел. А вот поставленная из любопытства строчка с __impot__ом помогла. В скрипте ищется файл с именем класса, который прописан в объекте-потомке confaUnit. Этот объект своеобразный пакет в командирском сейфе - в случае начала БД его вскрывают и читают, что надо делать. Потом уничтожают.<br />
В качестве подопытного кролика был выбран МиГ-23БН. Но это скорее уже опытный кролик - сколько на нем всего опробовалось... Скрипт класса-наследника выглядит так:<br />
<br />
import bge<br />
<br />
from ClassUnitAir import UnitAir<br />
<br />
class MiG23BN_Libya_(UnitAir):<br />
def __init__(self, old_owner):<br />
UnitAir.__init__(self, old_owner)<br />
#self.__dict__.update(JSONaircraft)<br />
<br />
def mutate(cont):<br />
MiG23BN_Libya_(cont.owner)<br />
<br />
Скрипт содержится в стороннем файле и надо обязательно вызвать сначала класс-родитель - ClassUnitAir. Точнее скрипт, а из него выдернуть сам класс - UnitAir.<br />
Схема эта сработала и я кинулся вносить строчки с названием класса в json. После чего опробовал МиГ-23МФ и Ф-16. На сей раз никто не искал несуществующие детали и механика работала нормально.<br />
А теперь вот мыслю, пока припрятать наследование класса для дальнейшего использования. А классы наследники сделать самостоятельными. для этого надо слегка поменять аргументы в скриптах и ввести чтение json. Поскольку на мой взгляд, наследование здесь не имеет пока особого смысла. Но оно может пригодиться в других местах и полученный опыт никогда не бвает бесполезным.<br />
Да и попробую резко сократить файл вызова и старта самого класса, если их можно отыскивать и грузить по именам.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-4264114132648797892017-10-09T00:09:00.000-07:002017-10-09T00:09:01.923-07:00Отработка действия БЧ снарядов на юниты. Наведение артиллерийских орудий. <div dir="ltr" style="text-align: left;" trbidi="on">
В двух предыдущих постах я писал про взаимодействие снарядов и юнитов. Точнее, воздействие поражающих факторов на сами юниты. Было это все чисто умозрительно и пока не было воплощено на практике, так и оставалось у меня в голове.<br />
Скрипт работы снаряда, точнее, его БЧ (боевой части) был собран не сразу. Точнее, не скрипт, а функция в скрипте CONTROL_Weapon. А когда она была написана, то пришлось поломать голову, почему оно не работает (ну как всегда - там опечатка, там не то имя указал). А главной причиной была нихкая точность - снаряды рвались уж очень далеко от зенитки. Я использовал миссию теста МиГ-23БН для проверки работы НАР С-8 по зенитке GDF-001 Oerlicon. Выяснилось, что прицельная сетка на самолете не соответствует дальности полета НАР. Пришлось лезть в файл кабины и корректировать ее положение. Теперь, видимо, ту же процедуру надо провести и в остальных машинах. Ничего не поделаешь - до НАР и пушек я просто до сих пор еще не добирался - руки просто не доходили.<br />
Но в конце концов скрипт заработал полностью в том виде, какой он сейчас есть. Необходимо дописать проверку лучом от эпицентра взрыва до юнита на наличие препятствия (укрытия) на пути ударной волны и осколков. Приведу текст функции для БЧ типа "осколочно-фугасная":<br />
def OskolFugas(own):<br /> <br /> for units in ArbitrGame.UNITS[1]:<br /> #Проверяем наличие атрибута повреждений у объекта - ударной волне и осколкам все равно, что разрушать<br /> if hasattr(units, "crash") == True:<br /> #В зависимости от расстояния подрыва рассчитывается величина поражающего фактора<br /> if own.radiusExplode*1.5 < own.getDistanceTo(units) < own.radiusExplode*3:<br /> own.LEVELS_CRASH = 0.4*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units)<br /> elif own.radiusExplode < own.getDistanceTo(units) < own.radiusExplode*1.5:<br /> own.LEVELS_CRASH = 0.8*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units) <br /> elif own.getDistanceTo(units) < own.radiusExplode:<br /> own.LEVELS_CRASH = own.levelsCrash<br /> <br /> #Вычисление нанесенного урона<br /> if own.LEVELS_CRASH > units.levelDefens:<br /> #При условии, что защита "пробита", смотрим на уровень повреждений юнита и вносим коррективы<br /> if own.LEVELS_CRASH/units.levelDefens < units.crash:<br /> units.crash -= own.LEVELS_CRASH/units.levelDefens<br /> else:<br /> units.crash = 0.0<br /> print(units, units.crash, own.getDistanceTo(units), own.LEVELS_CRASH)<br />
<br />
Последнюю строчку можног не принимать во внимание, я ее потом закомментирую или вообще уберу. В ней я отсматривал работу функции. В принципе, все остальные функции типа "проникающе-фугасной", "Фугасной" и прочей БЧ будут работать так же. Разница будет заключаться лишь в градации расстояния для ослабления действия снаряда.<br />
А вот ненаписанные пока функции для кумулятивных БЧ и бронебойно-подкалиберных снарядов будут вести себя по-другому. Там не нужен цикл перебора юнитов сцены - цель одна-единственная и вступают в действие такие факторы, как толщина брони, угол встречи с броней, наличие динамической защиты и ее тип... То же самое, кстиати, относится к пулям стрелкового оружия - они действуют примерно по тому же принципу - скорость, калибр, плюс дальность выстрела. Мда, придется еще как-то по бронепробиваемости в зависимости от дальности , с которой был сделан выстрел, что-то придумывать...<br />
И в конце об артиллерии. Здесь я приведу скрипт работы артиллерийского орудия. На данный момент на нем работает "Эрликон", но там еще надо смотреть по его точности - в скрипт введен разброс снарядов и ошибки прицеливания. Весьма вероятно, что с этим я переборщил...<br />
import bge<br />import json<br />import sys<br />import random<br /><br />cont = bge.logic.getCurrentController()<br />own = cont.owner<br />scene = bge.logic.getCurrentScene()<br />ArbitrGame = scene.objects["ArbitrGame"]<br />#Импортируем модуль юнита<br />#unit_module = __import__(own.unitModule)<br />t = 0.0<br />xS = 0.0<br />yS = 0.0<br />zS = 0.0<br />S = 0.0<br /><br />newPos = [xS, yS, zS]<br />import CONTROL_Operations<br />import CONTROL_Sensor<br />import mathutils<br /><br />scene = bge.logic.getCurrentScene()<br /><br /><br />def UnitArtillery():<br /> cont = bge.logic.getCurrentController()<br /> own = cont.owner<br /> <br /> ################################################################<br /> #Просчет уровней детализации<br /> if own.maxVisibleDist < own.getDistanceTo(scene.active_camera):<br /> own.levelsDetails = 2<br /> elif own.maxVisibleDist/10 < own.getDistanceTo(scene.active_camera) < own.maxVisibleDist:<br /> own.levelsDetails = 1 <br /> elif own.maxVisibleDist/10 > own.getDistanceTo(scene.active_camera):<br /> own.levelsDetails = 0<br /> #print(own.getDistanceTo(scene.active_camera))<br /> <br /> #Работа с детализацией <br /> if own.Temp_levelsDetails != own.levelsDetails:<br /> CONTROL_Operations.levelsDetailArtillery(own)<br /> own.Temp_levelsDetails = own.levelsDetails<br /> <br /> if own.crash > 0.1: <br /> if own.typeMissions != "":<br /> if own.statusBattery == "Commander":<br /> own.timerTargetChanged += 1<br /> if own.timerTargetChanged == 600:<br /> targetChangedOwn(own)<br /> own.timerTargetChanged = 0<br /> if own.targetID != "":<br /> Ballistica(own)<br /> <br /> #Блокировка возможности стрельбы при отсутствии бокомплекта<br /> if own.BK == 0:<br /> own.shoot = 0<br /> own.targetID = ""<br /> own.PR = 0<br /> <br /> #Разрешение на открытие огня<br /> if own.PR == 1:<br /> own.shoot = 1<br /> else:<br /> own.shoot = 0<br /> <br /> #Стрельба<br /> if own.shoot == 1:<br /> shooting(own) <br /> #Остановка стрельбы<br /> if own.Temp_shoot != own.shoot:<br /> if own.Temp_shoot == 1:<br /> own.Temp_shoot = own.shoot<br /> <br /> <br /> <br />#Функция выбора ближайшей цели. Используется для зениток и стрельбы прямой наводкой<br />#по конкретной цели, для стрельбы с закрытых позиций по площадям и квадратам <br />#используются координаты<br />def targetChangedOwn(own):<br /> if own.typeMissions == "AntiAircraft":<br /> own.targetType = 0<br /> sceneObjList = ArbitrGame.UNITS[0][own.targetType][own.target]<br /> tempSortedList = []<br /> if len(sceneObjList) > 0:<br /> tempsortedList = sorted(sceneObjList, key = lambda obj:own.getDistanceTo(obj))<br /> #print(tempsortedList)<br /> own.targetID = str(id(tempsortedList[0]))<br /> else:<br /> own.PR = 0<br /> own.targetID = ""<br /><br />#Расчет точки упреждения <br />def Ballistica(own):<br /> <br /> try:<br /> target = scene.objects.from_id(int(own.targetID))<br /> #Тип прицеливания - отслеживание самой цели<br /> if own.typeCoordTarget == "AimingTarget":<br /> newPos = target.worldPosition<br /> #Тип прицеливание с упреждением<br /> if own.typeCoordTarget == "VisibleTarget":<br /> # Рассчитываем время полета снаряда. <br /> t = own.getDistanceTo(target) / own.speedBullet <br /> # Рассчитываем путь перемещения объекта за это время. <br /> S = target.worldLinearVelocity * t<br /> <br /> xS = target.worldPosition[0] + S[0] + random.randrange(-own.razbros,own.razbros)<br /> yS = target.worldPosition[1] + S[1] + random.randrange(-own.razbros,own.razbros)<br /> zS = target.worldPosition[2] + S[2] + abs((-10*t*t)/2) + random.randrange(-own.razbros,own.razbros)<br /> newPos = [xS, yS, zS]<br /> #print(target)<br /> Turret(own, newPos)<br /> except:<br /> own.PR = 0<br /> own.targetID != ""<br /><br />#Управление наведением в горизонтальной плоскости <br />def Turret(own, newPos):<br /> #print(own.PR)<br /> Turret = own.childrenRecursive[own.unitName + "Turret_"]<br /> #Данные по ориентации <br /> orientY = Turret.getVectTo(newPos)[2][1]<br /> orientX = Turret.getVectTo(newPos)[2][0]<br /> <br /> #Скорость наведения<br /> kZ = own.speedMaxGor<br /> <br /> correct = 1.0<br /> znak = 0<br /> <br /> if orientX > 0:<br /> znak = -1<br /> elif orientX < 0:<br /> znak = 1<br /> <br /> if orientY > 0.9:<br /> correct = 1 - orientY<br /> else:<br /> correct = 1.0 <br /> <br /> Turret.applyRotation([0.0,0.0,kZ*correct*znak], True) <br /> Canon = own.childrenRecursive[own.unitName + "Canon_"]<br /> orientZ = Canon.getVectTo(newPos)[2][2]<br /> kX = own.speedMaxVert<br /> correctCanon = 1.0<br /> znakCanon = 0<br /> <br /> if orientY > 0.7:<br /> if orientZ > 0:<br /> znakCanon = 1<br /> elif orientZ < 0:<br /> znakCanon = -1<br /> <br /> if abs(orientZ) < 0.2:<br /> correctCanon = abs(orientZ)<br /> else:<br /> correctCanon = 1.0<br /> <br /> Canon.applyRotation([kX*correctCanon*znakCanon,0.0,0.0], True)<br /> <br /> if abs(orientZ) < 0.1 and orientY > 0.9 and own.getDistanceTo(newPos) < own.distMax:<br /> #print(own.getDistanceTo(newPos))<br /> if own.Temp_cassetteBK < own.cassetteBK:<br /> own.PR = 1<br /> <br /> else:<br /> own.PR = 0<br /> else:<br /> own.PR = 0<br /> <br /> #Перезарядка - магазин, кассета, обойма или снаряд<br /> if own.Temp_cassetteBK == own.cassetteBK:<br /> own.PR = 0<br /> if own.Temp_cassetteBK > 0:<br /> if own.Temp_timerCassetteChanged < own.timerCassetteChanged:<br /> own.Temp_timerCassetteChanged += 1<br /> elif own.Temp_timerCassetteChanged == own.timerCassetteChanged:<br /> own.Temp_timerCassetteChanged = 0<br /> own.Temp_cassetteBK = 0<br /> <br /> <br /><br /> <br />def shooting(own):<br /> #Стрельба из пушек, пулеметов и многоствольных систем<br /> if own.typeShoot == "Zalp":<br /> for FLAME in own.fireGun:<br /> addBullet(own, FLAME)<br /><br /> #Стрельба из РСЗО, РБУ и прочих реактивных(ракетных) систем<br /> elif own.typeShoot == "Paket":<br /> FLAME = own.fireGun[own.BK-1]<br /> addBullet(own, FLAME)<br /> <br /> own.BK -= 1<br /> own.Temp_cassetteBK += 1<br /><br />#Функция добавления снаряда и прочего - звука, вспышки... <br />def addBullet(own, FLAME):<br /> Canon = own.childrenRecursive[own.unitName + "Canon_"]<br /> #Вспышка - лампа<br /> vspyshka = scene.addObject('LampUniversal', Canon, own.gunPausa+1)<br /> vspyshka.setParent(Canon, False, False)<br /> vspyshka.localPosition = FLAME<br /> #Вспышка - меш<br /> vspyshkaMesh = scene.addObject('AudioGun', Canon, own.gunPausa+5)<br /> vspyshkaMesh.setParent(Canon, False, False)<br /> vspyshkaMesh["audioProp"] = own.audioGun<br /> vspyshkaMesh.replaceMesh("FireUniversal", True, False)<br /> vspyshkaMesh.localPosition = FLAME<br /> razmer = own.scaleGun<br /> vspyshkaMesh.worldScale = [razmer, razmer, razmer]<br /> vspyshkaMesh.visible = 1<br /> <br /> #Снаряд цепляется к самому юниту и происходит мутация<br /> #Проверяется наличие атрибута вроде калибрСнаряд = 23мм <br /> #и объект мутирует в своем классе <br /> dynObj = scene.addObject('UniversalBullet',vspyshka , 300)<br /> dynObj.worldScale = [1.0,1.0,1.0]<br /> if "calibr" not in dynObj:<br /> dynObj["calibr"] = own.gunBullet <br /> dynObj.localLinearVelocity = [random.randrange(-own.randomSpeed,own.randomSpeed),<br /> random.randrange(-own.randomSpeed,own.randomSpeed)+own.speedBullet,<br /> random.randrange(-own.randomSpeed,own.randomSpeed)] <br />
<br />
Пост, конечно, длинноватый получился, да и сами скрипты сыроваты, хотя и работают. Но как "история развития", может, кому-то и будет интересно. А то и полезно. В завершение приведу скрин работы "Эрликона". Пушка смотрит в сторну цели , вот только пока она еще безобидна. Надеюсь, ненадолго. Освещение, конечно еще то... Все никак не начну ковыряться в скриптах упитиса, чтобы понять, как писать шейдер... Ну, не все ж сразу...<br /><br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-AZdSBRRAN3A/Wdse3pC3hCI/AAAAAAAAAZE/qO0MZw8rWxYC4CHd72cpWKfmMlTSr7lEQCLcBGAs/s1600/Oerlikon.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="640" height="480" src="https://2.bp.blogspot.com/-AZdSBRRAN3A/Wdse3pC3hCI/AAAAAAAAAZE/qO0MZw8rWxYC4CHd72cpWKfmMlTSr7lEQCLcBGAs/s640/Oerlikon.jpg" width="640" /></a></div>
<br />
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-87415239232418026072017-10-02T06:37:00.000-07:002017-10-02T06:37:36.149-07:00Размышления о наземных объектах-2. Как бы все это разрушить...<div dir="ltr" style="text-align: left;" trbidi="on">
Как обычно, после перерыва в использовании блога по причине израсходованого трафика, узнаешь много нового... Так, сегодня freenome отказался пускать меня в мой же блог. Это было для меня в первый раз, но не первый раз в Сети. быстро пробежавшись по сети, нашел упоминания о подобном саботаже, причем не только freenome, но и других хостингов. Ну раз так, то вникать я в причины блокировки не стал. Это достаточно хроническое явление для бесплатных доменов и у меня нет времени и сил, чтобы вникать в тонкости взаимоотношений сайтоы, блогов и хостингов. спросил у dron-а, что с этим делать и утвердился в мысле, что надо просто убрать использование домена из настроек. Надеюсь, получилось и эти строчки могут прочитать все остальные.<br />
Несколько дней я пытался нащупать хоть какую-то схему воздействия на игровые объекты. Задача состояла в том, что разные типы боевых частей по-разному воздействуют на объекты. В конце концов, пришел к выводу, что объект должен обладать неким атрибутом, назовем его "уровнем боевой устойчивости". Или crashDefens, как-то так. Сей уровень характеризуется неким порогом, ниже которого поразить объект оружием с низким ТТХ невозможно. Например, как-то натолкнулся на вопрос:"Может ли ЗУ-23-2 остановить танк?". Ответ воевавшего в Чечне танкиста был таков: "Может. Если экипаж танка ее заметит и остановится. Для прицеливания..."<br />
Вывод:<br />
В оружейные ТТХ надо вводить уровень "преодоления защиты", эффективности, что ли, так скажем.<br />
И тут начинается самое нитересное. Нужен какойто эталон, от которого можно отталкиваться. И в качестве эталона была выбрана бомба ФАБ-100. Известно, что ее радиус поражения (сплошной) составляет 12 метров. Вообще в самих бомбах где-то половина веса - это металл, остальное - взрывчатка. Ну что ж, берем 50 кг за 0.1 . Извращение, конечно, ну ладно, в конце концв изготовлять эталон веса или длины мне не надо. Примем за основу. С бомбами далее оказалось проще - 28 метров для ФАБ-250 и 40 - для полутонных бомб. исходя из этого я и правил показатель...<br />
предполагаю, что внутри этого радиуса сплошного поражения наземные юниты уничтожаются, если, это, конечно, не бункер с уровне защиты 0.5. Тому же "Абрамсу" будет фиолетово, что упало ему на башню - ФАБ-100, 250 или ОФАБ-250. А вот потом...<br />
На расстоянии двойного радиуса поражения эффективность оружия падаеь вдвое, еще дальше - вчетверо. Но, если рядом с "Абрамсом" в момент взрыва стоял еще один танк и БМП в зоне падения ущерба, то порог воздействия на танк преодолен не будет, а вот БМП может и выйти из строя после попадания осколков и удара взрывной волны.<br />
Понимаю, сии измышления неплохо бы подкрепить кодом, но был занят - срочно вписывал в json бомб и УРВВ данные об их могуществе и типе БЧ.<br />
Прикол заключается в том, что БЧ ракет "воздух-воздух" на три четверти состоят из поражающих элементов и только четверть забирает взрывчатка. В общем-то опять натягивание совы на глобус получается, но вменяемой и систематизированной информации по этому вопросу нет. нет, найти массу БЧ ракет не проблема, но далеко не всегда указывается вес ВВ. Ну ладно, решил так сделать.<br />
Объем работ оказался приличный. А впереди еще ракеты "воздух-земля"...<br />
Попутно выяснил еще ое-каие вещи. Например, что ракета Р-27ЭМ заточена под перехват КР "томагавк" или ПКР "Гарпун". А вот ракета Р-27ЭП специализируется на выбивании постановщиков помех. Это дело надо учесть. Еще ракетами типа Р-77, Р-73, Р-77 и AIM-9X можно организовать персональное ПРО, отстреливая пущенные по тебе ракеты.<br />
Также попалась серия интересных статей на Афтершоке по авианосцам и крылатым ракетам. Выяснилось, что многие, так называемые эксперты не знают некоторых элементарных вещей. Например, среди поклонников западной военной мощи бытует мнение, что ПКР российских кораблей посшибают ракетами и пушками "Хорнеты", причем за считанные минуты, а потом наши корабли будут расстреляны, как в тире. Ну-ну... Другие, наоборот, впадают в крайность и яростно отмахиваются от необходимости иметь свои авианосцы для обеспечения ПВО кораблей. Но тут есть свои нюансы...<br />
Недавно на Дальнем востоке прошли учения по отражению удара КР с моря силами ВВС и ПВО. Причем с боевыми стрельбами. и хотя цели были поражены, выяснилась одна настораживающая деталь. МиГ-31 дал залп двумя ракетами по ПКР "Гранит", шедшей по маршруту и попал. Цель была изрешечена, но невозмутимо продолжала себе лететь. Последовал второй залп. Только после этого ПКР удалось свалить. все дело в том, что у "Гранитов2 есть бронирование, защишающее важные узлы и сбить ее не так то просто. На одну ракету ушло 4 Р-33 - весь БК одного МиГ-31. "Хорнеты" могут стрелять AIM-120, БЧ которых сдабее больше чем в два раза. Итого, ладно, с натяжкой будем считать - 6 АМРААМ на один "Гранит". Но в залпе 20 ПКР. И кроме них, кое-что еще помельче. По поводу пушек - даже не смешно - отстрел "Гранитов2 на полигоне из 20-мм пушки не дал НИЧЕГО. Поразить эту ПКР может лишь 30-мм пушка, да и то с рядом условий.<br />
Придется ставить уровень боевой устойчивости еще и на оружие... Добавлю, что для надежного уничтожения ПКР нужен ЗРК, но обнаружить подлетающую над волнами ПКР он может не так далеко, есть еще время реакции, что приводит к тому, что ЗУР стартует, когда расстояние до корабля для ПКР уже меньше километра (а иногда сильно меньше). И есть еще мертвые зоны, и есть бортовая РЭБ самой ракеты и есть увеличенная точность попадания в уязвимые места.<br />
Короче, морской бой, если я до него доберусь, обещает быть очень интересной задачкой.<br />
Ладно, вернемся к нашим баранам, то бишь наземным объектам. Действие фугасных и осколочно-фугасных бомб и снарядов я описал выше. Все зависти от расстояния до окружающих объектов.<br />
Но есть подкалиберные снаряды и кумулятивные снаряды. Вот для них радиус поражения нулевой. Они втыкаются в броню конкретного танка, БМП или БТР, "не отвлекаясь" на окружающее. Если при взрыве фугаса надо циклом перебирать все объекты-юниты по расстоянию, то в этом случае просто идет проверка условий по единственной цели - в которую снаряд попал. Тут уже вступает в строй условие наличия-отсутствия динамической защиты и угла встречи с броней, если таковая есть и имеются данные по ее толщине.<br />
На b3d.ua есть WIP от exooman-a, который занимается танками, в отличие от меня. Он тоже всячески рассчитывает подобные мелочи, у него тоже есть ДЗ, толщина и угол брони и много чего...<br />
Надо добивать ракеты класса "воздух-земля", дописывая для них json, в основном с оружием будет закончено, но остаются еще зажигательные баки, кассеты, контейнеры с суббоеприпасами и тому подобная мелось. И такие извращения, как "радость танкиста" - кассеты с самоприцеливающимися боевыми элементами, опускающимися на парашютах и прошибающими крыши танков и прочей техники кумулятивной струей.<br />
А еще шейдеры и карта высот, которую я еще не успел поновой закачать у denis8424 (движение по карте высот без физики). Разные интересные мысли крутятся, однако... Вроде полногог отказа от физики, кроме снарядов и бомб. В ракетах же отказался и ничего, все нормально...</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-37579104926926362782017-09-23T05:03:00.000-07:002017-09-23T05:03:28.430-07:00Размышления о наземных объектах.<div dir="ltr" style="text-align: left;" trbidi="on">
Не далее, как вчера удалось добиться добавления и работы уровней детализации артиллерийскогог юнита. Это новый вид юнитов - вкючает в себя буксируемые артсистемы. Как зенитки, так и гаубицы, возможно, пусковые установки ракет стационарных ЗРК и тому подобное.<br />
Впереди введение САУ, танков , легкой БТТ и просто автотехники.<br />
И возникает новый спектр проблем. Пока не будем касаться ИИ, потому как "слона едят по кусочкам".<br />
Пока рассмотрим проблемы работы уровней детализации.<br />
На данный момент уровней детализации 4.<br />
Недетализированный. За пределами дистанции различимости юнита (атрибут maxVisibleDist) все меши всех деталей юнита меняются на плейны.<br />
Малодетализированный уровень действует в пределах от половины до максимальной величины maxVisibleDist. Я обзываю меши, на которые меняются меши деталей юнита ЛОДами (хотя это не совсем верно, строго говоря, ног привык уже). По сути своей - это очень грубые объекты, скорее напоминающие силуэты.<br />
Среднедетализированный уровень - в пределах от одной десятой до половины maxVisibleDist. Для этого уровня используется, как правило уже вполне высокопоигональный меш для деталей. Но есть нюанс. Нет большого количества мелких деталей вроде закрылков, рулей, элеронов и прочего для самолета.<br />
Высокодетализированный уровень - весь самолет в сборе со всеми мелкими деталями - на расстоянии менее одной десятой maxVisibleDist.<br />
Зачем это надо? БГЕ сильно не любит большого количесива объектов, поэтому необходимо всячески соблюдать правило: "Краткость - сестра таланта". Не так много юнитов будет так близко от камеры, да и перескоки с одного уровня на другой не так уж чтобы частые.<br />
Интересно, что для вновь созданных артиллерийских юнитов отсутствует последний - высокодетелизированный уровень. а вот для самодвижущихся объектов он обязателен. Причина в том, что у танков , БТР-БМП, автомашин и прочего имеются колеса и иногда - гусеницы.. И они должны крутиться.<br />
Вариантов несколько.<br />
Первый - объект типа "гусянка+катки" - единый и неподвижный для среднего уровня. Для выского уровня добавляем родителя с катками и гусеницами в товарном количестве. Самый простой, кстати, вариант.<br />
Вариант два. Ничего не добавляем, ибо есть UV-скроллинг. Мы его просто запустим, когда нужно и остановим, когда нет нуждя. Лично я склоняюсь именно к этому варианту.<br />
Однако у тех же танков есть фары, поручни, патрубки и всякая мелочь, которая тоже нужна. Ну вот, значит, ее тоже добавлять и убирать. Как уже говорилось выше, смена уровней детализации - явление нечастое. Да, можно и без этого, но иметь про запас не помешает - как не помешает законсервировать часть кода в скрипте на будущее.<br />
И тут вдруг выползает проблема вроде блоков ДЗ и тех же бортовых экранов. Срываемых снарядами, к примеру. Удалять вершины мешей нельзя в реал-тайме. Но их можно двигать. нароимер, "схлопнув в одну точку (точнее придав нулевые координаты всем вершинам) того же блока ДЗ. Проблема только в том, как вытащить НУЖНЫЕ координаты. Просто я достаточно мало работал с мешами и пока не умею этого. Надо просто почитать АПИ и найти нужное.<br />
Вторая вещь, которую тоже придется решать - взаимодействие снаряда и брони. По-видимому, здесь придется действовать через списки координат вершин, "выбрасывая" отработавшие координаты, пбразуя "ослабленные" участки. Пока толкои еще сам не понимаю - мысль нуждается в провеоке практикой. Предположительно, имеем "слепок" брони объекта, который вызыается при попадании снаряда. Снаряд проверяет функцией ray координаты полигона, ищет его среди "пробитых" в списке. Если он там есть - пробитие. нет - добавляет его в этот список, не поражая юнит.<br />
Понимаю, несколько заумно, да и кода нет пока, все на уровне мыслей. Но хоть что-то. К сожалению, БГЕ не позволяет как-то маркировать полигоны и вершины - тут надо полагаться на координаты. Да еще и научиться с ними работать.<br />
Сегодня вытащил из первой версии скрипты наведения артиллерии и ЗРК (кстати, к стрельбе с применением упреждения приложил руку denis8424, приер турели у него в блоге есть).<br />
Пока что отработано добавление и работа уровней детализации зенитной спаренной автоматической 35-мм пушки "Эрликон" GDF-001, Я рассчитываю на еще тр варианта такой пушки. В том числе и роботизированный 005. Кстати, этот робот отличился в ЮАР - попросту говоря, взбесился и принялся палить во все стороны. Были убитые и раненые и сожженная техника. К счастью, давить клаву, моделируя работу артсистемы, совершенно безопасно.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com1tag:blogger.com,1999:blog-1904509602056652692.post-16090218697498208912017-09-20T23:44:00.000-07:002017-09-20T23:44:39.432-07:00Сбывшееся пророчество.<div dir="ltr" style="text-align: left;" trbidi="on">
https://www.youtube.com/watch?v=86U4EUtIklk<br />
<br />
Более десяти лет тому назад на основе реальных событий, а именно, последнего боя 6-ой роты десантников под Улус-Кертом в ходе Второй Чеченской был снят четырехсерийный фильм "Честь имею!". В этом фильме нет лихих суперменов, кладущих направо и налево толпы врагов и не получающих при этом ни одной царапины. Зато есть много чего другого. Например - приказ начальства выполнить вылет в интересах миссии ОБСЕ, когда вертолеты нужны для эвакуации зажатой в ущелье разведгруппы. Или когда приехавший в родной Питер офицер, сопровождавший гроб с телом своего солдата (погибшего как раз из-за этого приказа) видит, как мать, потерявшая единственного сына, сходит с ума. Или когда старик-чеченец, у которого задавили последнего барана, приходит к солдатам жаловаться и те дают ему в качестве компенсации ящик консервов, после чего старик, когда-то служивший в Советской Армии, приходит к десантникам при полном параде - в дембельском кителе, и угощает их мясли того своего последнего барана и предупреждает о готовящейся "духами" провокации. Он просто не хотел зачистки своего села, которая неизбежно случилась бы в этом случае. Его так и нашли - в этом кителе, повешенным, на следующее утро...<br />
Еще в Питере один из главных героев с "той стороны", молодой чеченский учитель, ставший боевиком по той причине, что ему нечем было платить калым родителям своей невесты, подвергся нападению местных нациков. Вчетвером на одног7о, там, где не стреляют - очень комфортно для таких лбов - в армию они не пошли, ведь там же в Чечню послать могут.. И если бы не тот самый капитан-десантник, которого играет Александр Лазарев-младший, быть бы молодому педагогу как минимум изувеченным...<br />
Они встретились потом, в бою. И чеченец не смог выстрелить в своего спасителя. За что получил пулю от своего командира (и родственника, между прочим).<br />
В конце фильма - тайминг по ссылке - 42-24, звучит монолог командира полка, которого играет Юрий Цурило. Тогда, в 2004, это казалось несбыточной мечтой.<br />
-Верю, будем жить по-другому. Будем воевать по-другому. Будет другая страна, другая армия. Может, и воевать не придется...<br />
Воевать все же пришлось.<br />
Вчера, 20 сентября 2017 года завершилась операция по разгрому наступавших террористов в провинции Идлиб. В сети уже полно роликов с кадрами методичного расстрела бандитов и их техники. Но все это началось раньше, гораздо раньше - в 2015. Сколько было тогда криков о том, что и "крестик не там", и крылатые ракеты у России кончились и исправных Ту-160 всего один. Потом пошли крики о школах и больницах, которые почему-то либо не подтверждались, либо внезапно выяснялось, что их бомбили американцы. А потом пошли уже сообщения об эвакуации командиров террористов вертолетами ВВС США (пока не подтвержденные) или удары по сирийской армии...<br />
Война в Сирии давно перестала быть гражданской, она превратилась в завуалированную интервенцию Запада. слишком много интересного находится в самой Сирии, или вокруг нее. Например залежи нефти и газа в прибрежной зоне. Или удобное положение для прокладки газопровода из Катара в Европу. Хотя тому же Катару вполне можно было бы договориться с Башаром Асадом. Тем более, что сейчас Катар налаживает отношения с другим союзником сирии Ираном. Что мешало ему это сделать раньше?<br />
Однако вернемся к словам героя Юрия Цурило. Да, теперь мы воюем по-другому. И учим, вполне неплохо учим этому своих союзников. Как оказалось, сирийцы вполне обучаемы. Просто учителя разные бывают. Американцы учили грузин - итог известен, что бы там ни говорили. Американцы учили умеренных бандитов из "Новой сирийской армии" воевать с неумеренными - ИГИЛ (запрещена в Росии). Итог - печальный - оглушительный провал наступления НСА с публичным отрезанием голов пленным. До сих пор курды не могут взять Ракку, при этом делая все, чтобы восстановить против себя тамошнее арабское население. Впрочем, курды, веря, что "Америка с нами", с успехом восстанавливают против себя всех подряд, не только арабов и турок, от которых они действительно натерпелись...<br />
По странному совпадению, атака в идлибе приключилась как раз к Генассамблее ООН. По странному совпадению, в окружение попал российский взвод военной полиции. Итог боя - деблокирующая группа пробила кольцо окружения. Наши потери - трое раненых. Потери террористов - более 850 человек. Пока нечсны цифры потерь союзников-ополченцев. Но, судя по тому, что вылазку ждали и большая часть нападавших легла еще на подходе, они не должны быть большими. Тут есть еще пара интересных моментов.<br />
Деблокадой руководил офицер, когда-то штурмовавший здание совмина в Грозном в 1995 году. Что это такое, те, кто хотя бы поверхностно знаком с историей Первой Чеченской, означает - поймут. Про ветеранов я уж и не говорю - они все это вынесли на себе.<br />
Так вот, этот офицер трижды ездил в Москву за честног заработанной им Звездой Героя. Только на третий раз Ельцин оказался "здоров" и соизволил награду вручить.<br />
Второй момент - поведение боевиков в момент расстрела их колонны. Они бестолково мечутся по открытой местности и их методично выбивают одного за другим. Это не в первый раз. Притом, что они вполне грамотно воюют против сирийской армиив ряде случаев. Объяснение простое - в их распоряжении есть Гугл-мап и дроны. Они тщательно изучают место своего будущего удара, но не уделяют внимания запасным вариантам развития событий. К примеру, не изучают местность в районе маршрута движения. Оказавшись в незнакомой местности, они теряются и становятся легкой добычей. Пожелаем же им и дальше быть такими умными.<br />
<br />
P.S. На брифинге представитель нашего МО, Рудской открыто обвинил американцев в подготовке и организации этого провального для террористов наступления. Также поползли слухи, что в рядах наступавших был американский спецназ и какое-то количество американцев так и осталось лежать среди трупов боевиков. Конспирология, конечно, но были слухи о захвате взбунтовавшимися боевиками в Алеппо иностранных военных советников. По странному совпадению, пришли сообщения о неудачном десанте американского спецназа на Табию (если я правильно понял), которое боевики ИГИЛ отбили. А был ли десант именно там?<br />
Пентагон уже начал открещиваться от своего участия в этом авантюрном наступлении, но тут на ум приходит цитата из книги "Штормовые стражи" Виктора Северова:<br />
-Американцу верь наполовину, американскому солдату на четверть, цэрэушнику не верь никогда.</div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0tag:blogger.com,1999:blog-1904509602056652692.post-74147189227523684552017-09-08T03:41:00.000-07:002017-09-08T03:41:47.682-07:00Бюрократия. Ревизионизм. Реанимация. Тестирование. Су-25. И снова про blf - избавление от кракозябр.<div dir="ltr" style="text-align: left;" trbidi="on">
Следующим после МиГ-27К к реанимации был назначен Су-25. Работа выдалась вполне рутинной. Сначала. А вот потом вдруг всплыла нерешенная еще в пераой версии проблема - кабина Су-25, которую я перенес во вторую версию, была меньше требуемых размеров. Произошло это из-за ошибки в моделировании Су-25 в первой версии. Тогда я вывернулся с оверлейной сценой и благополусно забыл об этом. во второй версии из-за ряда новых нюансов подобная халява не прокатила и пришлось кабину "увеличивать". А вместе с ней и остальные детали - индикаторы, стрелочки и тд. Также пришлось подгонять названия деталей под единый стандарт и править текст скрипта работы приборов. Попутно выяснил, что можно UV-скроллинг задавать из единого центра, называя имена объектов в скрипте - все равно мешей у них в списке всего один. по-видимому, придется все кабины зачистить от лишней логики работы курвиметров и вообще потихоньку оптимизировать скрипты, перечислив газвания стрелочек в "шапках" модулей. Ну, ладно, не в первый раз оптимизирую.<br />
Пришлось повозиться с json для Су-25. Постоянно выплывала ошибка в строке с данными сенсора. В конце концов мое терпение лопнуло и я ее заменил на строчку из другого json, подкорректировав цифры. Ошибка пропала. Что это было я так и не понял.<br />
Установил в кабине прицел АСП-17, про который я писал уже в своем блоге здесь, но пока толком не тестировал.<br />
Большую работу пришлось проделать с меню. Серией различных ухищрений мне удалось достичь того, что теперь текст может более-менее точно располагаться на кнопке, а не только рядом с ней, он может быть русским или английским, иметь определенный цвет и прозрасность. К сожаленпию, смена текстур в 2.78 была таки сломана, да и видеотекстура теперь работает странно... Так что смена заставок в меню была сделана через замену мешей, зато раотает железно.<br />
Наконец, с подсказки dron-а, были ликвидированы жуткие кракозябры вместо русского текста. Понятно, что во всем была виновата кодировка. преодолевается это так:<br />
<br />
with open(bge.logic.expandPath('//Menu/StartMenu.json'), 'r', encoding = 'utf-8') as directMenu:<br /> JSONmenu = json.load(directMenu)<br />
<br />
В данном случае encoding = 'utf-8' - это убийца кракозябр. Русский текст после этого нормально воспринимается и читается в БГЕ и можно его пропечатать на экране хоть в текстовом объекте, хоть в blf. Кстати о последнем. Я уже писал о выведении текста на экран. У меня работают текстовые метки целей (наконец-то получилось отсечь цели позади активной камеры), но надо было обязательно сделать меню справку о клавишах и командах. Приводить весь текст json, пожалуй, не буду, он длинный и однообразный. Приведу строчку с разъяснением структуры, хотя, там скорее всего и так будет более-менее поянтно. <br />
<br />
"str1":{"az":35,"buki":45,"vediR":1.0,"vediG":1.0,"vediB":1.0,"vediA":1.0,"strX":0.01,"strY":0.975,<br /> "textRus":"Команды","textEng":"Option"},<br />"str2":{"az":35,"buki":45,"vediR":1.0,"vediG":1.0,"vediB":1.0,"vediA":1.0,"strX":0.01,"strY":0.95,<br /> "textRus":"+ - увеличение тяги двигателя","textEng":"+ - Engine power plus"}<br />
<br />
Хотя нет, не все. Тут еще дело упирается в мой специфический юмор. Недолго думя, я обозвал переменные сами видите как. az и buki - это размер текста. vedi с заглавными буквами - это цвет и прозрасность текста - RGBA, strX-Y- координаты начала строчки на экране. Далее понятно - русский и английский текст. Дело в том, что я постепенно наращивал число ключей в словаре json, не зная толком, что понадобится в работе - в итоге вот так и получилось. Хотя можно и упростить.<br />
Как бы то ни было, меню стало более вменяемым и происходит его сборка-пересборка при смене разделов. у меня не было ни малейшего желания громоздить кучу сцен ради меню, надеюсь, удастся итоговую сцену тоже не делать, а возвращаться в сцену с меню.<br />
И да, наконец удалось ввести в сцену террайна наземную цель - бункер, взаимодействие которого при попадании оружия еще надо как-то отработать - обрушение, разлет обломков там...<br />
Пока меню имеет пять миссий - для МиГ-23МФ и МиГ-29 это перехват Ф-16 и Ф-15 соответственно. Для Су-25, МиГ-23БН и МиГ-27К я противников из воздуха убрал, но оставил в качестве цели бункер. Надо попробовать ввести МЗА и СЗРК. В первой версии МЗА "Эрликон" был, надо перетаскивать. Плюс "Стрела-1" и БРДМ-2, но их текстурить надо. Когда-то я сделал модель БМП-1. Ее надо избавить от высокополигональных катков и гусениц, посмотреть, что можно упростить и туда же, тем более, она была затекстурена...<br />
В общем, продолжим...<br />
Ниже скрины - нельзя же совсем ез картинок - Су-25 и его кабина. Подвешены по две пары блоков С-8 и бомб ФАБ-250, плюс пара ракет Р-60М.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Se0GipuHhhk/WbJzHTNpLgI/AAAAAAAAAYw/R9DhADmLdH0BeeWYtKooBSpmiGi2VeI2gCLcBGAs/s1600/BlendSim22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1518" height="184" src="https://4.bp.blogspot.com/-Se0GipuHhhk/WbJzHTNpLgI/AAAAAAAAAYw/R9DhADmLdH0BeeWYtKooBSpmiGi2VeI2gCLcBGAs/s320/BlendSim22.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-bzIBhTvxHn0/WbJzNM6WNyI/AAAAAAAAAY0/Yp6N68nwbIQBBXNZ3CNvxy2HmoxqdwFzwCLcBGAs/s1600/BlendSim20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1518" height="184" src="https://4.bp.blogspot.com/-bzIBhTvxHn0/WbJzNM6WNyI/AAAAAAAAAY0/Yp6N68nwbIQBBXNZ3CNvxy2HmoxqdwFzwCLcBGAs/s320/BlendSim20.png" width="320" /></a></div>
<br /></div>
Flogger-Khttp://www.blogger.com/profile/16923211074264534293noreply@blogger.com0