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

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

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

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

понедельник, 2 апреля 2018 г.

Ведущий-ведомый. Эксперименты в коде.

Итак, задача. Провести перестроение пары ведущий-ведомый в БГЕ из строя "правый пеленг" в "левый пеленг". Задача решалась в БГЕ, объекты имели тип физики "но коллижн", то есть физика отсутствовала. Ведомый при получении команды немного "приотставал", разворачивался по отношению к ведущему под углом, после чего занимал свое место с противоположной стороны и, догнав "командира" уравнивал с ним скорость. Активно использовался метод getVectTo. В итоге, после опробования различных вариантов кода, получилось следующее:import bge
 
scene = bge.logic.getCurrentScene()
 
def Wingman():
    V_own = 0.0
    V_wingman = 0.0
    R_own_x = 0.0
    R_wingman_x = 0.0
    R_own_y = 0.0
    R_wingman_y = 0.0
    R_own_z = 0.0
    R_wingman_z = 0.0
 
 
    Wingman = scene.objects["MiG21Wingman"]
 
    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    
    
    
    if own["prop"] == 0:
        V_own = 0.3
        V_wingman = 0.3
        
    
        
    #Перестройка в пеленг
    elif own["prop"] == 1:
        V_own = 0.3
        V_wingman = 0.3
        if own.getVectTo(Wingman)[2][0] > 0:
            V_own += 0.05
            V_wingman -= 0.05
            if abs(Wingman.getVectTo(own)[2][0]) < 0.5 and own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z += 0.01
            elif abs(Wingman.getVectTo(own)[2][0]) > 0.5 and own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z -= 0.01
            
        else:
            V_own = 0.3
            V_wingman = 0.3
            if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z -= 0.01
            else:
                R_wingman_z += 0.01
            
            if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:
                V_own -= 0.05
                V_wingman += 0.05
            else:
                if Wingman.worldOrientation[2] != own.worldOrientation[2]:
                    V_own = 0.3
                    V_wingman = 0.3
                    Wingman.worldOrientation[2] = own.worldOrientation[2]
                else:
                    if own.getDistanceTo(Wingman) > V_own*60:
                        V_own = 0.3
                        V_wingman += 0.1 
                    else:
                        own["prop"] == 0
                        V_own = 0.3
                        V_wingman = 0.3 
            
    
    elif own["prop"] == 2:
        V_own = 0.3
        V_wingman = 0.3
        if own.getVectTo(Wingman)[2][0] < 0:
            V_own += 0.05
            V_wingman -= 0.05
            if abs(Wingman.getVectTo(own)[2][0] > 0.5) and own.getVectTo(Wingman)[2][0] > 0:
                R_wingman_z += 0.01
            elif abs(Wingman.getVectTo(own)[2][0] < 0.5) and own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z -= 0.01
            
        else:
            V_own = 0.3
            V_wingman = 0.3
            if Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0] < 0:
                R_wingman_z += 0.01
            else:
                R_wingman_z -= 0.01
            
            if abs(Wingman.getVectTo(own)[2][0] + own.getVectTo(Wingman)[2][0]) > 0.01:
                V_own -= 0.05
                V_wingman += 0.05
            else:
                if Wingman.worldOrientation[2] != own.worldOrientation[2]:
                    V_own = 0.3
                    V_wingman = 0.3
                    Wingman.worldOrientation[2] = own.worldOrientation[2]
                else:
                    if own.getDistanceTo(Wingman) > V_own*60:
                        V_own = 0.3
                        V_wingman += 0.1 
                    else:
                        own["prop"] == 0
                        V_own = 0.3
                        V_wingman = 0.3 
            
                       
    own.applyMovement([0.0, V_own, 0.0], True)
    Wingman.applyMovement([0.0, V_wingman, 0.0], True)
    own.applyRotation([R_own_x, R_own_y, R_own_z], True)
    Wingman.applyRotation([R_wingman_x, R_wingman_y, R_wingman_z], True)
 
