понедельник, 1 апреля 2019 г.

Большой Апдейт. И прочие новости...

Не писал я довольно давно. Тут причин мног7о, но главная, просто не знал, о чем писать...
БГЕ таки мертв, будут ли из него делать форки, помимо известных мне upBGE, неясно... С другой стороны, мне все ж стало жалко своих трудов на ниве быдлокодерства, и, помявшись, походив вокруг да около, я все же решил начать Большой Апдейт. Так сказать, прощальный аккорд для БГЕ. Плюс, таким образом я рассчитываю не забыть совсем уж навыки работы с Питоном, а заодно отработать кое-какие приемчики, могущие мне пригодиться в работе уже с Юнити (о нем чуть позже).
Правда, я не знаю, к чему и когда все это приведет, потому как назревает уже четвертая по счету операция для моего глаза (вполне ожидаемая вторичная катаракта), на какое время и когда мне придется "заткнуть свой фонтан" (С), пока непонятн6о, но и сидеть, сложа ручки, тоже неохота, а водку и прочие средства расширения сознания я не уважаю категорически.
Данный пост должен сыграть (и я надеюсь, сыграет), роль своеобразного пинка самому себе, дабы подьянуть расщатавшуюся за последнее время самодисциплину и хотя бы частично прогнать лень.
Итак.
В чем сущность Большого Апдейта (данное понятие следует рассматривать сугубо с иронической точки зрения)?
Сущность его в том, чтобы довести проект до хоть какой-то завершенности. Здесь основной задачей является упорядочивание структуры проекта и обретения новых навыков работы с файлами. А именно - новые методы поиска и загрузки моделей в игру, создание редактора миссий и кампаний и введения в состав доступных игроку юнитов зенитно-ракетных комплексов, вертолетов и танков. Хотя бы и в единственном экземпляре, причем необходимо повысить гибкость проекта и сделать его открытым для добавления новых юнитов (точнее, сохранить открытость). Это основная задача.
Вспомогательная задача - избавиться от завышенных ожиданий, ввести ограничения в проект, превращающий его в, гм... в симулятор конфликтов малой интенсивности (поскольку БГЕ не любит большого количества объектов). Например, для редакторов миссий и кампаний ограничить число одновременно находящихся в воздухе самолетов и вертолетов, скажем, не больше 20 с обеих сторон (в "Ил-2", кстати, ЕМНИП максимум можно было выставить 32 самолета с разных сторон). Скорее всего, онлайн-версию делать не буду (а так, не знаю, что там получится, зарекаться не буду). Приведу в порядок код, в соответствии с новыми знаниями, почерпнутыми мною из всевозможных наставлений и инструкций. Пейзаж покрасивше сделаю (кроме БГЕ, у меня есть и другие планы, но о них я пока помолчу ввиду неопределенности с кое чем - см. выше).

Теперь немного кода. Меня весьма раздражала выстроенная мною система подгрузки объектов в игру из блендов и необходимость долго и нудно расставлять разного рода слеши и двоеточия в файлах json. Почитав учебники и форумы, а также примеры по Питону и воспользовавшись подсказкой dron-a, я наваял очередной блок кода, в котором происходит поиск и установление всех путей ко всем файлам проекта и их сортировка. Пути к файлам Питона добавляются в json файл, лежащий рядом с пусковым блендом проекта. По задумке, игра должна сама смотреть нужные ей пути и тащить в себя необходимые бленды, а не пользоваться готовеньким и разжеванным заранее, что должно сильно сократить как сам код загрузки объектов, так и объем файлов json, в которых не надо будет прописывать пути.

import bge
import sys
import os
import json

#Функция создания файла json с путями к файлам всего проекта
def startPath():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner

    #Создаем переменные - список путей и пустую строку для путей
    pathList = []
    k = ""
   
    #Циклом перебираем файлы, директории и папки, создаем и сшиваем строки в пути
    for d, dirs, files in os.walk(bge.logic.expandPath("//")):
        for f in files:
           
            #Обрабатываем вид путей, убирая слеши и название стартовой директории
            k = os.path.join(d,f)
            k = k.split("BlendSim_2.1")[1]
            k = k.replace("\\", "/")
           
            #Питон-файлы добавляем в sys.path, остальные в список путей
            if ".py" not in k:
                pathList.append("/"+k)
            else:
                sys.path.append("/"+k)
                   
    #Открываем файл json  с путями (если его нет, то он создается) и сбрасываем туда список путей
    with open(bge.logic.expandPath('//NodePath.json'), 'w') as NODEPATH:
        json.dump(pathList, NODEPATH)
   
    #Это просто проверка   
    proverka(own, scene)
 
def proverka(own, scene):
    missile = None

    with open(bge.logic.expandPath('//NodePath.json'), 'r') as READPATH:
        JSONREADPATH = json.load(READPATH)
       
        for ob in JSONREADPATH:
            if "R-24R.blend" in ob:
                print(ob)
                bge.logic.LibLoad(bge.logic.expandPath(ob), 'Scene', load_actions = True)
                missile = scene.addObject("R-24R", own)
       
Ну, я думаю, с комментариями будет все ясно, для чего все это надо. Проверка в виде появления на сцене ракеты Р-24Р прошла успешно и неоднократно...

