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

вторник, 28 июля 2020 г.

Снова о ландшафтах и квадратах, то есть блоках.

Чтение учебной литературы по разным темам отнимает много времени, но работа над проектом все ще продолжается, хотя и непонятно, чем все это закончится и закончится ли вообще. Но и сидеть, ничего не делая, тоже неохота.
Недавно отработал самонаведение ракеты в Unity, отыскал аналогичный урок для UE4, провел массовое обновление ПО - Юнити и UE4, плюс VisualStudio, обновил Блендер несколько раз, пробовал UPBGE для EEVEE. Увы, для последнего альфа сборка - это альфа и есть. Категорически не хочет работать подгрузка бленд-файлов через LibLoad - UPBGE мгновенно вылетает. Смотрел и Armory3D, но толком не занимался. Просмотрел и прослушал курсы по Блендеру от А. Слаквы, для Блендер 2.8, пытаюсь в нем работать - с непривычки тяжело.
Есть мысли по поводу упрощения и сокращения проектов в Юнити и БГЕ, касающиеся прежде всего моделей оружия и их json файлов. Если вкратце - стоит провести объединение в одном файле вариантов подвески для ракет однотипного семейства, например AIM-120, AIM-7, Р-3/13, Р-23/24, Р-27 и так далее. Все дело в том, что для нескольких файлов json имеются одинаковые координаты и углы поворота ракет для подвески, отличаются они лишь наименованиями самих ракет - всего-то пара слов, даже не строчек. Поэтому имеет смысл просто перечислить в отдельной строке меши ракет, которые надо найти для подгрузки, а вместо названий ракет в словах проставить missileStr, а не R-23R, которые будут указаны выше.

{

"objList":["R-23R", "R-23T", "R-24R", "R-24T", "R-24RM"],

"obves":{"FLG_APU23_|":{"parentObj":"CntAircraft","locObj":[0.0,0.0,0.0],"rotObj":[0.0,0.0,0.0]},
         "missileStr|1":{"parentObj":"CntAircraft","locObj":[-1.428,-2.0,0.03],"rotObj":[-0.035,0.0,0.0],"weapon":1,"CatapultSbros":0},
         "missileStr|2":{"parentObj":"CntAircraft","locObj":[1.428,-2.0,0.03],"rotObj":[-0.035,0.0,0.0],"weapon":1,"CatapultSbros":0}
         }
}

Поскольку у меня отлажена система поиска файлов с разным расширением, то поисковик-скрипт отыщет и подгрузит все нужное. А вместо пяти файлов для ракет можно получить один. Это для Р-23 и Р-24, а для aIM-120 вместо 8 будет 1 и для "Спэрроу" вместо 12 - 1, столько же для "Сайдуиндеров"...
Есть еще один нюанс. Для ракет типа "Спэрроу" и "АМРААМ модели внешне почти неотличимы или совсем неотличимы, поэтому в файле ТТХ ракеты надо просто перечислить названия моделей ракет например Sparrow_II и скоратить число блендов. А сами ТТХ ракет объединить в json - поисковик все найдет...

Но все это по ракетам, а есть еще ландшафт. Тут тоже есть новые задумки. Ландшафт разбивается на квадраты типа А1, Б4 и так далее. Все эти квадраты упакованы в отдельные бленды, и снабжены json  с перечислениями стоящих на них объектов. В зависимости от положения активной камеры сначала грузятся стартовый квадрат и 8 квадратов вокруг него. В адльнейшем идет отслеживание положения камеры и "догрузка", если надо, но происходить это будет редко. Фактически, выбирается один квадрат, что-то вроде "центра мира" и вокруг него выстраивается "периферия". Но и это еще не все. Новый "центр мира" "оттаскивается" в нулевое исходное положение, вместе с ним на ту же величину переносятся и ранее сгенеренные "квадраты" со всем их содержимым. Плюс юниты игры также сменяют сове положение на величину "единицы" ландшафта. А создавалась эта система с прицелом на Юнити. Большой ландшафт единым кусокм делать неудобно, плюс говорилось, что координаты больше 100 тысяч единипц приводят к некорректной работе и тормозам, значит, надо уменьшать масштаб самих юнитов, ну раз в 10. Тогда надо учесть, что и их скорости и величина ускорения свободного падения и размеры статических объектов (деревья, здания, дороги) надо также отмасшатбировать, уменьшив в 10 раз.
Возвращаясь к ландшафтуи отрабатываемой сейчас системой его "постройки", скажу, что с "передвиганиями" юнитов, по идее, должно получиться "удерживать" юнит игрока внутри некоторого предела, да и остальные юниты, в общем-то тоже.

Чкрипт  на данный момент:
import bge
scene = bge.logic.getCurrentScene()

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

bge.logic.globalDict["nameBlock"] = "D4"
scaleBlock = 2.5

#Конфигурация расположения блоков террайна - вложенные списки
configBlock = [
              ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"],
              ["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"],
              ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"],
              ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8"],
              ["E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8"],
              ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"],
              ["G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8"],
              ["H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"]                             
              ]
   
