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

суббота, 24 июля 2021 г.

Игра "Морской бой. След торпеды" Альфа-версия.

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


И вот подумалось, а почему бы не попробовать сделать то же самое в БГЕ. Пусть и мертв ныне этот движок, в смысле, не занимаются им больше создатели Блендера, но уж такое-то должен он потянуть.

Сказано- сделано. Альфа версия


Простенькая игра. "Морской бой. след торпеды" Создана в BGE, для работы игры испольуйте Blender 2.79b.Управление - перископ влево-вправо - стрелочки влево-вправо, выстрел - - пробел. При переходе на сцену подведения итогов - выход из игры - стрелочка вниз, новый круг - стрелочка вверх.
Длительность игрового сеанса - 3 минуты, боезапас - 20 торпед. После первого сеанса в разделе "Лучший результат будут нули, затем идет сравнение и результат улучшается в первую очередь по количеству побед за сеанс, во-вторых, если количество текущих побед совпало с лучшим результатом, идет сравнение с количеством израсходованного боезапаса, чем меньше израсходовано торпед на одну цель, тем лучше.

Можете поэкспериментировать с дальностью до генерируемых кораблей - открывайте скрипт Submarine_Control.py и ищите строчку почти в самом конце randomXY = random.randrange(200, 1400). В скобках эта самая дальность, можете расширить ее пределы, но не более 4500, хотя сомневаюсь, что на такой дистанции удастся вообще когда-нибудь попасть. А вот уменьшить, скажем до 800-1000 - улучшит количество попаданий. Отрицательные цифры ставить нельзя, и меньше 100 тоже.

Объем упакованного файла - 49 Мб, распакованного проекта - 74. Запускаемый файл - бленд SeaBattle>blend. Скрипт, о котором я говорил, находится в нем же.

Альфа версия. Только один тип мишени - незатекстуренный, тонет при попадании без всяких дополнительных эффектов, движение слева-направо. Предполагаю следующие изменения/дополнения:
1) Еще пара-тройка моделей кораблей.
2) Все модели с текстурами.
3) Смена текстуры неба случайным образом при старте (для разнообразия).
4) Возможность появления кораблей с другой стороны, но движение все равно будет только в одну сторону (типа конвой в море)
5) Если БГЕ не будет сильно возмущаться - добавить эффект разрушения - пожар на тонущем корабле с дымом.

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

https://drive.google.com/file/d/1cfOjeJLvPIUFGBjxt1e19u2ubN6V5XBh/view?usp=sharing
https://yadi.sk/d/nqPDWcWfxCe93Q


Скрины:

Старт


Сама игра.

Подведение итогов



пятница, 2 февраля 2018 г.

Классы-наследники для оружия. Доводка напильником.

На некоторое время работа над прпоектом как бы подзависла. Причиной тому было продолжавшееся"дробление" скриптов, кстановление взаимосвязи между ними, шлифовка кода и его "притирка". Долгое время никак не удавалось отработать поражающее действие оружия на юнитах - из-за того, что когда я ломал старую схему, в импортируемом новом коде сдублировал функции. После чего корректная работа кода нарушилась. Также я долго и безуспешно пытался понять причину странного поведения ракет Р-27Р - после пуска с МиГ-29 одна обязательно взрывалась почти сразу (самоликвидация), а вторая шла в цель. Позже выяснилось, что причина просто в некорректном разрешении на пуск - олно выдавалось за пределами дальности, поэтому придется еще пошерстить скрипт сеносрра юнитов, чтобы этот баг устранить.
Но сначала я грешил на сам класс оружия и такие основания у меня были. Хотя я вроде как и освоил классы, и даже классы наследники, но не все так просто. Когда идет мутация объекта - при создании класса, то данные в самом классе меняются для всех объектов, которые ранее этот самый классс использовали. Иначе я никак не могу объяснить феномен поиска деталей МиГ-29 у Ф-15 (а именно такую ошибку мне выадвала консоль) или деталей МиГ-23 у Ф-16. Это безобразие продолжалось ровно до тех пор, пока я не ввел наследование классов для юнитов. В итоге подобные вещи прекратились, потому что классы МиГ-29 и Ф-15, а также МиГ-23 и Ф-16 более не пересекаются. Это отдельные классы, хотя и имеющие схожую структуру.
Весьма раздраженный поведением Р-27Р и еще не ведая об истинных причинах, я приступил к созданию классов наследников для оружия. По-видимому, я все же набил руку, потому что сделал это практически с первого залпа. Отработав технику на Р-27Р, принялся терпеливо дописывать json  с данными для оружия и для оружия же писать скрипты класса. Папок было довольно много, поэтому делал я это в несколько приемов, затратив пару дней. Несложно, но одноообразно и надо быть внимательным, чтобы не проскочили символы типа плюс-минус в заголовке класса - консоль тут же выразит свое возмущение...
Теперь превращение 30-мм снаряда в 152-мм во время выстрела и его полета к цели исключено. Схема выглядит так.
Запускаем скрипт в игровом файле - тащим из нужной папки класс оружия, название питоновского файла обычно выглядит так - R27R.py.

