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

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

вторник, 9 января 2018 г.

Размышления на тему игрового ИИ.

Всех  с прошедшими праздниками, которые вновь сумели пережить... правда, новгодние каникулы закончились не для всех, и явно еще не израсъодована пиротехника, заботливо приберегаемая для встречи Сатрого Нового Года...
Пока что успешно завершена война с анимацией подвижных частей авиатехники - ее последним аккордом стала борьба с элеронами, которая отрабатывалась уже на МиГ-29. Как обычно, казавшаяся простой и легкорешаемая задача сожрала больше времени, чем та, которая признавалась сложной и запутанной. Это я про интерцепторы. Затем последовала стандартизация уже собранных ранее классов самолетов, которая пока не коснулась Ф-16 и Су-25. У Ф-16, кстати, как и у Су-27 имеются "интерцепоторы наоборот" - флапероны, так что там придется менять знаки перед углами отклонения - интерцепторы поднимаются, а флапероны опускаются...
Подошла очередь искусственного интеллекта. Скрипт этого имитатора интеллекта, будем уж честными до конца, у меня насчитывал уже больше пары тысяч строк и отыскивать в нем нужное  место для внесения изменений становилось все сложнее... Поскольку метод дробления больших скриптов на узкоспециализированные модули уже отработан, то супер-скрипт ИИ (в смысле размеров) постигла та же участь. Он раздробился примерно на дюжину модулей, относительно небольших. Пока раздробление прошло начерно - я еще не приступал к отработке связей между отдельными модулями. Но примерная схема уже вырисовывается. Модули можно разделить на несколько категорий.
1. Стандартные модули разворотов юнита и его ориентаций.
-Модуль стандартного движения при крене-тангаже-рыске -приложение сил в определенном направлении.
-Модуль ориентации в пространстве для горизонтального полета. Выравнивание по крену и тангажу, чтоб самолет летел прямо с выдерживанием нужной высоты. Также в этом модуле есть набор высоты и снижение. Некоторые машины имеют ограничения по крену и тангажу (это характерно для тяжелых машин, таких, как транспортники, хотя были уникумы, крутившие "бочку" на Ту-16, но для тяжелых бомберов такая вещь ни к чему, на мой взглядд), так что в этом модуле они так же соблюдаются.
-Модуль с ориентацией на цель - противника или точку маршрута. Ту, думаю, понятно.
-Модуль выдерживания строя. Пока в природе не существует - только в моей голове и весьма смутно.
2. Модули сканирования и выбора сенсоров и вооружения.
-Пока один модуль - в нем есть функции выбора отимального вооружения (подальнобойнее) и подбора к нему сенсора. Также в этом модуле идет сканирование окружающего мира на предмет бодания лбом земли и наличия угроз - например летящей к боту ракеты.
3. Стандартные модели поведения ботов на земле и в полете. Их много, этих модулей...
-Модуль руления на взлет.
-Модуль руления после посадки.
-Модуль взлета.
-Модуль посадки.
-Модуль полета по маршруту.
-Модуль уклонения от атаки. Включает в себя выбор варианта уклоонения от атаки при помощи сочетания маневров типа "горка" с "размезанно бочкой" и тд. А также постановку активных и пассивных помех.
-Модуль атаки цели. в нем имеются подварианты - атака наземной или воздушной цели. и этот модуль будет потихоньку разрастаться по мере наработки опыта.
-Модуль уклонения от столкновения с землей.
-Модуль набора энергии после маневра. Как бы ни был самолет маневрен, но он может потерять скорость на том же вираже и сорваться в штопор, чего допускать нельзя. Поэтому самолет надлежит аккуратно выровнять, врубить форсаж и снова разогнать.

