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

Переворот 12 Термидора. Великая Анимационная КОНТРРеволюция.

В названии этого поста скрывается грустная шутка. В свое время я писал посл "Вкликая Анимационная Революция", в котором подробно описал способ массового проигрывания анимации у объектов-потомков в БГЕ. К сожалению, иногда разработчики Блендера делают ляпы, из-за которых стройная и уже отлаженная система дает сбой. Тем более обидно, когда этот сбой проявляется в новой версии, вынуждая откатываться на старую и лишая тем самым новых плюшек. Так и получилось с версией 2.77. Проигрывание анимации при помощи скрипта там возможно лишь при 2прерывистом" состоянии сенсора - пульсация там должна быть равна 1, как минимум. Постоянно проигрываться анимация не в состоянии - происходит "рваный" переход от начального кадра к конечному по истечении некоторого времени. И если для работы приборов этого вполне хватит, то вод для проигрывания анимаций механики - никакне подходит (и какого художника я перешел со старой доброй ХР на "семерку", сидел бы себе на 2.75 и Виндовс ХР?!).
В свое время denis8424 как-то подначивал меня, задавая вопрос насчет того, когда же я буду просто вращать детали самолетов, не используя анимацию? Тогда я отбрехался сложной траекторией движения и всем таким. Однако жить захочешь - не так раскорячишься. Поэтому решение все же было найдено. Окончательно оно оформилось сегодня, после успешного добавления и отработки функции шасси. По республикансокму календарю сегодня 12 Термидора, получается прямо Термидорианский переворот ("Я тебя породил, я тебя и убью!"(С)). Для начала я принялся "искривлять" или, точнее, "перекашивать" детали самолета. Дело в том, что тормозные щитки, к примеру, расположены на том же Миг-23 не прямо, а под некоторым углом, то же касается деталей крыла. Поэтому я брал деталь, крутил ее, так чтобы передняя кромка становилась "прямой" и потом уже жал Ctrl+A и - Rotation, затем "доворачивал", чтобы деталь укладывалась обратно на место, но ее стартовый угол был уже ненулевым. Таким образом я получил возможность крутить деталь только по одной оси, а не по трем, как в анимации. Далее требовалось понять, как все это безобразие отклонить на нужный угол... Отклонить на нужный угол можно путем отсчета проперти, которое "гуляет" в строго отведенном ему промежутке от минимума до максимума. А ишшо нужна переменная, которая отслеживает, куда проперти идет - вправо или влево. Решалось это путем создания уже привычного словаря свойств и сравнения текущего значенияпроперти со словарным. И когда нужно - эти значения выравнивались. Сама команда н6а поворот детали осуществляется строчкой типа  obj.applyRotation([три цифрычереззапятую],True). Теперь предстояло понять, как эти трицифрычереззапятую выдавать. В Блендере поворот дается в радианах, что несколько неудобно, но справиться с этим можно. Я выбрал основной величиной полградуса. Градус при переводе в радианы давал 0.0174444444444444, я это число укоротил до первой четверки, полградуса выдается 0.087. И вот тогда началось самое интересное. Для наглядности приведу кусочек кода для тормозных щитков.
Команда для тормоза:
if keyboard.events[bge.events.AKEY] == JUST_ACTIVATED:
           
                if self['localDict']['AIRBRAKE'] == self['AIRBRAKE']:
                    self['AIRBRAKE'] += 1
Как видим, клавиша все  время дает прирост проперти на 1. Чтобы не возиться, потому как у разных самолетов времени анимации тормоза может отличаться. А вот как выглядит исполнение "поворотом":
#Псевдоанимации тормозных щитков               
def airbrake():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #Направоение перекладки крыла
    airbrakes = 0
   
    #Ограничения по максимальному и минимальному значению проперти
    if own['AIRBRAKE'] > 100:
        own['AIRBRAKE'] = 99
    elif own['AIRBRAKE'] < 0:
        own['AIRBRAKE'] = 0
   
    #Выставление напрaвления перекладки и убывания-возрастания проперти
    if own['localDict']['AIRBRAKE'] < own['AIRBRAKE']:
        airbrakes = 1
        own['AIRBRAKE'] += 1
    elif own['localDict']['AIRBRAKE'] > own['AIRBRAKE']:
        airbrakes = -1
        own['AIRBRAKE'] -= 1
   
    if own['levelsDetails'] == 0:       
        #Собственно, движение тормозных щитков
        if 0 < own['AIRBRAKE'] < 90:
            own.childrenRecursive['ArbUL_'].applyRotation([-0.0087*airbrakes,0.0,0.0],True)
            own.childrenRecursive['ArbUR_'].applyRotation([-0.0087*airbrakes,0.0,0.0],True)
        if 0 < own['AIRBRAKE'] < 80:
            own.childrenRecursive['ArbDL_'].applyRotation([0.0087*airbrakes,0.0,0.0],True)
            own.childrenRecursive['ArbDR_'].applyRotation([0.0087*airbrakes,0.0,0.0],True)
           
    #Остановка псевдоанимации на кадрах со значением 0,100  
    if own['AIRBRAKE'] in [0,100]:
        own['localDict']['AIRBRAKE'] = own['AIRBRAKE']
       