Параллельно с написанием пробного скрипта перестроения пары самолетов пришлось составлять таблийу для значений векторов метода getVectTo[2] . Если попытаться представить это в трехмерном пространстве, то получается куб, в центра которого находится ведущий, а в углах, центрах граней и ребер - ведомый. Условно говоря, конечно. То есть ведомый может идти параллельно ведущему рядом с ним. Или с отставанием справа и сверху или с опережением слева по центру и так далее. В итоге табличку я сюда солью, может потом пригодиться (хотя бы и самому себе).
 
НА ОДНОМ УРОВНЕ
Спереди по центру [0.0, 1.0, 0.0]
Сзади по центру [0.0, -1.0, 0.0]
Слева спереди [-0.7071, 0.7071, 0.0]
Справа спереди [0.7071, 0.7071, 0.0]
Слева сзади [-0.7071, -0.7071, 0.0]
Справа сзади [0.7071, -0.7071, 0.0]
Справа по центру [1.0, 0.0, 0.0]
Слева по центру [-1.0, 0.0, 0.0]
 
ВЫШЕ
Выше по центру [0.0, 0.0, 1.0]
Спереди по центру [0.0, 0.7071, 0.7071]
Сзади по центру [0.0, -0.7071, 0.7071]
Слева спереди [-0.5774, 0.5774, 0.5774]
Справа спереди [0.5774, 0.5774, 0.5774]
Слева сзади [-0.5774, -0.5774, 0.5774]
Справа сзади [0.5774, -0.5774, 0.5774]
Справа по центру [0.7071, 0.0, 0.7071]
Слева по центру [-0.7071, 0.0, 0.7071]
 
НИЖЕ
Ниже по центру [0.0, 0.0, -1.0]
Спереди по центру [0.0, 0.7071, -0.7071]
Сзади по центру [0.0, -0.7071, -0.7071]
Слева спереди [-0.5774, 0.5774, -0.5774]
Справа спереди [0.5774, 0.5774, -0.5774]
Слева сзади [-0.5774, -0.5774, -0.5774]
Справа сзади [0.5774, -0.5774, -0.5774]
Справа по центру [0.7071, 0.0, -0.7071]
Слева по центру [-0.7071, 0.0, -0.7071]
 
Потом, когда все это было закончено, я попробовал сделать немного по-другому. А именно - сделать так, чтобы ведущий постоянно вычислял координаты ведомого относительно себя, любимого, чтобы подчиненный просто занимал эту область, не заморачиваясь с векторами. Тем более, что гораздо раньше у меня уже был отработан метод наведения ботов на цель с помощью крена, тангажа или рыска по отдельности или сразу всего одновременно. Никаких проблем с наведением ведомого на эту самую искомую точку быть не должно. Однако вот тут я застрял. Как поется в одной старой песне из кинофильма: "Уж я к ней и так и этак, со словами и без слов. Обломал немало веток, наломал немало дров". В итоге все же удалось решить проблему со строем "фронт" - когда ведомый и ведущий выстроены в одну линию параллельно друг другу и строем "колонна", когда ведомый держится строго позади ведущего. А вот со строем "пеленг" пока облом. Там нужно реализовать отставание ведомого от ведущего, либо его опережение.  Ну, хоть что-то получилось, а с пеленгом я все равно как-нибудь разберусь. Код написан уже для объектов с физикой типа Dynamics.
 
port bge
import math
scene = bge.logic.getCurrentScene()
 
def Wingman():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    #Задание объекта-цели
    target = scene.objects["wingman"]
    target.worldOrientation = own.worldOrientation
    #target.worldPosition = front(own, target)
    target.worldPosition = peleng(own, target)
    #target.worldPosition =colonna(own, target)
 
def colonna(own, target):
    targetX = own.worldPosition[0] - 3*own.worldLinearVelocity[0]
    targetY = own.worldPosition[1] - 3*own.worldLinearVelocity[1]
    targetZ = own.worldPosition[2] - 3*own.worldLinearVelocity[2]
    listCoord = [targetX, targetY, targetZ]
    return listCoord
    
