среда, 26 июля 2017 г.

Бюрократия. Ревизионизм. Реанимация. Тестирование. МиГ-27К.

Практически все дни, которые протекли со дня позапрошлого поста (не прошлого, а позапрошлого) я занимался бюрократией. А последние дней пять-шесть реанимацией МиГ-27К.
Первоначально я несколько офигел от объема предстоящей работы. Предстояло переписать, дописать, написать или ипоправить уйму json  вооружения для F-15/16, Су-25 и МиГ-27К. Для последнего их написать надо было с нуля, как и файл инициализации с боевыми характеристиками "двадцать седьмого". А еще реанимировать кабину, всадив туда СПО-15 с вытекакющими из этого доделкой скрипта кабины и переделкой скрипта МиГ-27К. Поскольку  у того имеются свои специфические особенности в виде шестиствольной пушки и меньшей скорости, чем у МиГ-23 при всей их схожести.
Впрочем, медленно и методично все json были переписаны - работа в принципе, несложная, но занудная. Может быть, в будущем, я что-нибудь придумаю либо для автоматизации процесса, либо для ликвидации такого большого их числа. Сколько я их написал-дописал-переписал-поправил - сказать затрудняюсь. Много. Особенно для F-16. Поскольку у того богатый набор ракет типа "воздух-земля". Ну ничего, после появления в наборе ракет X-23/25/27 и МиГ-27 с Су-25 тоже основательно разбогатеют...
После написания json тестовой миссии для МиГ-27К последовал отлов ошибок через провреки валидности json - в интеренете  такие инструментоы есть.
А потом пошла проверка-доводка модели МиГ-27К и его кабины. Кстати, кабину пришлось еперсобрать, поскольку БГЕ и Блендер матерятся на старые dds (тут я сам виноват - надо было указывать способ компрессии dxt1 и конвертить с png аддоном в ГИМПе).  Но все же ругань консоли прекратилась, как и выражения по поводу ненайденных объектов. Это первый этап. Второй этап состоит из реанимации телевизионного прицела в кабине, обеспечение прицеливания ракетой "воздух-воздух" с использованием непосредственно ТГСН ракеты (такой опции у меня пока нет, но она обязательна к исполнению, поскольку в жизни даже внешне безобидные турбовинтовые самолеты или учебные спарки типа Л-29 с минимальным набором оборудования на борту такие ракеты использовать могут и представляют угрозу для боевых реактивных машин). Также необходимо приступать к работе по земле и дописыванию ИИ ботов для атак наземных целей с учетом достижений первой версии. как бы ни был примитивен тогдашний мой ИИ, боты довольно уверенно поражали танки, ЗРК и артиллерию, порой сами получая в ответ.
Ниже приведен скрин тестирования МиГ-27К. Еще предстоит отладить стрельбу из пушки и написать в файле инициализации встроенную РЭБ, но самолет уже летает и несет оружие. Что уже хорошо...

А здесь для ознакомления приведен json для ракет Р-27А на первой паре подвесок  МиГ-29.
{"pathBlend":["//Weapon/R-27A/R-27A.blend","//Weapon/FLC_PilonR1_/FLC_PilonR1_.blend","//Weapon/FLC_PilonL1_/FLC_PilonL1_.blend"],
 "pathJSON":{"R-27A":"//Weapon/R-27A/R-27A.json",
             "FLC_PilonR1_":"//Weapon/FLC_PilonR1_/FLC_PilonR1_.json",
             "FLC_PilonL1_":"//Weapon/FLC_PilonL1_/FLC_PilonL1_.json"},
   
"obves":{
         "FLC_PilonR1_|":{"parentObj":"CntAircraft","locObj":[2.241,-1.004,0.094],"rotObj":[0.0,0.0,0.0]},
         "FLC_PilonL1_|":{"parentObj":"CntAircraft","locObj":[-2.241,-1.004,0.094],"rotObj":[0.0,0.0,0.0]},
         "R-27A|1":{"parentObj":"CntAircraft","locObj":[-2.24, -1.6, -0.18],"rotObj":[0.0,0.0,0.0],"weapon":1},
         "R-27A|2":{"parentObj":"CntAircraft","locObj":[2.24, -1.6, -0.18],"rotObj":[0.0,0.0,0.0],"weapon":1}
         }
}

Расшифровка довольно проста. Список pathBlend показывает пути к файлам блендов - пилонов и ракет, которые надо открыть. Словарь pathJSON показывает, какие надо открыть джейсоны с ТТХ оружия и пилонов. В этом словаре перечисляются названия пилонов и ракеты в данном случае - одной ракеты, левого и правого пилона. В словаре obves (термин стырен из терминологии стрелков) идет перечисление названий объектов, их координат, поворота (часто ракеты размещаются под некоторым углом, но иногда без поворота), объект в модели, к которому надо прицепмить ракету или пилон. Отдельно для ракет предусмотрена переменная weapon - это номер повески вооружения. Не всегда он совпадает у всех, есть смешанные варианты подвески ракет, как, к примеру Р-24Р+Р-24Т на поздних вариантах МиГ-23. Плюс иногда необходим  катапультируемый сброс - ракета сначала отделяется от самолета и только потом включается двигатель. Это характерно для полуутопленных ракет на F-4/14/15 или МиГ-31. В этом соучае в джейсон в строчку с оружием после weapon добавляется переменная CatapultSbros. Что сигнализирует о том, что ракета сперва должна слегка "просесть" и только потом лететь.
В общем, на данный момент, дела обстоят на такой стадии...

