понедельник, 27 июня 2016 г.

И снова о сенсорах...

Так вот получилось, что уже на следующий день после публикации предыдущего поста пишу вновь. Так сказать, мысли вслух...

01_120_60_1.2_1_15 - так выглядит запись в текстовом файле для БРЛС истребителя МиГ-23МЛАЭ2. Для ее расшифровки сначала приведу список проперти юнита - в данном случае - летательного аппарата.
target - "краные" (0) или "синие" (1)
targetScan - тип сканирования - "земля" (1) или "воздух" (0)
sensBRLS - проперти бортовой РЛС (как раз первая строчка из цифр).
sensTP - проперти теплопеленгатора
sensLD - проперти лазерного дальномера
sensTV - проперти телевизионного прицела

А теперь расшифруем символы в трочке, перемежаемые подчеркиваниями.

01 - способность к отслеживанию целей, в данном случае - как воздушных, так и наземных (может быть так, что сенсор приспособлен исключительно для работы по земле или небу - 1 или 0 соответственно).

120 -  дальность действия - 120 км.

60 - угол обзора - в данном случае - 60 градусов

1.2 - пауза между сканирующими импульсами, иначе БГЕ может зависнуть. Величина идет скорее "на глазок, меньше секунды точно не будет.

1 - количество подсвечиваемых целей - означает возможность обстреливать одну или несколько целей сразу ракетами с РЛ ГСН (например МиГ-31 и F-14 долгое время в мире были единственными, способными на такое).

15 - это "мертвая зона"  радара. во многом эта величина взята от балды и нуждается в дополнительной проработке. Пока что мертвая зона вычисляется таким образом - сначала берется высота самого перехватчика, делится на то число и полученный результат сравнивается с текущей высотой цели над данным участком местности (не высотой над нулевой отметкой!!!). Но, скорее всего, придется внести коррективы - не абсолютная высота перехватчика, а относительная, как и у цели. Ну, здесь можно сильно поизвращаться... Главное, чтоб движок выдержал.

Вообще-то "мертвая" зона вводится именно для радара, причем только в режиме прицеливания "воздух". Для ракет же существует своя мертвая зона - по абсолютной максимальной высоте и текущей минимальной высоте над землей. К примеру, старые ракеты типа Р-98 на высоте меньше 2 км уже теряли цель в любом случае, что на фоне земли, что на фоне неба - достаточно было атакуемому "нырнуть" пониже к земле (но в данном случае это было не слишком опасно, от Р-24 увернуться было уже проблематично - требовалось опуститься на высоту ниже 25 метров).
Сейчас идет подгонка формата файлов под новые обстоятельства. Учитывая, что я не слишком сильно заморачивался этим в свое время не успел наплодить кучу юнитов, процесс переделки(доработки) будет не таким сложным, как казалось...

воскресенье, 26 июня 2016 г.

Сенсоры. Боты не должны видеть тебя все время, ибо это нечестно...

