Показаны сообщения с ярлыком аркада. Показать все сообщения
Показаны сообщения с ярлыком аркада. Показать все сообщения

суббота, 14 декабря 2019 г.

Дорогу осилит ползущий...

Долгое время не писал. Особо не о ч ем было. Как-тог подзабросил дела - очередное лечение с его порцией уколов в глаза, виски и прочие места, чтение накопившихся книг, ползанья по Сети в поисках учебников, чтение оных же, поиск информации и примеров. Да и лень-матушка, куда ж без нее...
На днях выправил ситуацию с неправильной индикацией выбранного оружия на подвесках самолета игрока. Как оказалось, задача была не такой страшной, как виделось. Для этого понадобилось ввести в стартовый json летательного аппарата еще одну переменную, раскомментировать строку в одном скрипте, добавить пару строк в другом. В итоге теперь информация с названием и оружия и его БК послушно высвечивается при перебре оружия и гаснет при выключении системы управления вооружением. Вот так:
Цель - F-16C ВВС США, удаление 24 км, тип оружия - УРСД Р-23Р, БК-2, режим - РЛС с дальностью обнаружения - 60 км.

понедельник, 2 октября 2017 г.

Размышления о наземных объектах-2. Как бы все это разрушить...

Как обычно, после перерыва в использовании блога по причине израсходованого трафика, узнаешь много нового... Так, сегодня freenome отказался пускать меня в мой же блог. Это было для меня в первый раз, но не первый раз в Сети. быстро пробежавшись по сети, нашел упоминания о подобном саботаже, причем не только freenome, но и других хостингов. Ну раз так, то вникать я в причины блокировки не стал. Это достаточно хроническое явление для бесплатных доменов и у меня нет времени и сил, чтобы вникать в тонкости взаимоотношений сайтоы, блогов и хостингов. спросил у dron-а, что с этим делать и утвердился в мысле, что надо просто убрать использование домена из настроек. Надеюсь, получилось и эти строчки могут прочитать все остальные.
Несколько дней я пытался нащупать хоть какую-то схему воздействия на игровые объекты. Задача состояла в том, что разные типы боевых частей по-разному воздействуют на объекты. В конце концов, пришел к выводу, что объект должен обладать неким атрибутом, назовем его "уровнем боевой устойчивости". Или crashDefens, как-то так. Сей уровень характеризуется неким порогом, ниже которого поразить объект оружием с низким ТТХ невозможно. Например, как-то натолкнулся на вопрос:"Может ли ЗУ-23-2 остановить танк?". Ответ воевавшего в Чечне танкиста был таков: "Может. Если экипаж танка ее заметит и остановится. Для прицеливания..."
Вывод:
В оружейные ТТХ надо вводить уровень  "преодоления защиты", эффективности, что ли, так скажем.
И тут начинается самое нитересное.  Нужен какойто эталон, от которого можно отталкиваться.  И в качестве эталона была выбрана бомба ФАБ-100. Известно, что ее радиус поражения (сплошной) составляет 12 метров. Вообще в самих бомбах где-то половина веса - это металл, остальное - взрывчатка. Ну что ж, берем 50 кг за 0.1 . Извращение, конечно, ну ладно, в конце концв изготовлять эталон веса или длины мне не надо. Примем за основу. С бомбами далее оказалось проще - 28 метров для ФАБ-250 и 40 - для полутонных бомб. исходя из этого я и правил показатель...
предполагаю, что внутри этого радиуса сплошного поражения наземные юниты уничтожаются, если, это, конечно, не бункер с уровне защиты 0.5. Тому же "Абрамсу" будет фиолетово, что упало ему на башню - ФАБ-100, 250 или ОФАБ-250. А вот потом...
На расстоянии двойного радиуса поражения эффективность оружия падаеь  вдвое,  еще дальше - вчетверо. Но, если рядом с "Абрамсом" в момент взрыва стоял еще один танк и БМП в зоне падения ущерба, то порог воздействия на танк преодолен не будет, а вот БМП может и выйти из строя после попадания осколков и удара взрывной волны.
Понимаю, сии измышления неплохо бы подкрепить кодом, но был занят - срочно вписывал в json бомб и УРВВ  данные об их могуществе и типе БЧ.
Прикол заключается в том, что БЧ ракет "воздух-воздух" на три четверти состоят из поражающих элементов и только четверть забирает взрывчатка. В общем-то опять натягивание совы на глобус получается, но вменяемой и систематизированной информации по этому вопросу нет. нет, найти массу БЧ ракет не проблема, но далеко не всегда указывается вес ВВ. Ну ладно, решил так сделать.
Объем работ оказался приличный. А впереди еще ракеты "воздух-земля"...
Попутно выяснил еще ое-каие вещи. Например, что ракета Р-27ЭМ заточена под перехват КР "томагавк" или ПКР "Гарпун". А вот ракета Р-27ЭП специализируется на выбивании постановщиков помех. Это дело надо учесть. Еще ракетами типа Р-77, Р-73, Р-77 и AIM-9X можно организовать персональное ПРО, отстреливая пущенные по тебе ракеты.
Также попалась серия интересных статей на Афтершоке по авианосцам и крылатым ракетам. Выяснилось, что многие, так называемые эксперты не знают некоторых элементарных вещей. Например, среди поклонников западной военной мощи бытует мнение, что ПКР российских кораблей посшибают ракетами и пушками "Хорнеты", причем за считанные минуты, а потом наши корабли будут расстреляны, как в тире. Ну-ну... Другие, наоборот, впадают в крайность и яростно отмахиваются от необходимости иметь свои авианосцы для обеспечения ПВО кораблей. Но тут есть свои нюансы...
Недавно на Дальнем востоке прошли учения по отражению удара КР с моря силами ВВС и ПВО. Причем с боевыми стрельбами. и хотя цели были поражены, выяснилась одна настораживающая деталь. МиГ-31 дал залп двумя ракетами по ПКР "Гранит", шедшей по маршруту и попал. Цель была изрешечена, но невозмутимо продолжала себе лететь. Последовал второй залп. Только после этого ПКР удалось свалить. все дело в том, что у "Гранитов2 есть бронирование, защишающее важные узлы и сбить ее не так то просто. На одну ракету ушло 4 Р-33 - весь БК одного МиГ-31. "Хорнеты" могут стрелять AIM-120, БЧ которых сдабее больше чем в два раза. Итого, ладно, с натяжкой будем считать - 6 АМРААМ на один "Гранит". Но в залпе 20 ПКР. И кроме них, кое-что еще помельче. По поводу пушек - даже не смешно - отстрел "Гранитов2 на полигоне из 20-мм пушки не дал НИЧЕГО.  Поразить эту ПКР может лишь 30-мм пушка, да и то с рядом условий.
Придется ставить уровень боевой устойчивости еще и на оружие... Добавлю, что для надежного уничтожения ПКР нужен ЗРК, но обнаружить подлетающую над волнами ПКР он может не так далеко, есть еще время реакции, что приводит к тому, что ЗУР стартует, когда расстояние до корабля для ПКР уже меньше километра (а иногда сильно меньше). И есть еще мертвые зоны, и есть бортовая РЭБ самой ракеты и есть увеличенная точность попадания в уязвимые места.
Короче, морской бой, если я до него доберусь, обещает быть очень интересной задачкой.
Ладно, вернемся к нашим баранам, то бишь наземным объектам. Действие фугасных и осколочно-фугасных бомб и снарядов я описал выше. Все зависти от расстояния до окружающих объектов.
Но есть подкалиберные снаряды и кумулятивные снаряды. Вот для них радиус поражения нулевой. Они втыкаются в броню конкретного танка, БМП или БТР, "не отвлекаясь" на окружающее. Если при взрыве фугаса надо циклом перебирать все объекты-юниты по расстоянию, то в этом случае просто идет проверка условий по единственной цели - в которую снаряд попал. Тут уже вступает в строй условие наличия-отсутствия  динамической защиты и угла встречи с броней, если таковая есть и имеются данные по ее толщине.
На b3d.ua есть WIP от  exooman-a, который занимается танками, в отличие от меня. Он тоже всячески рассчитывает подобные мелочи, у него тоже есть ДЗ, толщина и угол брони и много чего...
Надо добивать ракеты класса "воздух-земля", дописывая для них json, в основном с оружием будет закончено, но остаются еще зажигательные баки, кассеты, контейнеры с суббоеприпасами и тому подобная мелось. И такие извращения, как "радость танкиста" - кассеты с самоприцеливающимися боевыми элементами, опускающимися на парашютах и прошибающими крыши танков и прочей техники кумулятивной струей.
А еще шейдеры и карта высот, которую я еще не успел поновой закачать у denis8424 (движение по карте высот без физики). Разные интересные мысли крутятся, однако... Вроде полногог отказа от физики, кроме снарядов и бомб. В ракетах же отказался и ничего, все нормально...