Прикол тут в том, что в начале функции выставлены ограничители - которые "отбрасывают" проперти в рамки от 0 до ста. Внутри этого промежутка проперти НЕ ОСТАНАВЛИВАЕТСЯ, пока не упрется в границу. как видно из кода, переменная airbrake имеет значения 1 (выпуск тормоза) или -1(уборка тормоза). Оно-то и загнано в строчку applyRotation вместе с величиной разового поворота.
Таким же образом теперь работают практически все детали, кроме основных стоек шасси и гидравлики при этих самых стойках. Хотя с течением времени можно заменить и их. Плохо то, что их - 16 штук на МиГ-23/27, но это меркнет на фоне F-5. Там вообще кошмар. Поэтому я принял решение пока сохранить анимации для такой мелочи, но постепенно по возможности отказаться и от нее.
И пока еще есть нерешенная проблема - со стабилизаторами. Кроме флаперонов, этот вид деталей имеет двойное назначение - они работают синхронно при тангаже и ножницами при крене. Примерно как это решить, я знаю, попробую в ближайшее время с этим справиться...
Помимо войны с анимацией занимался поиском решений по генерации юнитов. Тут впечатления двойственные. С одной стороны кое-как освоил загрузку через json файлы для стартовых установок проперти (которых на двиагтеле почти не осталось - только два таймера). с другой  - хотелось бы иметь к примеру юнит-модель типа МиГ-29 и в папке с ним набор материалов с текстурами разных стран - при генерации присвоить модели нужный материал и вперед. увы. dron, который с некоторых пор натаскивает меня по некоторым аспектам программирования (надо признать, что ученик ему попался туповатый, увы и ах), долго бился над этой проблемой, нашел много чего интересного в бленд-файлах попутно, найти оптимального решения не смог, все это дело можно решить через код на Си плюс, но это уже придется забираться в основы самого кода Блендера. в общем, пока со сменой материалов дело не пойдет. Блендер, как объяснил мне Андрей, не может импортировать независимые от материалов меши (во всяком случае я так понял). Жаль, но отрицательный результат - тоже результат. Попутно dron написал класс для проигрывания звука, к которму я так, к стыду своему по-настоящему еще не подступался - все анимации, блин (хотя гильотина там поработала уже, да (черный юмор)).
В общем пока идет переформатирование уже имеющегося. JSON файлы гораздо лучше читаются и понимаются (в том числе и их создателем, который спустя некоторое время смотрит на свой текстовый файл с палочками и мучительно пытается вспомнить, что означает вот эта четвертая слева цифра). На JSON, скорее всего перейдет вся или почти вся стартовая генерация объектов.
Сама модель создания юнита также претерпела резкие изменения. Замена мешей резкао сократилась. Теперь смена уровней детализации идет путем добавления -удаления родителей с потомками. В принципе, все так и осталось - ведь ранее у меня импортировалась система-скелет для замены мешей, но там тоже были свои группы объектов и они также добавлялись-удалялись. После исчезновения почти всей анимации (шасс в расчет особо можно не брать - оно используется за игру пару раз, а чаще - так и вовсе не используется).отпал смысл в импорте скелета (я не хотед набирать в игровой файл большое количество абсолютно одинаковых анимаций). В общем, новая система ЛОДов - компромисс между продвинутой системой замены мешей, о которой я тут распространялся и системой из первой версии, в которой замены мешей не было. Опять таки цитата:"На всякое придыхательное пыльношлемие мы ответим самым суровым булкохрустом" - разумный консерватизм вполне оправдан. А революции часто или почти всегда заканчиваются контрревоюциями...
Ну вот, хоть одна запись в юле все-таки появилась.

понедельник, 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Р

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






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

понедельник, 30 мая 2016 г.

Поход за консенсусом.