def Weapon(cont):
    own = cont.owner
   
    nameWeapon = ""
    nameGeneral = own.parent.name
    nameSysPath = ""
    #print(own.parent, own.childrenRecursive)
    if hasattr(own.parent, "dictWeapon") == True:
        if "WPU" in own.parent.dictWeapon:
            nameGeneral = own.parent.dictWeapon["WPU"]["gunBullet"]
    if hasattr(own.parent, "parent") == True:
        #print(own.parent.parent)
        if hasattr(own.parent.parent, "parent") == True:
            #print(own.parent.parent.parent)
            if hasattr(own.parent.parent.parent, "dictWeapon") == True:
                if "bulletGen" in own.parent.parent.parent.dictWeapon:
                    #print("True")
                    nameGeneral = own.parent.parent.parent.dictWeapon["bulletGen"]
                    #print(nameGeneral)
           
    nameSysPath = "//Weapon/"+nameGeneral   
   
    sys.path.append(bge.logic.expandPath(nameSysPath))
    with open(bge.logic.expandPath("//Weapon/"+nameGeneral+"/"+nameGeneral+".json"), 'r') as directWeaponClass:
        JSONweaponClass = json.load(directWeaponClass)
        nameWeapon = JSONweaponClass["ownClass"]
   
    unit_module = __import__(nameWeapon)
    #print(nameWeapon)
    unit_class = getattr(unit_module, nameWeapon)
    #print(nameGeneral)
    x = unit_class(own)
   
    #import ClassWeapon
    #own = cont.owner
    #x = ClassWeapon.typeWeapon(own)
   

А потом уже тащим класс-наследник. Он совсем маленький.

import bge

from ClassWeapon import typeWeapon

class R27R(typeWeapon):
    def __init__(self, old_owner):
        typeWeapon.__init__(self, old_owner)
   
def mutate(cont):
    R27R(cont.owner)

А этот класс сначаа тащит стандартный класс оружия, который лежит в папке со скриптами для оружия.

import bge
import json

with open(bge.logic.expandPath('//Weapon/WEAPON_CLASS.json'), 'r') as directWeapon:
    JSONweapon = json.load(directWeapon)

class typeWeapon(bge.types.KX_GameObject):   
   
    def __init__(self, old_owner):
        self.__dict__.update(JSONweapon)
       
def mutate(cont):
    typeWeapon(cont.owner)


Тоже ничего особенного. Опять матрешка - отыскивается json  со стартовыми данными и идет присвоение атрибутов.
Потом все это  подвергается доводке другим скриптом - функцией сартовой инициализации - я об этом раньше писал - просто обновляется словарь класса оружия на основе json той же Р-27Р и все.
По-хорошему, наверное, следовало бы прямо в класс вписать эту самую инициализацию, навеное. Но сделаю это чуть позже. Там свои нюансы, и опять придется перебирать все скрипты ручками...
Поражающее действие ракет я таки отработал и теперь полра шлифовать модель повреждений, а также ИИ ботов, до которого все ника не доходят руки. Что касаемо модели повреждений, то тут меня душит жаба. БГЕ сильно не любит большого количества объектов, а с огнем и дымом именно так дело и обстоит. Очевидно, придется жульничать. Бывает, что сбитые машины падают либо относительно целые (заглох двигатель), либо слабо дымя. Или просто взрываются в воздухе. Вообще же, Евгений Пепеляев, воевавший в Корее, в своей книге "МиГи против Сейбров" писал, что реактивные самолеты горят плохо, в отличие от поршневых, что ж приму его слова в качестве рекомендации и излоупотреблять красивым огненным хвостом не буду. 
             
             
     


пятница, 20 октября 2017 г.

Опытный кролик-2. Новые методы использования классов.

В прошлом посте я писал о своих опытах с МиГ-23БН, который был моим подопытным кроликом в деле отработки новых методов использования классов.
Тогда речь шла всего-навсего о создании класса-наследника с целью "развести" и не допустить "перекрещивания" данных при создании юнитов.
однако совет dron-a по поводу использования методов в самом классе заставил меня задуматься... когда я только начинал осваивать классы, я уже, сам того не зная, писал в них методы, но не знал, как правильно их использовать. видимо, для осознания новых возможностей и способов требуется время и с "лету" схватывать я таки не способен. Но все-таки медленно, но учусь. Для начала приведу пример с использованием методов теле класса на примере класса уже очень опытного виртуального колика - многострадального МиГ-23БН.

import bge

from ClassUnitAir  import UnitAir