def BlockTerrain():
    cont = bge.logic.getCurrentController()
    own = cont.owner       
    #Индексы списка блоков - внешний и вложенный
    x = 0
    y = 0
    #Величина перемещения блоков и их направление
    posX = 0.0
    posY = 0.0
   
    nameBlock = bge.logic.globalDict["nameBlock"]
   
    #Список элементов, содержащих информацию о блоках и их смещении при появлении
    listIndex = []
    #Список уже имеющихся блоков террайна
    terrainList = []
   
    #Сначала ищем в общем списке вложенный с наименованием "центра", вокруг которого
    # выстраиваются еще 8 дополнительных блоков террайна 
    for listObj in configBlock:
        for obj in listObj:
            #После нахождения "центра мира" в списке блоков внутри общего списка блоков террайна
            if nameBlock == obj:
                #Заносим его в список индексов, это обязательно, смещение для "центра" нулевое
                listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj) ) + "|" + "0.0" + "_" + "0.0" )
                #Загоняем в список блоки "перед" и "позади" "центра мира", учитывая пределы индексов списка
                if listObj.index(obj)-1 > -1:
                    listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj)-1 ) + "|" + "0.0" + "_" + str(-scaleBlock) )
                if listObj.index(obj)+1 < len(listObj):
                    listIndex.append( str( configBlock.index(listObj) ) + "_" + str( listObj.index(obj)+1 ) + "|" + "0.0" + "_" + str(scaleBlock) )
               
                #А теперь осматриваем вложенные списки "выше" и "ниже"  найденного, опять учитываем пределы индексов
                #Если такие списки есть, то заносим в listIndex информацию о блоках с индеками "центра мира" и плюс-минус 1
                if configBlock.index(listObj)-1 > -1:         
                    listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj) ) + "|" + str(scaleBlock) + "_" + "0.0" )
                    if listObj.index(obj)-1 > -1:
                        listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj)-1 ) + "|" + str(scaleBlock) + "_" + str(-scaleBlock) )
                    if listObj.index(obj)+1 < len(listObj):
                        listIndex.append( str( configBlock.index(listObj)-1 ) + "_" + str( listObj.index(obj)+1 ) + "|" + str(scaleBlock) + "_" + str(scaleBlock) )
               
                if configBlock.index(listObj)+1 < len(configBlock):
                    listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj) ) + "|" + str(-scaleBlock) + "_" + "0.0" )
                    if listObj.index(obj)-1 > -1:
                        listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj)-1 ) + "|" + str(-scaleBlock) + "_" + str(-scaleBlock) )
                    if listObj.index(obj)+1 < len(listObj): 
                        listIndex.append( str( configBlock.index(listObj)+1 ) + "_" + str( listObj.index(obj)+1 ) + "|" + str(-scaleBlock) + "_" + str(scaleBlock) )
   
    #Проверка на нличие блоков террайна в сцене
    for obj in scene.objects:
        if "Terrain" in obj.name:
            terrainList.append(obj.name)
   
    #По окончании составления списка препарируем каждый его элемент типа 3_2|2.5_-2.5     
    for element in listIndex:
        #Перед "|" указаны индексы общего списка и вложенного списка, они дают выход на элемент "D3" в данном случае
        x = int( element.split("|")[0].split("_")[0] )
        y = int( element.split("|")[0].split("_")[1] )
        if "TerrainQuad_" + configBlock[x][y] not in terrainList:
            #Добавляем блок террайна и препарируем элементы после "|"
            BlockTerrain = scene.addObject("TerrainQuad_" + configBlock[x][y], own)
            #Получаем смещение ОТНОСИТЕЛЬНО "ЦЕНТРАЛЬНОГО" блока, весь отсчет идет относительно него
            posX = float( element.split("|")[1].split("_")[1] )
            posY = float( element.split("|")[1].split("_")[0] )
            #Смещаем только что добавленный блок и переходим к следующему - и так до конца списка
            BlockTerrain.worldPosition[0] += posX
            BlockTerrain.worldPosition[1] += posY
           
def control():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    cam = scene.objects["Camera"]
    deltaX = 0.0
    deltaY = 0.0
    nameBlock = ""
    if cam.worldPosition[0] < -scaleBlock or cam.worldPosition[1] < -scaleBlock or cam.worldPosition[1] > scaleBlock or cam.worldPosition[0] > scaleBlock:
        if cam.worldPosition[0] < -scaleBlock:
            deltaX = -scaleBlock
        elif cam.worldPosition[0] > scaleBlock:
            deltaX = scaleBlock
        elif cam.worldPosition[1] < -scaleBlock:
            deltaY = -scaleBlock
        elif cam.worldPosition[1] > scaleBlock:
            deltaY = scaleBlock
       
        for obj in scene.objects:
            if "Terrain" in obj.name:
                obj.worldPosition[0] -= deltaX
                obj.worldPosition[1] -= deltaY
               
                if -scaleBlock * 0.1 < obj.worldPosition[0] < scaleBlock * 0.1 and -scaleBlock * 0.1 < obj.worldPosition[1] < scaleBlock * 0.1:
                    bge.logic.globalDict["nameBlock"] = obj.name.split("_")[1]
                   
        cam.worldPosition[0] -= deltaX
        cam.worldPosition[1] -= deltaY
        BlockTerrain()
       
#Первый стартовый запуск функции генерации и расстановки блоков террайна
BlockTerrain()

Думаю, комментарии делают этот код понятным... Надеюсь, во всяком случае. )))
Сам же террайн в БГЕ (или УПБГЕ, когда его отладят) планируется рскрасить по способу denis8424 - с разделением материалов по высоте, но с одним дополнением. Для каждого блока террайна провести смешивание текстур через маски, правда, это уж как получится. Там надо большое разрешение масок, все же даже масштабированные блоки - это 10 км, но можно попробовать маски при наложении дублировать - при больших размерах повторяемость не будет сильно бросаться в глаза, плюс для разных блоков маски будут разными, а число блоков в сумме - не слишком велико, вряд ли больше 100 (уж точно не 2500).


понедельник, 1 апреля 2019 г.

Большой Апдейт. И прочие новости...