вот пока примерно так.Есть еще любопытные мысли на этот счет. А именно - по поводу сканирования встречи сземлей. Пока у меня имеется один террайн из 2500 блоков. На мой взгляд, это все же жирно, поэтому лучше сделать из пары сотен блоков. Или 400 - макимум. Причем для террайна ввести свой json, в котором можно перечислить положение блока в прсотранстве, его высоту, отметить координаты горных районов и тд. Зачем? А вот зачем...
Как известно, модули стандартных функций БГЕ типа луча много кушают, поэтому лучше лишний раз их не трогать. А зачем врубать этот самый луч, если самолет находится над блоком, самая высокая вершина которого имеет 500 метров, а высотв полета самолета - 2000? Незачем... Вот для этого и нужны данные по высоте блока. Но это еще не все. В горных районах ботам следует соблюдать осторожность и идти с огибанием рельефа - тут пригодится список маршрутных точек в этом районе. Чтобы не впечататься в стену ущелья, например. Там еще придется вводить алгоритм постройки маршрута по этим точкам, но суть, я думаю, понятна.
В свое время denis8424 продемонстрировал в своем блоге приер движения бота по земле без использования физики. Но при этом бот плавно повторяет все неровности ландшафта - используется словарь координат вершин. он генерится после появления ландшафта, но можно попытаться забить этот словарь в json блока террайна. Но тут еще думать надо.
Пока с ИИ придется слегка притормозить - надо раздробить хотя бы наскоро скрипт оружия - он тоже здоровенный и с ним тоже надо решать кое-какие набившие оскомину вопросы...

четверг, 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)

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

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

Отработка действия БЧ снарядов на юниты. Наведение артиллерийских орудий.

В двух предыдущих постах  я писал про взаимодействие снарядов и юнитов. Точнее, воздействие поражающих факторов на сами юниты. Было это все чисто умозрительно и пока не было воплощено на практике, так и оставалось у меня в голове.
Скрипт работы снаряда, точнее, его БЧ (боевой части) был собран не сразу. Точнее, не скрипт, а функция в скрипте CONTROL_Weapon. А когда она была написана, то пришлось поломать голову, почему оно не работает (ну как всегда - там опечатка, там не то имя указал). А главной причиной была нихкая точность - снаряды рвались уж очень далеко от зенитки.  Я использовал миссию теста МиГ-23БН для проверки работы НАР С-8 по зенитке GDF-001 Oerlicon. Выяснилось, что прицельная сетка на самолете не соответствует дальности полета НАР. Пришлось лезть в файл кабины и корректировать ее положение. Теперь, видимо, ту же процедуру надо провести и в остальных машинах. Ничего не поделаешь - до НАР и пушек я просто до сих пор еще не добирался - руки просто не доходили.
Но в конце концов  скрипт заработал полностью в том виде, какой он сейчас есть. Необходимо дописать проверку лучом от эпицентра взрыва до юнита на наличие препятствия (укрытия) на пути ударной волны и осколков. Приведу текст функции для БЧ типа "осколочно-фугасная":
 def OskolFugas(own):
  
    for units in ArbitrGame.UNITS[1]:
        #Проверяем наличие атрибута повреждений у объекта - ударной волне и осколкам все равно, что разрушать
        if hasattr(units, "crash") == True:
            #В зависимости от расстояния подрыва  рассчитывается величина поражающего фактора
            if own.radiusExplode*1.5 < own.getDistanceTo(units) < own.radiusExplode*3:
                own.LEVELS_CRASH = 0.4*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units)
            elif own.radiusExplode < own.getDistanceTo(units) < own.radiusExplode*1.5:
                own.LEVELS_CRASH = 0.8*own.levelsCrash*own.radiusExplode/own.getDistanceTo(units)
            elif own.getDistanceTo(units) < own.radiusExplode:
                own.LEVELS_CRASH = own.levelsCrash
           
            #Вычисление нанесенного урона
            if own.LEVELS_CRASH > units.levelDefens:
                #При условии, что защита "пробита", смотрим на уровень повреждений юнита и вносим коррективы
                if own.LEVELS_CRASH/units.levelDefens < units.crash:
                    units.crash -= own.LEVELS_CRASH/units.levelDefens
                else:
                    units.crash = 0.0
            print(units, units.crash, own.getDistanceTo(units), own.LEVELS_CRASH)