def front(own, target):
    LeftRightX = 0
    LeftRightY = 0
    
    kY = own.worldLinearVelocity[0]/own.localLinearVelocity[1]
    kX = own.worldLinearVelocity[1]/own.localLinearVelocity[1]
    kZ = own.worldLinearVelocity[2]/own.localLinearVelocity[1]
    
    PRAVOLEVO = 1
    
    if kX > 0 and kY > 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX < 0 and kY > 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX > 0 and kY < 0:
        LeftRightX = 1
        LeftRightY = -1
    elif kX < 0 and kY < 0:
        LeftRightX = 1
        LeftRightY = -1
    
    ownX = own.worldPosition[0]
    ownY = own.worldPosition[1]
    ownZ = own.worldPosition[2]
    
    DIST = 20.0
    
    targetX = ownX + DIST*kX*LeftRightX*PRAVOLEVO
    targetY = ownY + DIST*kY*LeftRightY*PRAVOLEVO
    targetZ = ownZ + DIST*kZ
    
    listCoord = [targetX, targetY, targetZ]
    return listCoord
 
В функциях colonna и front  идет как раз вычисление и выдача искомых координат ведомого. Комментариев в коде почти нет, потому как работа над этим делом еще не закончена. Получится ли из этого что-нибудь, пока неясно. Во всяком случае, в основном коде проекта включить их можно хоть сейчас, но хотелось бы добить строй "пеленг"...

пятница, 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Р на один "Фантом" (который еще делать надо).
Простор для инкапсуляции еще есть, но не будем пока о грустном. После отлова ошибки и доводки сриптов, наконец, боты стали вести себя правильно. А именно - разворачиваться в сторону противника, сканировать местность, сбрасывать баки, разгоняться и маневрировать, и даже стрелять... Вновь ожила СПО, заработали звуковые маркеры. Короче, жизнь стала налаживаться.
             
             
     

четверг, 28 декабря 2017 г.

Дао скриптов-2 Анимация и лазейки для ИИ..

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


Этот код находится внутри класса-наследника самолета. Из него можно узнать имена деталей. скорость их отклонения за один тик, а также ограничения по отклонению.  Первая функция говорит о том, есть ли такие детали в принципе. Втораяя перечисляет детали и их параметры, а вот третья... Как видно из кода, она способна выдать ограничения на угол отклонения. Это, на мой взгляд, проще, чем менять списки для деталей во второй функции. Да и скорость отклонения не меняется.
А вот далее пошла война с интерцепторами. Засада заключалась в том, что рабоать должен ТОЛЬКО один интерцептор, но НЕ ОБА ОДНОВРЕМЕННО. Для элеронов подобной проблемы нет. Они работают аналогично стабилизаторам - смотрим выше.
В общем, перпробовав уйму вариантов, я решение все-таки нашел. Пока один интерцептор не займет нулевое положение, второй работать не будет. Учитываются направление крена или его отсутствие...
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

Из последней функции можно заметить отключение интерцепторов на максимальном угле стреловидности. Так и должно быть. Хотя можно и просто влепить выдачу False в первой функции. Может, так и сделаю...
Ладно, с анимацией все, переходим к шасси и лазейкам для ИИ.
import bge
#print("animat")
cont = bge.logic.getCurrentController()
own = cont.owner
#Импортируем модуль юнита
#unit_module = __import__(own.unitModule)
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)


