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

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

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

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

воскресенье, 4 марта 2018 г.

Доводка классов. Прячем все.

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

import mathutils
import random
import bge
scene = bge.logic.getCurrentScene()

def control(self):
    cont = bge.logic.getCurrentController()
    own = self
    if own.engineWeapon == 'CatapultWeapon':
        CatapultWeapon(own)
    else:
        Missile(own)
   
   
#Полет ракеты
def CatapultWeapon(own):
    if own.vzryvatel == 10:
        own.suspendDynamics
        own.engineWeapon = "Missile"

#Полет ракеты
def Missile(own):
   
    own.applyMovement([0.0,own.speed/60,0.0],True)
    own.timerAmountFire += 1
    if own.amountFire > own.timerAmountFire:
        #Срабатывание добавления частиц дыма от ракет
        own.timerSmoke += 1
        if own.timerSmoke == 1:
            #Дым снаряда
            smoke = scene.addObject('ParticleUniversal', own)
            smoke.replaceMesh("SmokeLong", True, False)
            smoke.setParent(own, False, False)
        if own.timerSmoke == 3:
            if 'ParticleUniversal' in own.childrenRecursive:
                own.childrenRecursive['ParticleUniversal'].scaleX = own.speed/360
                own.childrenRecursive['ParticleUniversal'].scaleY = own.speed/12
                own.childrenRecursive['ParticleUniversal'].scaleZ = own.speed/360
                own.childrenRecursive['ParticleUniversal'].Delta_scaleX = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_scaleY = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_scaleZ = 0.1
                own.childrenRecursive['ParticleUniversal'].Delta_colorAlpha = -0.0005
                own.childrenRecursive['ParticleUniversal'].colorAlpha = 0.2
                own.childrenRecursive['ParticleUniversal'].removeParent()
        if own.timerSmoke == 5:
            own.timerSmoke = 0
             


А теперь скрипт класса Р-27Р, который вызывает этот самый скрипт и скрипт головки самонаведения.

import bge

from ClassWeapon import typeWeapon

class R27R(typeWeapon):
       
    import Weapon_Missile as __Weapon_Missile
    import Weapon_GSN as __Weapon_GSN
   
    def __init__(self, old_owner):
        typeWeapon.__init__(self, old_owner)

    def engine(self):
        self.__Weapon_GSN.GSN(self)
        self.__Weapon_Missile.control(self)
       
def mutate(cont):
    R27R(cont.owner)
             

Ну, и собственно, функция работы оружия в игровом файле.

#Функция эффекта взрывов
def controlWeapon():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    own.engine()
   

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

import random
scene = bge.logic.getCurrentScene()

#Эта переменная определяет длину "хвоса" огня
fireLong = random.randrange(8, 20)

def FireAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #Длина списка потомков
    particleLen = len(own.childrenRecursive)
   
    #Это просто списко для ускорения добавления частиц - удвоение или утроение за один проход в тик
    listObj = ["num0"]
    #listObj = ["num0","num1"]
   
    #Характеристики частицы - локация, размер и цыкт с прозрачностью
    partColor = random.randrange(2, 10)/10
    partLoc = random.randrange(particleLen, particleLen+2)
    partScale = random.randrange(particleLen, (particleLen+2)*2)/(particleLen+2)*4
   
    #Эта переменная используется для отслеживания индекса потомка в списке
    indexObj = 0
   
    #Этап1 - добавляем, парентим и раставляем частицы, отслеживая длину списка потомков
    if particleLen < fireLong:
        for newParticle in listObj:
            newParticle = scene.addObject('Plane', own)
            newParticle.setParent(own, False, False)
            newParticle.replaceMesh("FireAir_", True, False)
            newParticle.localPosition = [partLoc/10, -partLoc, partLoc/10]
            newParticle.worldScale = [partScale, partScale, partScale]
           
    #Этап 2 - работаем с тем, что есть, больше ничего не добавляем
    else:
        for obj in own.childrenRecursive:
            #Здесь переменные локации и размера используются в качестве эталона,
            #к которому подтягиваются ТТХ частиц
            indexObj = own.childrenRecursive.index(obj)
            #partLoc = random.randrange(indexObj, indexObj*indexObj*indexObj+2)
            partLoc = random.randrange(indexObj, indexObj+2)
            partScale = random.randrange(indexObj, (indexObj+2)*2)/(indexObj+2)*3
           
            #Можно поиграться с цветом и прозрачностью
            obj.color = [1.0, partColor, partColor, partColor]
           
            #Хвост пламени - локальные координаты Х
            if obj.localPosition[1] < -partLoc:
                obj.localPosition[1] += partLoc/50
            elif obj.localPosition[1] > -partLoc:
                obj.localPosition[1] -= partLoc/50 
            #Размазанность и толщина хвоста пламени - координаты Икс и Зет
            obj.localPosition[0] = partLoc/4
            obj.localPosition[2] = partLoc/4
           
         