class MiG23BN_Libya_(UnitAir):
    def __init__(self, old_owner):
        UnitAir.__init__(self, old_owner)
    #Летательный аппарат
    def is_AIR(self):
        return True
    #Подтип ЛA - вртолет, самолет (реактивный или внитовой)
    def is_AIRTYPE(self):
        return "JET"
    #Пилотируемый или БПЛА
    def is_DRON(self):
        return False
   
    #Одно- или многодвигательный
    def is_MULTIENGINE(self):
        return False
    def is_CHASSY(self):
        return True
   
    #######################################
    def is_CANOPY(self):
        return True
    def is_CANOPY_Device(self):
        CANOPY_Device = [
                        ["Cnp_",[0.0087,0.0,0.0],"rotat"]
                        ]
        return CANOPY_Device
    def is_POINT_CANOPY(self):
        reper = [0, 114]
        return reper
    ########################################
    def is_FLAPS(self):
        return True
    def is_FLAPS_Device(self):
        FLAPS_Device = [
                        ["FlpR_",[-0.0087,0.0,0.0],"rotat"],
                        ["FlpL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return FLAPS_Device
    def is_POINT_FLAPS(self):
        reper = [-50, 0, 100]
        return reper
   
   
    ######################################
    #Наличие-отсутствие предкрылков
    def is_SLATS(self):
        return True
    def is_SLATS_Device(self):
        SLATS_Device = [
                        ["SltR_",[-0.0087,0.0,0.0],"rotat"],
                        ["SltL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return SLATS_Device
    def is_POINT_SLATS(self):
        reper = [0, 40]
        return reper
   
    #####################################
    #Наличие-отсутствие крыла изменяемой стреловидности
    def is_SWINGWING(self):
        return True
    def is_POINT_WINGS(self):
        reper = [0, 300, 550]
        return reper
   
    def is_WINGS_Device(self):
        WINGS_Device = [
                        ["WngR_",[0.0,0.0,-0.00174],"rotat"],
                        ["WngL_",[0.0,0.0,0.00174],"rotat"]
                        ]
        return WINGS_Device
   
    ####################################
    #Наличие-отсутствие воздушных тормозов
    def is_AIRBRAKE(self):
        return True
    def is_AIRBRAKE_Device(self):
        AIRBRAKE_Device = [
                           ["ArbUR_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbUL_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbDR_",[0.00696,0.0,0.0],"rotat"],
                           ["ArbDL_",[0.00696,0.0,0.0],"rotat"]
                          ]
        return AIRBRAKE_Device
   
    def is_POINT_AIRBRAKE(self):
        reper = [0, 100]
        return reper

             
def mutate(cont):
    MiG23BN_Libya_(cont.owner)

Смотрим на функции типа def is_чегго-то-там. Это не что иное, как некие константы - например наличие или отсутствие крыла изменяемой стреловидности, принадлежности к воздушным юнитам, но эти константы, например, могут нести такую ценную информацию, как скорость поворота крыла и его название. Первоначально я хотел ставить не многомерные списки, а словари. Однако, после многократной ругани со стороны консоли БГЕ о невозможности прочесть из функции класса метод с этим самым словарем, решил все же поставить многомерные списки. Посмотрим, мрожет потом я все-таки пойму, где ошибался и поставлю словари.
Что это дает? Это дакт возможность пока для стандартных анимаций (точнее псевдоанимаций, потому как идет просто движение деталей через скрипт) загнать их работу в единый скрипт, избавив бленды моделей от лишнего кода по тем же перемещениям КИС для МиГ-23. Экономия существенная.
Посмотрим, как это работает. Для начала команда на перекладку крыла из скрипта для игрока CONTROL_gamer.

#Смена стреловидности - для самолетов с КИС
    if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED or keyboard.events[bge.events.TABKEY] == JUST_ACTIVATED:
        #Проперти wing у самолетов с КИС имеет минимальное значение 0. При смене стреловидности вызывается класс юнита
        #и проводится смена значений максимальных значений скорости и маневренности
        if own.FLAPS == 0:
            if own.Temp_WINGS == own.WINGS:
                if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED:
                    if own.is_SWINGWING():
                        own.WINGS += 1

Сначала при нажатии клавиши идет проверка наличия крыла изменяемой стреловидности у данного юнита. смотрим is_SWINGWING  в коде выше.Если там стоит True, то команда проходит и начинается самое  интересное. Функция перекладки в скрипте CONTTROL_UnitAir универсальна для всех типов самолетов с КИС. Вообще всех. Такова особенность построения моделей и использования в них названий.
#Псевдоанимации крыльев
def wings(own):
    #Направоение перекладки крыла
    wings = 0
   
    #Смена стреловидности возможна лишь при убранных закрылках
    if own.WINGS > own.is_POINT_WINGS()[-1]:
        own.WINGS = own.is_POINT_WINGS()[-1]
    elif own.WINGS < 0:
        own.WINGS = 0
   
    #Выставление напрaвления перекладки и убывания-возрастания проперти
    if own.Temp_WINGS < own.WINGS:
        wings = 1
        if own.WINGS-own.Temp_WINGS == 5:
            AudioEmitter = scene.addObject('AudioEmitter', own, 220)
            AudioEmitter.setParent(own, False, False)
            AudioEmitter["audioProp"] = "FLAPS"
           
        own.WINGS += 1
   
    elif own.Temp_WINGS > own.WINGS:
        wings = -1
        own.WINGS -= 1
        if own.WINGS-own.Temp_WINGS == -5:
            AudioEmitter = scene.addObject('AudioEmitter', own, 220)
            AudioEmitter.setParent(own, False, False)
            AudioEmitter["audioProp"] = "FLAPS"
       
    #Собственно, движение крыльев - это действие не зависит от уровня детализации
    for key in own.is_WINGS_Device():
        if key[2] == "rotat":
            own.childrenRecursive[key[0]].applyRotation([key[1][0]*wings,
                                                         key[1][1]*wings,
                                                         key[1][2]*wings
                                                         ],True)
   
    #Остановка псевдоанимации на кадрах со значением 0,300,550   
    if own.WINGS in own.is_POINT_WINGS():
        own.Temp_WINGS = own.WINGS

Здесь используются данные о реперных точках положения крыла - там , где должна прекратиться анимация и жестко выставлены ограничения слева и справа. Это is_POINT_WINGS.  А еще используются данные из многомерного списка. я думаю, понятно, как - название детали, скорость поворота, флаг - "поворот".

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

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

суббота, 23 сентября 2017 г.

Размышления о наземных объектах.

Не далее, как вчера удалось добиться добавления и работы уровней детализации артиллерийскогог юнита. Это новый вид юнитов - вкючает в себя буксируемые артсистемы. Как зенитки, так и гаубицы, возможно, пусковые установки ракет стационарных ЗРК и тому подобное.
Впереди введение САУ, танков , легкой БТТ и просто автотехники.
И возникает новый спектр проблем. Пока не будем касаться ИИ, потому как "слона едят по кусочкам".
Пока рассмотрим проблемы работы уровней детализации.
На данный момент уровней детализации 4.
Недетализированный. За пределами дистанции различимости юнита (атрибут maxVisibleDist) все меши всех деталей юнита меняются на плейны.
Малодетализированный уровень действует в пределах от половины до максимальной величины maxVisibleDist. Я обзываю меши, на которые меняются меши деталей юнита ЛОДами (хотя это не совсем верно, строго говоря, ног привык уже). По сути своей - это очень грубые объекты, скорее напоминающие силуэты.
Среднедетализированный уровень - в пределах от одной десятой до половины maxVisibleDist. Для этого уровня используется, как правило уже вполне высокопоигональный меш для деталей. Но есть нюанс. Нет большого количества мелких деталей вроде закрылков, рулей, элеронов и прочего для самолета.
Высокодетализированный уровень - весь самолет в сборе со всеми мелкими деталями - на расстоянии менее одной десятой maxVisibleDist.
Зачем это надо? БГЕ сильно не любит большого количесива объектов, поэтому необходимо всячески соблюдать правило: "Краткость - сестра таланта". Не так много юнитов будет так близко от камеры, да и перескоки с одного уровня на другой не так уж чтобы частые.
Интересно, что для вновь созданных артиллерийских юнитов отсутствует последний - высокодетелизированный уровень. а вот для самодвижущихся объектов он обязателен. Причина в том, что у танков , БТР-БМП, автомашин и прочего имеются колеса и иногда - гусеницы.. И они должны крутиться.
Вариантов несколько.
Первый - объект типа "гусянка+катки" - единый и неподвижный для среднего уровня. Для выского уровня добавляем родителя с катками и гусеницами в товарном количестве. Самый простой, кстати, вариант.
Вариант два. Ничего не добавляем, ибо есть UV-скроллинг. Мы его просто запустим, когда нужно и остановим, когда нет нуждя.  Лично я склоняюсь именно к этому варианту.
Однако у тех же танков есть фары, поручни, патрубки и всякая мелочь, которая тоже нужна. Ну вот, значит, ее тоже добавлять и убирать. Как уже говорилось выше, смена уровней детализации - явление нечастое. Да, можно и без этого, но иметь про запас не помешает - как не помешает законсервировать часть кода в скрипте на будущее.
И тут вдруг выползает проблема вроде блоков ДЗ и тех же бортовых экранов. Срываемых снарядами, к примеру. Удалять вершины мешей нельзя в реал-тайме. Но их можно двигать. нароимер, "схлопнув в одну точку (точнее придав нулевые координаты всем вершинам) того же блока ДЗ. Проблема только в том, как вытащить НУЖНЫЕ координаты. Просто я достаточно мало работал с мешами и пока не умею этого. Надо просто почитать АПИ и найти нужное.
Вторая вещь, которую тоже придется решать - взаимодействие снаряда и брони. По-видимому, здесь придется действовать через списки координат вершин, "выбрасывая" отработавшие координаты,  пбразуя "ослабленные" участки. Пока толкои еще сам не понимаю - мысль нуждается в провеоке практикой. Предположительно, имеем "слепок" брони объекта, который вызыается при попадании снаряда. Снаряд проверяет функцией ray координаты полигона, ищет его среди "пробитых" в списке. Если он там есть - пробитие. нет - добавляет его в этот список, не поражая юнит.
Понимаю, несколько заумно, да и кода нет пока, все на уровне мыслей. Но хоть что-то. К сожалению, БГЕ не позволяет как-то маркировать полигоны и вершины - тут надо полагаться на координаты. Да еще и научиться с ними работать.
Сегодня вытащил из первой версии скрипты наведения артиллерии и ЗРК (кстати, к стрельбе с применением упреждения приложил руку denis8424, приер турели у него в блоге есть).
Пока что отработано добавление и работа уровней детализации зенитной спаренной автоматической 35-мм пушки  "Эрликон" GDF-001, Я рассчитываю на еще тр варианта такой пушки. В том числе и роботизированный 005. Кстати, этот робот отличился в ЮАР - попросту говоря, взбесился и принялся палить во все стороны. Были убитые и раненые и сожженная техника. К счастью, давить клаву, моделируя работу артсистемы, совершенно безопасно.

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

 

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

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

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

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

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

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

пятница, 21 апреля 2017 г.

И снова "Операция "Святой Януарий". Теория классовой борьбы.

Программист из меня, мягко говоря, средненький, специального образования я на этот счет не получал, так что можног сказать, самоучка.
Некоторые вещи доходят до своего осознания довольно долго, даже не с десятого раза. Вероятно, виной тому зацикленность на практических результатах и более-менее постоянно тем же Питоном я стал заниматься где-то с 2013.
Довольно давно, года уж два как минимум, мне говорили, что осознание дзена классов объектов Питона ведет к экономии ресурсов и выигрышу в использовании БГЕ. Несколько неудачных попыток, в том числе с применением мутации, попытки написания собственных классов в конце концов навели на путь истинный. Опять получилось что-то вроде бросания зажигалки на бронированное стекло в фильме "Операция "Святой Януарий". Все оказалось довольно просто. Определение класса я здесь излагать не буду - есть много статей на эту тему. Изложу свой крайне конкретный и приземленный взгляд на эту тему. И главное, зачем, это надо? 
Сточки зрения дилетанта, меня то есть, проблема вот в чем - в коде постоянно используются проперти вида ["speed"], которые пишутся после священного слова own, либо какого-то другого имени объекта. Если их много, они начинают мешать, а если учесть, что очень многие из них используются редко, либо вообще разово, получается мертвый груз. Хотелось бы их убрать, да нельзя - раз проперти названо - все, уничтожить егог нельзя. Да и от скобочек с кавычками в глазах рябит...
А теперь смотрим, что делает класс на примере создания экземпляра класса (если я правильно написал это название) юнита летательного аппарата.

import bge
import json

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

Ind = "UnitAir"+str(id(own))
ArbitrGame = scene.objects["ArbitrGame"]
KEY = getattr(ArbitrGame, Ind)
  
#Часть данных записываем сразу с нулевыми значениями
config = {"WINGS":-1, "Temp_WINGS":-1, "FLAPS":0, "Temp_FLAPS":0, "SLATS":0, "Temp_SLATS":0, "CANOPY":0, "Temp_CANOPY":0,
          "AIRBRAKE":0, "Temp_AIRBRAKE":0, "CHASSY":0, "Temp_CHASSY":0, "ROLL":0, "Temp_ROLL":0, "YAW":0, "Temp_YAW":0,
          "PITCH":0, "Temp_PITCH":0, "ROLLwings":0, "Temp_ROLLwings":0, "avto":0, "Temp_avto":0, "PR":0, "Temp_PR":0,"LockOn":0,"enemy":0,
          "weapon":-1, "Temp_weapon":-1, "crash":1.0, "Temp_crash":1.0, "correctSpeedAngleOfAttack":0.0,
          "levelsDetails":3, "Temp_levelsDetails":3, "angleOfAttack":0.0,"ownType":0,
          "rotatX":0.0,"rotatY":0.0,"rotatZ":0.0,"rollSelf":0, "pitchSelf":0, "yawSelf":0, "antiForce":0.0,
          "massFuel":0.0, "massChild":0.0,"massFuelTank":0.0, "ownAntiForce":0.0001,"lovushki":0,
          "correctRotat":1.0, "correctSpeed":1.0, "slideRotat":0.0, "speedTemp":0.0,"typeShtopor":"NULL",
          "Temp_BK":0 ,"BK":0,"typeSensor":0,"targetType":0, "timerImpulse":0,"sonicBoom":0, "idTarget":["00000"],
          "PuskSbros":0, "Temp_PuskSbros":0, "indexRadarDist":0, "timerShoot":0, "classWeapon":"", "nameWeapon":"",
          "Temp_typeSensor":0, "localTargetList":[], "THREAT":{
          "UnitAir":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]},
          "UnitGround":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]}},
          "threatWeapon":[], "sbrosInterval":0, "ochered":0, "Temp_ochred":0, "dictWeapon":{}}
   