Теперь о Юнити. В свое время я скачал и Юнити и Анрил Энжин 4. Второй движок ощутимо мощнее и совершеннее первого, да вот беда, пользоваться им смогут не все, я - пока точно нет. Его недостатком является отсутствие большого количества документации, в отличие от Юнити, к тому же, на форуме b3d.ua мне много раз говорили, что Си Шарп относительно простой в освоении язык (но, к сожалению, я тугодум и с налету его понять не могу, изображая обезьяну, повторяющую поведение человека, а именно, создавая код по примерам и образцам, не понимая, для чего и ка, впрочем, при освоении Питона начиналось так же). В Юнити для меня еще остались кое-какие нюансы, лежащие за пределами моего понимания, например, как присвоить материал дереву и его листьям (перетаскивание порой сильно раздражает, проше было бы набрать на клавиатуре). Но все-таки, я добавил в Юнити многострадального подопытного кролика - МиГ-23МФ из моего же бленд-проекта и попробовал для начала помахать крылышками. Не сразу, но это получилось. Код, наверное, я пока приводить не буду - он состоит из занудного объявления публичных переменных и всего-то из десятка строчек кода для крыльев. Пока я не столько работаю в Юнити, сколько пытаюсь уложить в свою голову хотя бы азы Сишарпа. И прежде всего - взаимодействие скриптов между собой, минуя Инспектор Юнити. У меня он ассоциируется с логическими кирпичами БГЕ, которые необходимо использовать только там, где это действительно нужно. А многократно переделывать  еще и переменные в инспекторе - нет уж. Лучше сразу научиться хотя бы по минимуму работать с кодом, а не таскать переменные и оббъекты с одно края экрана на другой (да и окошечки там маленькие, с моим зрением сейчас...)
Ну ладно, будем считать, что стартовый шаг сделан (точнее, самопинок) и блогспот не накроется медным тазом, как Гуглплюс (ходили т акие слухи).

понедельник, 24 сентября 2018 г.

Прерывая вынужденное долгое молчание...

Пришлось надолго прервать свою "блогерскую" деятельность. Причиной тому были проблемы со зрением. В течение полутора лет медленно, но верно прогрессировала катаракта, которая в моем случае являлась довольно сложным случаем. Настолько сложным, что наши, местные, врачи-офтальмологи, хорошо меня знающие, как постоянного клиента соответствующего лечебного учреждения, не рискнули проводить операцию имеющимися у них средствами, посчитав это слишком опасным - вероятность полдной потери зрения была ненулевой. Так что направили меня в Москву, в МНТК имени академика Федорова. Самым сложным из всей эпопеи с документами, необходимыми для поездки, оказалось попасть на прием к нашему областному офтальмологу. Регистратура в нашей областной клинике работает по принципу "правая рука не знает, что делает левая", так что первый раз записали на прием в 18:57, разумеется, рабочий день уже закончился, и пришлось переться на прием второй раз, да еще и вроде как она собралась в отпуск... Записались на 7:00, пришлось сначала ждать до 12:00, как всем сказали, но пришла врач только в 16:00... Особо ее винить в этом не стогит, у нее, между прочим, своих пациентов хватает, да и считается она хорошим хирургом, и операции делает, и весьма сложные. Просто система такая у нас теперь - все квоты на бесплатные высокотехнологичные оепрации теперь сосредоточены в одних руках, поэтому и на прием надо пробиваться. Ну ладно, это понятно. Но была в очереди одна тетка, возмущавшаяся "палачами в белых халатах", которая будучи второй, все же проперлась впереди нас, как танк (наверное, потенциальный правозащитник, не иначе). Пришлось ждать, когда она свалит, наконец из кабинета офтальмолога. Потом уже меня приняли, вместе с мамой, которая последние лет двадцать выступает в качестве моего сопровождающего везде, кроме дома и собственного двора (где я все и так наизусть знаю и даже в темноте или с закрытыми глазами могу перемщаться). Врач и ее ассистент не очерствели, сразу бросились помогать найти стул и сесть перед прибором. Посмотрели, затем офтальмолог выписала кучу направлений на анализы и снимки, проинструктировала, что и как нужно сделать, предупредила, что если что понадобится, то обратились к ее заму и тот ее из отпуска вызовет, если что понадобится. Дальше наша бюрократическая машина закрутилась с бешеной скоростью - анализы и снимки были сделаны очень быстро, в Москву ушел запрос, а ответ пришел уже на следующий день.Как-то так вышло, что мама взяла билеты в Москву очень удачно - мы приехали туда за два дня до операции, остановились в гостинице при МНТК, взяв заранее забронированный номер (бронирование осущетсвил мой друг и тезка, которому за это отдельное спасибо - он сейчас в Москве работает). Там и дождались госпитализации. Вышла, правда заминка - не было анализа крови на сифилис(есть подозрение, что тамошняя лаборатория таким нехитрым образом немного подрабатывает - деньги за нализ небольшие, но капают регулярно, а с другой стороны, зная, как просела медицина в бывших советских республиках после ухода "русских дармоедов и оккупатнов", а потом обретшие "долгожданную независимость" рванули на заработки в страну-"оккупанта и агрессора", в основном ту же Москву...), но и с этим тоже справились и в "последней волне" - около 17:00 мы отправились в больничные коридоры. Толпа страждущих и сопровождающих идет за санитаркой, как стадо слонов со своими рюкзаками и сумками. Встречные обитатели жмутся к стенам, а проходящая медсестра, под общий хохот спрашивает у нашей вожатой: "Где ты их столько нбрала?!" Определили нас с мамой в отдельный бокс - две койки - для больного и его сопровождающего, небольшой, но по-своему уютный, тем же вечером к нам заглянула ассистент хирурга, представилась, сказала, что меня прооперируют после обеда, после чего я поставил свю кракозябру под согласием на лечение и стали ждать. Оперировали меня раньше - около 11 часов дня - врач пришел гораздо раньше и откладывать дело в долгий ящик не стал. Группу больных и меня в том числе привели в оперционный корпус (или как он там нзывается), и стали одного за другим заводить в операционные. Сначала - закапывание в глаз обезболивающего, затем укол в морду возле уха анестетиком (у меня богатая практика получения уколов в глаза и виски, поэтому особых эмоций это не вызвало). Потом дождался своей очереди и лег на операционный стол. Зафиксировали веки, в глаз ударил яркий белый свет, который потом стал желтым (лазер? - наверное...) и глазу стало сильно горячо, но терпимо. Потом все исчезло и осталась только ослепительно белая пелена - будто все поле зрения занял лист чистой бумаги. Затем на ее фоне проявилось нечто - это если бы на лист бумаги положили бы, ну скажем, точилку для карандашей и стали ею двигать в разные стороны (ну как-то так, в 1996 году я видел картинку и пожестче - скальпель, входящий в мой глаз - это как бы кто-то сует палку в чистую воду, а ты, значит, находишься под пленкой воды и это целится тебе точно в лицо - как-то так, да еще два столба из воды торчат, то бишь из твоего галаза - какой-то еще инструмент тогда применялся). Предмет то появлялся, то исчезал, я слышал, как работает что-то вроде микромотора, но могу и ошибаться - там всяких звуков хватает... Затем в глаз начало что-то влезать - больно, но не сильно, И я стал видеть. Уже перебираясь на каталку, я слышал, как хирург подробно объясняет своей ассистентке, как надо работать с оборудованием - готовит молодое поколение, так сказать... Мама потом мне говорила, что когда мне открыли склеившиеся веки на следующий день при осмотре, что глаз у меня был абсолютно чистый и совсем не походил на тот, каким он был после операции в 1996 - подозреваю, что тогда с пол-мордой в зеленке и кроваво-красным белком оперированного глаза я сильно смахивал на вурдалака... Но что делать - лазера тогда не было и много чег7о не было. После осмотра на следующий день после операции врач констатировал, что осложнений нет и нас выпишут на следующий день. А мама взяла обратные билеты как раз на этот день (кто-то наверху подсказал, не иначе). Так и отбыли обратно... В Москве я был в 1995 году и тоже по офтальмолшогическим делам - потерял зрение на левый глаз из-за отслойки сетчатки и тогда я ездил с моим, к сожалению, ныне покойным папой, и запомнилась мне Моска тогдашняя неустроенностью, суетой, кучами мусора и всеобщим бардаком... Сейчас это другая Москва - чистая, опрятная, ярко освещенная, с ровными дорогами, где дети не боятся играть на улицах после часа ночи... Да и наш родной город все же прибавил, пусть и не так сильно - Россия выкарабкивается из той ямы, в которую мы угодили почти тридцать лет назад. Да и народ изменился - в лучшую сторону (за исключением отдельных индивидуумов - смотрии выше, но мама была свидетелем того, как в нашей поликлинике очередь дружно пропустила вперед женщину на инвалидной коляске, так что...). Такие дела.