Вот так. Хотя, все это - костыли. Пусть и работающие. Подожду пока прояснения обстоятельств с перхватом рендера и управления БГЕ. Если обстоятельства позволят, то откроются новые перспективы.

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

Прячься, кто может! Инкапсуляция в классах. Кажется, выхожу на оперативный простор...

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

import bge
#import UnitAir_engine

from ClassUnitAir  import UnitAir

class MiG23BN_Libya_(UnitAir):
    import UnitAir_engine as __UnitAir_engine
 
    def __init__(self, old_owner):
        UnitAir.__init__(self, old_owner)
   
    __air = True
    __airtype = "JET"
    __dron = False
    __multiengine = False
    __airbrake = True
    __slats = True
    __flaps = True
    __swingwing = True
    __canopy = True
    __chassy = True
    __yaw = True
   
    #Летательный аппарат
    def is_AIR(self):
        return self.__air
    #Подтип ЛA - вертолет, самолет (реактивный или внитовой)
    def is_AIRTYPE(self):
        return self.__airtype
    #Пилотируемый или БПЛА
    def is_DRON(self):
        return self.__dron
    def is_FLAPS(self):
        return self.__flaps
    def is_SLATS(self):
        return self.__slats
    def is_SWINGWING(self):
        return self.__swingwing
    def is_AIRBRAKE(self):
        return self.__airbrake
    #Одно- или многодвигательный
    def is_MULTIENGINE(self):
        return self.__multiengine
    def is_CHASSY(self):
        return self.__chassy
    def is_CANOPY(self):
        return self.__canopy
    def is_YAW(self):
        return self.__yaw
   
   
    #######################################
    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_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_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_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_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 is_YAW_Device(self):
        YAW_Device = [
                      ["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",-1],
                      ["Rdd_",[-0.0018,0.0036,-0.0174],"rotat",1]
                     ]
        return YAW_Device
    def is_POINT_YAW(self):
        reper = [-25, 25]
        return reper
   
    ####################################
    #Наличие-отсутствие стабилизатора
    def is_ROLL(self):
        return True
    def is_ROLL_Device(self):
        ROLL_Device = [
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvR_",[0.0087,0.0,0.0],"rotat",-1],
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",1],
                      ["ElvR_",[0.0087,0.0,0.0],"rotat",1]
                     ]
        return ROLL_Device
    def is_POINT_ROLL(self):
        reper = [-20, 20]
        if self.WINGS > 300:
            reper = [-13, 13]
        #print(reper)
        return reper
    #Наличие-отсутствие стабилизатора
    def is_ROLLwings(self):
        return True
    def is_ROLLwings_Device(self):
        ROLLwings_Device = [
                            ["InpR_",[0.0,0.0,0.0],"rotat",0],
                            ["InpL_",[0.0,0.0,0.0],"rotat",0]
                           ]
                   
        if self.ROLLwings < 0 and self.rollSelf == -1:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",-1]]
        elif self.ROLLwings > 0 and self.rollSelf == 1:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings < 0 and self.rollSelf == 0:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings > 0 and self.rollSelf == 0:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]
        elif self.ROLLwings < 0 and self.rollSelf == 1:
            ROLLwings_Device = [["InpL_",[0.0174,0.0,0.0],"rotat",1]]
        elif self.ROLLwings > 0 and self.rollSelf == -1:
            ROLLwings_Device = [["InpR_",[-0.0174,0.0,0.0],"rotat",-1]]
       
        #print(self.ROLLwings,self.rotatY)
        return ROLLwings_Device
    def is_POINT_ROLLwings(self):
        reper = [-45, 45]
        if 299 < self.WINGS < 549:
            reper = [-30, 30]
        elif self.WINGS > 549:
            reper = [0, 0]
        #print(reper)
        return reper
   
    ####################################
    #Наличие-отсутствие стабилизатора
    def is_PITCH(self):
        return True
    def is_PITCH_Device(self):
        PITCH_Device = [
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvR_",[-0.0087,0.0,0.0],"rotat",-1],
                      ["ElvL_",[-0.0087,0.0,0.0],"rotat",1],
                      ["ElvR_",[-0.0087,0.0,0.0],"rotat",1]
                     ]
        return PITCH_Device
    def is_POINT_PITCH(self):
        reper = [-17, 49]
        return reper 
           
    #################################################
    #Методы движения, работы ЛОД и прочего
    def engine(self):
        self.__UnitAir_engine.engine(self)
 