вторник, 18 июля 2017 г.

Небольшая подборка клипов про авиацию. про авиацию

Небольшая подбока клипов про авиацию и летчиков.

Николай Анисимов "Где-то в небесах"
 https://www.youtube.com/watch?v=I9xkxwSwJBs

Алексей Краев "Жетая дорога"
https://www.youtube.com/watch?v=GMLBTZ8Rygo

Александр Пашанов "Памяти летчиhttps://www.youtube.com/watch?v=aePYEyLdBS0ков"
https://www.youtube.com/watch?v=9XewOH8co2o

Автор неизвестен. Неофициальный гимн стратегической авиации.
"50 Хиросим"
https://www.youtube.com/watch?v=6JgyTpRRJ9A

Николай Анисимов "Грачи прилетели"
https://www.youtube.com/watch?v=fOaTEDYJmi0

ЧиЖ. "Мой Фантом". Очень старая песня, точный автор неизвестен. Существует и еврейский вариант с "зеркальным" изложением событий.
 https://www.youtube.com/watch?v=9XewOH8co2o

"Я - самолет с душою человека, с особой геометрией крыла". автор - Виталий Журба, хотя более известна песня в исполнении А. Маршала.
https://www.youtube.com/watch?v=jRPlsOcUCKU

Николай Анисимов. "Я - летчик". У Анисимова вообще много хороших песен на авиационную тему...
https://www.youtube.com/watch?v=LdbXWehbZRc








воскресенье, 16 июля 2017 г.

Меню, руины и прочая лабуда. Дао дятла.

После недолгого скорбного замешательства, вызванного серией неудачных попыток создать хоть что-нибудь, изображающее меню с возможностью выбора миссии, эту задачу все же удалось решить. Частично. Все упиралось в генерацию кнопок, а также придании им нужных свойств. Очень не хотелось вешать на кнопки логические кирпичи, ограничив все бриками на камере сцены меню. чтобы потом не искать судорожног нужную логику на нужном объекте, да еще и на нужном слое. как водится, с первого и даже с третьего раза не вышло. По моей просьбе dron сделал пример с применением положения курсора мыши. именно в нем и был затык - как-то так вышло, что с курсором мыши я никогда не работал раньше и кнопки меню первой версии обладали своей логикой.
Однако выяснилось, что вменяемое управление кнопками с этим крсором создать сложно - необходимо пересчитывать экранные координаты и мировые координаты кнопок просто так не персчитаешь. В довершение ко всему, игра может быть и не во весь экран, и тд и тп.
Опять извечный русский вопрос - что делать? Ну, раз стандартный курсор не  помогает, можно и свой запилить. Ниже привожу код, в котором есть функция cursorPos -  это и есть самодельный курсор. суть в том, что некий объект на экране постоянно сравнивает положение курсора мыши с положением его же в предыдущем тике (опять словари). Тут идет просто копирование направление движения курсора мыши в нужном направлении. Скорость передвижения самодельного курсора пришлось понизить, а то он летал с бешеной скоростью. Плюс ввести ограничения, чтобы курсор из плейна не вылетал за пределы меню.
import json
#import bgl
#import blf

scene = bge.logic.getCurrentScene()
PlaneCursor = scene.objects["PlaneCursor"]



#Далее идет блок клавиатурных команд
keyboard = bge.logic.keyboard
JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
INPUT_ACTIVE = bge.logic.KX_INPUT_ACTIVE

cont = bge.logic.getCurrentController()
own = cont.owner

if "cursorPos" not in own:
    own["cursorPos"] = {"tempX":0.0,"tempY":0.0,
                        "listButton":[],"animationButton":0,
                        "indexButton":""}



def textureFonLoad(own, nameTexture):
    #bge.logic.expandPath("//Menu/textureMenu/" + nameTexture)
    obj = scene.objects["FonMenu"]
   
    ID = bge.texture.materialID(obj, 'IMStartFon.png')
    object_texture = bge.texture.Texture(obj, ID)
    url = bge.logic.expandPath("//Menu/textureMenu/" + nameTexture)
    new_source = bge.texture.ImageFFmpeg(url)
    bge.logic.texture = object_texture
    bge.logic.texture.source = new_source
    bge.logic.texture.refresh(False)
    obj["FonMenuTexture"] = new_source
   
    
   
def cursorPos():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if own["cursorPos"]["animationButton"] > 0:
        buttonMotion(own)      
   
    mouseNot = cont.sensors["MouseNot"]
   
    if mouseNot.positive:
        if own["cursorPos"]["tempX"] != mouseNot.position[0]:
            PlaneCursor.worldPosition[0] += (mouseNot.position[0] - own["cursorPos"]["tempX"])/4
            own["cursorPos"]["tempX"] = mouseNot.position[0]
        if own["cursorPos"]["tempY"] != mouseNot.position[1]:
            PlaneCursor.worldPosition[1] -= (mouseNot.position[1] - own["cursorPos"]["tempY"])/4
            own["cursorPos"]["tempY"] = mouseNot.position[1]

        if PlaneCursor.worldPosition[0] > 45.0:
            PlaneCursor.worldPosition[0] = 45.0
        if PlaneCursor.worldPosition[0] < -45.0:
            PlaneCursor.worldPosition[0] = -45.0
           
        if PlaneCursor.worldPosition[1] > 25.0:
            PlaneCursor.worldPosition[1] = 25.0
        if PlaneCursor.worldPosition[1] < -25.0:
            PlaneCursor.worldPosition[1] = -25.0
       
       