среда, 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. Что сигнализирует о том, что ракета сперва должна слегка "просесть" и только потом лететь.
В общем, на данный момент, дела обстоят на такой стадии...

воскресенье, 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...

четверг, 12 января 2017 г.

Возвращаясь к напечатанному. Работа с blf - текстовые маркеры.

В конце прошлого года с помощью dron-fя ввел текстовый маркер цели на эуране. Об этом я подробно писал в посте о работе с blf и приводил срипт с кодом. Прошло время и я вновь был вынужден заняться этой темой. Как известно, во многих играх существуют текстовые подсказки, которые, как правило, сопровождают юниты или предметы, показывают боекомплект, направление движения и так далее.
В том варианте скрипта текстовый маркер был один и касался он только одной - захваченной цели. Однако хотелось получить более полную информацию об обстановке вокруг и иметь возможность включения-выключения текстовых маркеров. Практически с первой попытки удалось добиться маркировки всех целей в поле зрения камеры, плюс маркировка выбранной цели. Однако скрипт вышел усложненным и корявым. dron указал на некоторые мои промахи (в частности совершенно ненужный повторный вызов функций), после чего я вновь переработал скрипт. Пришлось вводить функцию "затирания" текста, иначе маркеры не выключались. Дополнительно пришлось переводить индекс выбранной цели в глобальный словарь,дописать строчки кода для выбора режима отображения информации - но это так - по мелочам. Теперь скрипт имеет измененный вид, но изменения еще произойдут, поскольку Андрей в своих подсказках применил мною ранее не употреблявшиеся способы форматирования текста и их требуется осмыслить  и принять на вооружение.Итак, код (надо еще разобраться с подсветкой кода в Блоггере - первая попытка окончилась провалом - но программист в html из меня не очень пока что):