Последнюю строчку можног не принимать во внимание, я ее потом закомментирую или вообще уберу. В ней я отсматривал работу функции. В принципе, все остальные функции типа "проникающе-фугасной", "Фугасной" и прочей БЧ будут работать так же. Разница будет заключаться лишь в градации расстояния для ослабления действия снаряда.
А вот ненаписанные пока функции для кумулятивных БЧ и бронебойно-подкалиберных снарядов будут вести себя по-другому. Там не нужен цикл перебора юнитов сцены - цель одна-единственная и вступают в действие такие факторы, как толщина брони, угол встречи с броней, наличие динамической защиты и ее тип... То же самое, кстиати, относится к пулям стрелкового оружия - они действуют примерно по тому же принципу - скорость, калибр, плюс дальность выстрела. Мда, придется еще как-то по бронепробиваемости в зависимости от дальности , с которой был сделан выстрел, что-то придумывать...
И в конце об артиллерии. Здесь я приведу  скрипт работы артиллерийского орудия. На данный момент на нем работает "Эрликон", но там еще надо смотреть по его точности - в скрипт введен разброс снарядов и ошибки прицеливания. Весьма вероятно, что с этим я переборщил...
import bge
import json
import sys
import random

cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
ArbitrGame = scene.objects["ArbitrGame"]
#Импортируем модуль юнита
#unit_module = __import__(own.unitModule)
t = 0.0
xS = 0.0
yS = 0.0
zS = 0.0
S = 0.0

newPos = [xS, yS, zS]
import CONTROL_Operations
import CONTROL_Sensor
import mathutils

scene = bge.logic.getCurrentScene()


def UnitArtillery():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    ################################################################
    #Просчет уровней детализации
    if own.maxVisibleDist < own.getDistanceTo(scene.active_camera):
        own.levelsDetails = 2
    elif own.maxVisibleDist/10 < own.getDistanceTo(scene.active_camera) < own.maxVisibleDist:
        own.levelsDetails = 1
    elif own.maxVisibleDist/10 > own.getDistanceTo(scene.active_camera):
        own.levelsDetails = 0
    #print(own.getDistanceTo(scene.active_camera))
       
    #Работа с детализацией   
    if own.Temp_levelsDetails != own.levelsDetails:
        CONTROL_Operations.levelsDetailArtillery(own)
        own.Temp_levelsDetails = own.levelsDetails
   
    if own.crash > 0.1:   
        if own.typeMissions != "":
            if own.statusBattery == "Commander":
                own.timerTargetChanged += 1
                if own.timerTargetChanged == 600:
                    targetChangedOwn(own)
                    own.timerTargetChanged = 0
                if own.targetID != "":
                    Ballistica(own)
   
        #Блокировка возможности стрельбы при отсутствии бокомплекта
        if own.BK == 0:
            own.shoot = 0
            own.targetID = ""
            own.PR = 0
       
        #Разрешение на открытие огня
        if own.PR == 1:
            own.shoot = 1
        else:
            own.shoot = 0
   
        #Стрельба
        if own.shoot == 1:
            shooting(own)   
        #Остановка стрельбы
        if own.Temp_shoot != own.shoot:
            if own.Temp_shoot == 1:
                own.Temp_shoot = own.shoot
           
           
           
#Функция выбора ближайшей цели. Используется для зениток и стрельбы прямой наводкой
#по конкретной цели, для стрельбы с закрытых позиций по площадям и квадратам
#используются координаты
def targetChangedOwn(own):
    if own.typeMissions == "AntiAircraft":
        own.targetType = 0
    sceneObjList = ArbitrGame.UNITS[0][own.targetType][own.target]
    tempSortedList = []
    if len(sceneObjList) > 0:
        tempsortedList = sorted(sceneObjList, key = lambda obj:own.getDistanceTo(obj))
        #print(tempsortedList)
        own.targetID = str(id(tempsortedList[0]))
    else:
        own.PR = 0
        own.targetID = ""

#Расчет точки упреждения      
def Ballistica(own):
   
    try:
        target = scene.objects.from_id(int(own.targetID))
        #Тип прицеливания - отслеживание самой цели
        if own.typeCoordTarget == "AimingTarget":
            newPos = target.worldPosition
        #Тип прицеливание с упреждением
        if own.typeCoordTarget == "VisibleTarget":
            # Рассчитываем время полета снаряда.       
            t = own.getDistanceTo(target) / own.speedBullet  
            # Рассчитываем путь перемещения объекта за это время.
            S = target.worldLinearVelocity * t
       
            xS = target.worldPosition[0] + S[0] + random.randrange(-own.razbros,own.razbros)
            yS = target.worldPosition[1] + S[1] + random.randrange(-own.razbros,own.razbros)
            zS = target.worldPosition[2] + S[2] + abs((-10*t*t)/2) + random.randrange(-own.razbros,own.razbros)
            newPos = [xS, yS, zS]
        #print(target)
        Turret(own, newPos)
    except:
        own.PR = 0
        own.targetID != ""