def on_click(cont):
   
    if cont.sensors["Mouse"].positive:
        #print(own["cursorPos"]["listButton"])
        for button in own["cursorPos"]["listButton"]:
            if button.worldPosition[0]-0.9 < PlaneCursor.worldPosition[0] < button.worldPosition[0]+0.9:
                if button.worldPosition[1]-0.9 < PlaneCursor.worldPosition[1] < button.worldPosition[1]+0.9:
                    own["cursorPos"]["indexButton"] = str(id(button))
                    own["cursorPos"]["animationButton"] = 1
                    buttonMove(own, button)
       
       
def buttonMotion(own):
   
    own["cursorPos"]["animationButton"] += 1
    try:
        button = scene.objects.from_id(int(own["cursorPos"]["indexButton"]))
        if 1 < own["cursorPos"]["animationButton"] < 3:
            button.applyMovement([0.3,-0.3,0.0],True)
        if 3 < own["cursorPos"]["animationButton"] < 5:
            button.applyMovement([-0.3,0.3,0.0],True)
        if own["cursorPos"]["animationButton"] > 4:
            own["cursorPos"]["indexButton"] = ""
            own["cursorPos"]["animationButton"] = 0
            if "GAME_body" in button:
                if button["GAME_body"] == "Stop_Game":
                    bge.logic.endGame()
                elif button["GAME_body"] == "Start_Game":
                    own["startGame"] = 1
    except:
        own["cursorPos"]["indexButton"] = ""

def buttonMove(own, button):
    if "GLOBAL_DICT_name" in button:
        if "GLOBAL_DICT_body" in button:
            if button["GLOBAL_DICT_name"] not in bge.logic.globalDict:
                bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]
            else:
                bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]
               
   

def control():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    listButton = []
    with open(bge.logic.expandPath('//Menu/' + 'Test_Missions' + '.json'), 'r') as directMenu:
        JSONmenu = json.load(directMenu)
   
    nameTexture = "FonSingleMission.png"
    textureFonLoad(own, nameTexture)
   
    for obj in JSONmenu["Buttons"]:   
        newSceneObject = scene.addObject(obj.split('|')[0],own)
        newSceneObject.worldPosition = JSONmenu["Buttons"][obj]["coordObj"]
        newSceneObject.color = JSONmenu["Buttons"][obj]["colorObj"]
        newSceneObject.worldScale = JSONmenu["Buttons"][obj]["scaleObj"]
        own["cursorPos"]["listButton"].append(newSceneObject)
        #Задаем кнопке проперти
        for key in JSONmenu["Buttons"][obj]["propObj"]:
            if key not in newSceneObject:
                newSceneObject[key] = JSONmenu["Buttons"][obj]["propObj"][key]
        if "TextButtons" in newSceneObject.childrenRecursive:
            with open(bge.logic.expandPath('//Menu/TranslationMenu.txt'), 'r') as TranslationMenu:
                for stringer in TranslationMenu:
                    if stringer[0] == "a":
                        if stringer.split('|')[1] == JSONmenu["Buttons"][obj]["textObj"]:
                            newSceneObject.childrenRecursive["TextButtons"]["Text"] = stringer.split('|')[1]
   

В сущности, сначала идет чтение json для расстановки кнопок и придания им нужных свойств - стандартная операция, затем опять стандартная операция - расстановка кнопок и присваивание им свойств - одноразовая вещь (пока) - ничего сложногог. Затем начинает работать курсор. Просто сравнивается положение курсора в момент щелчка кнопкой мыши с положением  кнопок и кнопки дергаются при попадании в некоторую область (buttonMotion), функция buttonMove как раз что-то пишет в глобальный словарь, например, название миссии.
В конце концов, меню заработало, обеспечив выбор аж двух миссий со стартом игры или выходом из нее.
После меню последовало создание наземных статичных объектов. Таковыми были выбраны руины. Из гугловских моделей в качестве времнной (надеюсь) затычки были закачаны с десяток моделей и терепеливо обработаны до удобоваримого состояния в Блендер. Плюс была создана модель аэродрома из одного объекта.
Теперь консоль матерится на лишние UV-развертки. Пока приходится терпеть. Руины и авиабазы были вписаны в json миссий и проврены. пока без маркировки и присущих им совйств.
Есть предположение, что для относительно простых объектов можно реализовать частичное разрушение меша. Условно, конечно, удалять вершины нельзя, но их можно двигать. Суть в том, что при попадании снаряда в нужной области меша все вершины в этой области принимают нулевое значение координаты Зет (по высоте), как бы сплющиваются. а вторым циклом все вершины в данном "квадрате" , находящиеся, скажем, ниже 50, получают прибавку к своим координатам по оси Зет - на месте "целого" ангара (чсти меша) появляются развалины. Правда, для этого, объект должен быть относительно простым. Ну, для простейших кубиков - ангаров, сраев и домиков сойдет...
Далее последовало создание катапультируемого пуска для ракет. Это надо было сделать давно, но руки дошли только сейчас. Суть в том, что сначала ракета отбрасывается от самолета вниз, и только потом включается двигатель. С первой попытки не удалось, но руководствуясь дао дятла, я повторил попытку, специально для такого случая учредив новый тип физического движения в игре - отсроченный старт... В конце концов заработалог...
Вообще все вышеперчисленное не слишком повлияло на уже существующую картинку, поэтому скринов не будет. Зато впереди полно работы по доводке json F-15 под этот самый катапультируемый пуск. А куда деваться.
-Вот ты говоришь: "Мафия! Мафия!". Конечно хотелось бы, чтобы у нас была мафия, но для этого надо много работать, - говорил герой Николая Караченцова в фильме "Дежа Вю". Последую же этому призыву, не мафии ради, но для красивой картинки на экране. Которая непременно появится. Когда-нибудь...

 