import bge
import bgl
import blf

#МЕТКИ ЦЕЛИ НА КОЛЛИМАТОРЕ - ОБЕСПЕЧИВАЕТСЯ ЕЕ ПЛАВНОЕ ПЕРЕМЕЩЕНИЕ ВСЛЕД ЗА ЦЕЛЬЮ  
#Чтение экранных координат    
def readCoord():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    camera = scene.active_camera
   
    targetLabel = scene.objects['targetMarka']
   
    camera = scene.active_camera
   
    #Метка цели работает только при включенном радаре и выставленной дальности
    try:
        #
        marka = own.getScreenPosition(targetLabel)
       
        if abs(marka[0] - bge.logic.globalDict['targetCoord'][0]) > 0.001:
            targetLabel.worldPosition[0] -= (marka[0] - bge.logic.globalDict['targetCoord'][0])/5
        if abs(marka[1] - bge.logic.globalDict['targetCoord'][1]) > 0.001:
            targetLabel.worldPosition[2] += (marka[1] - bge.logic.globalDict['targetCoord'][1])/5
       
        plusX = targetLabel['limXplus'] #collimatorRight.worldPosition[0]
        minusX = targetLabel['limXminus'] #collimatorLeft.worldPosition[0]
        plusY = targetLabel['limYplus'] #collimatorRight.worldPosition[2]
        minusY = targetLabel['limYminus'] #collimatorLeft.worldPosition[2]
         
        if targetLabel.worldPosition[0] < minusX:
            targetLabel.worldPosition[0] = minusX
        elif targetLabel.worldPosition[0] > plusX:
            targetLabel.worldPosition[0] = plusX
        if targetLabel.worldPosition[2] < minusY:
            targetLabel.worldPosition[2] = minusY
        elif targetLabel.worldPosition[2] > plusY:
            targetLabel.worldPosition[2] = plusY
       
       
           
    except:
        centerX = (targetLabel['limXminus'] + targetLabel['limXplus'])/2
        centerY = (-targetLabel['limYminus'] + targetLabel['limYplus'])/2
        targetLabel.worldPosition[0] = centerX
        targetLabel.worldPosition[2] = centerY

def writeCoord():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    bge.logic.font_id = 0 #blf.load(font_path)
   
    if 'localDict' not in own:
        own['localDict'] = {'typeWriteCoord':own['typeWriteCoord']}
    else:
        if own['localDict']['typeWriteCoord'] != own['typeWriteCoord']:
            #Однократный прогон функции стирания текста при выкоючении маркеров
            if own['typeWriteCoord'] == 0:
                scene.post_draw = [resetWrite]
            own['localDict']['typeWriteCoord'] = own['typeWriteCoord']
       
        #Маркеры текстовой информации включены
        if own['typeWriteCoord'] == 1:
            scene.post_draw = [write]
        #Если макеры выключены, работает  упрощенная функция слежения только за одной целью  
        else:
            targetWrite()
   
   