#Первое обновление - стартовые проперти - управление, сторона, статус, задача
config.update(KEY['startProp'])
   
#Второе обновление
with open(bge.logic.expandPath(KEY['unitPath']), 'r') as directClass:
    config.update(json.load(directClass)["listPropertys"])
   
class air(bge.types.KX_GameObject):
    nameIndex = Ind
    def __init__(self, old_owner):
        for key in config:
            setattr(self, key, config[key])
            #print(key)
       
        #Небольшое примечание - если самолет изображает мишень на стоянке, то он переводится в разряд наземных объектов
        if self.controlUnit == "Statist":
            self.ownType = 1
       
        if self.controlUnit == "Gamer":                   
           
            gamerConfig = ["a_x","a_y","a_z","Temp_a_x","Temp_a_y","Temp_a_z"]
            for key in gamerConfig:
                 setattr(self, key, 0.0)
           
            setattr(self, "timerFuel", 0)
            setattr(self, "colorHUD", 0) 
           
            sensor1 = scene.objects['SENSOR1']
            sensor2 = scene.objects['SENSOR2']
            sensor3 = scene.objects['SENSOR3']
            #Расстановка сенсоров пустышек для отслеживания ориентации в пространстве
            sensor1.setParent(self, False,False)
            sensor1.worldOrientation = self.worldOrientation
            sensor1.localPosition = [0.0, 1.0, 0.0]
            sensor2.setParent(self, False,False)
            sensor2.worldOrientation = self.worldOrientation
            sensor2.localPosition = [1.0, 0.0, 0.0]
            sensor3.setParent(self, False,False)
            sensor3.worldOrientation = self.worldOrientation
            sensor3.localPosition = [0.0, 0.0, 1.0]

            if 'Cockpit' not in bge.logic.getSceneList():
                bge.logic.addScene('Cockpit',1)
                unit = self.unitName + self.unitNation
                typeCockpit = "//Aircraft/" + unit + "/Cockpit_" + unit + "/Cockpit_" + unit + ".blend"
                bge.logic.globalDict["cockpitPath"] = typeCockpit
               
        elif self.controlUnit == "Bot":                   
            setattr(self, "etalonDvig", 0)
            setattr(self, "typeManeur", "")