суббота, 24 июня 2017 г.

Цветомузыка СПО. Возрождение МиГ-29. Код и картинки...

После успешного завершения работы над кабиной МиГ-23 и восстановлением систем РЭБ, прицеливания и наведения встал вопрос о восстановлении кабины МиГ-29. Кроме него надо было привести под общий стандарт F-5E и F-15C. Дабы оппоненты были более разнообразными. Это было проделано, правда половинчато. У "Тайгера" надо дописывать скрипт псевдоанимаций шасси (что меня не приводит в восторг из-за его сложности, точнее, многословности). Для МиГ-29 были сменены "шкурки" камуфляжа, а потом еще и кабины.

Дело в том, что Блендер начал ругаться на потерю способа компрессии dds, как я это понял из сообщений консоли. По совету Андрея (aka dron) я применил компрессию типа dxt1 после установки аддона для dds в ГИМПе, причем конверитровал картинки типа png. Пока ругань консоли ограничивается моими ошибками в коде, что радует. В сущности, кабина МиГ-29 особо не изменилась, если смотреть на текстуры. А вот ИЛС, работа приборов и СПО претерпели некоторые изменения. Особенно много возни было с системой предупреждения об облучении СПО-15. В отличие от СПО-10 в ней на порядок больше ламп и гореть должны только нужные.Первая версия СПО-15 двухлетней давности, если мнен не изменяет память, была мною здесь выложена, она длинная и выполнена методом "против лома нет приема" с помощью "дао дятла" - методичное построчное перебирание ламп, пусть и с некоторыми исключениями части кода с помощью условий. Меня не радовала перспектива  повторить сей подвиг, тем более, что вводные данные изменились и перебирать строчки в поисках ненужных - да ну на фиг...
Вместо этого я обратился к спискам и сравнению списков. Я разбил имена объектов или сами объекты по спискам, к примеру лампы дистанции до угрозы, лампы вектора на угрозу, и тд. Некоторые списки содержат в именах объектв для сравнения например LampThreat60km. смотрим на последние символы в названии (в действительности оно немного другное, но суть та же). Режем имя методом split, чтобы "вышелушить" число 60. Чтобы строку превратить в число применяем int и умножаем на 1000. Все. Если в данных дистанция до угрозы больше 60000, зажигаем лампу с этим названием, если меньше - гасим.
С вектором на угрозу пришлось поступить хитрее. Кроме списка ламп для индикации вектора на угрозу был создан список,э-э, секторов угрозы вида  [[-0.1,0.1],[-0.5,0.5]],[опять цифры во вложенном списке]. Список содержит примерно 10 элементов, в которых есть два вложенных списка - это вектор на угрозу справа и слева и вектор на угрозу спереди и сзади. На входе получается вектор на угрозу и проверяется на "укладывание" в некоторые условия, заданные этим списком с квадратными скобочками. Если данные вектора ПОЛНОСТЬЮ соответствуют вложенным спискам, то лампа горит, иначе - гаснет А прикол в том, что список секторов угрозы по последовательности соответствует списку лампочек. Все делает цикл. Ламп примерно дюжина, так что БГЕ несильно напрягается, если учесть, что СПО работает раз в пару секунд. В итоге код резко "съежился".
А теперь код для СПО-15. На лишние списки внимания не обращать - они для других функций.
 import bge
import random
import mathutils
import  CONTROL_gamer

A_X = 0
#Далее идет блок клавиатурных команд
keyboard = bge.logic.keyboard

JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
INPUT_ACTIVE = bge.logic.KX_INPUT_ACTIVE 

scene = bge.logic.getCurrentScene()
sceneCockpit = scene

pitchListDigital = [scene.objects["pitchILS0"],
                    scene.objects["pitchILS1"]]
           

spo15lampGrad = ["SPO-15grad10R","SPO-15grad30R","SPO-15grad50R","SPO-15grad90R",
                 "SPO-15grad10L","SPO-15grad30L","SPO-15grad50L","SPO-15grad90L",
                 "SPO-15LampLzps","SPO-15LampRzps","SPO-15PPS"]

spo15lampGreen = ["SPO-15green10R","SPO-15green30R","SPO-15green50R","SPO-15green90R",
                 "SPO-15green10L","SPO-15green30L","SPO-15green50L","SPO-15green90L",
                 "SPO-15greenLzps","SPO-15greenRzps","SPO-15PPS"]
                