def write():
    scene = bge.logic.getCurrentScene()
    camera = scene.active_camera
   
    width = bge.render.getWindowWidth()
    height = bge.render.getWindowHeight()
 
    textInfo = ''
    distance = 0
   
    targetInScene = 0
   
    bgl.glDisable(bgl.GL_DEPTH_TEST)
    bgl.glMatrixMode(bgl.GL_PROJECTION)
    bgl.glLoadIdentity()
    bgl.gluOrtho2D(0, width, 0, height)
    bgl.glMatrixMode(bgl.GL_MODELVIEW)
    bgl.glLoadIdentity()
    font_id = bge.logic.font_id
   
   
    if len(bge.logic.globalDict['listAllObject']) > 0:
       for target in bge.logic.globalDict['listAllObject']:
           #Отрисовке информации подлежат только объекты в поле зрения камеры
           if -0.6 < camera.getVectTo(target)[2][0] < 0.6 or str(id(target)) == bge.logic.globalDict['idTarget']:
               #Несколько коряво с повтором условия
               if -0.32 < camera.getVectTo(target)[2][1] < 0.32 or str(id(target)) == bge.logic.globalDict['idTarget']:
                   if camera.getDistanceTo(target) > 30:
                     
                       ScreenPositionTarget = camera.getScreenPosition(target)
                     
                       winXcoord = bge.render.getWindowWidth(target) * ScreenPositionTarget[0]
                       winYcoord = bge.render.getWindowHeight(target) *(1-ScreenPositionTarget[1])
                 
                       distance = int(camera.getDistanceTo(target)/1000)
                       #Формируем текстовый блок
                       textInfo = target['unitName'] + target['unitNation'] + str(distance)
                     
                       blf.position(font_id, winXcoord, winYcoord, 0)
                       blf.size(font_id, 25, 36)
                       if target['target'] == 0:
                           bgl.glColor3f(1.0,0.0,0.0)
                       if target['target'] == 1:
                           bgl.glColor3f(0.0,0.0,1.0)
                       #bgl.glColor4f
                       bgl.glDisable(bgl.GL_LIGHTING)
                       blf.draw(font_id, textInfo)
                       blf.draw(font_id, textInfo)
                     
                       #Для захваченной цели
                       if str(id(target)) == bge.logic.globalDict['idTarget']:
                           targetInScene = 1
                           bge.logic.globalDict['targetCoord'] = ScreenPositionTarget
                           targetDistance = camera.getDistanceTo(target)
                           targetSpeed = target.localLinearVelocity[1]
                           targetHeight = target.worldPosition[2]
                           bge.logic.globalDict['targetData'] = [targetDistance, targetSpeed, targetHeight]
                 
    #Последнее уточнение -  если после прохождения цикла цели в сцене не имеется
    if targetInScene == 0:
        bge.logic.globalDict['targetCoord'] = [0, 0]
        bge.logic.globalDict['targetData'] = [0, 0, 0]
   
    bgl.glEnable(bgl.GL_DEPTH_TEST)
               

#Упрощенная функция чтения экранных координат целм
def targetWrite():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
   
    try:
        target = scene.objects.from_id(int(own['idTarget']))
        camera = scene.active_camera
        bge.logic.globalDict['targetCoord'] = camera.getScreenPosition(target)
        targetDistance = own.getDistanceTo(target)
        targetSpeed = target.localLinearVelocity[1]
        targetHeight = target.worldPosition[2]
        bge.logic.globalDict['targetData'] = [targetDistance, targetSpeed, targetHeight]
    except:
        bge.logic.globalDict['targetCoord'] = [0, 0]
        bge.logic.globalDict['targetData'] = [0, 0, 0]
       
#Функция сброса экранных маркеров  
def resetWrite():
    scene = bge.logic.getCurrentScene()
    camera = scene.active_camera
    width = bge.render.getWindowWidth()
    height = bge.render.getWindowHeight()
    #bgl.glDisable(bgl.GL_DEPTH_TEST)
    bgl.glMatrixMode(bgl.GL_PROJECTION)
    bgl.glLoadIdentity()
    bgl.gluOrtho2D(0, width, 0, height)
    bgl.glMatrixMode(bgl.GL_MODELVIEW)
    bgl.glLoadIdentity()
    font_id = bge.logic.font_id
    blf.position(font_id, 0, 0, 0)
    blf.size(font_id, 25, 36)
    bgl.glDisable(bgl.GL_LIGHTING)
    blf.draw(font_id, '')
    blf.draw(font_id, '')
    #bgl.glEnable(bgl.GL_DEPTH_TEST)
               