def mutate(cont):
    air(cont.owner)


До строчки со словом class происходит вскрывание json-файлов и обновление словаря config. В этот словарь уже "вшиты" некоторые стандартные значения будущих атрибутов (ну, тех же свойтв-проперти), которые присущи всем машинам этого класса. Это позволило выкинуть эти свойства из перечисления в файле json, уменьшив его объем и сделав более удобочитаемым. В принципе, словарь config можно было бы втиснуть в тело класса (опять же, не совсем уверен в правильности терминологии), а по-простому говоря - между строчками со словами class и def __init__, но в этом случае объект получит еще один атрибут - этот самый словарь с дублированием всех данных внутри него - оно мне надо? Сами свойства объект получает из словаря config либо циклом-перебором всех ключей и значений из config, либо можно и проще  self.__dict__.update(config). В итоге вместо доставших скобочек и кавычек получаем значения типа self.speed = 50. Что немаловажно, этими свойствами атрибутами можно оперировать - добавляя новые - в коде вы увидите строчки с командой setattr - добавть новый атрибут. Кроме них имеются еще hasattr и delattr. Настоятельно рекомендую эти вещи, и в особенности getattr. Что это такое и с чем их едят, вы найдете сами (я имею в виду новичков и самоучек вроде меня, серьезные программисты и так все знают). С помощью этих команд можно закручивать хитроумные комбинации, получая нужные результаты.
После добавления всех атрибутов следует мутация объекта - его трансформация в новый объект с атрибутам, перечисленными в классе. Это можно сравнить с превращением гусеницы в бабочку или фиксацией проявленной фотографии раствором фиксажа (было дело, работал я с ними, но очень недолго - уже наступала эра цветных пленок, а теперь и они сошли на нет - цифровики вытеснили. Прогресс, одним словом).
Функция фиксации результата - def mutate. Кстати, dron предупреждал, что в примере к БГЕ по созданию класса, разработчики намудрили лишнего - достаточно обойтись лишь этой простенькой функцией.
Теперь процесс создания юнита выглядит так - срабатывает ОДНОРАЗОВО сенсор, дающий команду контроллеру на создание экземпляра класса. После чего включается скрипт поведения юнита - этот уже идет непрерывно. Да, логики стало чуть больше, может потом допру, как сделать одноразовый запуск скрипта создания экземпляра класса.
Уже проврено создание и удаление атрибутов сенсоров в зависимости от его названия, написаны новые функции стрельбы ракетами, пушками, сброса бомб и баков, работают кабины для МиГ-23МФ и БН. Подобрался к сенсорам и поведению ботов. плюс можно начинать отработку самонаведения ракет и противодействия им. Теперь борьбу ведут классы.
На мой взгляд, использование классов сильно упрощает работу и дает прирост в скорости.  Единственно, что надо осознать сразу - создание экземпляра класса и мутация объекта делается ОДИН РАЗ. Моя проблема была именно в том, что я этого не понимал и заставлял объект непрерывно мутировать и пересоздавать класс. Надеюсь, те новички, которые читатют этот текст, избегнут моих ошибок.
Картинок в этот раз не будет, потому как идет процесс переписывания по большей части кода с вырезанием кавычек и скобок. Скорее всего, после восстановления работы сенсоров, РЭБ, самонаведения и ИИ ботов я займусь меню. Что-то мне влом писать json для каждой миссии вручную - лучше один раз напрячься с написанием редактора миссий...