Не писал я довольно давно. Тут причин мног7о, но главная, просто не знал, о чем писать...
БГЕ таки мертв, будут ли из него делать форки, помимо известных мне upBGE, неясно... С другой стороны, мне все ж стало жалко своих трудов на ниве быдлокодерства, и, помявшись, походив вокруг да около, я все же решил начать Большой Апдейт. Так сказать, прощальный аккорд для БГЕ. Плюс, таким образом я рассчитываю не забыть совсем уж навыки работы с Питоном, а заодно отработать кое-какие приемчики, могущие мне пригодиться в работе уже с Юнити (о нем чуть позже).
Правда, я не знаю, к чему и когда все это приведет, потому как назревает уже четвертая по счету операция для моего глаза (вполне ожидаемая вторичная катаракта), на какое время и когда мне придется "заткнуть свой фонтан" (С), пока непонятн6о, но и сидеть, сложа ручки, тоже неохота, а водку и прочие средства расширения сознания я не уважаю категорически.
Данный пост должен сыграть (и я надеюсь, сыграет), роль своеобразного пинка самому себе, дабы подьянуть расщатавшуюся за последнее время самодисциплину и хотя бы частично прогнать лень.
Итак.
В чем сущность Большого Апдейта (данное понятие следует рассматривать сугубо с иронической точки зрения)?
Сущность его в том, чтобы довести проект до хоть какой-то завершенности. Здесь основной задачей является упорядочивание структуры проекта и обретения новых навыков работы с файлами. А именно - новые методы поиска и загрузки моделей в игру, создание редактора миссий и кампаний и введения в состав доступных игроку юнитов зенитно-ракетных комплексов, вертолетов и танков. Хотя бы и в единственном экземпляре, причем необходимо повысить гибкость проекта и сделать его открытым для добавления новых юнитов (точнее, сохранить открытость). Это основная задача.
Вспомогательная задача - избавиться от завышенных ожиданий, ввести ограничения в проект, превращающий его в, гм... в симулятор конфликтов малой интенсивности (поскольку БГЕ не любит большого количества объектов). Например, для редакторов миссий и кампаний ограничить число одновременно находящихся в воздухе самолетов и вертолетов, скажем, не больше 20 с обеих сторон (в "Ил-2", кстати, ЕМНИП максимум можно было выставить 32 самолета с разных сторон). Скорее всего, онлайн-версию делать не буду (а так, не знаю, что там получится, зарекаться не буду). Приведу в порядок код, в соответствии с новыми знаниями, почерпнутыми мною из всевозможных наставлений и инструкций. Пейзаж покрасивше сделаю (кроме БГЕ, у меня есть и другие планы, но о них я пока помолчу ввиду неопределенности с кое чем - см. выше).

Теперь немного кода. Меня весьма раздражала выстроенная мною система подгрузки объектов в игру из блендов и необходимость долго и нудно расставлять разного рода слеши и двоеточия в файлах json. Почитав учебники и форумы, а также примеры по Питону и воспользовавшись подсказкой dron-a, я наваял очередной блок кода, в котором происходит поиск и установление всех путей ко всем файлам проекта и их сортировка. Пути к файлам Питона добавляются в json файл, лежащий рядом с пусковым блендом проекта. По задумке, игра должна сама смотреть нужные ей пути и тащить в себя необходимые бленды, а не пользоваться готовеньким и разжеванным заранее, что должно сильно сократить как сам код загрузки объектов, так и объем файлов json, в которых не надо будет прописывать пути.

import bge
import sys
import os
import json

#Функция создания файла json с путями к файлам всего проекта
def startPath():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner

    #Создаем переменные - список путей и пустую строку для путей
    pathList = []
    k = ""
   
    #Циклом перебираем файлы, директории и папки, создаем и сшиваем строки в пути
    for d, dirs, files in os.walk(bge.logic.expandPath("//")):
        for f in files:
           
            #Обрабатываем вид путей, убирая слеши и название стартовой директории
            k = os.path.join(d,f)
            k = k.split("BlendSim_2.1")[1]
            k = k.replace("\\", "/")
           
            #Питон-файлы добавляем в sys.path, остальные в список путей
            if ".py" not in k:
                pathList.append("/"+k)
            else:
                sys.path.append("/"+k)
                   
    #Открываем файл json  с путями (если его нет, то он создается) и сбрасываем туда список путей
    with open(bge.logic.expandPath('//NodePath.json'), 'w') as NODEPATH:
        json.dump(pathList, NODEPATH)
   
    #Это просто проверка   
    proverka(own, scene)
 
def proverka(own, scene):
    missile = None

    with open(bge.logic.expandPath('//NodePath.json'), 'r') as READPATH:
        JSONREADPATH = json.load(READPATH)
       
        for ob in JSONREADPATH:
            if "R-24R.blend" in ob:
                print(ob)
                bge.logic.LibLoad(bge.logic.expandPath(ob), 'Scene', load_actions = True)
                missile = scene.addObject("R-24R", own)
       
Ну, я думаю, с комментариями будет все ясно, для чего все это надо. Проверка в виде появления на сцене ракеты Р-24Р прошла успешно и неоднократно...

Теперь о Юнити. В свое время я скачал и Юнити и Анрил Энжин 4. Второй движок ощутимо мощнее и совершеннее первого, да вот беда, пользоваться им смогут не все, я - пока точно нет. Его недостатком является отсутствие большого количества документации, в отличие от Юнити, к тому же, на форуме b3d.ua мне много раз говорили, что Си Шарп относительно простой в освоении язык (но, к сожалению, я тугодум и с налету его понять не могу, изображая обезьяну, повторяющую поведение человека, а именно, создавая код по примерам и образцам, не понимая, для чего и ка, впрочем, при освоении Питона начиналось так же). В Юнити для меня еще остались кое-какие нюансы, лежащие за пределами моего понимания, например, как присвоить материал дереву и его листьям (перетаскивание порой сильно раздражает, проше было бы набрать на клавиатуре). Но все-таки, я добавил в Юнити многострадального подопытного кролика - МиГ-23МФ из моего же бленд-проекта и попробовал для начала помахать крылышками. Не сразу, но это получилось. Код, наверное, я пока приводить не буду - он состоит из занудного объявления публичных переменных и всего-то из десятка строчек кода для крыльев. Пока я не столько работаю в Юнити, сколько пытаюсь уложить в свою голову хотя бы азы Сишарпа. И прежде всего - взаимодействие скриптов между собой, минуя Инспектор Юнити. У меня он ассоциируется с логическими кирпичами БГЕ, которые необходимо использовать только там, где это действительно нужно. А многократно переделывать  еще и переменные в инспекторе - нет уж. Лучше сразу научиться хотя бы по минимуму работать с кодом, а не таскать переменные и оббъекты с одно края экрана на другой (да и окошечки там маленькие, с моим зрением сейчас...)
Ну ладно, будем считать, что стартовый шаг сделан (точнее, самопинок) и блогспот не накроется медным тазом, как Гуглплюс (ходили т акие слухи).

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