понедельник, 2 апреля 2018 г.

Ведущий-ведомый. Эксперименты в коде.

Итак, задача. Провести перестроение пары ведущий-ведомый в БГЕ из строя "правый пеленг" в "левый пеленг". Задача решалась в БГЕ, объекты имели тип физики "но коллижн", то есть физика отсутствовала. Ведомый при получении команды немного "приотставал", разворачивался по отношению к ведущему под углом, после чего занимал свое место с противоположной стороны и, догнав "командира" уравнивал с ним скорость. Активно использовался метод getVectTo. В итоге, после опробования различных вариантов кода, получилось следующее:import bge
 
scene = bge.logic.getCurrentScene()
 
def Wingman():
    V_own = 0.0
    V_wingman = 0.0
    R_own_x = 0.0
    R_wingman_x = 0.0
    R_own_y = 0.0
    R_wingman_y = 0.0
    R_own_z = 0.0
    R_wingman_z = 0.0
 
 
    Wingman = scene.objects["MiG21Wingman"]
 
    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    
    
    
    if own["prop"] == 0:
        V_own = 0.3
        V_wingman = 0.3
        
    
        
    #Перестройка в пеленг
    elif own["prop"] == 1:
        V_own = 0.3
        V_wingman = 0.3
        if own.getVectTo(Wingman)[2][0] > 0:
            V_own += 0.05
            V_wingman -= 0.05
            if abs(Wingman.getVectTo(own)[2][0]) < 0.5 and own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z += 0.01
            elif abs(Wingman.getVectTo(own)[2][0]) > 0.5 and own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z -= 0.01
            
        else:
            V_own = 0.3
            V_wingman = 0.3
            if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z -= 0.01
            else:
                R_wingman_z += 0.01
            
            if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:
                V_own -= 0.05
                V_wingman += 0.05
            else:
                if Wingman.worldOrientation[2] != own.worldOrientation[2]:
                    V_own = 0.3
                    V_wingman = 0.3
                    Wingman.worldOrientation[2] = own.worldOrientation[2]
                else:
                    if own.getDistanceTo(Wingman) > V_own*60:
                        V_own = 0.3
                        V_wingman += 0.1 
                    else:
                        own["prop"] == 0
                        V_own = 0.3
                        V_wingman = 0.3 
            
    
    elif own["prop"] == 2:
        V_own = 0.3
        V_wingman = 0.3
        if own.getVectTo(Wingman)[2][0] < 0:
            V_own += 0.05
            V_wingman -= 0.05
            if abs(Wingman.getVectTo(own)[2][0] > 0.5) and own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z += 0.01
            elif abs(Wingman.getVectTo(own)[2][0] < 0.5) and own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z -= 0.01
            
        else:
            V_own = 0.3
            V_wingman = 0.3
            if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z += 0.01
            else:
                R_wingman_z -= 0.01
            
            if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:
                V_own -= 0.05
                V_wingman += 0.05
            else:
                if Wingman.worldOrientation[2] != own.worldOrientation[2]:
                    V_own = 0.3
                    V_wingman = 0.3
                    Wingman.worldOrientation[2] = own.worldOrientation[2]
                else:
                    if own.getDistanceTo(Wingman) > V_own*60:
                        V_own = 0.3
                        V_wingman += 0.1 
                    else:
                        own["prop"] == 0
                        V_own = 0.3
                        V_wingman = 0.3 
            
                       
    own.applyMovement([0.0, V_own, 0.0], True)
    Wingman.applyMovement([0.0, V_wingman, 0.0], True)
    own.applyRotation([R_own_x, R_own_y, R_own_z], True)
    Wingman.applyRotation([R_wingman_x, R_wingman_y, R_wingman_z], True)
 
Параллельно с написанием пробного скрипта перестроения пары самолетов пришлось составлять таблийу для значений векторов метода getVectTo[2] . Если попытаться представить это в трехмерном пространстве, то получается куб, в центра которого находится ведущий, а в углах, центрах граней и ребер - ведомый. Условно говоря, конечно. То есть ведомый может идти параллельно ведущему рядом с ним. Или с отставанием справа и сверху или с опережением слева по центру и так далее. В итоге табличку я сюда солью, может потом пригодиться (хотя бы и самому себе).
 