Ну и, как это выглядит на экране. Пара Ф-16 с национальной принадлежностью и дистанцией в км. Кстати, при тестировании, когда мой МиГ-23МФ был сбит и камера "отсоединилась", то я увидел и красную строчку с указанием моего юнита и дистанцией с нацпринадлежностью (когда сбитый самолет пошел вперед и вниз). Клавиша М(лат) срабатывает - можно включать и отключать текст.

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

Вам слово, благородный дон РЭБа!

Любовь к странным заголовкам у меня непреходяща, есть такой грешок. Тем, кто читал книгу братьев Стругацких "Трудно быть богом", прекрасно известно имя арканарского министра Охраны Короны и кем он был, так сказать, как личность. Пусть и вымышленная, но очень реальная, как ни странно. А еще я бы посоветовал поискать статьи Сергея Переслегина, которые были написаны уже довольно давно - лет двадцать тому назад, в качестве предисловия к этой самой книге. Тогда, во второй половине 90-х состоялось переиздание если не всех, то подавляющего большинства произведений Стругацких и я горжусь тем, что я их выловил. Так вот-с, глядя на сегодняшнее, начинаешь сильно удивляться тому, насколько пророческими могут оказаться многие вещи. Кстати, у Переслегина весьма интересная версия "тайнфх пружин" событий, развернувшихся в конце книги - когда сломанный гибелью возлюбленной, взбешенный и озверевший земной разведчик-Прогрессор Антон, он же дон Румата Эсторский, устраивает резню в Арканаре, убивая и дона Рэбу. Весьма возможно, что как раз министр Охраны Короны, ставший боевым Магистром и наместником Святого Ордена в Арканарской Области, учинивший кровавый переворот с убийством королевской семьи, которая его же вознесла к вершинам власти, дон Рэба никакого отношения к гибели Киры не имел. Зато провокация удалась на славу и закончилось все еще более кроваво и страшно...
Был еще совместный советско-польский фильм по книге, с тем же названием, так сказать, по мотивам, потому что от книги он отличается очень уж сильно. особенно отсутствием вожака восставших по имени Арата Горбатый, которого почему-то заменили Цурэном - поэтом, которого в книге дон Румата смог выпихнуть из Арканара, спасая ему жизнь. В жизни во главе восстаний стоят как правило не Цурэны, идеалисты-романтики, которые могут послужить только знаменем (как правило, до своей героической гибели), а такие вот Араты, которые почти н7ичем не отличаются от дона Рэбы. Пожалуй, ке в чем они дона Рэбу и переплюнут...
Ну ладно, дань уважения покойным, к сожалению,ныне, писателям отдана, Фильм и статьи критиков упомянуты, мысль по древу растеклась. Теперь, собственно, к делу...
После некоторой возни с ГСН ракет я на время отвлекся от кодинга и в течение трех дней усердно моделил советские контейнеры РЭБ - Радио Электронной Борьбы (теперь вы поняли, откуда заголовок- да? Миссия у РЭБ самая что ни есть благородная - уберечь самолет от вражеских ракет). Были мною смоделены "Сирень", "Гвоздика", "Омуль" и "Сорбция".
Скрин контейнеров:


Как обычно, последовало создание json для СПС-142 "Гвоздика" и подгружаемого бленда. А далее последовало написание скрипта РЭБ. Суть работы "Гвоздики" (кстати, вроде как еще ее называют СПС-141МВГ и эта система опровергает тезис об отставании СССР в области РЭБ от США - во время ирано-иракской вофны ни один иракский самолет, оснащенный этим контейнером не был сбит УР с радиолокационной головкой, а вот иранцам американские системы помогали мало) заключается в ослеплении БРЛС вражеских машин и выдаче ложных координат летящей в самолет-носитель вражеской ракете. Это - "уводящая помеха" - ракета идет не на сам самолет,а на его "призрак". После некоторой возни скрипт заработал. После проверки его работоспособности пришлось срочно ухудшать свойства СПС-142, потому что она делала самолет игрока неуязвимым и бессмертным.
Скрин - СПС-142 "Гвоздика" на МиГ-23МФ.


Кроме того, по аналогу с работой РЭБ для РЛ-головок и бортовых РЛС, написал функцию работы аналога для оптического диапазона - для ракет с ТГСН. Такие системы известны под названием "Витебск" (хорошо показавшей себя в Сирии) и более старой - "Липа". Они ослепляют ГСН тепловых ракет мощными УФ-импульсами, также "уводя" их в сторону (это чисто дилетантское мнение, все, конечно, сложнее на самом деле, но, понятное дело, раскрывать эти подробности никто не будет). Пока скрипт выглядит так:


# -*- coding: utf8 -*-

"""
"JAMMER":"ECM",
"timerCycle":0,
"limitCycle":360,
"timerPause":0,
"limitPause":300,
"limitCoord":100,
"distanceAntiMissile":25000,
"distanceAntiBRLS":30000,
 "massChild":0.1,
 "antiForce":0.0018
 """


import sys
import bge
import random

#import mathutils

#Скрипт работы систем противодействия - электронные помехи, УФ-помехи и так далее

#Функция-коммутатор - определяет специализацию систем противодействия
def jammer():
    scene = bge.logic.getCurrentScene()
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if 'threatDict' not in own:
        own['threatDict'] = {"threatList":[],"enemyList":[],"kX":0,"ky":0,"kZ":0}
   
    else:
        #Вызов метода
        g = globals()
       
        #Тип головки самонаведения - название функции
        typeJAMMER = own['JAMMER']
       
        #Проверка на функцирнирование ГСН и ее особенностей
        g[typeJAMMER](own)

#Активные помехи - контейнерные и встроенные системы РЭБ
def ECM(own):
    scene = bge.logic.getCurrentScene()
   
    randomBLINDER = random.randrange(0,100)
    randomJAMMER = random.randrange(1,101)
    phantomCoord = random.randrange(0,own['limitCoord'])
    #print('JAMMER')
    #Словари носителя - списки угроз и списрк в словаре обрабатываемого вражеского оружия  
    #own.parent.parent['threatListWeapon']
    #own['weaponDict'] = {'dopX':0,'dopY':0,'dopZ':0}
   
    #Таймер цикла противодействия
    if own['timerCycle'] < own['limitCycle']:
        own['timerCycle'] += 1
   
    #Формируем список угроз от ракет с радиолокационными головками
    if own['timerCycle'] == 1:
        CoordPlusMinus(own)
        try:
            for missile in own.parent['localDict']['threatListWeapon']:
                #print('localDict')
                if 'typeGSN' in missile:
                    if 'RGSN' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
                    elif 'RK' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
        except:
            pass
   
    #Начинаем воздействие
    if own['timerCycle'] > 1:
       
        #Ослепляющая помеха для РЛС противника
        if randomBLINDER > 90:
            if len(own.parent['localDict']['threatListUnit'][0]):
                for objThreatRLS in own.parent['localDict']['threatListUnit'][0]:
                    #Ослепляющая помеха срабатывает на дальних рубежах, вблизи радары противника помехи "прожигают"
                    if own.getDistanceTo(objThreatRLS) > own['distanceAntiBRLS']:
                        #При низком уровне помехозащищенности захват будет сорван и противнику придется вновь выполнять прицеливание
                        if randomJAMMER/100 > objThreatRLS['antiECM']:
                            objThreatRLS['idTarget'] = '1'
       
        if len(own['threatDict']['threatList']) > 0:
            try:
                #Начинаем попытку  "взломать" ГСН ракеты и подчинить ее системе РЭБ
                MISSILEattack = own['threatDict']['threatList'][0]
                if own.getDistanceTo(MISSILEattack) < own['distanceAntiMissile']*MISSILEattack['antiAC']/100:
                    #print(randomJAMMER)
                    if MISSILEattack['antiAC'] > randomJAMMER:
                       
                        if MISSILEattack['antiAC'] < 101:
                            #Взлом произошел
                            MISSILEattack['antiAC'] = 101
                        #Включаем функцию исажений координат
                        escapeCoord(own, MISSILEattack)
                       
            except:
                own['threatDict']['threatList'] = []
   
    #Включаем таймер паузы              
    if own['timerCycle'] == own['limitCycle']:
        if own['timerPause'] < own['limitPause']:
            own['timerPause'] += 1
       
        if own['timerPause'] == 1:
            #Сбрасываем в ноль искажение для ракеты, можно даже вернуть ей привычную стойкость к помехам
            if len(own['threatDict']['threatList']) > 0:
                CoordNull(own, MISSILEattack)
               
    #Сброс таймеров в ноль  
    if own['timerPause'] == own['limitPause']:
        own['threatDict']['threatList'] = []
        own['timerPause'] = 0
        own['timerCycle'] = 0
           
       