Отработка действия БЧ снарядов на юниты. Наведение артиллерийских орудий.

В двух предыдущих постах  я писал про взаимодействие снарядов и юнитов. Точнее, воздействие поражающих факторов на сами юниты. Было это все чисто умозрительно и пока не было воплощено на практике, так и оставалось у меня в голове.
Скрипт работы снаряда, точнее, его БЧ (боевой части) был собран не сразу. Точнее, не скрипт, а функция в скрипте CONTROL_Weapon. А когда она была написана, то пришлось поломать голову, почему оно не работает (ну как всегда - там опечатка, там не то имя указал). А главной причиной была нихкая точность - снаряды рвались уж очень далеко от зенитки.  Я использовал миссию теста МиГ-23БН для проверки работы НАР С-8 по зенитке GDF-001 Oerlicon. Выяснилось, что прицельная сетка на самолете не соответствует дальности полета НАР. Пришлось лезть в файл кабины и корректировать ее положение. Теперь, видимо, ту же процедуру надо провести и в остальных машинах. Ничего не поделаешь - до НАР и пушек я просто до сих пор еще не добирался - руки просто не доходили.
Но в конце концов  скрипт заработал полностью в том виде, какой он сейчас есть. Необходимо дописать проверку лучом от эпицентра взрыва до юнита на наличие препятствия (укрытия) на пути ударной волны и осколков. Приведу текст функции для БЧ типа "осколочно-фугасная":
 def OskolFugas(own):
  
    for units in ArbitrGame.UNITS[1]:
        #Проверяем наличие атрибута повреждений у объекта - ударной волне и осколкам все равно, что разрушать
        if hasattr(units, "crash") == True:
            #В зависимости от расстояния подрыва  рассчитывается величина поражающего фактора
            if own.radiusExplode*1.5 < own.getDistanceTo(units) < own.radiusExplode*3:
                own.LEVELS_CRASH = 0.4*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units)
            elif own.radiusExplode < own.getDistanceTo(units) < own.radiusExplode*1.5:
                own.LEVELS_CRASH = 0.8*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units)
            elif own.getDistanceTo(units) < own.radiusExplode:
                own.LEVELS_CRASH = own.levelsCrash
           
            #Вычисление нанесенного урона
            if own.LEVELS_CRASH > units.levelDefens:
                #При условии, что защита "пробита", смотрим на уровень повреждений юнита и вносим коррективы
                if own.LEVELS_CRASH/units.levelDefens < units.crash:
                    units.crash -= own.LEVELS_CRASH/units.levelDefens
                else:
                    units.crash = 0.0
            print(units, units.crash, own.getDistanceTo(units), own.LEVELS_CRASH)

Последнюю строчку можног не принимать во внимание, я ее потом закомментирую или вообще уберу. В ней я отсматривал работу функции. В принципе, все остальные функции типа "проникающе-фугасной", "Фугасной" и прочей БЧ будут работать так же. Разница будет заключаться лишь в градации расстояния для ослабления действия снаряда.
А вот ненаписанные пока функции для кумулятивных БЧ и бронебойно-подкалиберных снарядов будут вести себя по-другому. Там не нужен цикл перебора юнитов сцены - цель одна-единственная и вступают в действие такие факторы, как толщина брони, угол встречи с броней, наличие динамической защиты и ее тип... То же самое, кстиати, относится к пулям стрелкового оружия - они действуют примерно по тому же принципу - скорость, калибр, плюс дальность выстрела. Мда, придется еще как-то по бронепробиваемости в зависимости от дальности , с которой был сделан выстрел, что-то придумывать...
И в конце об артиллерии. Здесь я приведу  скрипт работы артиллерийского орудия. На данный момент на нем работает "Эрликон", но там еще надо смотреть по его точности - в скрипт введен разброс снарядов и ошибки прицеливания. Весьма вероятно, что с этим я переборщил...
import bge
import json
import sys
import random

cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
ArbitrGame = scene.objects["ArbitrGame"]
#Импортируем модуль юнита
#unit_module = __import__(own.unitModule)
t = 0.0
xS = 0.0
yS = 0.0
zS = 0.0
S = 0.0

newPos = [xS, yS, zS]
import CONTROL_Operations
import CONTROL_Sensor
import mathutils

scene = bge.logic.getCurrentScene()


def UnitArtillery():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    ################################################################
    #Просчет уровней детализации
    if own.maxVisibleDist < own.getDistanceTo(scene.active_camera):
        own.levelsDetails = 2
    elif own.maxVisibleDist/10 < own.getDistanceTo(scene.active_camera) < own.maxVisibleDist:
        own.levelsDetails = 1
    elif own.maxVisibleDist/10 > own.getDistanceTo(scene.active_camera):
        own.levelsDetails = 0
    #print(own.getDistanceTo(scene.active_camera))
       
    #Работа с детализацией   
    if own.Temp_levelsDetails != own.levelsDetails:
        CONTROL_Operations.levelsDetailArtillery(own)
        own.Temp_levelsDetails = own.levelsDetails
   
    if own.crash > 0.1:   
        if own.typeMissions != "":
            if own.statusBattery == "Commander":
                own.timerTargetChanged += 1
                if own.timerTargetChanged == 600:
                    targetChangedOwn(own)
                    own.timerTargetChanged = 0
                if own.targetID != "":
                    Ballistica(own)
   
        #Блокировка возможности стрельбы при отсутствии бокомплекта
        if own.BK == 0:
            own.shoot = 0
            own.targetID = ""
            own.PR = 0
       
        #Разрешение на открытие огня
        if own.PR == 1:
            own.shoot = 1
        else:
            own.shoot = 0
   
        #Стрельба
        if own.shoot == 1:
            shooting(own)   
        #Остановка стрельбы
        if own.Temp_shoot != own.shoot:
            if own.Temp_shoot == 1:
                own.Temp_shoot = own.shoot
           
           
           
