среда, 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 под этот самый катапультируемый пуск. А куда деваться.
-Вот ты говоришь: "Мафия! Мафия!". Конечно хотелось бы, чтобы у нас была мафия, но для этого надо много работать, - говорил герой Николая Караченцова в фильме "Дежа Вю". Последую же этому призыву, не мафии ради, но для красивой картинки на экране. Которая непременно появится. Когда-нибудь...