#Активные помехи - оптика - инфракрасные ГСН (тепловы)
def UF(own):
    scene = bge.logic.getCurrentScene()
   
    randomJAMMER = random.randrange(1,101)
    phantomCoord = random.randrange(0,own['limitCoord'])
    #print('JAMMER')
    #Словари носителя - списки угроз и списрк в словаре обрабатываемого вражеского оружия  
    #own.parent.parent['threatListWeapon']
    #own['weaponDict'] = {'dopX':0,'dopY':0,'dopZ':0}
   
    #Таймер цикла противодействия
    if own['timerCycle'] < own['limitCycle']:
        own['timerCycle'] += 1
   
    #Формируем список угроз от ракет с радиолокационными головками
    if own['timerCycle'] == 1:
        CoordPlusMinus(own)
        try:
            for missile in own.parent['localDict']['threatListWeapon']:
                #print('localDict')
                if 'typeGSN' in missile:
                    if 'IKGSN' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
                       
                       
        except:
            pass
   
    #Начинаем воздействие
    if own['timerCycle'] > 1:
        if len(own['threatDict']['threatList']) > 0:
            try:
                #Начинаем попытку  "взломать" ГСН ракеты и подчинить ее системе РЭБ
                MISSILEattack = own['threatDict']['threatList'][0]
                if own.getDistanceTo(MISSILEattack) < own['distanceAntiMissile']*MISSILEattack['antiUF']/100:
                    #print(randomJAMMER)
                    if MISSILEattack['antiUF'] > randomJAMMER:
                        if MISSILEattack['antiUF'] < 101:
                            #Взлом произошел
                            MISSILEattack['antiUF'] = 101
                        #Включаем функцию исажений координат
                        escapeCoord(own, MISSILEattack)
                       
            except:
                own['threatDict']['threatList'] = []
   
    #Включаем таймер паузы              
    if own['timerCycle'] == own['limitCycle']:
        if own['timerPause'] < own['limitPause']:
            own['timerPause'] += 1
       
        if own['timerPause'] == 1:
            #Сбрасываем в ноль искажение для ракеты, можно даже вернуть ей привычную стойкость к помехам
            if len(own['threatDict']['threatList']) > 0:
                CoordNull(own, MISSILEattack)
               
    #Сброс такмеров в ноль  
    if own['timerPause'] == own['limitPause']:
        own['threatDict']['threatList'] = []
        own['timerPause'] = 0
        own['timerCycle'] = 0


#Выборнаправления уводящей помехи
def CoordPlusMinus(own):
   
    #Показатели
    own['threatDict']["kX"] = random.randrange(-2,2)
    own['threatDict']["kY"] = random.randrange(-2,2)
    own['threatDict']["kZ"] = random.randrange(-2,2)

#Уводящая помеха
def escapeCoord(own, MISSILEattack):
   
    #Искажаем координаты - доработать - слишком длинная запись
    MISSILEattack['weaponDict']['dopZ'] += phantomCoord*own['threatDict']["kX"]
    MISSILEattack['weaponDict']['dopY'] += phantomCoord*own['threatDict']["kY"]
    MISSILEattack['weaponDict']['dopX'] += phantomCoord*own['threatDict']["kZ"]
   
   
#Обнуление искажений
def CoordNull(own, MISSILEattack):  
   
    MISSILEattack['weaponDict']['dopZ'] = 0.0
    MISSILEattack['weaponDict']['dopY'] = 0.0
    MISSILEattack['weaponDict']['dopX'] = 0.0
               
    Думаю, понятно. Весь скрипт висит на объекте Jammer, который и занимается своей работой. Причем информацию об угрозах он получает от своего родителя юнита. Работает он длинными импульсами с длинными же паузами - циклами. система РЭБ не всесильна, но одну ракету точно успевает отвести, если сильно повезет - пару. В общем-то, так и задумывалось.
Кроме систем РЭБ я в авральном режиме занимался головками самонаведения ракет - вводил подсветку, реакцию на ловушки и диполи и так далее. А еще писал дальше искусственный интеллект. В первом приближении систему воздушного боя можно считать выстроенной. Конечно, придется ее корректировать, дополнять, настраивать, но основные параметры и величины, принцип работы уже выстроены. Теперь надо начинать выстраивать работу ИИ по земле и заканчивать с анархией - учить ботов воевать группами, держать строй и даже принимать "командные" решения...

пятница, 19 августа 2016 г.

Контрольный выстрел. Анимации больше нет...