Давным-давно как-то раз я просматривал форум, посвященный знаменитой серии "Ил-2" (благодаря которой на Западе таки выучили слово "штурмовик", поняв, что "стормовик" - это неправильно). И был там вопрос по поводу "зрения" ботов. Вопрошающий, получив ответ: "Боты видят тебя всегда", возопил: "Но так нечестно!!!". Вот и я тоже считаю, что нечестно. Разумеется, "мне сверху видно все, ты так и знай" (С), но все же очень не хотелось бы, чтобы бот сумел бы разглядеть тебя, крадущегося, где-нибудь по ущелью прямо сквозь горный хребет... К тому же есть такая штука, как помехи , создаваемые подстилающей поверхностью, которые причиняли на заре развития БРЭО (да и потом) разработчикам этого самого БРЭА и летчикам немало хлопот. Известно, например, что БРЛС "Фантомов" довольно плохо видит цели на фоне земли и неспособна произвести захват и сопровождение цели. Поджобные недостатки имеют и БРЛС МиГ-21, ранних МиГ-23, и в какой-то степени МиГ-25 и F-14(как ни странно). Во время ирано-иракской войны иракцы изрядно проредили парк "Фантомов" ВВС Ирана, применяя тактику засад и приманок. Обычно пара МиГ-23МС или МиГ-23БН начинала либо бомбить и обстреливать позиции наземных войск иранцев, либо демонстративно обращалась в бегство, провоцируя F-4 на погоню. Но убегали иракцы не абы куда, а в заранее оговоренный район, в котором на малых высотах нарезали круги и восьмерки МиГ-25 или Mirage F.1. Их летчики, получив соответствующую информацию с земли или от своих товарищей, работавших в качестве "приманки", шли навстречу  "Фантомам" на предельно малой высоте, РЛС иранцев их не видели. Проскочив под оппонентами, перхватчики выполняли полупетлю с полупереворотом и оказывались на хвосте у "Фантомов". Как правило, когда иранцы понимали, что из охотников они сами превратились в жертву, предпринимать что-либо было уже поздно. За довольно короткое время иранцы недосчитались примерно десятка F-4/Любопытно, что против "Тайгеров" F-5 подобная тактика почти не работала - у них просто не хватало скорости, чтобы хотя бы не отстать от МиГов. Похоже, при схожих же обстоятельствах иранцы потеряли и два "Томкэта" от МиГ-23МЛАЭ-2 (экспортный вариант МЛД, поставлявшийся в Ирак, без "клыков" у неподвижной части крыла) -  во всяком случае, на сайте Тома Купера указывается, что один из F-14 был сбит ракетой Р-60 (учитывая ее очень небольшой радиус действия и высокие летные характеристики  "Томкэта", рискну предположить, что МиГ-23 достал своего противника неожиданно и с задней полусферы, БРЛС "Томкэта" видит очень далеко, но на фоне земли цели различает плоховато).
Ну да ладно, вернемся к проекту, а точнее, к созданию скрипта-сенсора. Когда-то denis8424 создал пару примеров радара с использованием Питона. Я же, всячески модифицируя, дополняя и подгоняя под свои требования скрипт, получил в итоге пару скриптов-монстров, которые, тем не менее, все-таки работали и вокруг них во многом игра и строилась.
Теперь же задачка стояла создать на основе того же скрипта радара более совершенный скрипт-сенсор. Дабы отделить одно от другого, чтобы потом не морщить лоб в попытках вспомнить, что же я имел в виду этой строчкой, я решил в модуле-сенсоре сделать несколько функций, которые будут отвечать за разные способы навигации, прицеливания и ориентирования.
Было сделано ника не меньше дюжины попыток, которые заканчивались иногда более-менее приемлемыми результатами, но, поскольку я толком сам не понимал, чего хотел, изыскания пришлось продолжить. в конце концов я пришел к следующей схеме - вначале идет так называемый сканирующий импульс, который в списке оппонентов отсекает всех, кто не укладывается в конус радара или скрыт за препятствием. А уже затем идет "сопровождающий" импульс, который будет повторяться до тех пор, пока цель не будет сменена, сорвет завхват или будет уничтожена. Была сперва мысль сделать для каждого типа сенсора свою функцию, но пока я от этого отказался (хотя и не окончательно). Сейчас у меня идет работа над отладкой двух типов сенсоров - радара и "взгляда летчика". Но сначала я отладил принцип сортировки юнитов в игре. Занимается этим объект с говорящим названием "Arbitr". скрипт его работы привожу чуть ниже, думаю, все понятно:

 import bge
cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
sens = cont.sensors[0]
    
#Главный список всех задействованных юнитов
listUnit = [[[],[]],[[],[]]]
bge.logic.globalDict['listUnit'] = listUnit
  
#Запись индексов объектов-юнитов в подсписки и общий список
if sens.positive:
    for obj in scene.objects:
          
        if 'target' in obj:
            #Если в имени присутствует 'Air'  - то это - летательный аппарат
            if 'Air' in obj.name:
                if obj.get('target') == 0:
                    listUnit[0][1].append(obj)
                elif obj.get('target') == 1:
                    listUnit[0][0].append(obj)
            #Если в имени присутствует 'Ground'  - то это наземный объект
            elif 'Ground' in obj.name:
                if obj.get('target') == 0:
                    listUnit[1][1].append(obj)
                elif obj.get('target') == 1:
                    listUnit[1][0].append(obj)
    #print(bge.logic.globalDict['listUnit'])

