пятница, 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 для каждой миссии вручную - лучше один раз напрячься с написанием редактора миссий...

2 комментария:

  1. А есть ведь еще такая удобная штука как наследование...

    ОтветитьУдалить
  2. Да, возможностей у класса много. Кое-что почитывал по этому, но пока не "вкурил". Плюс спешно переделываю код - вчера чистил ИИ от остатков скобочек с кавычками. Когда-то с подсказки dron-а еще применял @classmethod, но пока временно убрал. Плюс в пробах был единоразовый вызов метода в нутри самого класса - тоже убрал, пока не нужен. Полиморфизм, наследование, классовый и статический методы - много чего еще надо осваивать...

    ОтветитьУдалить