spo15Grad = [[[0.01, 0.1],[0.0, 1.1]],[[0.1, 0.3],[0.0, 1.1]],[[0.3, 0.55],[0.0, 1.1]],[[0.55, 1.0],[0.0, 1.1]],
             [[-0.1, -0.01],[0.0, 1.1]],[[-0.3, -0.1],[0.0, 1.1]],[[-0.55, -0.3],[0.0, 1.1]],[[-1.0, -0.55],[0.0, 1.1]],
             [[-1.0, 0.0],[-1.1, 0.0]],[[0.0, 1.0],[-1.1, 0.0]],[[-0.01, 0.01],[0.0, 1.1]]]

spo15lampDist = ["SPO-15Lamp65km","SPO-15Lamp60km","SPO-15Lamp55km","SPO-15Lamp50km",
                 "SPO-15Lamp45km","SPO-15Lamp40km","SPO-15Lamp35km","SPO-15Lamp30km",
                 "SPO-15Lamp25km","SPO-15Lamp20km","SPO-15Lamp15km","SPO-15Lamp10km",
                 "SPO-15Lamp5km","SPO-15Lamp1km"]
                
spo15typeThreat = ["SPO-15AirObj","SPO-15ZRKBD","SPO-15ZRKSD","SPO-15ZRKMD","SPO-15DRLOW","SPO-15DRLOZ"]
                

textFuel = [scene.objects["fuel0"],
            scene.objects["fuel1"],
            scene.objects["fuel2"],
            scene.objects["fuel3"]]

textSpeedTarget = [scene.objects["targetSpeed0"],
                   scene.objects["targetSpeed1"],
                   scene.objects["targetSpeed2"],
                   scene.objects["targetSpeed3"]]
           
textHeightTarget = [scene.objects["targetHeight0"],
                    scene.objects["targetHeight1"],
                    scene.objects["targetHeight2"],
                    scene.objects["targetHeight3"],
                    scene.objects["targetHeight4"]]
                   
textSpeedOwn = [scene.objects["ownSpeed0"],
                scene.objects["ownSpeed1"],
                scene.objects["ownSpeed2"],
                scene.objects["ownSpeed3"]]
           
textHeightOwn = [scene.objects["ownHeight0"],
                 scene.objects["ownHeight1"],
                 scene.objects["ownHeight2"],
                 scene.objects["ownHeight3"],
                 scene.objects["ownHeight4"]]


def spo15(own):
   
    """ 
    bge.logic.globalDict['SPODATA'][0] = own.getDistanceTo(threat)
    bge.logic.globalDict['SPODATA'][1] = threat.LockOn
    bge.logic.globalDict['SPODATA'][2] = threat.PR
    bge.logic.globalDict['SPODATA'][3] = threat.avto
    bge.logic.globalDict['SPODATA'][4] = own.worldPosition[2] - threat.worldPosition[2]
    bge.logic.globalDict['SPODATA'][5] = own.getVectTo(threat)[2][0]
    bge.logic.globalDict['SPODATA'][6] = own.getVectTo(threat)[2][1]
    bge.logic.globalDict['SPODATA'][7] = threat.unitName
    bge.logic.globalDict['SPODATA'][8] = threat.subTypeUnit
    """
   
    if bge.logic.globalDict['SPODATA'] != [0.0, 0, 0, 0, 0.0, 0.0, 0.0, "", ""]:
        for element in spo15Grad:
            #print(bge.logic.globalDict['SPODATA'][5],bge.logic.globalDict['SPODATA'][6])
            if element[0][0] < bge.logic.globalDict['SPODATA'][5] < element[0][1] and element[1][0] < bge.logic.globalDict['SPODATA'][6] < element[1][1]:
                scene.objects[spo15lampGrad[spo15Grad.index(element)]].visible = 1
                if bge.logic.globalDict['SPODATA'][2] == 1:
                    scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 1
                else:
                    scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 0
               
            else:
                scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 0
                scene.objects[spo15lampGrad[spo15Grad.index(element)]].visible = 0
       
        CONTROL_gamer.SPOaudio()
       
        #Распознавание типа угрозы
        for typeThreat in spo15typeThreat:
            if bge.logic.globalDict['SPODATA'][8] in typeThreat:
                scene.objects[typeThreat].visible = 1
            else:
                scene.objects[typeThreat].visible = 0
       
        if bge.logic.globalDict['SPODATA'][1] == 1:
            scene.objects["SPO-15alarm"].visible = 1
            if bge.logic.globalDict['SPODATA'][4] > 0:
                scene.objects["SPO-15alarmUp"].visible = 1
                scene.objects["SPO-15alarmDown"].visible = 0
            else:
                scene.objects["SPO-15alarmUp"].visible = 0
                scene.objects["SPO-15alarmDown"].visible = 1
        else:
            scene.objects["SPO-15alarm"].visible = 0
            scene.objects["SPO-15alarmDown"].visible = 0
            scene.objects["SPO-15alarmUp"].visible = 0
           
        #Дистанция до угрозы
        for distObj in spo15lampDist:
            #if distObj in scene.objects:
               
                if int(distObj.split("SPO-15Lamp")[1].split("km")[0])*1000 < bge.logic.globalDict['SPODATA'][0]:
                    scene.objects[distObj].visible = 1
                    #print(scene.objects[distObj].visible)
                elif int(distObj.split("SPO-15Lamp")[1].split("km")[0])*1000 > bge.logic.globalDict['SPODATA'][0]:
                    scene.objects[distObj].visible = 0

    #При обнулении угрозы все лампы выключаются
    else:
        scene.objects["SPO-15alarm"].visible = 0
        scene.objects["SPO-15alarmDown"].visible = 0
        scene.objects["SPO-15alarmUp"].visible = 0
       
        for obj in spo15lampGrad:
            scene.objects[obj].visible = 0
        for obj in spo15lampGreen:
            scene.objects[obj].visible = 0
        for obj in spo15lampDist:
            scene.objects[obj].visible = 0
        for obj in spo15typeThreat:
            scene.objects[obj].visible = 0
           