Сортировка идет по части названия объектов - чуть быстрее, как мне кажется, нежели чем искать какое-то проперти в свойствах объектов.
А далее мы имеем на ботах и своем юните проперти targetScan - которое отвечает за просмотр списка наземных (1) или воздушных (0) целей. Анаоргично 0 или 1 работает свойство target, которое, собственно, обозначаете сторону - условно - 0 - "красные", 1 - "синие". Я не стал во второй версии умножать сущности и вводить еще два значения проперти target, для наземки. И так сработает.
На данный момент скрипт радара выглядит так:
import bge
import mathutils

#Сенсор обнаружения и сопровождения цели
def unitSensor():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = bge.logic.getCurrentScene()
    sens = cont.sensors[0]
   
    print(own['PR'])
   
    #Создаем словарь свойств
    if 'unitSensor' not in own:
        own['unitSensor'] = {}
        own['unitSensor']['idTarget'] = own['idTarget'] #Индекс текущей цели
   
   
    if sens.positive:
       
       
        if own['idTarget'] == '' or own['unitSensor']['idTarget'] != own['idTarget']:
            def inConeOfRadar(own, target):
               axisVect = mathutils.Vector((0.0, 1.0, 0.0))
               targetData = own.getVectTo(target)
               targetVect = targetData[2]
               dist = targetData[0]
               angle = own['radarAngle']
               if angle > axisVect.angle(targetVect, None): 
                   rayOwnTarget = own.rayCastTo(target, dist, 'objScene')
                   if rayOwnTarget == None: 
                       #Если тип сканирования - воздушные цели, то дополнительно проверяем еще и "мертвую зону" радара
                       if own['targetScan'] == 0:
                           #Некоторые радары плохо видят цель на фоне земли, поэтому не способны засекать низколетящие цели
                           #Высота "мертвой зоны радара" - по высоте цели над землей
                           deadZoneRadar = own.worldPosition[2]/own['antiEarth']
                           #Введение поправки на мертвую зону
                           targetDeadZone = [target.worldPosition[0], target.worldPosition[1], target.worldPosition[2] - deadZoneRadar]
                           targetPosition = target.worldPosition
           
                           #Мертвая зона радара
                           hitEarth = target.rayCast(targetPosition ,targetDeadZone, deadZoneRadar, 'objScene', 0)               
                           if hitEarth == (None, None, None):
                               return True
                   
                       #В противном случае для наземных объектов такой проверки не требуется
                       else:
                           return True
                   
                   
               else:
                   return False
               return False      
                               
            def aiming():
                cont = bge.logic.getCurrentController()
                scene = bge.logic.getCurrentScene()
                own = cont.owner
   
                ownTargetList = []
                sceneObjList = bge.logic.globalDict['listUnit'][own['targetScan']][own['target']]
                       
                if len(sceneObjList) > 0:
               
                    for target in sceneObjList:
                           
                        if own['distRadarMax']*target['stealth'] > own.getDistanceTo(target):
                            if inConeOfRadar(own, target):
                                ownTargetList.append(target)
                                if len(ownTargetList) - 1 == own['enemy']:
                                    own['idTarget'] = str(id(target))
                    #print(own, ownTargetList, own['idTarget'])
                               
            aiming()
            own['unitSensor']['idTarget'] = own['idTarget']
           
            #
            if own['unitSensor']['idTarget'] == own['idTarget']:
               
               
               
               
                #print(own, own['idTarget'])
                try:
                    ob = scene.objects.from_id(int(own['idTarget']))

                    #if inConeOfRadar(own, target):                                    #Это - условие НАВЕДЕНИЯ на цель
                    vect = own.getVectTo(ob)[1]
                    own.alignAxisToVect(vect, 1, 0.5)
                   
                    vectX = own.getVectTo(ob)[2][0]
                    vectY = own.getVectTo(ob)[2][1]
                    vectZ = own.getVectTo(ob)[2][2]
                   
                    if abs(vectX) < 0.2 and abs(vectZ) < 0.2:
                        own['PR'] = 1
                    else:
                        own['PR'] = 0
                       
                except:
                    pass