#Управление наведением в горизонтальной плоскости       
def Turret(own, newPos):
    #print(own.PR)
    Turret = own.childrenRecursive[own.unitName + "Turret_"]
    #Данные по ориентации
    orientY = Turret.getVectTo(newPos)[2][1]
    orientX = Turret.getVectTo(newPos)[2][0]
   
    #Скорость наведения
    kZ = own.speedMaxGor
   
    correct = 1.0
    znak = 0
   
    if orientX > 0:
        znak = -1
    elif orientX < 0:
        znak = 1
   
    if orientY > 0.9:
        correct = 1 - orientY
    else:
        correct = 1.0  
   
    Turret.applyRotation([0.0,0.0,kZ*correct*znak], True)
    Canon = own.childrenRecursive[own.unitName + "Canon_"]
    orientZ = Canon.getVectTo(newPos)[2][2]
    kX = own.speedMaxVert
    correctCanon = 1.0
    znakCanon = 0
   
    if orientY > 0.7:
        if orientZ > 0:
            znakCanon = 1
        elif orientZ < 0:
            znakCanon = -1
     
        if abs(orientZ) < 0.2:
            correctCanon = abs(orientZ)
        else:
            correctCanon = 1.0
       
        Canon.applyRotation([kX*correctCanon*znakCanon,0.0,0.0], True)
       
    if abs(orientZ) < 0.1 and orientY > 0.9 and own.getDistanceTo(newPos) < own.distMax:
        #print(own.getDistanceTo(newPos))
        if own.Temp_cassetteBK < own.cassetteBK:
            own.PR = 1
           
        else:
            own.PR = 0
    else:
        own.PR = 0
   
    #Перезарядка - магазин, кассета, обойма или снаряд
    if own.Temp_cassetteBK == own.cassetteBK:
        own.PR = 0
        if own.Temp_cassetteBK > 0:
            if own.Temp_timerCassetteChanged < own.timerCassetteChanged:
                own.Temp_timerCassetteChanged += 1
            elif own.Temp_timerCassetteChanged == own.timerCassetteChanged:
                own.Temp_timerCassetteChanged = 0
                own.Temp_cassetteBK = 0
               
       

   
def shooting(own):
    #Стрельба из пушек, пулеметов и многоствольных систем
    if own.typeShoot == "Zalp":
        for FLAME in own.fireGun:
            addBullet(own, FLAME)

    #Стрельба из РСЗО, РБУ и прочих реактивных(ракетных) систем
    elif own.typeShoot == "Paket":
        FLAME = own.fireGun[own.BK-1]
        addBullet(own, FLAME)
       
    own.BK -= 1
    own.Temp_cassetteBK += 1

#Функция добавления снаряда и прочего - звука, вспышки...           
def addBullet(own, FLAME):
    Canon = own.childrenRecursive[own.unitName + "Canon_"]
    #Вспышка - лампа
    vspyshka = scene.addObject('LampUniversal', Canon, own.gunPausa+1)
    vspyshka.setParent(Canon, False, False)
    vspyshka.localPosition = FLAME
    #Вспышка - меш
    vspyshkaMesh = scene.addObject('AudioGun', Canon, own.gunPausa+5)
    vspyshkaMesh.setParent(Canon, False, False)
    vspyshkaMesh["audioProp"] = own.audioGun
    vspyshkaMesh.replaceMesh("FireUniversal", True, False)
    vspyshkaMesh.localPosition = FLAME
    razmer = own.scaleGun
    vspyshkaMesh.worldScale = [razmer, razmer, razmer]
    vspyshkaMesh.visible = 1
       
    #Снаряд цепляется к самому юниту и происходит мутация
    #Проверяется наличие атрибута вроде калибрСнаряд = 23мм
    #и объект мутирует в своем классе
    dynObj = scene.addObject('UniversalBullet',vspyshka , 300)
    dynObj.worldScale = [1.0,1.0,1.0]
    if "calibr" not in dynObj:
        dynObj["calibr"] = own.gunBullet   
    dynObj.localLinearVelocity = [random.randrange(-own.randomSpeed,own.randomSpeed),
                                  random.randrange(-own.randomSpeed,own.randomSpeed)+own.speedBullet,
                                  random.randrange(-own.randomSpeed,own.randomSpeed)] 

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




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

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

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

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

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

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

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

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

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