суббота, 1 апреля 2017 г.

Напоминание о своем существовании...

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

среда, 8 февраля 2017 г.

Как не надо кодить. Создание и ликвидация бага своими руками.

Я не раз замечал за собой особенность создавать такие ситуации, которые, по-видимому, кроме меня никто не создаст. Во всяком случае в здравом уме, трезвом состоянии и твердой памяти. По-видимому, это у меня такой талант.
Продолжая работу над проектом, я внезапно столкнулся с одной очень специфической проблемой. Как известно, если самолет ударяется об землю, он взрывается. Дальше все зависти от фантазии автора проекта - появляется ли черный дымщийся остов или самолет просто исчезает. В любом случае юнит "убирается с доски". Так вот, неожиданно выяснилось, что если об землю разбивается самолет игрока, то после нажатия кнопки Esc немедленно следует вылет Блендера. Причем было непонятно, что это за баг. dron написал файл для запуска файла через блендерплейер с выводом ошибок в файл error, но ситуацию это не прояснило. Файл оставался пустым.
Баг крайне непрятный и его надо было ликвидировать любой ценой. Но и понять, в чем причина было сложно. я рассуждал так: если вылет следует после исчезновения самолета игрока, значит, это как-то связано с управлением. спешно переделал код управления клавишами - уже тогда у меня появились подозрения, что в этом виновато исезновение оверлейной сцены кабины. Запустил - то же самое. Хорошо, тогда почистим скрипт управления камерами. Дело в том, что я по жадности своей и лени не стал, как в первой версии делать обмен данными между сценами через глобальный словарь, а запускал цикл проверки списка сцен с выбором сцены кабины и дальше уже традиционно обращался к scene.objects с названием объекта. Как говорится: "Жадность фраера сгубила." Однако и после зачистки скрипта камер веселье продолжилось. У меня оставался один-единственный скрипт, где еще существовал вызов списка сцен. А теперь смотрим...