Текст будет еще многократно меняться, в него будут внесены поправки  на использование средств РЭБ. Кроме того, отдельно будудт функции баллистического прицела и "взгляда летчика". Впрочем, последняя, уже создана и в ней большую роль играет проперти maxVisibleDist - у "просматриваемых" объектов - максимальная дистанция видимости. Дело в том, что здоровенную тушу Б-52 видно гораздо дальше, чем, скажем маленький Миг-21, поэтому и надо вносить коррекцию на размеры для ботов.
Отдельный вопрос - функции наведения для УР и всевозможныъ КАБов - там необходимо прописать скорость реакции на маневр цели, нужность-ненужность подсветки, условия прекращения наведения, снижение энергии при полете или маневре, реакцию на РЭБ, облака, дым, да много чего. Похоже, все это будет отлаживаться, как и в пераой версии - постепенно - шаг за шагом...

среда, 15 июня 2016 г.

Огонь, дым и взрывы. Новый подход к снарядам...

Как-то так вышло, что я так и не добрался в первой версии до дыма от стартующих НАР. Хотя, как теперь выясняется, из-за короткого времени жизни факела НУРСа дым вполне можно было бы сделать одной частицей, припаренченной к вылетевшей ракете. Что я и сделал. Была у меня текстурка анимированного дыма, явно из трубы какогог-то домика, если всмотреться. Нарыл я ее в Сети так, на всякий случай. Как оказалось, хомяческая привычка тащить все в загашник часто себя оправдывает (я это повторял не раз). В том виде, в каком я текстуру добыл, использовать картинку возможным не представлялось. Из-за черного фона и особенностей наложения рисунка никак не удавалось организовать "истаивание2 дыма, которое играло весьма важную роль. Приведу кусочек кода.

#В этом блоке прописано поведение частицы дыма до и после отсоедиенения   
    if own['tikTimer'] < own['tikPausa']-1:  
        own['propScale'] += own.parent.localLinearVelocity[1]/60
      
    else:
        own.removeParent()
        own['propScale'] += 0.1
       
    own.color = [own['propRGB'], own['propRGB'], own['propRGB'], own['propAlpha']]
    own.worldScale = [own['standartScale'], own['propScale'], own['standartScale']]
   
    #Самоликвидация после вытаивания дыма   
    if own['propAlpha'] < 0.01:
        own.endObject()
    #print(own.color)

С прозрчностью дыма, которая постепенно растет, думаю, понятно. Кроме того, я выполнил свою угрозу насчет автоматического выставления длины частицы дыма в зависимости от скорости ее генератора - об этом говорится в строке с localLinearVelocity. У меня за НУРС и УРВВ работают две разные функции. Для НАР частица одна и некоторое время она прицеплена к ракете - за это отвечают таймеры, их значение сравнивается с временем жизни факела огня - как только переходится порог, частица растет уже не бешеными темпами, а весьма неторопливо, при этом еще и растворяясь в воздухе. По достижении некоторого порога прозрачности иедт самоликвидация отживших свое объектов.
Что касаемо долгоживущих УР, то там частица при появлении сразу приобретает величину скорости носителя, но к нему не прицепляется и ведет себя подобно вышеописанной. Только частиц гораздо больше (но пока на ФПС особо не влияет).
Разумеется, в реале дым тает не столь быстро, но БГЕ все же не настолько силен, чтобы угнаться за большим количеством объектов...
Была решена проблема пуска НАР из блоков. Все упиралось в то, что стартуют они из ячеек и координаты старта ракет не совпадают. В серии SF, из-за которой я и затеял весь этот долгострой, не стали заморачиваться - там НУРСЫ вылетают строго из центра блока. Но мы, русские, простых путей не ищем, нам подавай сложную проблему (которую мы себе же зачастую и сами придумали), чтобы героически ее решить...  Снова кусочек кода.
Строка проперти coordList для Б-8:
 -0.09,0.0,-0.07B0.09,0.0,-0.07B-0.11,0.0,0.03B0.11,0.0,0.03B-0.05,0.0,0.1B0.05,0.0,0.1B