def mutate(cont):
    MiG23BN_Libya_(cont.owner)

Самая вкусная строчка почти в самом низу:
self.__UnitAir_engine.engine(self)
Символ __ означает как раз эту самую инкапсуляцию. Вызов идет изнутри самого класса и других объектов не касается. Если посмотреть на класс с методами, то там тоже можно встретить переменные вида __air. Это специфические переменные, сообщающие, например, что у МиГ-23БН один двигатель, крыло с изменяемой стреловидностью, есть тормоза, закрылки и прочее. Это чисто его внутреннее дело.
Теперь же движение объекта из логического кирпича в пусковом файле выглядит так:
ge
import json
import sys
import random

cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
import sys
pathFolder = own.unitName + own.unitNation + "/folderPy"
sys.path.append(bge.logic.expandPath("//Aircraft/" + pathFolder))

unitmodule = own.unitNode + own.unitNation + "NodeScript"
unit_module = __import__(unitmodule)

UNITCLASS = own.unitNode + own.unitNation
UNIT_CLASS = __import__(UNITCLASS)


#Импорт из папки для юнитов ЛА модклей
import UnitAir_engine
import UnitAir_LODes
import UnitAir_shoot
import CONTROL_Operations
import mathutils

import UnitGround_Sensores
import UnitGround_Artillery
import UnitGround_LODes

import Weapon
ArbitrGame = scene.objects["ArbitrGame"]

#############################################
#############################################
def UnitAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #if len(own.idTarget) > 0:
     #   print(own.unitName, scene.objects.from_id(int(own.idTarget[0])).unitName)     
         
   
    if own.controlUnit != "Statist":
        own.globalTargetList = ArbitrGame.UNITS[0][own.targetType][own.target]
        #UnitAir_engine.engine(own, scene)
       
        own.engine()
       
        unit_module.correctData(own)
   
       
    #Работа уровня детализации идет ВСЕГДА
    UnitAir_LODes.LODes(own)
       
   
Думаю, извивы моей мысли понятны - импорттируем класс по названию - MiG-23BN_Libya_.py, а из него тащим метод движения, который, в свою очередь, дергает скрипт движения. Если посмотреть класс, то в начале можно найти строку import UnitAir_engine.  Это он и есть...
Теперь подобные вещи придется делать для оружия, от греха подальше. А то вдруг нацелятся все ракеты типа Р-23Р на один "Фантом" (который еще делать надо).
Простор для инкапсуляции еще есть, но не будем пока о грустном. После отлова ошибки и доводки сриптов, наконец, боты стали вести себя правильно. А именно - разворачиваться в сторону противника, сканировать местность, сбрасывать баки, разгоняться и маневрировать, и даже стрелять... Вновь ожила СПО, заработали звуковые маркеры. Короче, жизнь стала налаживаться.
             
             
     

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


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

Опытный кролик и наследство. Классы и новый (небольшой) погром.