def animat(own):
    audioProp = "NULL"
   
    ########################
    #Анимации механики юнита
   
    #Перекладка крыла
    if own.Temp_WINGS != own.WINGS:
        wings(own)
   
    #Шасси
    if own.Temp_CHASSY != own.CHASSY:
        #Звук
        audioProp = "GEAR"
        if own.Temp_CHASSY > own.CHASSY and own.CHASSY == 98:
            audioGen(own, audioProp)
        elif own.Temp_CHASSY < own.CHASSY and own.CHASSY == 2:
            audioGen(own, audioProp)
        unit_module.CHASSY(
       
Это часть кода  из скрипта анимаций самолета. Он работает для всех машин. Изюминка здесь в том, что отыскивается и импортируется модуль с анимацией шасс по названию, указанному в атрибутах юнита. Смотрим строчки с sys.path  и ниже. В импортированном модуле есть не только шасси, но есть и функция корректировки поведения самолета, КОНКРЕТНОГО самолета, в зависимости от разных условий. Смотрим код из этого "нодового" скрипта.
   
        import bge
scene = bge.logic.getCurrentScene()

def correctData(own):
   
    Airbrake = 0.35*own.AIRBRAKE/100
    Chassy = 0.25*own.CHASSY/100
    own.correctSpeed = 1.0 - Airbrake - Chassy
   
    if own.Temp_WINGS != own.WINGS:
        #Изменения в поведении самолета
        changeWings(own)
        #Сброс подкрыльевых ьаков, если они есть
        if own.WINGS == 2:
            sbrosPTB(own)

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

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

Дао скриптов. Перманентный погром, как оптимизация.

"Лев Революции", товарищ Троцкий (ставший потом Иудушкой после изменения политичекой еонъюктуры) как-то провозгласил: "Есть у революции начало и нет у революции конца". Это изречение вполне применимо к моему проекту, хотя, если честно, мне гораздо больше нпавится другое его изречение: "И пусть наши враги знают - на всякую принципиальность с их стороны мы ответим абсолютной беспринципностью!" Но что есть - то есть.
В прошлом посте я писал, по поводу "дробления" огромных кусков кода на удобочитаемые скрипты относительно небольшого объема. Что ж, схема обретает свои очертания. Итак, сначала мы используем священное заклинание import sys. А потом перечисляем требующиеся нам пути к папкам с новыми скриптами.
import bge

import sys
for pathFolder in ["Scene_Scripts","UnitAir_Scripts", "UnitGround_Scripts", "Weapon_Scripts"]:
    sys.path.append(bge.logic.expandPath("//Scripts/" + pathFolder))

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

1. Первый уровень, видимый в пусковом бленде и там же находящийся. Я его обозвал скрипт-нод. Например CONTROL_UnitAir. Он отвечает за поведение летательного аппарата, безразлично от того, летит ли он или стоит на аэродроме, изображая мишень. Теперь от огромного скрипта остались только пара десятков строчек. Непрервыно в этом скрипте работает лишь система детализации. При условии, что ЛА летит, из папки со скриптами вызывается скрипт UnitAir_engine.

def UnitAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if own.controlUnit != "Statist":
        UnitAir_engine.engine(own)
       
    #Работа уровня детализации идет ВСЕГДА
    UnitAir_LODes.LODes(own)

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

3. Уровень три. так сказать, уровень нюансов. Здесь скриптов гораздо больше и лни помельче. Это, к примеру, описание работы сенсоров самолета (РЛС, теплопеленгатор и тд), описание анимации подвижных деталей (рули высоты, элероны и тд), описание поведения самолета при штопоре, модель разрушения и так далее. В свою очередь, некоторые из этих скриптов ведут все дальге и вниз. На четвертый уровень, если надо.

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

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

вторник, 12 декабря 2017 г.

Прерву несколько затянувшееся молчание...

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

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

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

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

import bge

from ClassUnitAir  import UnitAir

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

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

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

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

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

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

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

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

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

Размышления о наземных объектах-2. Как бы все это разрушить...

Как обычно, после перерыва в использовании блога по причине израсходованого трафика, узнаешь много нового... Так, сегодня freenome отказался пускать меня в мой же блог. Это было для меня в первый раз, но не первый раз в Сети. быстро пробежавшись по сети, нашел упоминания о подобном саботаже, причем не только freenome, но и других хостингов. Ну раз так, то вникать я в причины блокировки не стал. Это достаточно хроническое явление для бесплатных доменов и у меня нет времени и сил, чтобы вникать в тонкости взаимоотношений сайтоы, блогов и хостингов. спросил у dron-а, что с этим делать и утвердился в мысле, что надо просто убрать использование домена из настроек. Надеюсь, получилось и эти строчки могут прочитать все остальные.
Несколько дней я пытался нащупать хоть какую-то схему воздействия на игровые объекты. Задача состояла в том, что разные типы боевых частей по-разному воздействуют на объекты. В конце концов, пришел к выводу, что объект должен обладать неким атрибутом, назовем его "уровнем боевой устойчивости". Или crashDefens, как-то так. Сей уровень характеризуется неким порогом, ниже которого поразить объект оружием с низким ТТХ невозможно. Например, как-то натолкнулся на вопрос:"Может ли ЗУ-23-2 остановить танк?". Ответ воевавшего в Чечне танкиста был таков: "Может. Если экипаж танка ее заметит и остановится. Для прицеливания..."
Вывод:
В оружейные ТТХ надо вводить уровень  "преодоления защиты", эффективности, что ли, так скажем.
И тут начинается самое нитересное.  Нужен какойто эталон, от которого можно отталкиваться.  И в качестве эталона была выбрана бомба ФАБ-100. Известно, что ее радиус поражения (сплошной) составляет 12 метров. Вообще в самих бомбах где-то половина веса - это металл, остальное - взрывчатка. Ну что ж, берем 50 кг за 0.1 . Извращение, конечно, ну ладно, в конце концв изготовлять эталон веса или длины мне не надо. Примем за основу. С бомбами далее оказалось проще - 28 метров для ФАБ-250 и 40 - для полутонных бомб. исходя из этого я и правил показатель...
предполагаю, что внутри этого радиуса сплошного поражения наземные юниты уничтожаются, если, это, конечно, не бункер с уровне защиты 0.5. Тому же "Абрамсу" будет фиолетово, что упало ему на башню - ФАБ-100, 250 или ОФАБ-250. А вот потом...
На расстоянии двойного радиуса поражения эффективность оружия падаеь  вдвое,  еще дальше - вчетверо. Но, если рядом с "Абрамсом" в момент взрыва стоял еще один танк и БМП в зоне падения ущерба, то порог воздействия на танк преодолен не будет, а вот БМП может и выйти из строя после попадания осколков и удара взрывной волны.
Понимаю, сии измышления неплохо бы подкрепить кодом, но был занят - срочно вписывал в json бомб и УРВВ  данные об их могуществе и типе БЧ.
Прикол заключается в том, что БЧ ракет "воздух-воздух" на три четверти состоят из поражающих элементов и только четверть забирает взрывчатка. В общем-то опять натягивание совы на глобус получается, но вменяемой и систематизированной информации по этому вопросу нет. нет, найти массу БЧ ракет не проблема, но далеко не всегда указывается вес ВВ. Ну ладно, решил так сделать.
Объем работ оказался приличный. А впереди еще ракеты "воздух-земля"...
Попутно выяснил еще ое-каие вещи. Например, что ракета Р-27ЭМ заточена под перехват КР "томагавк" или ПКР "Гарпун". А вот ракета Р-27ЭП специализируется на выбивании постановщиков помех. Это дело надо учесть. Еще ракетами типа Р-77, Р-73, Р-77 и AIM-9X можно организовать персональное ПРО, отстреливая пущенные по тебе ракеты.
Также попалась серия интересных статей на Афтершоке по авианосцам и крылатым ракетам. Выяснилось, что многие, так называемые эксперты не знают некоторых элементарных вещей. Например, среди поклонников западной военной мощи бытует мнение, что ПКР российских кораблей посшибают ракетами и пушками "Хорнеты", причем за считанные минуты, а потом наши корабли будут расстреляны, как в тире. Ну-ну... Другие, наоборот, впадают в крайность и яростно отмахиваются от необходимости иметь свои авианосцы для обеспечения ПВО кораблей. Но тут есть свои нюансы...
Недавно на Дальнем востоке прошли учения по отражению удара КР с моря силами ВВС и ПВО. Причем с боевыми стрельбами. и хотя цели были поражены, выяснилась одна настораживающая деталь. МиГ-31 дал залп двумя ракетами по ПКР "Гранит", шедшей по маршруту и попал. Цель была изрешечена, но невозмутимо продолжала себе лететь. Последовал второй залп. Только после этого ПКР удалось свалить. все дело в том, что у "Гранитов2 есть бронирование, защишающее важные узлы и сбить ее не так то просто. На одну ракету ушло 4 Р-33 - весь БК одного МиГ-31. "Хорнеты" могут стрелять AIM-120, БЧ которых сдабее больше чем в два раза. Итого, ладно, с натяжкой будем считать - 6 АМРААМ на один "Гранит". Но в залпе 20 ПКР. И кроме них, кое-что еще помельче. По поводу пушек - даже не смешно - отстрел "Гранитов2 на полигоне из 20-мм пушки не дал НИЧЕГО.  Поразить эту ПКР может лишь 30-мм пушка, да и то с рядом условий.
Придется ставить уровень боевой устойчивости еще и на оружие... Добавлю, что для надежного уничтожения ПКР нужен ЗРК, но обнаружить подлетающую над волнами ПКР он может не так далеко, есть еще время реакции, что приводит к тому, что ЗУР стартует, когда расстояние до корабля для ПКР уже меньше километра (а иногда сильно меньше). И есть еще мертвые зоны, и есть бортовая РЭБ самой ракеты и есть увеличенная точность попадания в уязвимые места.
Короче, морской бой, если я до него доберусь, обещает быть очень интересной задачкой.
Ладно, вернемся к нашим баранам, то бишь наземным объектам. Действие фугасных и осколочно-фугасных бомб и снарядов я описал выше. Все зависти от расстояния до окружающих объектов.
Но есть подкалиберные снаряды и кумулятивные снаряды. Вот для них радиус поражения нулевой. Они втыкаются в броню конкретного танка, БМП или БТР, "не отвлекаясь" на окружающее. Если при взрыве фугаса надо циклом перебирать все объекты-юниты по расстоянию, то в этом случае просто идет проверка условий по единственной цели - в которую снаряд попал. Тут уже вступает в строй условие наличия-отсутствия  динамической защиты и угла встречи с броней, если таковая есть и имеются данные по ее толщине.
На b3d.ua есть WIP от  exooman-a, который занимается танками, в отличие от меня. Он тоже всячески рассчитывает подобные мелочи, у него тоже есть ДЗ, толщина и угол брони и много чего...
Надо добивать ракеты класса "воздух-земля", дописывая для них json, в основном с оружием будет закончено, но остаются еще зажигательные баки, кассеты, контейнеры с суббоеприпасами и тому подобная мелось. И такие извращения, как "радость танкиста" - кассеты с самоприцеливающимися боевыми элементами, опускающимися на парашютах и прошибающими крыши танков и прочей техники кумулятивной струей.
А еще шейдеры и карта высот, которую я еще не успел поновой закачать у denis8424 (движение по карте высот без физики). Разные интересные мысли крутятся, однако... Вроде полногог отказа от физики, кроме снарядов и бомб. В ракетах же отказался и ничего, все нормально...

среда, 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. Что сигнализирует о том, что ракета сперва должна слегка "просесть" и только потом лететь.
В общем, на данный момент, дела обстоят на такой стадии...

воскресенье, 16 июля 2017 г.

Меню, руины и прочая лабуда. Дао дятла.

После недолгого скорбного замешательства, вызванного серией неудачных попыток создать хоть что-нибудь, изображающее меню с возможностью выбора миссии, эту задачу все же удалось решить. Частично. Все упиралось в генерацию кнопок, а также придании им нужных свойств. Очень не хотелось вешать на кнопки логические кирпичи, ограничив все бриками на камере сцены меню. чтобы потом не искать судорожног нужную логику на нужном объекте, да еще и на нужном слое. как водится, с первого и даже с третьего раза не вышло. По моей просьбе dron сделал пример с применением положения курсора мыши. именно в нем и был затык - как-то так вышло, что с курсором мыши я никогда не работал раньше и кнопки меню первой версии обладали своей логикой.
Однако выяснилось, что вменяемое управление кнопками с этим крсором создать сложно - необходимо пересчитывать экранные координаты и мировые координаты кнопок просто так не персчитаешь. В довершение ко всему, игра может быть и не во весь экран, и тд и тп.
Опять извечный русский вопрос - что делать? Ну, раз стандартный курсор не  помогает, можно и свой запилить. Ниже привожу код, в котором есть функция cursorPos -  это и есть самодельный курсор. суть в том, что некий объект на экране постоянно сравнивает положение курсора мыши с положением его же в предыдущем тике (опять словари). Тут идет просто копирование направление движения курсора мыши в нужном направлении. Скорость передвижения самодельного курсора пришлось понизить, а то он летал с бешеной скоростью. Плюс ввести ограничения, чтобы курсор из плейна не вылетал за пределы меню.
import json
#import bgl
#import blf

scene = bge.logic.getCurrentScene()
PlaneCursor = scene.objects["PlaneCursor"]



#Далее идет блок клавиатурных команд
keyboard = bge.logic.keyboard
JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
INPUT_ACTIVE = bge.logic.KX_INPUT_ACTIVE

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

if "cursorPos" not in own:
    own["cursorPos"] = {"tempX":0.0,"tempY":0.0,
                        "listButton":[],"animationButton":0,
                        "indexButton":""}



def textureFonLoad(own, nameTexture):
    #bge.logic.expandPath("//Menu/textureMenu/" + nameTexture)
    obj = scene.objects["FonMenu"]
   
    ID = bge.texture.materialID(obj, 'IMStartFon.png')
    object_texture = bge.texture.Texture(obj, ID)
    url = bge.logic.expandPath("//Menu/textureMenu/" + nameTexture)
    new_source = bge.texture.ImageFFmpeg(url)
    bge.logic.texture = object_texture
    bge.logic.texture.source = new_source
    bge.logic.texture.refresh(False)
    obj["FonMenuTexture"] = new_source
   
    
   
def cursorPos():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if own["cursorPos"]["animationButton"] > 0:
        buttonMotion(own)      
   
    mouseNot = cont.sensors["MouseNot"]
   
    if mouseNot.positive:
        if own["cursorPos"]["tempX"] != mouseNot.position[0]:
            PlaneCursor.worldPosition[0] += (mouseNot.position[0] - own["cursorPos"]["tempX"])/4
            own["cursorPos"]["tempX"] = mouseNot.position[0]
        if own["cursorPos"]["tempY"] != mouseNot.position[1]:
            PlaneCursor.worldPosition[1] -= (mouseNot.position[1] - own["cursorPos"]["tempY"])/4
            own["cursorPos"]["tempY"] = mouseNot.position[1]

        if PlaneCursor.worldPosition[0] > 45.0:
            PlaneCursor.worldPosition[0] = 45.0
        if PlaneCursor.worldPosition[0] < -45.0:
            PlaneCursor.worldPosition[0] = -45.0
           
        if PlaneCursor.worldPosition[1] > 25.0:
            PlaneCursor.worldPosition[1] = 25.0
        if PlaneCursor.worldPosition[1] < -25.0:
            PlaneCursor.worldPosition[1] = -25.0
       
       

def on_click(cont):
   
    if cont.sensors["Mouse"].positive:
        #print(own["cursorPos"]["listButton"])
        for button in own["cursorPos"]["listButton"]:
            if button.worldPosition[0]-0.9 < PlaneCursor.worldPosition[0] < button.worldPosition[0]+0.9:
                if button.worldPosition[1]-0.9 < PlaneCursor.worldPosition[1] < button.worldPosition[1]+0.9:
                    own["cursorPos"]["indexButton"] = str(id(button))
                    own["cursorPos"]["animationButton"] = 1
                    buttonMove(own, button)
       
       
def buttonMotion(own):
   
    own["cursorPos"]["animationButton"] += 1
    try:
        button = scene.objects.from_id(int(own["cursorPos"]["indexButton"]))
        if 1 < own["cursorPos"]["animationButton"] < 3:
            button.applyMovement([0.3,-0.3,0.0],True)
        if 3 < own["cursorPos"]["animationButton"] < 5:
            button.applyMovement([-0.3,0.3,0.0],True)
        if own["cursorPos"]["animationButton"] > 4:
            own["cursorPos"]["indexButton"] = ""
            own["cursorPos"]["animationButton"] = 0
            if "GAME_body" in button:
                if button["GAME_body"] == "Stop_Game":
                    bge.logic.endGame()
                elif button["GAME_body"] == "Start_Game":
                    own["startGame"] = 1
    except:
        own["cursorPos"]["indexButton"] = ""

def buttonMove(own, button):
    if "GLOBAL_DICT_name" in button:
        if "GLOBAL_DICT_body" in button:
            if button["GLOBAL_DICT_name"] not in bge.logic.globalDict:
                bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]
            else:
                bge.logic.globalDict[button["GLOBAL_DICT_name"]] = button["GLOBAL_DICT_body"]
               
   