НА ОДНОМ УРОВНЕ
Спереди по центру [0.0, 1.0, 0.0]
Сзади по центру [0.0, -1.0, 0.0]
Слева спереди [-0.7071, 0.7071, 0.0]
Справа спереди [0.7071, 0.7071, 0.0]
Слева сзади [-0.7071, -0.7071, 0.0]
Справа сзади [0.7071, -0.7071, 0.0]
Справа по центру [1.0, 0.0, 0.0]
Слева по центру [-1.0, 0.0, 0.0]
 
ВЫШЕ
Выше по центру [0.0, 0.0, 1.0]
Спереди по центру [0.0, 0.7071, 0.7071]
Сзади по центру [0.0, -0.7071, 0.7071]
Слева спереди [-0.5774, 0.5774, 0.5774]
Справа спереди [0.5774, 0.5774, 0.5774]
Слева сзади [-0.5774, -0.5774, 0.5774]
Справа сзади [0.5774, -0.5774, 0.5774]
Справа по центру [0.7071, 0.0, 0.7071]
Слева по центру [-0.7071, 0.0, 0.7071]
 
НИЖЕ
Ниже по центру [0.0, 0.0, -1.0]
Спереди по центру [0.0, 0.7071, -0.7071]
Сзади по центру [0.0, -0.7071, -0.7071]
Слева спереди [-0.5774, 0.5774, -0.5774]
Справа спереди [0.5774, 0.5774, -0.5774]
Слева сзади [-0.5774, -0.5774, -0.5774]
Справа сзади [0.5774, -0.5774, -0.5774]
Справа по центру [0.7071, 0.0, -0.7071]
Слева по центру [-0.7071, 0.0, -0.7071]
 
Потом, когда все это было закончено, я попробовал сделать немного по-другому. А именно - сделать так, чтобы ведущий постоянно вычислял координаты ведомого относительно себя, любимого, чтобы подчиненный просто занимал эту область, не заморачиваясь с векторами. Тем более, что гораздо раньше у меня уже был отработан метод наведения ботов на цель с помощью крена, тангажа или рыска по отдельности или сразу всего одновременно. Никаких проблем с наведением ведомого на эту самую искомую точку быть не должно. Однако вот тут я застрял. Как поется в одной старой песне из кинофильма: "Уж я к ней и так и этак, со словами и без слов. Обломал немало веток, наломал немало дров". В итоге все же удалось решить проблему со строем "фронт" - когда ведомый и ведущий выстроены в одну линию параллельно друг другу и строем "колонна", когда ведомый держится строго позади ведущего. А вот со строем "пеленг" пока облом. Там нужно реализовать отставание ведомого от ведущего, либо его опережение.  Ну, хоть что-то получилось, а с пеленгом я все равно как-нибудь разберусь. Код написан уже для объектов с физикой типа Dynamics.
 
port bge
import math
scene = bge.logic.getCurrentScene()
 
def Wingman():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    #Задание объекта-цели
    target = scene.objects["wingman"]
    target.worldOrientation = own.worldOrientation
    #target.worldPosition = front(own, target)
    target.worldPosition = peleng(own, target)
    #target.worldPosition =colonna(own, target)
 
def colonna(own, target):
    targetX = own.worldPosition[0] - 3*own.worldLinearVelocity[0]
    targetY = own.worldPosition[1] - 3*own.worldLinearVelocity[1]
    targetZ = own.worldPosition[2] - 3*own.worldLinearVelocity[2]
    listCoord = [targetX, targetY, targetZ]
    return listCoord
    
def front(own, target):
    LeftRightX = 0
    LeftRightY = 0
    
    kY = own.worldLinearVelocity[0]/own.localLinearVelocity[1]
    kX = own.worldLinearVelocity[1]/own.localLinearVelocity[1]
    kZ = own.worldLinearVelocity[2]/own.localLinearVelocity[1]
    
    PRAVOLEVO = 1
    
    if kX > 0 and kY > 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX < 0 and kY > 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX > 0 and kY < 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX < 0 and kY < 0:
        LeftRightX = 1
        LeftRightY = -1
    
    ownX = own.worldPosition[0]
    ownY = own.worldPosition[1]
    ownZ = own.worldPosition[2]
    
    DIST = 20.0
    
    targetX = ownX + DIST*kX*LeftRightX*PRAVOLEVO
    targetY = ownY + DIST*kY*LeftRightY*PRAVOLEVO
    targetZ = ownZ + DIST*kZ
    
    listCoord = [targetX, targetY, targetZ]
    return listCoord
 
В функциях colonna и front  идет как раз вычисление и выдача искомых координат ведомого. Комментариев в коде почти нет, потому как работа над этим делом еще не закончена. Получится ли из этого что-нибудь, пока неясно. Во всяком случае, в основном коде проекта включить их можно хоть сейчас, но хотелось бы добить строй "пеленг"...

четверг, 15 марта 2018 г.

Будь готов! Всегда готов!