#Функция выбора ближайшей цели. Используется для зениток и стрельбы прямой наводкой
#по конкретной цели, для стрельбы с закрытых позиций по площадям и квадратам
#используются координаты
def targetChangedOwn(own):
    if own.typeMissions == "AntiAircraft":
        own.targetType = 0
    sceneObjList = ArbitrGame.UNITS[0][own.targetType][own.target]
    tempSortedList = []
    if len(sceneObjList) > 0:
        tempsortedList = sorted(sceneObjList, key = lambda obj:own.getDistanceTo(obj))
        #print(tempsortedList)
        own.targetID = str(id(tempsortedList[0]))
    else:
        own.PR = 0
        own.targetID = ""

#Расчет точки упреждения      
def Ballistica(own):
   
    try:
        target = scene.objects.from_id(int(own.targetID))
        #Тип прицеливания - отслеживание самой цели
        if own.typeCoordTarget == "AimingTarget":
            newPos = target.worldPosition
        #Тип прицеливание с упреждением
        if own.typeCoordTarget == "VisibleTarget":
            # Рассчитываем время полета снаряда.       
            t = own.getDistanceTo(target) / own.speedBullet  
            # Рассчитываем путь перемещения объекта за это время.
            S = target.worldLinearVelocity * t
       
            xS = target.worldPosition[0] + S[0] + random.randrange(-own.razbros,own.razbros)
            yS = target.worldPosition[1] + S[1] + random.randrange(-own.razbros,own.razbros)
            zS = target.worldPosition[2] + S[2] + abs((-10*t*t)/2) + random.randrange(-own.razbros,own.razbros)
            newPos = [xS, yS, zS]
        #print(target)
        Turret(own, newPos)
    except:
        own.PR = 0
        own.targetID != ""

#Управление наведением в горизонтальной плоскости       
def Turret(own, newPos):
    #print(own.PR)
    Turret = own.childrenRecursive[own.unitName + "Turret_"]
    #Данные по ориентации
    orientY = Turret.getVectTo(newPos)[2][1]
    orientX = Turret.getVectTo(newPos)[2][0]
   
    #Скорость наведения
    kZ = own.speedMaxGor
   
    correct = 1.0
    znak = 0
   
    if orientX > 0:
        znak = -1
    elif orientX < 0:
        znak = 1
   
    if orientY > 0.9:
        correct = 1 - orientY
    else:
        correct = 1.0  
   
    Turret.applyRotation([0.0,0.0,kZ*correct*znak], True)
    Canon = own.childrenRecursive[own.unitName + "Canon_"]
    orientZ = Canon.getVectTo(newPos)[2][2]
    kX = own.speedMaxVert
    correctCanon = 1.0
    znakCanon = 0
   
    if orientY > 0.7:
        if orientZ > 0:
            znakCanon = 1
        elif orientZ < 0:
            znakCanon = -1
     
        if abs(orientZ) < 0.2:
            correctCanon = abs(orientZ)
        else:
            correctCanon = 1.0
       
        Canon.applyRotation([kX*correctCanon*znakCanon,0.0,0.0], True)
       
    if abs(orientZ) < 0.1 and orientY > 0.9 and own.getDistanceTo(newPos) < own.distMax:
        #print(own.getDistanceTo(newPos))
        if own.Temp_cassetteBK < own.cassetteBK:
            own.PR = 1
           
        else:
            own.PR = 0
    else:
        own.PR = 0
   
    #Перезарядка - магазин, кассета, обойма или снаряд
    if own.Temp_cassetteBK == own.cassetteBK:
        own.PR = 0
        if own.Temp_cassetteBK > 0:
            if own.Temp_timerCassetteChanged < own.timerCassetteChanged:
                own.Temp_timerCassetteChanged += 1
            elif own.Temp_timerCassetteChanged == own.timerCassetteChanged:
                own.Temp_timerCassetteChanged = 0
                own.Temp_cassetteBK = 0
               
       

   
def shooting(own):
    #Стрельба из пушек, пулеметов и многоствольных систем
    if own.typeShoot == "Zalp":
        for FLAME in own.fireGun:
            addBullet(own, FLAME)

    #Стрельба из РСЗО, РБУ и прочих реактивных(ракетных) систем
    elif own.typeShoot == "Paket":
        FLAME = own.fireGun[own.BK-1]
        addBullet(own, FLAME)
       
    own.BK -= 1
    own.Temp_cassetteBK += 1

#Функция добавления снаряда и прочего - звука, вспышки...           
def addBullet(own, FLAME):
    Canon = own.childrenRecursive[own.unitName + "Canon_"]
    #Вспышка - лампа
    vspyshka = scene.addObject('LampUniversal', Canon, own.gunPausa+1)
    vspyshka.setParent(Canon, False, False)
    vspyshka.localPosition = FLAME
    #Вспышка - меш
    vspyshkaMesh = scene.addObject('AudioGun', Canon, own.gunPausa+5)
    vspyshkaMesh.setParent(Canon, False, False)
    vspyshkaMesh["audioProp"] = own.audioGun
    vspyshkaMesh.replaceMesh("FireUniversal", True, False)
    vspyshkaMesh.localPosition = FLAME
    razmer = own.scaleGun
    vspyshkaMesh.worldScale = [razmer, razmer, razmer]
    vspyshkaMesh.visible = 1
       
    #Снаряд цепляется к самому юниту и происходит мутация
    #Проверяется наличие атрибута вроде калибрСнаряд = 23мм
    #и объект мутирует в своем классе
    dynObj = scene.addObject('UniversalBullet',vspyshka , 300)
    dynObj.worldScale = [1.0,1.0,1.0]
    if "calibr" not in dynObj:
        dynObj["calibr"] = own.gunBullet   
    dynObj.localLinearVelocity = [random.randrange(-own.randomSpeed,own.randomSpeed),
                                  random.randrange(-own.randomSpeed,own.randomSpeed)+own.speedBullet,
                                  random.randrange(-own.randomSpeed,own.randomSpeed)] 