def control():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    listButton = []
    with open(bge.logic.expandPath('//Menu/' + 'Test_Missions' + '.json'), 'r') as directMenu:
        JSONmenu = json.load(directMenu)
   
    nameTexture = "FonSingleMission.png"
    textureFonLoad(own, nameTexture)
   
    for obj in JSONmenu["Buttons"]:   
        newSceneObject = scene.addObject(obj.split('|')[0],own)
        newSceneObject.worldPosition = JSONmenu["Buttons"][obj]["coordObj"]
        newSceneObject.color = JSONmenu["Buttons"][obj]["colorObj"]
        newSceneObject.worldScale = JSONmenu["Buttons"][obj]["scaleObj"]
        own["cursorPos"]["listButton"].append(newSceneObject)
        #Задаем кнопке проперти
        for key in JSONmenu["Buttons"][obj]["propObj"]:
            if key not in newSceneObject:
                newSceneObject[key] = JSONmenu["Buttons"][obj]["propObj"][key]
        if "TextButtons" in newSceneObject.childrenRecursive:
            with open(bge.logic.expandPath('//Menu/TranslationMenu.txt'), 'r') as TranslationMenu:
                for stringer in TranslationMenu:
                    if stringer[0] == "a":
                        if stringer.split('|')[1] == JSONmenu["Buttons"][obj]["textObj"]:
                            newSceneObject.childrenRecursive["TextButtons"]["Text"] = stringer.split('|')[1]
   