вторник, 10 января 2017 г.

Вам слово, благородный дон РЭБа!

Любовь к странным заголовкам у меня непреходяща, есть такой грешок. Тем, кто читал книгу братьев Стругацких "Трудно быть богом", прекрасно известно имя арканарского министра Охраны Короны и кем он был, так сказать, как личность. Пусть и вымышленная, но очень реальная, как ни странно. А еще я бы посоветовал поискать статьи Сергея Переслегина, которые были написаны уже довольно давно - лет двадцать тому назад, в качестве предисловия к этой самой книге. Тогда, во второй половине 90-х состоялось переиздание если не всех, то подавляющего большинства произведений Стругацких и я горжусь тем, что я их выловил. Так вот-с, глядя на сегодняшнее, начинаешь сильно удивляться тому, насколько пророческими могут оказаться многие вещи. Кстати, у Переслегина весьма интересная версия "тайнфх пружин" событий, развернувшихся в конце книги - когда сломанный гибелью возлюбленной, взбешенный и озверевший земной разведчик-Прогрессор Антон, он же дон Румата Эсторский, устраивает резню в Арканаре, убивая и дона Рэбу. Весьма возможно, что как раз министр Охраны Короны, ставший боевым Магистром и наместником Святого Ордена в Арканарской Области, учинивший кровавый переворот с убийством королевской семьи, которая его же вознесла к вершинам власти, дон Рэба никакого отношения к гибели Киры не имел. Зато провокация удалась на славу и закончилось все еще более кроваво и страшно...
Был еще совместный советско-польский фильм по книге, с тем же названием, так сказать, по мотивам, потому что от книги он отличается очень уж сильно. особенно отсутствием вожака восставших по имени Арата Горбатый, которого почему-то заменили Цурэном - поэтом, которого в книге дон Румата смог выпихнуть из Арканара, спасая ему жизнь. В жизни во главе восстаний стоят как правило не Цурэны, идеалисты-романтики, которые могут послужить только знаменем (как правило, до своей героической гибели), а такие вот Араты, которые почти н7ичем не отличаются от дона Рэбы. Пожалуй, ке в чем они дона Рэбу и переплюнут...
Ну ладно, дань уважения покойным, к сожалению,ныне, писателям отдана, Фильм и статьи критиков упомянуты, мысль по древу растеклась. Теперь, собственно, к делу...
После некоторой возни с ГСН ракет я на время отвлекся от кодинга и в течение трех дней усердно моделил советские контейнеры РЭБ - Радио Электронной Борьбы (теперь вы поняли, откуда заголовок- да? Миссия у РЭБ самая что ни есть благородная - уберечь самолет от вражеских ракет). Были мною смоделены "Сирень", "Гвоздика", "Омуль" и "Сорбция".
Скрин контейнеров:


Как обычно, последовало создание json для СПС-142 "Гвоздика" и подгружаемого бленда. А далее последовало написание скрипта РЭБ. Суть работы "Гвоздики" (кстати, вроде как еще ее называют СПС-141МВГ и эта система опровергает тезис об отставании СССР в области РЭБ от США - во время ирано-иракской вофны ни один иракский самолет, оснащенный этим контейнером не был сбит УР с радиолокационной головкой, а вот иранцам американские системы помогали мало) заключается в ослеплении БРЛС вражеских машин и выдаче ложных координат летящей в самолет-носитель вражеской ракете. Это - "уводящая помеха" - ракета идет не на сам самолет,а на его "призрак". После некоторой возни скрипт заработал. После проверки его работоспособности пришлось срочно ухудшать свойства СПС-142, потому что она делала самолет игрока неуязвимым и бессмертным.
Скрин - СПС-142 "Гвоздика" на МиГ-23МФ.


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


# -*- coding: utf8 -*-