def controlUnit():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = bge.logic.getCurrentScene()
    sensor = cont.sensors["unit"]
    kontakt = cont.sensors["kontakt"]
   
    #Название пропенрти для механики ведомого - при условии
    #что юнит - ведущий, при выпуске тормоза, крыла, шасси и тп он передает команду на ведомого для выполнения такой же операции
    mechanicWingman = ''
   
    #словарь свойств - для анимации, применения оружия и смены подвесок, плюс отслеживание повреждений
    if 'localDict' not in own:
        controlUnitStart()
       
    #Активная камера в сцене
    activeCam = scene.active_camera
       
    #Камера в кокпите юнита игрока
    cameraPilot = scene.objects["CameraPilot"]
   
    #Камера внешнего обзора, она же камера телевизионного прицела
    cameraWorld = scene.objects["CameraWorld"]
   
    #Центр юнита - опорный объект для всх потомков, он же нужен для смены меша на меш номера, объект присутствует изначально, это потомок
    #двигателя, причем неубираемый, также он нужен для анимации проворота фюзеляжа при выпуске-уборке шасси, поскольку на земле многие
    #машины не стоят ровно - как правилог их нос приподнят - больше или меньше
    CntAircraft = own.childrenRecursive['CntAircraft']
   
    #Это - собственно, полет
    if sensor.positive:
       
        #Контакт с ландшафтом
        if kontakt.positive: 
            #столкновение с землей сбитого самолета или вертолета
            if own['crash'] < 0.2 or own['CHASSY'] < 98:
                if 'CameraPilot' in own.childrenRecursive:
                    own.childrenRecursive['CameraPilot'].removeParent()
                    #Убираем оверлейную сцену
                    sceneList = bge.logic.getSceneList()
                    for sceneGame in sceneList:
                        if sceneGame.name == 'Cockpit':
                            sceneGame.end()
               
                CrashPropertyAir()
           
                import ControlScene
                ControlScene.nearUnitExplode()
                #Убираем сам объект
                own.endObject()
       
       
        if own['bot'] == 0:
            #Вызов управления юнита игрока с помощью клавиатуры, вызов идет постоянно
           
            bge.logic.globalDict['idTarget'] = own['idTarget']
            import ClassGamer
            ClassGamer.control()
        elif own['bot'] > 0:
            #Вызов искусственного интеллекта - необходимо доработать, поскольку own['bot']=1 - взлет, 2 - посадка, 3 - полет по маршруту,
            import ClassBotAir
            ClassBotAir.control()
       
        #Пока ЛА исправен, он может лететь
        if own['crash'] > 0 and own['bot'] > -1:
            import ClassEngineAir
            ClassEngineAir.control()
       
        #Падение по балиистической кривой для сбитого
        elif own['crash'] < 0.1 and own['bot'] > -1:
            unit_Crash = __import__(own['unitModule'])
            unit_Crash.CRASHscenery()
            #Для самолета игрока убираем сцену оверлей и камеру пилота
            if own['bot'] == 0:
                if 'sceneryCrash' in own:
                    if own['sceneryCrash']['timerCrash'] == 1: 
                        if 'CameraPilot' in own.childrenRecursive:
                            own.childrenRecursive['CameraPilot'].removeParent()
                    #Убираем оверлейную сцену
                    sceneList = bge.logic.getSceneList()
                    for sceneGame in sceneList:
                        if sceneGame.name == 'Cockpit':
                            sceneGame.end()        
            import ClassEngineAir
            ClassEngineAir.traectoryCrash()
         
        #Просчет уровней детализации
        #Высокий - детализированная анимированная модель
       
        if own.getDistanceTo(activeCam) < 200:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 0
        #Средний - детализированная неанимированная модель
        elif 200 < own.getDistanceTo(activeCam) < 3500:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 1
        #Низкий - малодетализированная модель
        elif 3500 < own.getDistanceTo(activeCam) < 10000:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 2
        #Малый - модели, как таковой, нет
        elif own.getDistanceTo(activeCam) > 10000:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 3
       
       
           
        unit_module = __import__(own['unitModule'])
        unit_module.correctData()
       
        ####################################
        #Смена уровня детализации
        if own['localDict']['levelsDetails'] != own['levelsDetails']:
            ClassOperation.LOD()
            own['localDict']['levelsDetails'] = own['levelsDetails']
           
        if own['localDict']['dvig'] != own['dvig']:
            ClassOperation.dvig()
            own['localDict']['dvig'] = own['dvig']
           
        if own['localDict']['crash'] != own['crash']:
            import ClassCrash
            ClassCrash.CrashModel()
            own['localDict']['crash'] = own['crash']
       
        #Пробитие звукового барьера  
        if own['localDict']['sonicBoom'] != own['sonicBoom']:
            ClassOperation.sonicBoom()
            own['localDict']['sonicBoom'] = own['sonicBoom']
       
        if own['localDict']['startLO'] != own['startLO']:
            for generatorLO in own.childrenRecursive:
                if generatorLO.name == 'generatorLO':
                    generatorLO['startLO'] = own['startLO']
            own['localDict']['startLO'] = own['startLO']
       
        if own['levelsDetails'] == 0:          
            #Работа проперти рысканья              
            if own['rotatZ'] != 0 or own['localDict']['YAW'] != own['YAW']:
                unit_module.yaw()
            #Работа проперти тангажа              
            if own['rotatY'] != 0 or own['localDict']['ROLL'] != own['ROLL']:
                unit_module.roll()
            #Работа проперти тангажа              
            if own['rotatX'] != 0 or own['localDict']['PITCH'] != own['PITCH']:
                unit_module.pitch()
           
        #Стандартная операция - отсчет кадров с помощью проперти
        #Шасси
        if own['localDict']['CHASSY'] != own['CHASSY']:
            unit_module.chassy()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['CHASSY'] > own['CHASSY']:
                if own['localDict']['CHASSY'] == own['CHASSY'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
            if own['localDict']['CHASSY'] < own['CHASSY']:
                if own['localDict']['CHASSY'] == own['CHASSY'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
       
        #Тормоз
        if own['localDict']['AIRBRAKE'] != own['AIRBRAKE']:
            unit_module.airbrake()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['AIRBRAKE'] > own['AIRBRAKE']:
                if own['localDict']['AIRBRAKE'] == own['AIRBRAKE'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'AIRBRAKE'
                    audioUnit()
            if own['localDict']['AIRBRAKE'] < own['AIRBRAKE']:
                if own['localDict']['AIRBRAKE'] == own['AIRBRAKE'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'AIRBRAKE'
                    audioUnit()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['AIRBRAKE'] - own['AIRBRAKE']) == 2:
                    mechanicWingman = 'AIRBRAKE'
                    controlUnitWingman(own, mechanicWingman)
                   
           
        #Перекладка крыла - для самолетоа с КИС
        if own['localDict']['WINGS'] != own['WINGS']:
            unit_module.wings()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['WINGS'] > own['WINGS']:
                if own['localDict']['WINGS'] == own['WINGS'] + 2:
                    own['localDict']['audioLife'] = 230
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            if own['localDict']['WINGS'] < own['WINGS']:
                if own['localDict']['WINGS'] == own['WINGS'] - 2:
                    own['localDict']['audioLife'] = 230
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['WINGS'] - own['WINGS']) == 2:
                    mechanicWingman = 'WINGS'
                    controlUnitWingman(own, mechanicWingman)
             
        #Закрылки
        if own['localDict']['FLAPS'] != own['FLAPS']:
            unit_module.flaps()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['FLAPS'] > own['FLAPS']:
                if own['localDict']['FLAPS'] == own['FLAPS'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            if own['localDict']['FLAPS'] < own['FLAPS']:
                if own['localDict']['FLAPS'] == own['FLAPS'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
           
           
        #Предкрылки
        if own['localDict']['SLATS'] != own['SLATS']:
            unit_module.slats()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['SLATS'] - own['SLATS']) == 2:
                    mechanicWingman = 'SLATS'
                    controlUnitWingman(own, mechanicWingman)
           
        #Фонарь кабины
        if own['localDict']['CANOPY'] != own['CANOPY']:
            unit_module.canopy()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['CANOPY'] > own['CANOPY']:
                if own['localDict']['CANOPY'] == own['CANOPY'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
            if own['localDict']['CANOPY'] < own['CANOPY']:
                if own['localDict']['CANOPY'] == own['CANOPY'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
       
        ######################################################
        ######################################################
        ######################################################
       
        #Смена подвесок - одиночный вызов функции из скрипта ClassOperation, он изначально вызван в этом скрипте
        if own['localDict']['weapon'] != own['weapon']:
            own['PR'] = 0
            own['sbros'] = 0
            #Запускаем функцию работы по отсоединению подвесок и использованию оружия
            ClassOperation.weaponsUnit()
            #Корректируем значения словаря юнита
            own['localDict']['weapon'] = own['weapon']
           
        #Стрельба - одиночный вызов функции из скрипта ClassOperation, он изначально вызван в этом скрипте
        if own['localDict']['sbros'] != own['sbros']:
            if own['sbros'] == 1:
                ClassOperation.shooting()
            own['localDict']['sbros'] = own['sbros']
       
        #Это - определение целей при смене текущй цели или выборе типа прицеливания
        if own['localDict']['enemy'] != own['enemy'] or own['localDict']['targetType'] != own['targetType']:
            if len(own['localDict']['localTargetList']) > 0:
                try:
                    own['idTarget'] = own['localDict']['localTargetList'][own['enemy']]
                    if 'CameraPilot' in own.childrenRecursive:
                        own.childrenRecursive['CameraPilot']['idTarget'] = own['idTarget']
                except:
                    own['enemy'] = 0
                    own['idTarget'] = own['localDict']['localTargetList'][0]
                    #При ошибке убираем индекс из списка
                    #if own['idTarget'] in own['localDict']['localTargetList']:
                       #own['localDict']['localTargetList'].remove(str(id(target)))
                   
           
            own['localDict']['enemy'] = own['enemy']
            own['localDict']['targetType'] = own['targetType']

        #Сортировка списка угроз
        if own['timerThreat'] > 3.0:
            threatSorted()
           
       
        #Работа сенсоров - вызов функции из скрипта ClassSensor, он вызывается через разные промежутки времени, минимум раз в секунду
        #и только при "живом юните", время паузы между сканированием регулируется - оно задается при переключении сенсоров
        if own['crash'] > 0.1 and own['bot'] > -1:
            #if own['limTimerScan'] > own['scanTimer']:
               
                sensorUnit = own['localDict']['sensList'][own['typeSensor']]
                ClassSensor.SensorDef()

Для облегчения понимания я подчеркнул место своей логической ошибки. Получается, что при получении нулевого уровня исправности происходит повторный вызов списка сцены и требование убрать уже несуществующую! Причем эта операция осуществляется за один тик - в один проход функции. Во всяком случае, я такой вывод сделал, скажем так. Из этого следует вывод, что так быдлокодить нельзя. Питон, конечно терпелив и высвечивает программеру-кодеру его ошибки в консоли, но и у него иногда лопается терпение. Может быть, кто-то из гораздо более сведущих в программировании людей посчитает этот вылет багом самого Питона, в котором не выполняется исключение перехвата ошибок. Однако подобный прискорбный случай говорит и том, что никто, кроме меня, подобную ситуацию еще не создавал...
В общем, проблема была решена путем удаления лишнего обращения к функции. я жестко указал в коде, что операция по поиску и удалению оверлейной сцены должна происходить только при получении проперти crash = 0. И неважно, чем это вызвано - стокновением с землей, сбитием, столкновением с другим юнитом. Меняется проперти crash? Оно равно нулю? Вызываем функцию CrashModel. Только ОДИН раз, и в ней же проверяем - есть в потомках камера пилота? Тогда убираем оверлейную сцену. ВСЕ!
После переделки кода последовал запуск-тест. специально гробанул многострадального "кролика" подопытного МиГ-23 об землю. Выхожу из игрового режима. Все в норме. Еще раз! все чисто. Но, товарищи, так кодить нельзя.
правда, попутно еще выловил и устранил баг с пушкой, которую я сломал в ходе своих "оптимизаций" довольно давно, почистил код. К тому же, действуя по принципу: "Коль пошла такая пьянка, режь последний огурец", в очередной раз устроил погром в скрипте кабины. Собирался я это сделать давно, просто удачно под руку подвернулось. Многие оперции в кабинном скрипте стандартны, как и имена объектов сцены. к примеру, лампы индикации подвесок, закрылков, тормоза, шасси. Нет смысла из файла в файл плодить одни и те же функции с одинаковыми названиями и одинаковым до буквы кодом. Поэтому в игровом файле-стартере я создал еще один скрипт ClassCockpit, куда и сбросил все стандартные функции из кабинногог кода. Теперь в коде кабины работают только стрелки приборов да те вещи, которые уникальны для того или иного самолета. Например индикация топливной системы.
Буквально на днях Андрей предложил переделать скрипт загрузки сторонних бленд-файлов через асинхронизацию и дал образец-пример. Надеюсь, сумею разобраться и применить относительно своего файла, потому что собирался это делать в любом случае, к тому же в способе dron-a есть возможность вставить красивую картинку, а не черный плейн, как у меня. Будем продолжать движение.