Только что удалось отработать генерацию юнитов на сцене путем чтения файла миссии. Во многом вторая версия своим появлением обязана крайне неудачно выбранному способу создания миссии в первой. Как таковая, миссия создавалась в виде отдельной функции со своим уникальным названием и при старте БГЕ приходилось открывать модуль миссий, а затем построчно читать данные для каждого юнита, генеря их, расставляя и раздавая им потомков и нужные проперти.
Во второй версии все это достигается чтением текстового файла в папке Scenery. Разумеется, от генерации, расстановки и прочего для юнитов не обойтись, но данные теперь не повисают в памяти, к тому же становится на порядок легче добавлять новые миссии и кампании. Пока правда, есть одна загвоздка. Необходимо в меню при щелчках по кнопкам, собирать данные в некую структуру и затем полдученную строчку загонять в файл. Задача решаемая, только несколько занудная - необходимо четко соблюсти порядок записи новых данных, отвечающий вырабатываемым стандартам. Так что если угодно, нужен консенсус (согласие) между меню и игровой сценой. Чем и занимаюсь - сближением позиций заинтересованных сторон, так сказать...
А пока пришло время для красивой картинки - тест на чтение текстового файла (точнее файлов, ибо теперь все проперти и потомки с скоординатами и ориентацией раздаются юнитам сразу после появления, что позволило выбросить лишнюю функцию и убрать лишний объект-потомок, который эту функцию и исполнял). Вместо полусотни сточек кода на эти четыре объекта были потрачены 4 строчки в текстовом файле, плюс некоторое количество строчек для его открытия, чтения и закрытия. В любом случае, дело это окупится.
На скотне ниже - звено ливийских МиГ-23МФ с единообразными подвесками - три ПТБ плюс по паре Р-23Р и Р-60.

суббота, 28 мая 2016 г.

Меню. Ощупью в полдень...

Теоретически создание меню вроде как вещь несложная.Небольшой опыт у меня худо-бедно имелся. Даже для создания меню выбора типа оружия когда-то добрался. Потом, правда все прикрыл, планируя заняться в конце создания первой версии. в итоге работу над первой версией остановил за ее бессмысленностью, и занялся относительно недавно тем же самым во второй версии.
В первую очередь занялся меню редактора миссии, как наиболее сложной частью меню. при создании миссии необходимо:
1. Создать коалиции
2. Выбрать юнит коалиции
3. Выдать юниту оружие (причем учесть все точки подвески).
4. Указать количество юнитов данного типа в группе
5. Цувзвть координаты группы на террайне
6. указать маршрут группы по точкам
7. Указать тип миссии юнитов группы (перехват, патруль, эскорт, БШУ, РБУ, разведка)
8. Собрать все данные в кучу в строго определенном порядке
9. Загнать весь список в большой мега-список миссии.
10. Выбрать следующий юнит и повторить вышеперечисленные пункты.
11. По окончании формирования сценария миссии записать мега-список в текстовый файл.

пока работа подошла к фазе выбора оружия. В моем меню сначала появляется набор флажков участвующих в данной миссии стран (потенциально участвующих). При нажатии на иконку флажка она помечается белой рамкой. Далее, нажав синюю или красную кнопку, отправляем флажок в нужный нам "лагерь" коалиции. По окончании формирования коалиции можно отменить результат или подтвердить, для этого есть кнопки с надписями рядом - Back (Назад) или Аccept(Принять).
После подтверждения появляется меню выбора юнитов и оружия для них. Названия юнитов формируются в виде столбца кнопок с иконками выбранной страны и текстом, цвет которого соотетствует "цвету стороны". При нажатии кнопки юнита появляется надпись Weapons(справа) и под ней кнопки выбора пар подвесок. Для МиГ-23МЛАЭ2 ВВС Лвии, к примеру, точек подвески три (считая нулевую - для подвесных топливных баков), но само оружие размещается на двух парах точек подвески. На скрине как раз показан набор оружия для подкрыльевых пилонов (пара точек подвески за номером 1).
Необходимо как-то отмечать выбранные (нажатые) кнопки юнитов и оружия (можно это сделать изменением цвета, но, скорее всего, это будет белая черта-подчеркивание - на мой взгляд - четче видно).
Пока чтог пишется скрипт управления меню, по мере 2отшлифовки" и вылавливания ошибок в него добавляются все новые и новые блоки генерации и расстановки кнопок, пишутся комментарии и пояснения, зачем все это нужно.. Помимо генерации самих кнопок их еще надо и расставить -так что там еще приходится учитывать координаты кнопок меню, чтобы они не "наезжали" друг на друга и на линии меню. Как обычно, скрины. Немного скучные - я не уделял пока оформлению меню много внимания, да и вообще, по идее, меню надо делать после окончания работы над самой игрой (так я думал раньше), но во второй версии слишком много завязано на меню, поэтому, сделав какой-то минимум в игровом процессе, пришлось браться за кногпки и надписи...
P.S. Пока в меню выбора сторон только два флага - Ливии и Саудовской Аравии- в папках имеются самолеты разной стпени готовности, но я пробовал и другие страны (пустые папки с названиями юнитов - чисто для теста). Все это работало, что позволяет надеяться, что в будущем процесс добавления новых юнитов будет походить быстро и безболезненно (а не так, как в первой версии, да и юниты могут стать какими угодно, к примеру имперский крейсер "Звездный разрушитель", хе-хе)...