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