Пост, конечно, длинноватый получился, да и сами скрипты сыроваты, хотя и работают. Но как "история развития", может, кому-то и будет интересно. А то и полезно. В завершение приведу скрин работы "Эрликона". Пушка смотрит в сторну цели , вот только пока она еще безобидна. Надеюсь, ненадолго. Освещение, конечно еще то... Все никак не начну ковыряться в скриптах упитиса, чтобы понять, как писать шейдер... Ну, не все ж сразу...




понедельник, 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 (движение по карте высот без физики). Разные интересные мысли крутятся, однако... Вроде полногог отказа от физики, кроме снарядов и бомб. В ракетах же отказался и ничего, все нормально...

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

 

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


понедельник, 28 ноября 2016 г.

И снова - записки бюрократа...

Давно я так плотно не работал с Geany... поскольку на подходе нарисовался F-15 Eagle, а характеристики многочисленных AIM-7/9/120 зияли пустотой, оставленные на потом, пришлось срочно  набивать джейсоны для этих (и не только) ракет "воздух-воздух". Помимо американских ракет я "раздал слонов" также нашим, плюс английским, французским, итальянским и израильским УРВВ (про английские зря я написал во множественном числе, потому как присутствует только одна ракета - Skyflash).
Работа велась по принципу "эталона". Брался аналог той или иной модели другой страны и по прикидкам и характеристикам в монографиях и всевозможных вики печатались ТТХ, в том числе скорость реакции слежения, устойчивость к помехам и тд. Понятно, что кроме дальности и скорости с массой ракеты, все остальные данные брались в общем-то "от балды", но джейсоны хороши тем, что если очень надо, достаточно открыть файл и изменить в нем нужные цифры, причем в строчке понятно, о чем идет речь.
В результате были "осчастливлены" ракеты следующих семейств:
Р-3/13/60/73/33/37/40/27/77/98/23/24,
AIM-7/9/120,
Python3/4/5,
Shafrir1/2,
Skyflash,
Matra Super 530D/F,
MICA-EM/IR,
Aspide Mk1/2,Matra R550 Magic.
Все? Кажись, все. Нет ракет типа Р-4, юаровских "Кукри", китайских PL-7/9/12 и шведских клонов. Ах да, еще есть "Метеоры" и "Ирис-Т", общеевропейские, так сказать. АМРАМ - 8 штук, Р-27 - 7 штук, "Сайдуиндеров" - около 15, дюжина (или уже больше?) - "Спэрроу". Много в сумме, короче получается.
А далее наступил черед подключения Ф-15... Сначала пришлось подправлять имена в файле модели. Затем спешно писать json для баков и пилонов самолета, поскольку БГЕ пребывал в недоумении, пытаясь понять, что от него хотят. Потом новый юнит вдруг полетел хвостом вперед и вниз, причем скорость начала приближаться к субсветовой... Как оказалось, у одной детали не был убран статки в настройках, а поскольку она оказалась внутри кубика-двигателя, тот попытался ее "выплюнуть" и "подавился". Наконец "Игл" полетел прямо вперед и строго по горизонтали. Включил внешнюю камеру обзора. Тому, что я увидел, в русском языке соответствует цензурное слово "фигня" (но есть много других, не столь благозвучных и начинающихся на "х"). Ракеты, баки и летчик сдвинуты вперед и вверх. Причем фигура летчика парит в воздухе перед носовым обтекателем.
Сначала я подумал, что где-то переборщил со шкалой потомков. Проверил - все норамльно. После нескольких попыток понять, что это было, отложил на сегодняшнее утро. Как оказалось, утро действительно мудренее вечера. Ошибку нашел почти сразу - несовпадение центра промежуточного потомка с нулевыми координатами. После чего вновь запустил тест и с гордостью пронаблюдал результат:

Сегодня успел основательно поработать над моделью F-16C, для которой теперь надо сделать текстуры формата dds и изменить скрипт (обычно я беру аналогичный скрипт из другого бленда и его изменяю под новую модель).
Что касаемо кабин, то здесь дела обстоят не столь радужно. Если центральную панель "Игла" еще как-то более-менее похожей на оригинал сделать можно, то с боковыми - просто беда. Для F-16 все обстоит гораздо лучше, подробных панелей и схем гораздо больше. Вероятно, придется делать версии кабин, а потом их обновлять по мере улучшения качества картинки.

Сегодня

аСе


четверг, 10 ноября 2016 г.

Первый сбитый, сверхзвук, штопор и подопытные кролики.