Как-то довольно быстро после последнего сообщения и боях с форматом json, было восстановлено все. Кроме текстовой индикации на кокпите, которую я намереваюсь рисовать через blf. К сожалению, то ли я удалил файлик с заготовкой, то ли запихнул его куда-то, но теперь придется это дело поновой начать, благо что образец скрипта я скопировас АПИ Блендера.
Все эти дни методично занимался подчисткой и сортировкой того, что уже было сделано и дописывал требующееся. Была восстановлена пушечная и ракетная стрельба, как УР, так и НАР, аварийный сброс подвесок, сброс топливных баков и бомб. Поскольку мои руки еще не добрались до кода со стрельбой я еще толком ничего не успел поломать, чтобы потом героически восстанавливать мною же порушенное. Так, переделал немного имена проперти в коде, поскольку лазить по json и исправлять там было влом.
Долго и тщательно "приглаживал" кокпит. В общем сама картина не поменялась - ноэто внешне. Хотя кое-какие видные глазу ихменения все же произошли. Я наконец добрался до стрелочек угла атаки, угла сноса и перегрузки. Эти стрелки у меня были фактически для декорации в первой версии, теперь у них функция несколько ближе к реальной. Поскольку изменения претерпела сама модель полета, появилась "боковая" составляющая и "вертикальная" составляющая. Это как имитация "заноса" машины на повороте. Только машину, по большому счету ведет влево-вправо, а самолет еще и вверх-вниз. Отдаю себе отчет в том, что деление "боковых" и "вертикальных" скоростей сноса на линейную скорость самолета это не аэродинамика, но все же лучше, чем ничего. Таким образом яполучил угол атаки, угол сноса и перегрузку (сумму "боковой" и "вертикально" составляющей, деленную на скорость машины). Пришлось повозиться с выставлением коэффициентов для ограничения работы стрелок, чтобы не вертелись как попало, но оно того стоило. Затем пришло время разбить приборы и индикаторы на группы объектов и создать в скрипте отдельные функции типа dvigInd, chassyInd, wingsInd и так далее. Все эти функции вызываются лишь при изменении текущих данных - выпуска-уборки тормозов-шасси-закрылков, изменении тяги двигателя и так далее. В итоге сама функция Cockpit в одноименном скрипте превратилась в своеобразный коммутатор, для вызова функций работы индикаторов и стрелочек. Индикаторов у меня что-то около 40 штук, плюс что-то около 20 стрелок. Все это хозяйство ныне исправно функционирует и выдает нужну. информацию.
После того, как кокпит МиГ-23МФ был отлажен и выловлены все ошибки и недочеты, занялся изничтожением оставшейся анимации. Дело Термидора должно было быть доведено до конца...
Как выяснилось, "глаз боится, руки стучат по клаве",  необходимо было убрать анимации из гидравлики и стоек шасси - всего - 16 объектов. Дело осложнялось тем, что некоторые объекты имели весьма сложную траекторию движения, по ходу исполнения самой анимации направление движения могло измениться на противоположное.
Тем не менее проблема была решена с помощью if own==blabla: own=bla. Я просто разбил участки работы проперти CHASSY на интервалы, внутри которых менялись скорости и направления движения. Пока, на мой взгляд, строчек многовато, но в будущем, надеюсь, и здесь часть строчек будет убрана. Пока работает и ладно. Что мне и нужно было. Таким образом, анимации в файлах модели больше нет. Совсем.
Возможно, что в 2.78 баг с проигрыванием анимации уже поправили,  но пока не проверял. Да и по большому счету, уже пока и не нужно.
Есть еще кое-какие фишки в Блендере, до которых пока руки не доходят. Так, есть возможность смешения текстур (декали, бортовые номера, имитация повреждений), но все это пока перекрывается тем, что объекты, добавленные в сцену через addObject, имеют одинаковый материал, поэтому и выглядеть будут одинаково...
В общем, пока вот так. Джейсон прочно утвердился в проекте (оказывается, есть еще кое-какие возможности, надо их изучить), анимация в модели и в кабине полностью убрана (это привело к уменьшению файлов примерно на 20 процентов), кабина отлажена полностью, за исключением прицела (не в кого пока целиться, Ла-17 взял из старого проекта, заодно выяснил, откуда там прозрачность и вывернутость полигонов - в настройках материала не отключил Transparency), работает оружие, механизация, стрельба и взрывы. Надо готовить мишени и на них отрабатывать до конца модель повреждений и воздействие оружия, плюс прицел и сенсоры.

воскресенье, 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 метров).
Сейчас идет подгонка формата файлов под новые обстоятельства. Учитывая, что я не слишком сильно заморачивался этим в свое время не успел наплодить кучу юнитов, процесс переделки(доработки) будет не таким сложным, как казалось...