"""
"JAMMER":"ECM",
"timerCycle":0,
"limitCycle":360,
"timerPause":0,
"limitPause":300,
"limitCoord":100,
"distanceAntiMissile":25000,
"distanceAntiBRLS":30000,
 "massChild":0.1,
 "antiForce":0.0018
 """


import sys
import bge
import random

#import mathutils

#Скрипт работы систем противодействия - электронные помехи, УФ-помехи и так далее

#Функция-коммутатор - определяет специализацию систем противодействия
def jammer():
    scene = bge.logic.getCurrentScene()
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if 'threatDict' not in own:
        own['threatDict'] = {"threatList":[],"enemyList":[],"kX":0,"ky":0,"kZ":0}
   
    else:
        #Вызов метода
        g = globals()
       
        #Тип головки самонаведения - название функции
        typeJAMMER = own['JAMMER']
       
        #Проверка на функцирнирование ГСН и ее особенностей
        g[typeJAMMER](own)

#Активные помехи - контейнерные и встроенные системы РЭБ
def ECM(own):
    scene = bge.logic.getCurrentScene()
   
    randomBLINDER = random.randrange(0,100)
    randomJAMMER = random.randrange(1,101)
    phantomCoord = random.randrange(0,own['limitCoord'])
    #print('JAMMER')
    #Словари носителя - списки угроз и списрк в словаре обрабатываемого вражеского оружия  
    #own.parent.parent['threatListWeapon']
    #own['weaponDict'] = {'dopX':0,'dopY':0,'dopZ':0}
   
    #Таймер цикла противодействия
    if own['timerCycle'] < own['limitCycle']:
        own['timerCycle'] += 1
   
    #Формируем список угроз от ракет с радиолокационными головками
    if own['timerCycle'] == 1:
        CoordPlusMinus(own)
        try:
            for missile in own.parent['localDict']['threatListWeapon']:
                #print('localDict')
                if 'typeGSN' in missile:
                    if 'RGSN' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
                    elif 'RK' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
        except:
            pass
   
    #Начинаем воздействие
    if own['timerCycle'] > 1:
       
        #Ослепляющая помеха для РЛС противника
        if randomBLINDER > 90:
            if len(own.parent['localDict']['threatListUnit'][0]):
                for objThreatRLS in own.parent['localDict']['threatListUnit'][0]:
                    #Ослепляющая помеха срабатывает на дальних рубежах, вблизи радары противника помехи "прожигают"
                    if own.getDistanceTo(objThreatRLS) > own['distanceAntiBRLS']:
                        #При низком уровне помехозащищенности захват будет сорван и противнику придется вновь выполнять прицеливание
                        if randomJAMMER/100 > objThreatRLS['antiECM']:
                            objThreatRLS['idTarget'] = '1'
       
        if len(own['threatDict']['threatList']) > 0:
            try:
                #Начинаем попытку  "взломать" ГСН ракеты и подчинить ее системе РЭБ
                MISSILEattack = own['threatDict']['threatList'][0]
                if own.getDistanceTo(MISSILEattack) < own['distanceAntiMissile']*MISSILEattack['antiAC']/100:
                    #print(randomJAMMER)
                    if MISSILEattack['antiAC'] > randomJAMMER:
                       
                        if MISSILEattack['antiAC'] < 101:
                            #Взлом произошел
                            MISSILEattack['antiAC'] = 101
                        #Включаем функцию исажений координат
                        escapeCoord(own, MISSILEattack)
                       
            except:
                own['threatDict']['threatList'] = []
   
    #Включаем таймер паузы              
    if own['timerCycle'] == own['limitCycle']:
        if own['timerPause'] < own['limitPause']:
            own['timerPause'] += 1
       
        if own['timerPause'] == 1:
            #Сбрасываем в ноль искажение для ракеты, можно даже вернуть ей привычную стойкость к помехам
            if len(own['threatDict']['threatList']) > 0:
                CoordNull(own, MISSILEattack)
               
    #Сброс таймеров в ноль  
    if own['timerPause'] == own['limitPause']:
        own['threatDict']['threatList'] = []
        own['timerPause'] = 0
        own['timerCycle'] = 0
           
       