Сие означает набор координат снарядов от 1 и до 20-го, который разделяется маркером  - буквой В (лат). Строчка гораздо длиннее и не самая страшная. Куда более жутко выглядит строка для УБ-32 с его 32 ракетами.
А теперь код извлечения цифр на свет божий:
if 's5' in obj.get('tipSbros'):
                            ownSelf['zalpSbros'] = 1
                            if obj['childBK'] > 0:
                                obj['timerTik'] += 1
                                if obj['timerTik'] == 10:
                                    #Стрельба ведется очередями, отсчет пауз идет через проперти timerMass
                                    #Сам снаряд
                                    dynObj = scene.addObject('dynObj',obj,2500)
                                    dynObj.setParent(obj, False, False)
                                    for i in obj.getPropertyNames():
                                        if i not in dynObj.getPropertyNames():
                                            dynObj[i] = obj[i]
                                        else:
                                            dynObj[i] = obj[i]
                                    #Дымная трасса
                                    smokeLong = scene.addObject('ParticlePlane',dynObj)
                                    smokeLong.setParent(dynObj,False,False)
                                    smokeLong['tikPausa'] = 75
                                    smokeLong['particleType'] = 'SmokeLong'
                                    smokeLong['propAlpha'] = 0.1
                                    smokeLong['propRGB'] = 0.95
                                    smokeLong['standartScale'] = 0.5
                                    #Трассер снаряда
                                    trasser = scene.addObject('UniversalMesh',dynObj,75)
                                    trasser.setParent(dynObj, False, False)
                                    trasser.replaceMesh('FireMissile',True,False)
                                    trasser.visible = 1
                                    trasser.worldScale = [obj['scaleFire'],obj['scaleFire'],obj['scaleFire']]
                                    #Вспышка - лампа
                                    vspyshka = scene.addObject('LampUniversal',trasser,75)
                                    vspyshka.setParent(trasser, False, False)
                                    #Звук снаряда
                                    audioEmitter = scene.addObject('AudioEmitter',dynObj,75)
                                    audioEmitter.setParent(dynObj, False, False)
                                    audioEmitter['audioProp'] = 'Missile'
                                    #Огонь из сопла блока НАР
                                    backFire = scene.addObject('UniversalMesh',obj,6)
                                    backFire.setParent(obj, False, False)
                                    backFire.replaceMesh('FireMissile',True,False)
                                    backFire.worldScale = [obj['scaleBackFire'],obj['scaleBackFire']/2,obj['scaleBackFire']]
                                    backFire.visible = 1
                                    #Вспышка пламени из сопла блока
                                    vspyshkaBack = scene.addObject('LampUniversal',backFire)
                                    vspyshkaBack.setParent(backFire, False, False)
                                    vspyshkaBack.localPosition[1] = -0.75
                                    #Отцепляем снаряд от блока и придаем ему начальную скорость и координаты (длинно, но ничего не поделаешь)
                                    dynObjX = float(obj['coordList'].split('B')[obj['childBK']-1].split(',')[0])
                                    dynObjY = float(obj['coordList'].split('B')[obj['childBK']-1].split(',')[1])
                                    dynObjZ = float(obj['coordList'].split('B')[obj['childBK']-1].split(',')[2])
                                    dynObj.localPosition = [dynObjX,dynObjY,dynObjZ]
                                    dynObj['engineWeapon'] = 'Ballistic'
                                    dynObj.removeParent()
                                    dynObj.localLinearVelocity = [random.randrange(-5,5),obj['speedBullet']+random.randrange(-20,20)+ownSelf.localLinearVelocity[1],random.randrange(-5,5)]      
                                    obj['childBK'] -= 1
                                    obj['timerTik'] = 0