Этот девиз помнят те, кто успел стать пионером во времена СССР. Я был одним из последних пионеров Советского Союза. Положа руку на сердце, скажу, что в общем-то ничего членство в пионерской организации мне не дало. К концу 80-х все это уже давно превратилось в формальность для галочки, набор ритуалов, смысл которых временами был совсем непонятен. Сама-то идея пионерской организации была неплоха, но, как всегда, подвела реализация. На этом нудное вступление считаю законченным и переходим к делу.
Пост будет коротким, не о Блендере, не об авиации, а о политике (вынужденно).
На данный момент давление на нас со стороны сами знаете кого продолжает нарастать. При этом звучат обвинения буквально во всем и не только высшего руководства страны, но и рядовых русских. Пока еще прямо не говорят в том, что мы виноваты в том, что вообще живем на этой планете, но все к тому и идет. Припоминается высказывание госпожи Тэтчер насчет того, что территория России экономически обоснована для проживания 12-15 миллионов человек. С явным намеком на судьбк остальных...
Как обычно, раз в сто лет у наших западных соседей начинается обострение.  Ныне времена другие, а сущность та же.
Высказывать свои политические взгляды, клеймит Запад и влезать в политические споры мне влом. Тем более, что переубедить собеседника в Сети невозможно- ты просто потеряешь время. Да у меня есть дела поважнее.
Учитывая сложившуюся ситуацию с ограничениями, накладываемыми на русских пользователей в том же Фейсбуке (на котором у меня нет аккаунта), а также дискриминацию русских же в том же Стиме (кому интересно, поищите информацию об игре Syrian Warfire, кажется, так она называлась), а также раздающиеся время от временеи призывы отключить Россию от мирового Интеренета, придется принять меры.
Недавно Герман Клименко, создатель сети Live Internet заявил, что Россия к отключению от мировой Сети готова. Что ж, я переношу "основную деятельность" на ЛиРу. С Гугла уходить я не буду, пока они сами не прикроют лавочку. А судя по нарастающему безумию наших соседей все к тому и идет.
https://www.liveinternet.ru/users/flogger-k/post416416506/
Пока там только переводы статей. Да и создавался дневник как резервная площадка на "всякий пожарный и при условии, что". Теперь пора преодолевать леностьи печатать свои опусы сначала там, а потом уже дублировать здесь. Да еще наверное и на компе сохранять. Мало ли...
В общем, если что, если кому-то мое творчество еще интересно - вы знаете, где меня найти в условиях Малого Песца. При Большом Песце (который, будем надеяться не придет совсем или придет через пару тысяч лет) все это уже не будет иметь никакогог значения.
P.S. А мы, оказывается, террористы, даже если не травили. https://aftershock.news/?q=node/627005
No comments

вторник, 13 марта 2018 г.

"Новые птички" и и их искусственный интеллект.

Затянувшаяся "перестройка", по-видимому, имеет все шансы не превратиться в "катастройку", как это получилось в реальном мире. Несмотря на все трудности и препятствия, процесс ползет вперед. И подтверждением тому стали две новые "птички", оживленные и подключенные к игровому процессу. Хотя, если честно, "новыми" их можно назвать именно так - в кавычках. Речь идет об F-5E Tiger и МиГ-23МС Flogger-E.
"Тайгер" был задействован еще в первой версии еще двухгодичной давности, но в данной ситуации его ведение в строй все время откладывалось на потом. Но, в конце концов, когда возниклас нужда в легком истребителе второго поколения в качестве оппонента упрощенной версии МиГ-23, я принялся спешно чинить эту машину и подключать ее к игре. Не сразу, но получилось.
Что касаемо МиГ-23МС, то это не что иное, как сильно упрощенная версия МиГ-23МФ, с авионикой от МиГ-21МФ, поставлявшаяся развивающимся странам. Тогда, в середине 70-х, промышленность еще не справлялась с массовой поставкой в войска МиГ-23М, а МФ был еще только в проекте, но советским союзникам новый самолет был нужен еще вчера.  В итоге и появился на свет упрощенный экспортный вариант со слабым, но отработанным БРЭО, увеличенным аж на полтонны запасом топлива (по причине свободного от оборудования места в закабинном отсеке), но не способный вести бой ракетами средней дальности, чье управляемое оружие ограничивалось только ракетами Р-3Р/С и Р-23М.
Отношение к МиГ-23МС довольно противоречивое - советские летчики, изображавшие на учениях "агрессора", любили этот самолет за надежность, увеличенную дальность полета и меньший, по сравнению с МиГ-23М, вес. А вот арабские пилоты, которым и приходилось на нем воевать, эту модификацию не любили за крайне слабое бортовое оборудование и отсутствие ракет средней дальности.
Но тут следует помнить, что даже хорошее оружие в неумелых руках автоматически не дает победы тому, кто воевать не умеет. Опытные летчики и на МиГ-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 года.
Самолеты F-5E весьма активно участвовали в ирано-иракской войне, на их счету много сбитых иракских самолетов, но их потери оказались выше. В основном, этот самолет использовался в качестве истребителя-бомбардировщика, но применялся и как охотник на вертолеты, а один раз даже сбил МиГ-25ПД. В тот раз иракский летчик сосредоточил свое внимание на паре "Томкэтов" и пропустил атаку "Тайгера" в хвост - после пушечной очереди иранский летчик увидел мощный выброс пламени- не менее 15 метров из пробитого топливного бака, после чего пилот сбитого МиГа катапультировался. Горящий самолет пролетел еще довольно большое расстояние и упал на территории Турции. Утверждается, что в Сети есть фото обломков этого самолета, но найти егл не удалось. Во время войны в Йемене один МиГ-29 южан, похоже, был сбит именно "Тайгером" северян, но информация об этом крайне скудна. Еще один интерсный штрих к судьбе F-5E - этот самолет едва не стал  основной боевой машиной ВВС Югославии - но в конечном итоге, конкурс выиграл МиГ-21. Воевали "Тайгеры" в составе ВВС Эфиопии в Огадене и не их счету есть МиГи сомалийцев.
Согласно реестру МиГ-23, в 2014 один МиГ-23МС был сбит террористами над Сирией, на момент потери машине было уже 40 лет - самолет был 1974 года выпуска. Недавно сняла с вооружения свои F-5E Мексика, фактически оставшись без авиации.
Но довольно экскурса в прошлое, вернемся в настоящее, виртуальное, правда... Я решил немного сжульничать и заодно проверить выстраиваемую мною архитектуру - в качестве кабины МиГ-23МС поставил кокпит от МиГ-23МФ, обозвав его в соответствие с моделью. Жульство прошло - кабина встала на место, как родная, Это внушает оптимизм - следовательно, при соблюдении некоторых правил, кабины в будущем легко менять, модернизировать и добавлять.
А потом пошла работа над ИИ ботов. Я условно разделил манеру поведения ботов на дальний воздушный и ближний воздушный бой - ДВБ и БВБ. Поведение бота в ДВБ предсказуемо - он выравнивается по крену в глобальных осях и направляет свой нос на цель - идет выравнивание на цель по рыску и тангажу, плюс поиск выгодной позиции для пуска ракеты. Пока нет разрешения на пуск, бот сближается с целью на форсаже, при разрешении пуска он "притормаживает" з все равно надо "подсвечивать" ракету, да и нет резона самому входить в зону пуска оппонента...
Что касаемо БВБ, то сейчас идет работа по отработке маневра "страхивания с хвоста" и создания видимости сообразительности бота - его поведение надо сделать достаточно непредсказуемым, но не пергнуть при этом палку - там еще много работы.
Ну, и в завершение - скрины - МиГ-23МС и F-5E.