Несколько дней назад случилось знаменательное событие в жизни второй, пока еще строящейся версии проекта. Ракетой Р-23Р с борта МиГ-23МФ ВВС Ливии была поражена мишень, рольк оторой исполнил БПЛА Ла-17. Один подопытный кролик скушал второго и стех пор происходит подобное регулярно.
После долгой возни с амонведением ракет и вылавливанием ошибок в скрипте самонаведения (которые заключались лишь в неверной расстановке последовательности строчек, ракетам удалось объяснить, кого надо выбирать из скопища объектов в сцене, как на него ориентироваться . А потом пришлось думать над нанесением ущерба путем подрыва на случайной (в разумных пределах) дистанции. В зависимости от дистанции подрыва и выдается ущерб, причем не только цели но и всем объектам вокруг нее, опять-таки в некотором радиусе. Чем дальше подрыв от объекта, тем меньше вероятность для него получить смертельные повреждения. Это было сделано, чтобы при удачном стечении обстоятельств могла повториться ситуация поражения одной ракетой нескольких самолетов. как это случилось во время ирано-иракской войны, когда иранский "Томкэт" одной ракетой "Феникс" сбил сразу три иракских МиГ-23БН, шедших в плотном строю или во Вьетнаме, когда одним из первых пусков ЗРК С-75 тоже было сбито три "Фантома" (американские летчики тогда еще были непугаными).
Затем пришлось с помощью скриптов объяснять сбитой мишени, что она должна плавно падать по баллистической кривой, да еще при этом испуская огонь и дым. Дым-то она испускает, а вот огонь пока нет... Попутно на b3d.ua прозвучали предложения делать системы частиц одним объектом, состоящим из многих плейнов, управляя через скрипт их геометрией, расположением в пространстве и другими параметрами. Идея хорошая, только мой уровень пока не достиг дзена управления вершинами мешей. Я пока  ограничился беглым просмотром АПИ, но детально не вникал. Потому что впереди будет 2.78 официальная сборка, плюс я занялся еще пробитием звукового барьера и штопором.
в первой версии пробитие звукового барьера приводило к снижению слышимости двигателей, резкому хлопку с появлением быстро тающего облака и только. На сей раз я не снижаю громкость двигателя, я просто отодвигаю все источники звука назад от самолета и возвращаю их на место при переходе со сверхвука на дозвуковой режим.
И вот сегодня утром мне удалось отрегулировать вывод самолета из штопора. Этот режим я так и не ввел в первой версии, зато теперь оттянулся. Обеспечив сваливание машины в штопор, мне пришлось искать условия выхода их него. Точнее, регулировать некоторые коэффиенты в  модели полета. Я прекрасно отдаю себе отчет, что это - псевдоаэродинамика, к реальности имеющая отношение разве что лишь внешне, но это лучше, чем ничего. Кроме того, попутно ввел ограничения по крену - чем больше тангаж самолета (опущен или задран нос), тем хуже (медленнее) он крнеится (прочитал об этом на форуме Лок Она и немедленно всадил в скрипт по принципу "чтоб было и на что-то было похоже). Модель полета и раньше отличалась от первой версии, а сейчас - тем более.
В общем-то, теперь можно переходить на изготовление искусственного интеллекта летающих ботов, попутно вводя радиопереговоры, звуки кабины и прочее.
Нныче что-то Гугл не хочет картинки добавлять, не знаю уж почему, так что скрин добавить не удалось. Кому интересно, идите на b3d.ua, там последние скрины и увидите.


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

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

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

вторник, 3 мая 2016 г.

Горячая пора-5. Снова бубен и танцы...

Заимев кое-какой опыт игростроения и программирования, со своей колокольни могу сказать, что самое пакостное в этом деле - выработка стандартов. Нет речь не идет о вещах типа формата кодировки типа Юникод или UTF-8(хотя очередной неожиданный облом с кодировкой заставил меня сильно пожалеть, что не существует одного раз и навсегда установленного стандарта кодировки, по крайней мере тогда  можно было бы не опасаться "взбрыков" Винды).
Стандарты в игростроении это некая сакральная вещь, к которым приходишь после долгих размышлений, проб, ошибок и разного рода неожиданностей, иногда столь же забавных, сколь и зловредных.
Целых три дня я пытался сообразить, с какого перепугу у меня не генерятся одинаковые подвески оружия на разных пилонах. Потом было выявлено "узкое место". Потом был применен метод "против лома нет приема, если нет другого лома"... В общем, очередные танцы с бубном. Однако по порядку.

Все варианты подвесок для того или иного юнита, а также его стартовые значения свойств (типа ТТХ, БРЭО и дополнительных объектов у меня прописаны в текстовом файле с именем - "ИмяЮнита_Нация_Инит" формата txt. Это именно что текстовый файл, только весьма своеобразного наполнения. Для подвесок вооружения строчки в нем имеют вид:
P|1|1-R-60-4|FLG_APU602W_|0.0025|0.034
P|1|1-R-60-4|R-60|-1.428|-0.81|-0.01|-0.0175|0.0|0.0|1|0.01|0.043|1|60|s3
P|1|1-R-60-4|R-60|1.428|-0.81|-0.01|-0.0175|0.0|0.0|1|0.01|0.043|1|60|s3
P|1|1-R-60-4|R-60|-1.66|-0.81|0.17|-0.0175|1.575|0.0|1|0.01|0.043|1|60|s3
P|1|1-R-60-4|R-60|1.66|-0.81|0.17|-0.0175|-1.575|0.0|1|0.01|0.043|1|60|s3
P|2|2-R-60-4|FLG_APU602F_|0.0025|0.034
P|2|2-R-60-4|R-60|-0.47|-0.21|-1.0|-0.0175|0.0|0.0|2|0.01|0.043|1|60|s3
P|2|2-R-60-4|R-60|0.47|-0.21|-1.0|-0.0175|0.0|0.0|2|0.01|0.043|1|60|s3
P|2|2-R-60-4|R-60|-0.7|-0.21|-0.71|-0.0175|1.575|0.0|2|0.01|0.043|1|60|s3
P|2|2-R-60-4|R-60|0.7|-0.21|-0.71|-0.0175|-1.575|0.0|2|0.01|0.043|1|60|s3

В данном случае после буквы Р и разделительной черты следует индекс пары подвески (первая, вторая...), затем следует куча других значений - координаты местонахождения, тип изменяемого меша, повороты объекта при появлении, вес, лобовое сопротивление и так далее... Замечу только, что сначала записи типа 1-R-60-4 и ей подобные выглядели так -  R-60-4. На подобное извращение пришлось пойти из-за весьма любопытного нюанса. Маленького такого нюанса... И зловредного.
Для того, чтобы не плодить кучу проперти типа пойнтВеапон1, пойнтВеапон2  и так далее, в отличие от первой версии у меня существует лишь одно такое проперти, тоже строковое и имеет оно вид (точнее имело вид) - 'FuelTank-3_R-60-4_R-60-4'. Чтобы получить список вооржения я эту строку преобразовал в список матодом
if weapon != 'No_Weapon':
        #То режем значение проперти по списку   
        weaponList = list(weapon.split('_'))

При распечатке списка получилось ['FuelTank-3','R-60-4','R-60-4']
Казалось бы, радуемся жизни. Генерация оружия у меня идет методом перебора символов в текстовом файле (как раньше писал):
#то заглядываем в файл
    for string in directClass:
        #Срез строки по маркеру |
        config = string.split('|')
        #Эта строка, с начальным символом # - комментарий и ее читать не надо
        if config[0] == '#':
            continue
     
        #Это означает, что строка относится к подвеске оружия
        elif config[0] == 'P' and weapon != 'No_Weapon':
            #Далее сравниваем значение названий вооружения и их ИНДЕКСА в списке weaponList, потому что
            #названия подвеско могут совпасть, а вот и названия и номера одновременно - нет.
            for obj in weaponList: 
                #print(weaponList.index(obj))
                if obj == config[2]:
                    #Это название МЕША, который следует загрузить из бленда
                    importObj = config[3]
                    #НЕ ПУТАТЬ НАЗВАНИЯ ПОДВЕСКИ  И МЕША ТРЕБУЕМОГО ОБЪЕКТА, ПОТМУ ЧТО ПОДВЕСКА МОЖЕТ БЫТЬ СМЕШАННОЙ
                  
                    if importObj not in bge.logic.globalDict["listLoadObj"]:
                        bge.logic.LibLoad('//Weapon/' + importObj + '/' + importObj + ".blend",'Mesh',load_actions = False)
                        bge.logic.globalDict["listLoadObj"].append(importObj)
                    #В строчках подвесок указываются пилоны подвески, но для них проперти веапон
                    #задаваться не будет - ни к чему, зато число объектов-ракет и бомб определяется числом строчек
                    newM = scene.addObject('UniversalMesh', own)
                    newM['meshs'] = config[3]
                    newM.setParent(own.parent, False, False)
                    #После парентинга и замены мешей раздаем координаты ракетам и бомбам
                    if 'APU' not in importObj:
                        if 'Pilon' not in importObj:
                            if 'Fuel' not in importObj:
                                newM.localPosition[0] = float(config[4])
                                newM.localPosition[1] = float(config[5])
                                newM.localPosition[2] = float(config[6])
                                newM.applyRotation([float(config[7]),float(config[8]),float(config[9])],True)
                                newM['weapon'] = int(config[10])
                                newM['antiForce'] = float(config[11])
                                newM['massChild'] = float(config[12])
                                newM['childBK'] = int(config[13])
                                newM['textWeapon'] = config[14]
                                newM['tipSbros'] = config[15]
                            #Зато у топливного бака есть емкость
                            elif 'Fuel' in importObj:
                                newM.localPosition[0] = float(config[4])
                                newM.localPosition[1] = float(config[5])
                                newM.localPosition[2] = float(config[6])
                                newM['fuel'] = float(config[7])
                                newM['antiForce'] = float(config[8])
                    #Для пилонов и АПУ предусмотрено лобовое сопротивление, как идля топливных баков, ракет и бомб
                    elif 'APU' in importObj:
                        newM['antiForce'] = float(config[4])
                    elif 'Pilon' in importObj:
                        newM['antiForce'] = float(config[4])
По окончании всего этого закрываем текстовый файл и ликвидируем генератор (мавр сделал свое дело). Сбольшим удивлением обнаружил, что генерится у меня только одна пара подвесок, причем ДВА РАЗА. то есть я вижу-то одну пару подвесок - с четырьмя Р-60, но их там ВОСЕМЬ и ДВА ПИЛОНА, они "вложены2 друг в друга. вот тут я и завис... Перепробовал разные способы, ничего не помогало. причем вроде как алгоритм-то верный - фигура летчика, баки, все это прекрасно работало. Ошибка была выявлена и оказалась весьма странной. Снова смотрим:if weapon != 'No_Weapon':
        #То режем значение проперти по списку   
        weaponList = list(weapon.split('_'))

Перед этим я исправил в текстовом файле R-60-4 и аналогичные строки на 1-R-60-4 и так далее. Думал, если дополнительно указать номер подвески перед ее названием, то это сработает.
Посмотрел на список:
 ['FuelTank-3','R-60-4','R-60-4']
После манипуляций с резкой и преобразованием проперти в список с учетом ИНДЕКСА полученных элементов в списке должно было пропечататься:
  ['0-FuelTank-3','1-R-60-4','2-R-60-4']. А вот получилось:
['0-FuelTank-3',1-'R-60-4','1-R-60-4'].
Теперь понятно, почему первая пара подвески генерилась дважды, а второй не было вообще? Я специально распечатал строковые значения индекса списка и получил 0,1,1. А должно было быть 0,1,2. Как у всех нормальных людей. Видно чего-то опять "гениальное" сотворил, талант у меня на такие вещи, м-дааааа...
Тогда я решил поступить по-простому, по пролетарски. Раз эта ошибка появляется при одинаковых названиях оружия на подвеске, значит, надо эти названия сделать неодинаковыми. Как хорошо, что я еще не добрался до меню, а экспериментирую, жестко задав значение проперти в исходном скрипте... Я просто поменял вид 2оружейного2 проперти на 0-FuelTank-3_1-R-60-4_2-R-64.
Против лома нет приема. Скрипт послушно проглотил наживку. И имеем результат:

Танцы с бубном продолжаются, denis8424 предложил просто задать значения подвески "справа-налево", причем для всех точек, что позволит сделать индивидуальные подвески. В том числе и асимметричные. Со своей стороны, кажется додумался, как выбирать оружие, несколько упростив эту процедуру, заодно это позволит не переключаться при наличии одинаковых вооружений на разных парах подвески. Выбор оружия должен производиться не по номеру подвески а по названию самого оружия. Правда, это дело еще надо обдумать...