Где dynObjX-Y-Z - и есть требуемое. в самом же коде прописано много чего. Например вспышка факела ракеты, вспышка факела из блока НАР, звук стартующей ракеты, и еще много чего. При авпуска всего арсенала  одной очередью из блока незначительно повышается уровень используемой логики, но ФПС не падает. Ну, во-первых, в код еще много можно чего подчистить, к примеру сделать факел из блока постоянным и его включать по мере необходимости. С копированием проперти я пока еще не возился досконально - там можно копировать не все. Во-вторых, снаряду еще надо прописать бронебойное, зажигательное, кумулятивное и фугасное действие. Эта работа еще впереди, тем более, что блоки можно снаряжать разными типами снарядов. Тут уже придется дописывать комплектацию блоков в файлах инициализации юнитов и оружия - это еще одно объяснение тому, почему я зациклился на МиГ-23. у него довольно широкий набор вооружения и можно отработать применение всех разновидностей оружия.
Мне был нужен более-менее реалистичный старт ракет из блоков и я его получил. Помимо всего мною, наконец, были введены в строй блоки УБ-32. Существует довольно много разновидностей блоков НАР для С-8 и С-5, но у всех у них одинаковый набор координат. Есть еще УБ-16, есть тяжелые пятизарядные блоки для С-13, есть еще иностранные блоки, о которых мне пока мало известно (в смысле размеров). Доберусь и до них как-нибудь.
в отличие от возни с НУРСами, доводка дыма для УР прошла как-то буднично и быстро. Зато я надолго застрял с разрывами от НАР и пушечных снарядов. Причина оказалась банальная - малое время жизни. Дым и огонь смотрелись красиво, но взрывов не было...
Но и эта проблема была решена. В отличие от первой версии, где каждый кадр взрыва вызывался отдельным объектом, во второй версии картинка взрыва показывается путем смены меша взрыва. Причем смена мешей происходит по принципу - "тип взрыва+кадр", который образует название меша, нужного в данный момент. снова кусочек кода:
        try:
            own['kadr'] += 1
            meshName = own['tipSprite'] + str(own['kadr'])
            own.replaceMesh(meshName, True, False)
        except:
            own.endObject()
Функция получилась совсем простенькая. Что касаемо взрывов, то у меня произошла некая "градация". Взрывы от УР, обычных бом или взрыв юнита - довольно большие по времени и количеству кадров. Взрывы от НАР - относительно короткие - 49 кадров вместо 256, а разрывы пушечных снарядов малого калибра -так и вообще 16 кадров. Что позволяет держать ФПС под контролем. За все это отвечает универсальный спрайт, на котором и меняются меши. Величина взрывов прописана в свойстве снаряда, так что здесь тоже все в порядке - взрыв полутонной фугаски куда как более впечатляющий, чем взрыв бомбы-"сотки" (кстати на одном из форумов бывший летчик ИБА назвал ФАБ-100 "мерзкой тварью" - несмотря на небольшой вес ВВ внутри, подброс осколков у нее достигал 99 с линим метров в высоту, и кидать ее следовало не абы как, можно было и самому пострадать).
В заключение - скрины.
 Первый и не вполне удачный тест с дымом - пришлось увеличить исходную прозрачность...
 МиГ-23БН - огонь из ГШ-23Л.
 МиГ-23БН - залп из УБ-32 НУРСами С-5.
 МиГ-23БН - бомбометание - ОФАБ-250 с хвостовых балок.
Как выглядит из кабины взрыв НАРа. Удавалось и сплошную дорожку из огня и взрывов создавать...

пятница, 10 июня 2016 г.

Явно верной дорогой идем, товарищи...