#Активные помехи - оптика - инфракрасные ГСН (тепловы)
def UF(own):
    scene = bge.logic.getCurrentScene()
   
    randomJAMMER = random.randrange(1,101)
    phantomCoord = random.randrange(0,own['limitCoord'])
    #print('JAMMER')
    #Словари носителя - списки угроз и списрк в словаре обрабатываемого вражеского оружия  
    #own.parent.parent['threatListWeapon']
    #own['weaponDict'] = {'dopX':0,'dopY':0,'dopZ':0}
   
    #Таймер цикла противодействия
    if own['timerCycle'] < own['limitCycle']:
        own['timerCycle'] += 1
   
    #Формируем список угроз от ракет с радиолокационными головками
    if own['timerCycle'] == 1:
        CoordPlusMinus(own)
        try:
            for missile in own.parent['localDict']['threatListWeapon']:
                #print('localDict')
                if 'typeGSN' in missile:
                    if 'IKGSN' in missile['typeGSN']:
                        own['threatDict']['threatList'].append(missile)
                       
                       
        except:
            pass
   
    #Начинаем воздействие
    if own['timerCycle'] > 1:
        if len(own['threatDict']['threatList']) > 0:
            try:
                #Начинаем попытку  "взломать" ГСН ракеты и подчинить ее системе РЭБ
                MISSILEattack = own['threatDict']['threatList'][0]
                if own.getDistanceTo(MISSILEattack) < own['distanceAntiMissile']*MISSILEattack['antiUF']/100:
                    #print(randomJAMMER)
                    if MISSILEattack['antiUF'] > randomJAMMER:
                        if MISSILEattack['antiUF'] < 101:
                            #Взлом произошел
                            MISSILEattack['antiUF'] = 101
                        #Включаем функцию исажений координат
                        escapeCoord(own, MISSILEattack)
                       
            except:
                own['threatDict']['threatList'] = []
   
    #Включаем таймер паузы              
    if own['timerCycle'] == own['limitCycle']:
        if own['timerPause'] < own['limitPause']:
            own['timerPause'] += 1
       
        if own['timerPause'] == 1:
            #Сбрасываем в ноль искажение для ракеты, можно даже вернуть ей привычную стойкость к помехам
            if len(own['threatDict']['threatList']) > 0:
                CoordNull(own, MISSILEattack)
               
    #Сброс такмеров в ноль  
    if own['timerPause'] == own['limitPause']:
        own['threatDict']['threatList'] = []
        own['timerPause'] = 0
        own['timerCycle'] = 0


#Выборнаправления уводящей помехи
def CoordPlusMinus(own):
   
    #Показатели
    own['threatDict']["kX"] = random.randrange(-2,2)
    own['threatDict']["kY"] = random.randrange(-2,2)
    own['threatDict']["kZ"] = random.randrange(-2,2)

#Уводящая помеха
def escapeCoord(own, MISSILEattack):
   
    #Искажаем координаты - доработать - слишком длинная запись
    MISSILEattack['weaponDict']['dopZ'] += phantomCoord*own['threatDict']["kX"]
    MISSILEattack['weaponDict']['dopY'] += phantomCoord*own['threatDict']["kY"]
    MISSILEattack['weaponDict']['dopX'] += phantomCoord*own['threatDict']["kZ"]
   
   
#Обнуление искажений
def CoordNull(own, MISSILEattack):  
   
    MISSILEattack['weaponDict']['dopZ'] = 0.0
    MISSILEattack['weaponDict']['dopY'] = 0.0
    MISSILEattack['weaponDict']['dopX'] = 0.0
               
    Думаю, понятно. Весь скрипт висит на объекте Jammer, который и занимается своей работой. Причем информацию об угрозах он получает от своего родителя юнита. Работает он длинными импульсами с длинными же паузами - циклами. система РЭБ не всесильна, но одну ракету точно успевает отвести, если сильно повезет - пару. В общем-то, так и задумывалось.
Кроме систем РЭБ я в авральном режиме занимался головками самонаведения ракет - вводил подсветку, реакцию на ловушки и диполи и так далее. А еще писал дальше искусственный интеллект. В первом приближении систему воздушного боя можно считать выстроенной. Конечно, придется ее корректировать, дополнять, настраивать, но основные параметры и величины, принцип работы уже выстроены. Теперь надо начинать выстраивать работу ИИ по земле и заканчивать с анархией - учить ботов воевать группами, держать строй и даже принимать "командные" решения...