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

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




Комментариев нет:

Отправить комментарий