Есть какая-то мистическая взаимосвязь между успешным кодингом и отключением света в моем доме... уже давно заметил, чт стоит создать солидный кусочек нового кода и приготовить солидный задел на будущее, как наши доблестные энергетики гасят свет. Причем надолго. Бывает это нечасто, но совпадения с успешным продвижением  по проекту настораживают)))... Как это было вчера. После некоторой возни я отладил пуски НАР, одиночные пуски ракет типа "воздух-воздух" и приготовился реализовать пушечный огонь, как последовало отключение (давненько такого не было, чтобы на 8 часов). В иоге вчерашний день был потерян.
Сегодня опять занялся оружием и сделал огонь из пушки. А к аркетам присобачил факел огня. Ничего особо сложного там не оказалось, разве что отработал вызов функций из другого скрипта - примитивно, но работает. В начале работающего скрипта пику - import moduleclass
А потом в нужном месте - moduleclass.blablabla(). То есть пишу, какую функцию надобно использовать из загруженного модуля moduleclass. Как всегда, нужные мысли не всегда приходят в нужное время. Что мешало в первой версии  таким образом разбить на функции хотя бы тот же искусственный интеллект ботов и спокойно вызывать нужную функцию в зависимости от ситуации? Но лучше поздно, чем никогда... Пока у меня таким образом работают функции сброса и пуска, плюс функции смены оружия, я сейчас пока стараюсь побыстрее создать "скелет" игры, создавая код в меру своего умения и сопровождая его подробным комментарием, чтобы потом при совершенствовании не вспоминать, чего я там нагородил. ну и стараюсь, чтобы объем поменьше был у пускового файла.  Относительно недавно один пользователь БУ, с которым я списался в ВК, любезно продемонстрировал мне, как можно мыслить абстракциями и что из этого можно извлечь. С помощью Питона. Результат впечатляющий, и, после завершения работы над основной частью кода, я, скорее всго, начну пробовать 2мыслить абстрактно", что должно привести к резкому сокращению кода и должно повысить скорость. К тому же он меня предупредил, что реализованный в БГЕ ныне метод мутации объекта (после создания класса) не совсем оптимален и может вызвать утечку памяти при определенных условиях.  Если честно, я пока в мутации разбираюсь слабо, но это дело придется поправлять.
Пока что на данный момент имеем пуски неуправляемых ракет, сбросы бомб, и пуски пока еще болванок, которые должны стать управляемыми ракетами. Плюс пушка. Из крайне неоходимых операций добавления объектов мне осталось реализовать алгоритм отстрела тепловых ловушек, а также дымные следы. Есть предположение, что можн7о будет добавить дымные шлейфы и к НАРам, от которых я был вынужден в первой версии отказаться. Есть у меня анимированная текстура длинного дыма, которую я и использую. Частица дыма для НАР будет одна- ее длина будет расти сообразно скорости ракеты,а потом частица будет 2отцепляться" от ракеты с исчезновением факела огня и быстро таять... Таким образом, БГЕ не должен сильн перегружаться при массовых пусках ракет. Что касаемо УР большой дальности, то для них дымный след останется тем же, но в алгоритм добавления частиц дыма будет добавлено выставление длины частицы сообразно скорости ракеты. Также это правило будет действовать и для отстреливаемых ложных целей 9ловушек). В первой версии неестественность дыма бросалась в глаза на малых скоростях самолета. К тому же УР будут набирать скорость слежения за целью плавно, а не рывком, как в первой версии - из-за этого "ломаность" линии дыма также бросалась в глаза.
После завершения работы над отстрелом ловушек и дфмами можно будет приступать к возвращению и совершенствованию самонаведения для ракет, а потом очередь дойдет и до бронепробиваемости под разными углами для разных типов снарядов... Работы много еще. а вот после завершения "скелета" игры и отработки взаимодействия объектов уже можно клепать модели и разнообразить технофауну. В первой версии примерно так и было, с той лишь разницей, что новые объекты там добавлялись с большим скрипом. Надеюсь, в этот раз такого безобразия не будет.
А теперь - красивые картинки (ну как красивые, довольно приешиеся, поди уже, с пустнным пейзажем и МиГ-23)...

 Пуск Р-23Р

 Пушечный огонь






 Залп НАРами
 Сброс бомб