В сущности, сначала идет чтение json для расстановки кнопок и придания им нужных свойств - стандартная операция, затем опять стандартная операция - расстановка кнопок и присваивание им свойств - одноразовая вещь (пока) - ничего сложногог. Затем начинает работать курсор. Просто сравнивается положение курсора в момент щелчка кнопкой мыши с положением  кнопок и кнопки дергаются при попадании в некоторую область (buttonMotion), функция buttonMove как раз что-то пишет в глобальный словарь, например, название миссии.
В конце концов, меню заработало, обеспечив выбор аж двух миссий со стартом игры или выходом из нее.
После меню последовало создание наземных статичных объектов. Таковыми были выбраны руины. Из гугловских моделей в качестве времнной (надеюсь) затычки были закачаны с десяток моделей и терепеливо обработаны до удобоваримого состояния в Блендер. Плюс была создана модель аэродрома из одного объекта.
Теперь консоль матерится на лишние UV-развертки. Пока приходится терпеть. Руины и авиабазы были вписаны в json миссий и проврены. пока без маркировки и присущих им совйств.
Есть предположение, что для относительно простых объектов можно реализовать частичное разрушение меша. Условно, конечно, удалять вершины нельзя, но их можно двигать. Суть в том, что при попадании снаряда в нужной области меша все вершины в этой области принимают нулевое значение координаты Зет (по высоте), как бы сплющиваются. а вторым циклом все вершины в данном "квадрате" , находящиеся, скажем, ниже 50, получают прибавку к своим координатам по оси Зет - на месте "целого" ангара (чсти меша) появляются развалины. Правда, для этого, объект должен быть относительно простым. Ну, для простейших кубиков - ангаров, сраев и домиков сойдет...
Далее последовало создание катапультируемого пуска для ракет. Это надо было сделать давно, но руки дошли только сейчас. Суть в том, что сначала ракета отбрасывается от самолета вниз, и только потом включается двигатель. С первой попытки не удалось, но руководствуясь дао дятла, я повторил попытку, специально для такого случая учредив новый тип физического движения в игре - отсроченный старт... В конце концов заработалог...
Вообще все вышеперчисленное не слишком повлияло на уже существующую картинку, поэтому скринов не будет. Зато впереди полно работы по доводке json F-15 под этот самый катапультируемый пуск. А куда деваться.
-Вот ты говоришь: "Мафия! Мафия!". Конечно хотелось бы, чтобы у нас была мафия, но для этого надо много работать, - говорил герой Николая Караченцова в фильме "Дежа Вю". Последую же этому призыву, не мафии ради, но для красивой картинки на экране. Которая непременно появится. Когда-нибудь...