воскресенье, 4 марта 2018 г.

Доводка классов. Прячем все.

Вполне закономерным итогом работы с классами стал полный переход на "внутриклассовую" работу скриптов... В конце концов, под зонтик класса перешла и работа уровней детализации и расчет поправочного коэффициента для выпуска-уборки шасси, тормозов и крыла изменяемой стреловидности. Приводить здесь скрипт класса того же МиГ-23, как представителя юнитов, у которого присутствует все вышеперчисленное, не стоит. Он довольно длинный и особо ничем не отличается от приведенных ранее. Ну, добавилось в метод движения расчет положения крыла, ну появился в классе новый метод для расчета уровня детализации. Все это чисто рабочие моменты...
Главное, что все это работает - и слава Богу, без сбоев. Пока, во всяком случае.
А потом пришел черед выполнить еще один пункт - добавить в классы оружия метод движения. И поведения заодно. Как водится первой жертвой исследования стала ракета Р-27Р. Сам ее класс был слегка модифицирован - всего с полдюжины строчек, но работа себя оправдала. Даже на фоне того, что пришлось аврально вносить коррективы во все остальные классы управляемых ракет. Заодно, кстати, дополнительно "раздробил" скрипт движения оружия, выделив из негог первый тип - ракета. Он характерен для всех типов ракет, кроме НАР. Причина - ракеты такого типа летают не по прямой, поэтому для них невозможно сделать дым одной частицей. О частицах чуть позже. Итак. Скрипт движения ракеты.

import mathutils
import random
import bge
scene = bge.logic.getCurrentScene()

def control(self):
    cont = bge.logic.getCurrentController()
    own = self
    if own.engineWeapon == 'CatapultWeapon':
        CatapultWeapon(own)
    else:
        Missile(own)
   
   
#Полет ракеты
def CatapultWeapon(own):
    if own.vzryvatel == 10:
        own.suspendDynamics
        own.engineWeapon = "Missile"

#Полет ракеты
def Missile(own):
   
    own.applyMovement([0.0,own.speed/60,0.0],True)
    own.timerAmountFire += 1
    if own.amountFire > own.timerAmountFire:
        #Срабатывание добавления частиц дыма от ракет
        own.timerSmoke += 1
        if own.timerSmoke == 1:
            #Дым снаряда
            smoke = scene.addObject('ParticleUniversal', own)
            smoke.replaceMesh("SmokeLong", True, False)
            smoke.setParent(own, False, False)
        if own.timerSmoke == 3:
            if 'ParticleUniversal' in own.childrenRecursive:
                own.childrenRecursive['ParticleUniversal'].scaleX = own.speed/360
                own.childrenRecursive['ParticleUniversal'].scaleY = own.speed/12
                own.childrenRecursive['ParticleUniversal'].scaleZ = own.speed/360
                own.childrenRecursive['ParticleUniversal'].Delta_scaleX = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_scaleY = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_scaleZ = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_colorAlpha = -0.0005
                own.childrenRecursive['ParticleUniversal'].colorAlpha = 0.2
                own.childrenRecursive['ParticleUniversal'].removeParent()
        if own.timerSmoke == 5:
            own.timerSmoke = 0
             


А теперь скрипт класса Р-27Р, который вызывает этот самый скрипт и скрипт головки самонаведения.

import bge

from ClassWeapon import typeWeapon

class R27R(typeWeapon):
       
    import Weapon_Missile as __Weapon_Missile
    import Weapon_GSN as __Weapon_GSN
   
    def __init__(self, old_owner):
        typeWeapon.__init__(self, old_owner)

    def engine(self):
        self.__Weapon_GSN.GSN(self)
        self.__Weapon_Missile.control(self)
       
def mutate(cont):
    R27R(cont.owner)
             

Ну, и собственно, функция работы оружия в игровом файле.

#Функция эффекта взрывов
def controlWeapon():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    own.engine()
   

Как видно, опять "матрешка".  Так сказать, в прямом смысле - в собранной матрешке не видно внутренних составляющих. Но они есть. Как тот суслик из фильма ДМБ".
За это время я восстановил работу РЭБ, восстановил катапультирование из сбитого самолета, и, наконец, добавил огонь для сбитого. Псевдочастицами-плейнами.
А теперь по поводу частиц. К сожалению, БГЕ имеет ограничения и довольно существенные, как раз в этой области, поэтому эти меры - паллиатив. Правда, есть возможность отобрать у БГЕ почти все функции, оставив только загрузку окна и еще что-то по мелочи. Но для этого нужен другой уровень программирования, мой очень сильно недотягивает. Хотя, повторяю, часть функций БГЕ можно передать другим программам. Поэтому извращаться по поводу частиц огня, дыма, облаков и прочего я прекращаю и сосредотачиваюсь на ИИ, оружии, систем противодействия и защиты, выстраивания иерархии ботов, поиска путей и так далее. А еще, наверное, я радикально сокращу количество блоков ландшафта. Раз в 10. Схема вполне работоспособна, но ее можно и нужно оптимизировать.
Все же приведу скрипт работы частиц огня. Суть в том, что их число растет, пока не достигает определенного предела. Все частицы являются потомками и занимают свое место в списке childrenRecursive. Они расставляются на удалении от родителя согласно своему номеру в списке - чем он больше, тем дальше от родителя частица. При этом присутствует некий элемент случайности. По координатам и размерам, но в некоем диапазоне.

import random
scene = bge.logic.getCurrentScene()

#Эта переменная определяет длину "хвоса" огня
fireLong = random.randrange(8, 20)

def FireAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #Длина списка потомков
    particleLen = len(own.childrenRecursive)
   
    #Это просто списко для ускорения добавления частиц - удвоение или утроение за один проход в тик
    listObj = ["num0"]
    #listObj = ["num0","num1"]
   
    #Характеристики частицы - локация, размер и цыкт с прозрачностью
    partColor = random.randrange(2, 10)/10
    partLoc = random.randrange(particleLen, particleLen+2)
    partScale = random.randrange(particleLen, (particleLen+2)*2)/(particleLen+2)*4
   
    #Эта переменная используется для отслеживания индекса потомка в списке
    indexObj = 0
   
    #Этап1 - добавляем, парентим и раставляем частицы, отслеживая длину списка потомков
    if particleLen < fireLong:
        for newParticle in listObj:
            newParticle = scene.addObject('Plane', own)
            newParticle.setParent(own, False, False)
            newParticle.replaceMesh("FireAir_", True, False)
            newParticle.localPosition = [partLoc/10, -partLoc, partLoc/10]
            newParticle.worldScale = [partScale, partScale, partScale]
           
    #Этап 2 - работаем с тем, что есть, больше ничего не добавляем
    else:
        for obj in own.childrenRecursive:
            #Здесь переменные локации и размера используются в качестве эталона,
            #к которому подтягиваются ТТХ частиц
            indexObj = own.childrenRecursive.index(obj)
            #partLoc = random.randrange(indexObj, indexObj*indexObj*indexObj+2)
            partLoc = random.randrange(indexObj, indexObj+2)
            partScale = random.randrange(indexObj, (indexObj+2)*2)/(indexObj+2)*3
           
            #Можно поиграться с цветом и прозрачностью
            obj.color = [1.0, partColor, partColor, partColor]
           
            #Хвост пламени - локальные координаты Х
            if obj.localPosition[1] < -partLoc:
                obj.localPosition[1] += partLoc/50
            elif obj.localPosition[1] > -partLoc:
                obj.localPosition[1] -= partLoc/50 
            #Размазанность и толщина хвоста пламени - координаты Икс и Зет
            obj.localPosition[0] = partLoc/4
            obj.localPosition[2] = partLoc/4
           
         
Вот так. Хотя, все это - костыли. Пусть и работающие. Подожду пока прояснения обстоятельств с перхватом рендера и управления БГЕ. Если обстоятельства позволят, то откроются новые перспективы.

пятница, 23 февраля 2018 г.

Прячься, кто может! Инкапсуляция в классах. Кажется, выхожу на оперативный простор...

На довольно долгое время я застрял. Хорошо так застрял, давно со мной такого не было. Причиной этому стала "размазанная" по нескольким скриптам ошибка в иде недостающих аргументов для функций. А учитывая то, что функции вызывались через try-except  с перхватом исключения, и внешне все выглядело благообразно... Едиснтвенно, что выглядело безобразно- так это целераспределение для ботов. Боты считали противниками своих собратьев и не стреляли по своим только потому, что в условие пуска был заложен запрет на стрельбу по объекту с одинаковыми для бота "цветами". Это выглядело смешно, если бы не отняло у меня три недели. Правда, насчет "отняли", я погорячился. По-всякому измываясь над собственным кодом, я научился с подсказки dron-a проводить инкапсуляцию внутри кода. Кода класса, я имею в виду. Теперь, значит, кроме наследования, еще и инкапсуляция... Собственно говоря, ошибка лежала в несколько другой плоскости, но это именно тот случай, когда не было бы счасться, да несчастья помогло. Ее устранение свелось всего-начсего к тщательной проверке "шапок" модулей и наборам переменных в скобках после названий функций...
Инкапсуляция - это другое дело. Она позвоялет исключить "накладок"  в поеведении объектов, "пряча" данные внутри класса от внешнего мира. Вообще-то до них все равно добраться можно, но сделать это гораздо сложнее. Основательной переделке подверглись все классы боевых самолетов, причем делалось это неоднократно, по мере выяснения свойств инкапсуляции и осознания области ее применения. Ниже приводится класс для МиГ-23БН. Мне влом его резать, проще клавишами копировать-вставить. Да и образцом в случае чего послужит.

import bge
#import UnitAir_engine

from ClassUnitAir  import UnitAir