Ободренный успехом с СПО-15, я принялся за ИЛС. В сущности, досточно стандартный набор вращений, замены мешей и так далее.

Вот только элементов много. Для цифр со сменой мешей пришлось ввести ограничение по исполнению - раз в 19 тиков, чтобы не утруждать БГЕ.

Из нововведений отмечу отклонение вверх вниз в некотором диапазоне указателя тангажа на ИЛС относительно линии горизонта, что показывает, вверх или вниз отклонен нос самолета. Кроме того рядом с линией горизонта на ИЛС появился цифровой указатель тангажа, замеряющий оный в пределах от минус до плюс 15 градусов. Также был введен более реалистичный монитор радара. По сути - это дубль ИЛС с индикатором крена и бегающей по экрану меткой цели. Круговой радар я делать непосредственно в кабинах не буду - нет смысла. Хватит и карты обстановки (которую тоже надо делать).
В итоге Ф-15 стали уверенно поражаться ракетами Р-27Р, СПО весело мигает лампочками и начинает орать при малейшем поводе, цифры на ИЛС мелькают, стрелочки крутятся, пушка тарахтит, ловушки отстреливаются с шипением и дымом...
Как общий результат - получена вторая более менее работоспособная миссия, отреставрирован МиГ-29 и не только он, изничтожено некоторое количество багов и получен задел на будущее. Дело в том, что СПО-15 стоит на МиГ-23МЛД, Су-33, Су-27. Су-25, Су-17М4, МиГ-27М/Д/К, а ИЛС МиГ-29 идентична ИЛС Су-27 и части его модификаций, вроде того же Су-33...

понедельник, 5 июня 2017 г.

Время собирать камни...

Перестройка под классы в общем-то, закончена. Была создана отключаемая РЭБ -при наличии таковой ее можно включить или выключить. Было переделано все, что касается обмена данными об угрозах, что обеспечило стабильную работу СПО. Были доведены до рабочего состояния самонаводящиеся ракеты. интересно, что в версии 2.78 физика динамических объектов изменилась. Если раньше при наличии "толкающей" силы или линейной скорости по оси Х или Y объект спокойно двигался по горизонтали без опоры и падать не собирался, то теперь без опоры он просто падает. Поэтому для ракет пришлось физику отключать совсем, оставив динамические свойства лишь для бомб и подобных им объектов. Для чего сие нововведение понадобилось - тайна скрытая мраком. Во всяком случае пару недель на осознание этой "фичи" с поиском решения ушло...
Была создана тестовая версия под все эти вещи - ссылку можно найти на b3d.ua. Здесь я ее не даю, поскольку вещь весьма сырая и представляет интерес разве что с точки зрения истории проекта. тем более, что и весит архив около 450 Мб.
Сейчас идет работа по восстановлению того, что было в версии 2015 года, а именно - подключение юнитов, пока только самолетов. Планируется сделать пару-тройку тестовых миссий  с возможностью выбора из меню. Кстати, меню я частично реанимировал, но его практическая ценность пока околонулевая. Для создания тестовых миссий необходимо восстановить и поправить F-5E, F-15C, F-16C, МиГ-29А и МиГ-21бис. В сущности, F-15/16 готовы, все дело упирается в файлы загрузки подвесок. Дело муторное и нудное, хотя и несложное. "Пятерка" же по имени "Тайгер" , как и МиГ-29 пока нуждается в доводке - сама модель и скрипт псевдоанимаций. МиГ-21 вообще новый юнит, в версии 2015 его не было даже близко. Его модель готова, но пока нет кабины и опять же скрипт псевдоанимаций с файлами загрузки оружия. В основном, сейчас все идет по написанию json, хотя пилоны и баки для МиГ-21 были распиханы по папкамс названиями и туда же вставил json  с их массами и лобовым сопротивлением.
По мере доводки и гготовности поланирую создать раздел миссий "Одноклассники". Суть в том, что в воздухе встречаются самолеты примерно "одной весосвой категории", список спарринг-партнеров примерно такой: Ф-5:МиГ-21, Ф-4:МиГ-23, Ф-16:МиГ-29, Ф-15:Су-27. Аналогично в перспективе Ф-104:Су-15, Ф-100:МиГ-19, Ф-86:МиГ-15, Ф-18:Су-33. Но это пока только планы. Делать прогнозы не берусь.
К тому же необходимо заняться доработкой ландшафта и освещения, шейдером неба и звуковым классом. Много чем заняться...

воскресенье, 7 мая 2017 г.