Наконец, я добрался до файлов Упитиса и честнго прочитал их. И даже сумел кое-что понять. Но все это касалось скрипта lum.py, а еще надо было разбираться с другими скриптами - для неба и земли, воды и еще мелочью вроде шумов и тп.
Но тут застарелая проблема все-таки выползла наружу и заявила о себе в полный голос.
Да, я вроде как научился создавать экземпляры классов и более-менее вменяемо могу ответить, зачем - для придания игровым объектам свойств, с помощью которых они реагируют на события. В более широком смысле - создания больших групп объектов, со своими схожими атрибутами (вроде полета) и своими ТТХ (скорость, потолок).
Закавыка, однако, заключалась в том, что класс один на все самолеты и вертолеты, артиллерию, танки и так далее. Юниты отличаются лишь своими ТТХ внутри группы.
Видимо, ручки у меня все же не совсем прямые, потому что время от времени происходило странное "перекрещивание". Например ф-15 у себя искал детали МиГ-29, если верить распечатке консоли. Причем этот прикол начинался после внесения изменений и сохранения пускового файла. Стало ясно, что с этим надо что-то делатью Окончательно мое терпение лопнуло после того, как в миссии с МиГ-23 и Ф-16 "двадцатьтретий" отказался менять стреловидность, а его атрибут WINGS стал равен минус 1, как у Ф-16, хотя в json прописан ноль.
И решил я попробовать наследование классов. Спасибо подсказкам dron-a, вроде получилось.
Для начала я создал еще один json и вынес его в папку с самолетами - это был общий файл для всех летательных аппратов. Приводить я его не буду, нет смысла - нудное перечисление через запятую ключей и их нулевых значений. Но все это привело к резкому сокращению скрипта класса ЛА. За счет выкидывания из него этих ключей - идет чтение json.

import bge
import json

with open(bge.logic.expandPath('//Aircraft/AIRCRAFT_CLASS.json'), 'r') as directAircraft:
    JSONaircraft = json.load(directAircraft)

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

Этот скрипт висит в пусковом файле и выдает создание класса для ЛА. А запускает его функция из другого скрипта, в том же бленде.

def UnitAir(cont):
    own = cont.owner
    nameGeneral = own.childrenRecursive["confaUnit"]["confaUnit"]["unitClass"]
    #unit_module = importlib.import_module(nameGeneral)
    unit_module = __import__(nameGeneral)
    unit_class = getattr(unit_module, nameGeneral)
    x = unit_class(own)

Тут была возня с поиском и импортом нужного скрипта класса-наследника из другого бленда (который загружается при запуске игры - файл модели). К моему удивлению импортлиб скрипт не нашел. А вот поставленная из любопытства строчка с __impot__ом помогла. В скрипте ищется файл с именем класса, который прописан в объекте-потомке confaUnit. Этот объект своеобразный пакет в командирском сейфе - в случае начала БД его вскрывают и читают, что надо делать. Потом уничтожают.
В качестве подопытного кролика был выбран МиГ-23БН. Но это скорее уже опытный кролик - сколько на нем всего опробовалось... Скрипт класса-наследника выглядит так:

import bge

from ClassUnitAir  import UnitAir

class MiG23BN_Libya_(UnitAir):
   def __init__(self, old_owner):
      UnitAir.__init__(self, old_owner)
      #self.__dict__.update(JSONaircraft)
               
def mutate(cont):
    MiG23BN_Libya_(cont.owner)

Скрипт содержится в стороннем файле и надо обязательно вызвать сначала класс-родитель - ClassUnitAir. Точнее скрипт, а из него выдернуть сам класс - UnitAir.
Схема эта сработала и я кинулся вносить строчки с названием класса в json. После чего опробовал МиГ-23МФ и Ф-16. На сей раз никто не искал несуществующие детали и механика работала нормально.
А теперь вот мыслю, пока припрятать наследование класса для дальнейшего использования. А классы наследники сделать самостоятельными. для этого надо слегка поменять аргументы в скриптах и ввести чтение json. Поскольку на мой взгляд, наследование здесь не имеет пока особого смысла. Но оно может пригодиться в других местах и полученный опыт никогда не бвает бесполезным.
Да и попробую резко сократить файл вызова и старта самого класса, если их можно отыскивать и грузить по именам.

понедельник, 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)] 

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




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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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