class MiG23BN_Libya_(UnitAir):
    import UnitAir_engine as __UnitAir_engine
 
    def __init__(self, old_owner):
        UnitAir.__init__(self, old_owner)
   
    __air = True
    __airtype = "JET"
    __dron = False
    __multiengine = False
    __airbrake = True
    __slats = True
    __flaps = True
    __swingwing = True
    __canopy = True
    __chassy = True
    __yaw = True
   
    #Летательный аппарат
    def is_AIR(self):
        return self.__air
    #Подтип ЛA - вертолет, самолет (реактивный или внитовой)
    def is_AIRTYPE(self):
        return self.__airtype
    #Пилотируемый или БПЛА
    def is_DRON(self):
        return self.__dron
    def is_FLAPS(self):
        return self.__flaps
    def is_SLATS(self):
        return self.__slats
    def is_SWINGWING(self):
        return self.__swingwing
    def is_AIRBRAKE(self):
        return self.__airbrake
    #Одно- или многодвигательный
    def is_MULTIENGINE(self):
        return self.__multiengine
    def is_CHASSY(self):
        return self.__chassy
    def is_CANOPY(self):
        return self.__canopy
    def is_YAW(self):
        return self.__yaw
   
   
    #######################################
    def is_CANOPY_Device(self):
        CANOPY_Device = [
                        ["Cnp_",[0.0087,0.0,0.0],"rotat"]
                        ]
        return CANOPY_Device
    def is_POINT_CANOPY(self):
        reper = [0, 114]
        return reper
    ########################################
    def is_FLAPS_Device(self):
        FLAPS_Device = [
                        ["FlpR_",[-0.0087,0.0,0.0],"rotat"],
                        ["FlpL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return FLAPS_Device
    def is_POINT_FLAPS(self):
        reper = [-50, 0, 100]
        return reper
   
   
    ######################################
    #Наличие-отсутствие предкрылков
    def is_SLATS_Device(self):
        SLATS_Device = [
                        ["SltR_",[-0.0087,0.0,0.0],"rotat"],
                        ["SltL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return SLATS_Device
    def is_POINT_SLATS(self):
        reper = [0, 40]
        return reper
   
    #####################################
    #Наличие-отсутствие крыла изменяемой стреловидности
    def is_POINT_WINGS(self):
        reper = [0, 300, 550]
        return reper
   
    def is_WINGS_Device(self):
        WINGS_Device = [
                        ["WngR_",[0.0,0.0,-0.00174],"rotat"],
                        ["WngL_",[0.0,0.0,0.00174],"rotat"]
                        ]
        return WINGS_Device
   
    ####################################
    #Наличие-отсутствие воздушных тормозов
    def is_AIRBRAKE_Device(self):
        AIRBRAKE_Device = [
                           ["ArbUR_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbUL_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbDR_",[0.00696,0.0,0.0],"rotat"],
                           ["ArbDL_",[0.00696,0.0,0.0],"rotat"]
                          ]
        return AIRBRAKE_Device
   
    def is_POINT_AIRBRAKE(self):
        reper = [0, 100]
        return reper
   
    ####################################
    #Наличие-отсутствие рулей направления
    def is_YAW_Device(self):
        YAW_Device = [
                      ["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",-1],
                      ["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",1]
                     ]
        return YAW_Device
    def is_POINT_YAW(self):
        reper = [-25, 25]
        return reper
   
    ####################################
    #Наличие-отсутствие стабилизатора
    def is_ROLL(self):
        return True
    def is_ROLL_Device(self):
        ROLL_Device = [
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvR_",[0.0087,0.0,0.0],"rotat",-1],
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",1],
                      ["ElvR_",[0.0087,0.0,0.0],"rotat",1]
                     ]
        return ROLL_Device
    def is_POINT_ROLL(self):
        reper = [-20, 20]
        if self.WINGS > 300:
            reper = [-13, 13]
        #print(reper)
        return reper
    #Наличие-отсутствие стабилизатора
    def is_ROLLwings(self):
        return True
    def is_ROLLwings_Device(self):
        ROLLwings_Device = [
                            ["InpR_",[0.0,0.0,0.0],"rotat",0],
                            ["InpL_",[0.0,0.0,0.0],"rotat",0]
                           ]
                   
        if self.ROLLwings < 0 and self.rollSelf == -1:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",-1]]
        elif self.ROLLwings > 0 and self.rollSelf == 1:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings < 0 and self.rollSelf == 0:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings > 0 and self.rollSelf == 0:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]
        elif self.ROLLwings < 0 and self.rollSelf == 1:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings > 0 and self.rollSelf == -1:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]
       
        #print(self.ROLLwings,self.rotatY)
        return ROLLwings_Device
    def is_POINT_ROLLwings(self):
        reper = [-45, 45]
        if 299 < self.WINGS < 549:
            reper = [-30, 30]
        elif self.WINGS > 549:
            reper = [0, 0]
        #print(reper)
        return reper
   
    ####################################
    #Наличие-отсутствие стабилизатора
    def is_PITCH(self):
        return True
    def is_PITCH_Device(self):
        PITCH_Device = [
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvR_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",1],
                      ["ElvR_",[-0.0087,0.0,0.0],"rotat",1]
                     ]
        return PITCH_Device
    def is_POINT_PITCH(self):
        reper = [-17, 49]
        return reper 
           
    #################################################
    #Методы движения, работы ЛОД и прочего
    def engine(self):
        self.__UnitAir_engine.engine(self)
 
def mutate(cont):
    MiG23BN_Libya_(cont.owner)

Самая вкусная строчка почти в самом низу:
self.__UnitAir_engine.engine(self)
Символ __ означает как раз эту самую инкапсуляцию. Вызов идет изнутри самого класса и других объектов не касается. Если посмотреть на класс с методами, то там тоже можно встретить переменные вида __air. Это специфические переменные, сообщающие, например, что у МиГ-23БН один двигатель, крыло с изменяемой стреловидностью, есть тормоза, закрылки и прочее. Это чисто его внутреннее дело.
Теперь же движение объекта из логического кирпича в пусковом файле выглядит так:
ge
import json
import sys
import random

cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
import sys
pathFolder = own.unitName + own.unitNation + "/folderPy"
sys.path.append(bge.logic.expandPath("//Aircraft/" + pathFolder))

unitmodule = own.unitNode + own.unitNation + "NodeScript"
unit_module = __import__(unitmodule)

UNITCLASS = own.unitNode + own.unitNation
UNIT_CLASS = __import__(UNITCLASS)


#Импорт из папки для юнитов ЛА модклей
import UnitAir_engine
import UnitAir_LODes
import UnitAir_shoot
import CONTROL_Operations
import mathutils

import UnitGround_Sensores
import UnitGround_Artillery
import UnitGround_LODes

import Weapon
ArbitrGame = scene.objects["ArbitrGame"]

#############################################
#############################################
def UnitAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #if len(own.idTarget) > 0:
     #   print(own.unitName, scene.objects.from_id(int(own.idTarget[0])).unitName)     
         
   
    if own.controlUnit != "Statist":
        own.globalTargetList = ArbitrGame.UNITS[0][own.targetType][own.target]
        #UnitAir_engine.engine(own, scene)
       
        own.engine()
       
        unit_module.correctData(own)
   
       
    #Работа уровня детализации идет ВСЕГДА
    UnitAir_LODes.LODes(own)
       
   
Думаю, извивы моей мысли понятны - импорттируем класс по названию - MiG-23BN_Libya_.py, а из него тащим метод движения, который, в свою очередь, дергает скрипт движения. Если посмотреть класс, то в начале можно найти строку import UnitAir_engine.  Это он и есть...
Теперь подобные вещи придется делать для оружия, от греха подальше. А то вдруг нацелятся все ракеты типа Р-23Р на один "Фантом" (который еще делать надо).
Простор для инкапсуляции еще есть, но не будем пока о грустном. После отлова ошибки и доводки сриптов, наконец, боты стали вести себя правильно. А именно - разворачиваться в сторону противника, сканировать местность, сбрасывать баки, разгоняться и маневрировать, и даже стрелять... Вновь ожила СПО, заработали звуковые маркеры. Короче, жизнь стала налаживаться.