Перестройка идет своим чередом...

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

вторник, 2 мая 2017 г.

Теория классовой борьбы-2. Метод мавра.

В прошлом посте я делился новообретенными знаниями о создании класса и изощрялся в раздаче данных в этом самом классе. И все бы хорошо - все работало, самолет летал, пускались ракеты, работал кокпит... Словом все было замечательно. Однако тут  при добавлении оппонентов вышла неувязочка. Сначала я к своему удивлению, обнаружил упорное нежелание модели МиГ-23МФ добавлять высокодетализированные меши. Погтом вдруг обнаружилось, что кабины нет и управления, кстати, тоже нет. И при всем этом консоль упорно выдавало отсутствие какой-либо ошибки. Еще раз вспомнив бессметрное изречение O.din13: "Боендер не тупит - тупят люди", я приступил к исследованию сего бага. В конце концов, пришлось признать, что дал маху. И весьма ощутимого. Ошибка была в том, что в БГЕ создается ОДИН класс на ВСЕ юниты типа "летательный аппарат". Но при этом его данные меняются в масштабах ВСЕЙ сцены, а не одного конкретного объекта. В итоге вместо пары МиГ-23МФ и пары Ф-16 мы получаем 4 самолета одного типа - чья сточка с данными будет прочитана последней. И это был Ф-16. Но вот добавление "основы" юнита у меня идет независимо от значения в классе, в итоге получаем Ф-16 с нормально работающей моделью и Миг-23 с ЛОД-основой, но тоже вроде как Ф-16! Прикол, ничего не скажешь...
Пришлось некоторое время поломать голову. В итоге, следуя дао дятла и с остервнением пробившись сквозь дебри своего же кода, получил работающую модель. Надо заметить, что у меня идет подключение и использование скриптов-модулей в некоем "коммутаторе". Чтобы совсем уж не ломать существующую структуру с неясным исходом, я применил старый испытанный прием - "против лома нет  приема".

Класс у меня стал простым и суровым:
import bge

class air(bge.types.KX_GameObject):
   
    def __init__(self, old_owner):
       
        self.nameIndex = str(id(self))
        self.WINGS = -1
        self.Temp_WINGS = -1
        self.FLAPS = 0
        self.Temp_FLAPS = 0
        self.SLATS = 0
        self.Temp_SLATS = 0
        self.CANOPY = 0
        self.Temp_CANOPY = 0
        self.AIRBRAKE = 0
        self.Temp_AIRBRAKE = 0
        self.CHASSY = 0
        self.Temp_CHASSY = 0
        self.ROLL = 0
        self.Temp_ROLL = 0
        self.YAW = 0
        self.Temp_YAW = 0
        self.PITCH = 0
        self.Temp_PITCH = 0
        self.ROLLwings = 0
        self.Temp_ROLLwings = 0
        self.avto = 0
        self.Temp_avto = 0
        self.PR = 0
        self.Temp_PR = 0
        self.LockOn = 0
        self.enemy = 0
        self.weapon = -1
        self.Temp_weapon = -1
        self.crash = 1.0
        self.Temp_crash = 1.0
        self.correctSpeedAngleOfAttack = 0.0
        self.levelsDetails = 3
        self.Temp_levelsDetails = 3
        self.angleOfAttack = 0.0
        self.ownType = 0
        self.sensEyesscanTimer = 300
        self.rotatX = 0.0
        self.rotatY = 0.0
        self.rotatZ = 0.0
        self.rollSelf = 0
        self.pitchSelf = 0
        self.yawSelf = 0
        self.antiForce = 0.0
        self.massFuel = 0.0
        self.massChild = 0.0
        self.massFuelTank = 0.0
        self.ownAntiForce = 0.0001
        self.lovushki = 0
        self.correctRotat = 1.0
        self.correctSpeed = 1.0
        self.slideRotat = 0.0
        self.speedTemp = 0.0
        self.typeShtopor = "NULL"
        self.Temp_BK = 0
        self.BK = 0
        self.typeSensor = 0
        self.targetType = 0
        self.timerImpulse = 0
        self.sonicBoom = 0
        self.idTarget = []
        self.PuskSbros = 0
        self.Temp_PuskSbros = 0
        self.indexRadarDist = 0
        self.timerShoot = 0
        self.classWeapon = ""
        self.nameWeapon = ""
        self.Temp_typeSensor = 0
        self.localTargetList = []
        self.threatWeapon = []
        self.sbrosInterval = 0
        self.ochered = 0
        self.Temp_ochred = 0
        self.dictWeapon = {}
       
        self.controlUnit = ""
        self.keyDict = ""
        self.bortNumber = 0
        self.target = -1
       
        self.typeMissions = ""
        self.Marshrut = []
        self.ownGroup = ""
        self.targetGroup = []
        self.escortGroup = []
        self.statusDict = {}
        self.ownBase = ""
        self.levelsSkill = 100
               
        self.THREAT = {"UnitAir":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]},
                       "UnitGround":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]},
                       "tnreatWeapon":[]}
       
        self.unitName = ""
        self.unitNation = ""
        self.unitModule = ""
        self.speedMax = 0.0
        self.heightMax = 0
        self.speedVert = 0
        self.startSpeed = 1.1
        self.limitRoll = 1.1
        self.limitPitch = 1.1
        self.alarmAngleOfAttack = 0.0
        self.globalRotatPitch = 0.005
        self.speedStall = 0.0
        self.angX = 0.0
        self.angY = 0.0
        self.angZ = 0.0
        self.rotatDyn = 0.0
        self.dvigDyn = 1
        self.MaxPower = 0.0
        self.maxFuelSpeed = 0.0
        self.massFuelOwn = 0.0
        self.massOwn = 0.0
        self.antiECM = 0.0
        self.stealth = 0.7
        self.maxVisibleDist = 0
        self.ownECM = 0.0
        self.lovushki = 0
        self.Temp_lovushki = 0
        self.sensorList = ["Eyes"]
        self.WINGS = -1
        self.Temp_WINGS = -1
        self.dvig = 0
        self.dvigRight = 0
        self.dvigLeft = 0
        self.Temp_dvig = 0
        self.Temp_dvigRight = 0
        self.Temp_dvigLeft = 0
        
        self.etalonDvig = 0
        self.typeManeur = ""
        self.bot = 0           
             
      
def mutate(cont):
    air(cont.owner)

В нем происходит появление всех атрибутов с исходными значениями. Сообсветственно, все юниты при рождении - братья-близнецы. Потом они получают свои качества...
Повозившись с открытиями-закрытиями json, я махнул рукой и стал при появлении юнитов цеплять к ним 2репер", хотя его можно назвать и "ярлыком" (на княжение, стало быть, угу). В этом ярлыке командой типа yarlyk["startDict"]= {} я создавал стартовый словарь и ссыпал туда из json-ов данные. Не абы как, чтобы потом можно было разобраться. В итоге юнит уже имел потомка при появлении, а впотомке было записано все, что нужно. Мне пришлось ввести еще пару бриков для стартовой инициализации юнита - разовая операция по чтению словаря потомка приводила к появлению у объектов одного класса разного набора характеристик. Ничего сложного - просто используем setattr или __dict__.
#Сборка атрибутов юнита
def startInit():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    for key in own.childrenRecursive["confaUnit"]["confaUnit"]["startProp"]:
        setattr(own, key, own.childrenRecursive["confaUnit"]["confaUnit"]["startProp"][key]) 
   
    setattr(ArbitrGame, own.keyDict, own.childrenRecursive["confaUnit"]["confaUnit"])
   
    with open(bge.logic.expandPath(own.childrenRecursive["confaUnit"]["confaUnit"]["unitPath"]), 'r') as directClass:
        JSONtechnic = json.load(directClass)
        for key in JSONtechnic["listPropertys"]:
            setattr(own, key, JSONtechnic["listPropertys"][key])
            #print(getattr(own, key))
   
    if own.controlUnit == "Gamer":
        gamerConfig = ["a_x","a_y","a_z","Temp_a_x","Temp_a_y","Temp_a_z"]
        for key in gamerConfig:
            setattr(own, key, 0.0)
           
        setattr(own, "timerFuel", 0)
        setattr(own, "colorHUD", 0) 
           
        sensor1 = scene.objects['SENSOR1']
        sensor2 = scene.objects['SENSOR2']
        sensor3 = scene.objects['SENSOR3']
        #Расстановка сенсоров пустышек для отслеживания ориентации в пространстве
        sensor1.setParent(own, False,False)
        sensor1.worldOrientation = own.worldOrientation
        sensor1.localPosition = [0.0, 1.0, 0.0]
        sensor2.setParent(own, False,False)
        sensor2.worldOrientation = own.worldOrientation
        sensor2.localPosition = [1.0, 0.0, 0.0]
        sensor3.setParent(own, False,False)
        sensor3.worldOrientation = own.worldOrientation
        sensor3.localPosition = [0.0, 0.0, 1.0]

        if 'Cockpit' not in bge.logic.getSceneList():
            bge.logic.addScene('Cockpit',1)
            unit = own.unitName + own.unitNation
            typeCockpit = "//Aircraft/" + unit + "/Cockpit_" + unit + "/Cockpit_" + unit + ".blend"
            bge.logic.globalDict["cockpitPath"] = typeCockpit
   
    elif own.controlUnit == "Statist":
        own.ownType = 1
   
    #Убирем более ненужный реперный объект
    own.childrenRecursive["confaUnit"].endObject()              
   
    #Обязательно заносим юнит в списки "лагерей", сортируя по ходу дела
    ArbitrGame.UNITS[1].append(own)
    ArbitrGame.UNITS[0][own.ownType][own.target-1].append(own)  
    #print(ArbitrGame.UNITS)
   
Судьба репеного объекта соответствует известному изречению:"Мавр сделал свое дело..."
Такая вот поправочка вышла.
Кстати, аналогично я поступил и с оружием. Мне только не хватало, чтобы пущенная мною Р-24Р вдруг трансформировалась в "Сайдуиндер".
Сейчас идет возня с оживлением ботов - с их ИИ и возвращению сенсоров. с сенсорами придется попотеть - во-первых, изменились некоторые названия, во-вторых, где-то скрывается ошибка, возможно, опечатка, сейчас я переписываю скрипт, заодно убирая ставшие ненужными моменты.
Удалось также отладить камеру с ее переключениями - имеется в виду внешний обзор, а также восставноить текстовую маркировку юнитов - по их названиям, лагерю и дистанции.