среда, 26 октября 2016 г.

Дао дятла, змеи и обезьяны.

Опять озадачиваю читающих эти строчки столь вычурным названием. Между тем, речь пойдет о кодинге, точнее о стиле, котрый еще именуют быдлокодингом.
Как известно, в восточных боевых искусствах существуют  "животные стили", адепты которых подражают поведению различных животных, дающих название тому или другому стилю. Програмирование -это тоже искусство и в нем тоже есть свои стили.
я лично использую в основном стиль дятла. Суть его состоит в штурме незапертой двери путем методичной долбежки стены рядом с ней. Код работает, он большой, такой, ято порой самого себя перестаешь спустя месяц понимать. В коде много повторов, выглядит он монолитно и впечатляюще.
Иногда, как недавно с форматом json, дверь оказывается запертой на сломанный замок, а пробить стенку "клювом своего кода"(о, завернул...) не удается, ибо стенка железная и дверь тоже. И тогда на помощь приходит обезьяна. Недаром же говорят: "Что человек - то и обезьяна". Наверное, именно этот способ - лучший путь к обретению новых знаний - почитать чужой код, поюзать чужие примеры, поковыряться в том, что когда-то сделали до тебя. Но иногда, как с json, не помогает и это. И тогда на помощь приходит змея. Достаточно отыскать небольшую щелку, чтобы пролезть туда, куда тебе ну очень надо. Я не слишком люблю этот стиль, потому что конструкция, выстраиваемая на этом принципе, громоздка и хрупка (но просто деваться некуда.).
Однако оставим json, и перейдем к текущим делам. dron не раз уже говорил о разбитии скрипта на множество мелких функций, которые имеют узкую специализацию и легко поддаются коррекции. Каюсь, до недавнего времени я понимал его предложения не до конца, но потом, после очередного штудирования скрипта радара и его "разборке-сборке" (Обезьяна в действии), до меня все же стало кое-чего доходить...
Сейчас идет работа над скриптом оружия, в который, кроме модели движения, должны присутствовать модель нанесения урона, эффектов взрыва, звуков и прочей мелочи, такие вещи, как самонаведение. Методов самноведения много. Как и головок самонаведения - иннфракрасные (ИКГСН), радилокационные активные (АРГСН) и полуактивные (ПАРГСН), лазерные, телевизионные инерциальные и так адалее.
Пока идет работа над ГСН УРВВ радийными и тепловыми. Алгоритм таков. в случае наличия проперти в объекте с названием typeGSN вызывается функция-коммутатор GSN. в ней производится проверка на поападание цели в обзорный конус, затем вызывается функция с названием типа ГСН, например PARGSN.  В этой функции по очереди вызываются СТАНДАРТНЫЕ функции-ограничители - по высоте, дальности, ракурсу, превышению-принижению цели, по воздействию помех, наличию препятствий и так адлее. Все дело в том, что головки самонаведения имеют различный набор ограничений - РЛ ГСН, напирмер, способны увидеть цель в облаках, а ИК ГСН в облаках "слепнут", зато им не нужна подсветка цели, как ПАРГСН. Но функции-то проверки на тип препятствия ОДИНАКОВЫ! Зачем повторять код из функции в функциюв самом модуле? Достаточно просто перечислить условия для каждого типа ГСН... Ниже я привожу сам код. Он большой, многое еще предстоит сделать, но общий принцип ясен.

# -*- coding: utf8 -*-

import sys
import bge

#Скрипт для обеспечения работы оружия - прежде всего его воздействии на юниты, движения, вызов эффектов взрывов, расчете урона
#Самонаведение планируется все же использовать в скрипте ClassSensor. Подлежит доработке в классы, с мутацией объекта.

#Класс снаряда для пушки или пули для стрелковки
class bulletGun(bge.types.KX_GameObject):
    def __init__(self, old_owner):
        bulletObj = scene.addObject('dynObj',self, 300)      #Добавляем сам снаряд
        bulletObj['calibr'] = self['calibr']                 #Снаряду даем проперти "калибр", дальше он приобретает необходимые свойства
        bulletObj.setParent(self, False, False)              #Временно парентим снаряд к источнику, чтоб летел правильно
        trasser = scene.addObject('UniversalMesh',self)      #Добавляем трассер к снаряду
        trasser.setParent(bulletObj, False, False)           #Парентим трассер к снаряду
        trasser.replaceMesh('Trasser', True, False)          #Меняем меш плейна на меш трассера
        self['BK'] -= 1
        #Возможно, стоит подумать просто над заменой меша у самого снаряда, но нужно еще выставить worldScale
       
    #Это - пушка ГШ-23Л   
    def GSh23L(self):
        self.childrenRecursive['dynObj'].removeParent()
   
def init(cont):
    sys.modules["bulletGun"] = bulletGun(cont.owner)
   


   
#Функция, задающая траекторию движения динамического объекта - баллистическая кривая
def traectory():
    import mathutils
    #Объект - dynObj, сцена - Scene, слой 2
    scene = bge.logic.getCurrentScene()
    cont = bge.logic.getCurrentController()
    own = cont.owner
    sens = cont.sensors[0]
   
    if own.worldLinearVelocity != mathutils.Vector((0.0, 0.0, 0.0)):
        own.alignAxisToVect(own.worldLinearVelocity, 1, 1.0)
       
#Функция двигателя ракеты
def engine():
    import mathutils
    #Объект - dynObj, сцена - Scene, слой 2
    scene = bge.logic.getCurrentScene()
    cont = bge.logic.getCurrentController()
    own = cont.owner
    sens = cont.sensors[0]
   
    own.localLinearVelocity.y = own['speed']
   
    if 'typeGSN' in own:
        GSN()
    

#Функция-коммутатор головок самонаведения
def GSN():
    import mathutils
    #Объект - dynObj, сцена - Scene, слой 2
    scene = bge.logic.getCurrentScene()
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #Проперти, задействованные в этой функции
    #['sensRLscanTarget']         #Типы распознаваемых целей(0 - только небо, 01 - небо и земля, 1  - только земля, проперти строчное)
    #['sensRLscanDistMax']        #Максимальная дистанция обзора
    #['sensRLscanAngle']          #Угол обзора
    #['sensRLscanTimer']          #Время сканирования
    #['sensRLscanCanal']          #Количество одновременно сопровождаемых целей
    #['sensRLscanDeadZone']
    if 'timerImpulse' not in own:
        own['timerImpulse'] = 0
   
    own['timerImpulse'] += 1
    if own['timerImpulse'] > 100:
        own['timerImpulse'] = 0
    try:
        #Определение цели, вектора на нее и так далее
        target = scene.objects.from_id(int(own['idTarget']))
       
        if inConeOfGSN(own, target):
            vect = own.getVectTo(target)[1]
            own.alignAxisToVect(vect, 1, 0.5)
            vectX = own.getVectTo(target)[2][0]
            vectY = own.getVectTo(target)[2][1]
            vectZ = own.getVectTo(target)[2][2]
            print("La-17")
    except:
        pass
             
#Этот   блок - конус сенсора
def inConeOfGSN(own, target):
    import mathutils
   
    axisVect = mathutils.Vector((0.0, 1.0, 0.0))
    targetData = own.getVectTo(target)
    targetVect = targetData[2]
    dist = targetData[0]
   
    angleGSN = own['angleGSN']
       
    #Если объект попадает в конус действия сенсора
    if angleGSN > axisVect.angle(targetVect, None): 
       
        #Вызов метода
        g = globals()
       
        #Тип головки самонаведения - название функции
        typeGSN = own['typeGSN']
       
        #Проверка на функцирнирование ГСН и ее особенностей
        if g[typeGSN](own, target):
            return True
       
    #Или не попадает
    else:
        return False
    return False

#Полуактивная головка самонаведения радиолокационная
def PARGSN(own, target):
    #Проверка на ограничения по высотам и превышению-принижению
    if limitHeightMax(own, target) and limitHeightAbs(own, target) and limitHeightMin(own, target):
        #Проверка по дистанции и отсутствию препятствия
        if limitDistMax(own, target) and rayEARTH(own, target):
            return True
    else:
        return False                   

#Активная головка самонаведения радиолокационная
def ARGSN(own, target):
    #Проверка на ограничения по высотам и превышению-принижению
    if limitHeightMax(own, target) and limitHeightAbs(own, target) and limitHeightMin(own, target):
        #Проверка по дистанции и отсутствию препятствия
        if limitDistMax(own, target) and rayEARTH(own, target):
            return True
    else:
        return False

#Инфракрасная головка самонаведения радиолокационная
def IKGSN(own, target):
    #Проверка на ограничения по высотам и превышению-принижению
    if limitHeightMax(own, target) and limitHeightAbs(own, target) and limitHeightMin(own, target):
        #Проверка по дистанции и отсутствию препятствия и отсутствие оптических помех (дым, туман, облака)
        if limitDistMax(own, target) and rayEARTH(own, target) and rayFOGS(own, target):
            return True
    else:
        return False

#Полуактивная головка самонаведения лазерная
def PLDGSN(own, target):
    return True

#Активная головка самонаведения лазерная
def ALDGSN(own, target):
    return True

#Полуактивная головка самонаведения телевизионная
def PTVGSN(own, target):
    return True

#Активная головка самонаведения телевизионная
def ATVGSN(own, target):
    return True

##################################################################################
#БЛОК ОГРАНИЧЕНИЙ ПО ВЫСОТЕ И ДИСТАНЦИИ
"""
Этот блок - проверка оограничений по дистанции и высотам, поскольку у многих видов ракет имеются
существенные ограничения по высотам применения, превышения-принижения над целью, что ведет к усложнению
модели поведения такого типа оружия в игре.
"""
#Проверка параметров дистанции
def limitDistance(own, target):
    targetDist = own.getDistanceTo(target)
    distLimitMax = own['distMax']
   
    if distLimitMax > targetDist:
        return True
    else:
        return False 

#Проверка параметров МЕНЯЮЩЕЙСЯ дистанции
def limitDistMax(own, target):
   
    #Координаты цели и собственные
    targetPosition = target.worldPosition
    ownPosition = own.worldPosition
    targetDist = own.getDistanceTo(target)
   
    #Собственная высот а и высота цели
    ownHeight = own.worldPosition[2]
    targetHeight = target.worldPosition[2]
   
    #Поправка на ракурс цели - стрельба на догонном курсе возможна с втрое меньшей дистанции
    racursX = (2 - abs(target.getVectTo(own)[2][0]))/2
    racursY = (2 + target.getVectTo(own)[2][1])/2
    racursZ = (2 - abs(target.getVectTo(own)[2][2]))/2
       
    #Еще одна переменная, влияющая на дальность пуска - общий ракурс цели
    RACURS = (racursX + racursY + racursZ)/3
       
    distLimitMax = own['distMax']
   
    #Введение поправки на дальность ниже 3 км дальность составляет 30 процентов
    if targetHeight < 3000:
        distLimitMax = own['distMax'] * target['stealth'] * RACURS * 0.3
       
    #Введение поправки на дальность от 3 до  10 км дальность изменяется в сторону уменьшения с уменьшением высоты
    if 3000 < targetHeight < 10000:
        distLimitMax = own['distMax'] * target['stealth'] * RACURS * targetHeight/10000
       
    if distLimitMax > targetDist:
        return True
    else:
        return False 
   

#Проверка параметров высоты - максимум
def limitHeightMax(own, target):
    if own['heightMax'] > own.worldPosition[2]:
        return True
    else:
        return False

#Проверка параметров высоты - минимум
def limitHeightMin(own, target):
    DeadZone = [target.worldPosition[0], target.worldPosition[1], target.worldPosition[2] - own['heightMin']]
    if own['timerImpulse'] > 100:
        #Проверка на лимит по минимальной высоте пуска по цели
        hitEarth = target.rayCast(targetPosition, DeadZone, own['heightMin'], 'objScene', 0)               
        if hitEarth == (None, None, None):
            return True
        else:
            return False
   
    elif own['timerImpulse'] < 100:
        return True

#Проверка параметров высоты превышение-принижение относительно цели
def limitHeightAbs(own, target):
    ownHeight = own.worldPosition[2]
    targetHeight = target.worldPosition[2]
    if own['heightAbsLimit'] > abs(ownHeight - targetHeight):
        return True
    else:
        return False

##################################################################################
#БЛОК ОГРАНИЧЕНИЙ ПО  НАЛИЧИЮ ПРЕПЯТСТВИЯ
"""
Этот блок - для проверки наличия препятствия перед целью для головок самонаведегия,точнее, здесь
несколько малых функций, поскольку операция стандартная дл я всех ГСН, хотя набор препятствий как
раз неодинаков, не стоит повторять код лишний раз, достаточно просто вызвать нужные  проверки по
цепочке, плюс это упростит коррекцию кода в дальнейшем.
"""
#Препятствие - туман, облако, дым
def rayFOGS(own, target):
    return True

#Препятствие - земля
def rayEARTH(own, target):
    return True

########################################################################
########################################################################
"""
Этот блок из нескольких функций задает поведение оружия в условиях воздействия разного рода помех -
тепловых ловушек, Солнца, электромагнитных, оптических, акустических  и так далее
"""

#Отстрел тепловых ловушек
def obstacleLO(own, target):
    return True

#Отстрел диполей
def obstacleDIPOL(own, target):
    return True

#Уводящая помеха
def obstacleESCAPIST(own, target):
    return True

#Солнце
def obstacleSUN(own, target):
    return True

#Радиопротиводействие
def obstacleECM(own, target):
    return True

Там, где в функции лишь одна строчка return True, еще надо дописывать код. Пока что проверка принтом "La-17" исправно выдает нужный результат - значит, цепочка функций работает. Отдельно замечу, что метод типа rayCast и ему подобные я стараюсь вызывать раз в секунду или больше. постоянные вызовы таких вещей могут притормозить игру из-за их прожорливости. В скрипте около 300 строчек, больше половины из них пояснения и комментарии, надеюсь те, кому это нужно, поймут без дополнительных обяснений. В сущности все "это новое слово" не более, чем компиляция из старых способов, которых я использовал в первой версии чисто механически, боясь слишком сильно менять что либо (а вдруг испорчу?). Ну а теперь дело - за шлифовкой и дописыванием скрипта - раеты уже летают и цели видят. как и сенсоры юнитов...

пятница, 30 сентября 2016 г.

Повторение пройденного. повторение - мать учения...

Работа идет не шибко торопясь. Приходится иногда прямо на ходу придумывать новые схемы или вносить коррективы в уже существующее. Тем не менее, было сделано следующее:
1. Заработали цифровые и буквенные символы ИЛС.
2. Были подчищены скрипты машин, ставшие более универсальными (в смысле можно внаглую скопировать большую часть текста и загнать ее в текст другого скрипта другой машины с минимальными правками).
3. Наконец-то вменяемо заработала коррекция скорости по высоте. Все дело в том, что у земли самолеты имеют меньшую скорость, нежели на средних и больших высотах, к примеру у МиГ-23МЛ у земли скорость 1350 км/ч, на высоте же - 2500, у МиГ-29 та же скорость на высоте, но у земли - 1500, у МиГ-25 скорость у земли 1200, зато на высоте - около или даже больше 3000 км/ч.
4. Также к своему неудовольствию отметил, что не работали показатели коррекции скорости по стреловидности крыла и работе тормозов. Как выяснилось, не в ту строчку вписал нужный коэффициент. Пока возился с мешами модели, было не до этого, но когда смена мешей была обеспечена, устранил и этот недочет.
5. Наконец заработала метка цели на ИЛС. в основе этого "явления" лежит схема, которую предложил еще в первой версии denis8424. Я, как обычно творчески подошел к пересмотру догматов и получил " те же яйца, только в профиль". Надо было бы смастряить файл примера, но все как-то не соберусь. Сентябрь вообще выдался довольно насыщенным и много чего так и не удалось сделать...

6. На ИЛС заработали метки измерения дальности до цели - это для МиГ-23, для Су-25 стал работать прицел АСП-17 (опять таки надо кинуть файл примера).
7. Начала работать возможность "убитьсяАПстену", то бишь разбить самолет о землю, хотя и тут надо доработать, потому что на тех скоростях, которые есть в игре БГЕ часто не успевает среагировать. Надо изощряться...
8. Как апофеоз  всех работ сентября отмечу введение отстрела тепловых ловушек, они же ЛТЦ, они же ЛО. Пока они работают на МиГ-23МЛАЭ2 и МиГ-23МЛД. Способ отстрела весьма извращенный. В файле json самолета выставляется значение проперти startLo = 0, которое говорит о наличии на борту патронов с ЛТЦ. Далее в файле есть список "суб-объектов", как я их обозвал. Это - фигура летчика, генераторы пушечных снарядов и эти самые генераторы ловушек. Все "суб-объекты" имеют чтение определения своего локального положения и разворота относительно родителя плюс проперти. Для генераторов ЛТЦ их два - число патронов и внушительный такой список из координат генераторов ловушек и их разворотов. собственно, можно было ограничиться только координатами, но у некоторых машин выброс ловушек может идти сначала сверху из верхних кассет, а потом снизу - из нижних. поэтому пришлось делать вложенный список. каждый элемент списка включает себя координаты (всегда) и разворот в радианах (очень редко). Поэтому алгоритм отстрела таков.
а) Подается команда на отстрел проперти startLO = 1.
б) Циклом отыскиваются потомки с именем "генераторЛО" и это свойство передается им.
в) Генератор ловушек выполняет команду на выстрел.
г) Генератор ловушек уменьшает свое проперти "патронЛО" (число ловушек) на 1.
д) Генератор ловушек сдвигается на новое 2место жительства" согласно списку координат и разворотов на одно деление (отыскивается элемент с номером, соответствующем текущему значению числа ловушек).
д)Генератор ловушек проверяет, есть ли в элементе списка еще и данные для разворота. Если есть, он еще и поворачивается.
Как только число ловушек равно нулю, генератор немедленно блоуирует у своего родителя возможность стрельбы, проперти startLO самолета становится равным минус 1. И генератор исчезает "мавр сдал свое дело".

Ну а дальше пошло нудное занятие по подгонке стартовых координат и поворотов, создание дыма горящих ЛТЦ - это уже рутина...
Из эффектов полета остается сделать  вихри при маневрировании и эффект пробития звукового барьера.
И еще, штопор. Вот с ним надо крепко подумать. Имея перед глазами показатели угла атаки, пусть и такие грубые и примитивные, как у меня, несложно добиться сваливания в штопор. Более-менее ясно, как его крутить. Остается придумать, как из него выйти. С этим у меня откровенно ничего не вышло и в первой версии я это дело просто убрал, отложив до лучших времен. Не знаю, как там насчет "лучших", но делать это необходимо.
Также я занимался и МиГ-21. Сделал развертку - в первом приближении, для камуфляжа и расшивки. Дополнительные объекты вроде ниш шасси, колес и пр., я наношу на развертку потом, места для них хватает. пока получается камуфляж МиГ-21бис ВВС ГДР.

четверг, 1 сентября 2016 г.

Реинкарнации.Новое слово в бортовых номерах.

Постараюсь набрать этлт пост без ошибок - пытаюсь освоить десятипальцевый метод набора текста. Получается со скоростью пока нешибко, нонадо когда-то начинать.
Пока идет работа по возврашению моделей из первой вермии встрой. В первую очередь это касается кабин. Ничего особо нового нет, кроме скрипта работы приборов, да текстуры там теперь работают в формате dds. Введен строй МиГ-23МЛД, готовится МиГ-27К...
Давно назревал вопрос с бортовыми номерами. Так уж получилось, что у меня действоало жесткое ограничеие по количеству номеров - 16 штук и не более. Номера представляли собой плейны с цифрами, меши которвх подменяли постоянный обьект CntAircraft. Но я помнил о возможностях UV-скроллинга и мне хотелось эти возможности использовать. В конце концов решение было найдено. Сам бортовой номер в моем случае - строковое проперти, состояшее из цифровых симвлов. UV-скроллинг предусматривает сдвиг развертки вправо-влево (по оси Х), и вверх-вниз(по оси Y). Отыскав в своих закромах заготовки бортовых номеров, приступил к созданию так называеемых стилей - текстур цифр от 0 до 9, обьединенных в строчки. В принципе можно было использовать всего одну и применять цвет объекта к материалу. И получить люьой цвет цифр. Однако существуют варианты номеров с кантом (обводкой по контуру) и их довльно много - и тут цвет объекта неприменим. Поэтому в итог у меня получились три стиля бортовых номеров в 6 вариантах - белый(без канта), контурный белый кант, прозрачный внутри и 4 варианта с обводкой - красный, желтый, синий и черный. Полученные варианты по моим прикидкам, перкрывают пока весь нужный мне диапазон - что натовский, что сербский, что наш, что ливийский стандврты.
Далее пошла реформа генерации бортового номера - теперь бортовой номер в комплекте к модели идет один, но состоит он  из объектов, число которых равно длине номера, к примеру, "541" - три объекта - numObj0-1-2, последний символ в имени - номер симола в последователности цифр. В данном случае - 5 - 0 , 4-1 и 1-2.  А далее следует алгоритм сдвига развертки. У нас 10 цифр, значит одно "деление" сдвига - 0.1, а цифра, извлекаемая из последовательности номера, указывает, насколько надо сдвинуть развертку. Привожу кусок кода. Надеюсь, мысль понятна.
   
 #ЭТОТ БЛОК ФОРМИРУЕТ НОМЕР ЮНИТА, ВЫБИРАЯ ЧИСЛА В ПРОМЕЖУТКЕ, УКАЗАННОМ В ФАЙЛЕ JSON
            #Ограничения слева и справа по номеру
            limitLeft = dataJSON["listBortNumber"][0]
            limitRight = dataJSON["listBortNumber"][1]
            
            numberUnit = random.randrange(limitLeft,limitRight)
            if numberUnit not in bge.logic.globalDict["importMesh"]:
                if numberUnit not in bge.logic.globalDict["importMesh"]:
                  
 if numberUnit < 10:
#для номеров типа 01,02.03 и так далее
                        unitNew['unitNum'] = '0' + str(numberUnit)
                    else:
                        unitNew['unitNum'] = str(numberUnit)
                    bge.logic.globalDict["importMesh"].append(numberUnit)
           
            #Далее идет выдача бортовых номеров
            for numObj in unitNew.childrenRecursive:
                #В названии составляющих бортового номера присутствует numObj
                if 'numObj' in numObj.name:
                    #Далее выбирается конкретная цифра в номере
                    number = int(numObj.name[-1])
                    sdvigUVplane = int(unitNew['unitNum'][number])  
                    mesh = numObj.meshes[0] 
                    array = mesh.getVertexArrayLength(0)
                    for k in range(0,array):
                        vertex = mesh.getVertex(0,k)
                        UV = vertex.getUV()
                        #Сдвиг УВ-сколиинга вправо на нужное деление
                        UV[0] = UV[0] - sdvigUVplane/10
                        vertex.setUV(UV)

плейн номера знает, какая цифра в номере ему нужна, извлекает ее, преобразует в число и сдвигает свою развертку на нужное деление. Максимальная длина бортового номера - 5 символов (ирак и Югославия), обычно 2-3 (мы и натовцы), реже - 4 - (Сирия, ЧССР, Ливия). Номера теперь в json юнитов задаются ограничениями слева и справа, типа 'listBortNumber':[0, 100].
Кстати о джейсонах. Файлы эти размножились и прочно обосновались как в файлах юнитов, так и в файлах оружия и стало их много. они выполняют стартовую работу - раздают проперти, парентят объекты, располоагают их и разворачивают, как надлежит. Для всех МиГ-23 работа над файлами джейсонов закончена, кроме МиГ-27, но и для них осталось лишь определить положение ракет Р-60, может быть по мере добавления новых видов оружия будут создаваться дополнительные json, но это дело еще впереди.
Пока что результат с бортовыми номерами и json установки оружия...
 МиГ-23МЛД, бортовой номер "31 красный" При первом пробном запуске был "24 крачный".
 Ливийский МиГ-23МФ, бортовой номер трудно разглядеть - запускал несколько раз, номер менялся типа 4782 или 4753 черный.
А это - эксперимент с json МиГ-23БН ВВС Ливии - кроме трех ПТБ - по паре РБК-500 и РБК-250, плюс пара ОФАБ-250 на хвостовых балках.

пятница, 19 августа 2016 г.

Контрольный выстрел. Анимации больше нет...

Как-то довольно быстро после последнего сообщения и боях с форматом json, было восстановлено все. Кроме текстовой индикации на кокпите, которую я намереваюсь рисовать через blf. К сожалению, то ли я удалил файлик с заготовкой, то ли запихнул его куда-то, но теперь придется это дело поновой начать, благо что образец скрипта я скопировас АПИ Блендера.
Все эти дни методично занимался подчисткой и сортировкой того, что уже было сделано и дописывал требующееся. Была восстановлена пушечная и ракетная стрельба, как УР, так и НАР, аварийный сброс подвесок, сброс топливных баков и бомб. Поскольку мои руки еще не добрались до кода со стрельбой я еще толком ничего не успел поломать, чтобы потом героически восстанавливать мною же порушенное. Так, переделал немного имена проперти в коде, поскольку лазить по json и исправлять там было влом.
Долго и тщательно "приглаживал" кокпит. В общем сама картина не поменялась - ноэто внешне. Хотя кое-какие видные глазу ихменения все же произошли. Я наконец добрался до стрелочек угла атаки, угла сноса и перегрузки. Эти стрелки у меня были фактически для декорации в первой версии, теперь у них функция несколько ближе к реальной. Поскольку изменения претерпела сама модель полета, появилась "боковая" составляющая и "вертикальная" составляющая. Это как имитация "заноса" машины на повороте. Только машину, по большому счету ведет влево-вправо, а самолет еще и вверх-вниз. Отдаю себе отчет в том, что деление "боковых" и "вертикальных" скоростей сноса на линейную скорость самолета это не аэродинамика, но все же лучше, чем ничего. Таким образом яполучил угол атаки, угол сноса и перегрузку (сумму "боковой" и "вертикально" составляющей, деленную на скорость машины). Пришлось повозиться с выставлением коэффициентов для ограничения работы стрелок, чтобы не вертелись как попало, но оно того стоило. Затем пришло время разбить приборы и индикаторы на группы объектов и создать в скрипте отдельные функции типа dvigInd, chassyInd, wingsInd и так далее. Все эти функции вызываются лишь при изменении текущих данных - выпуска-уборки тормозов-шасси-закрылков, изменении тяги двигателя и так далее. В итоге сама функция Cockpit в одноименном скрипте превратилась в своеобразный коммутатор, для вызова функций работы индикаторов и стрелочек. Индикаторов у меня что-то около 40 штук, плюс что-то около 20 стрелок. Все это хозяйство ныне исправно функционирует и выдает нужну. информацию.
После того, как кокпит МиГ-23МФ был отлажен и выловлены все ошибки и недочеты, занялся изничтожением оставшейся анимации. Дело Термидора должно было быть доведено до конца...
Как выяснилось, "глаз боится, руки стучат по клаве",  необходимо было убрать анимации из гидравлики и стоек шасси - всего - 16 объектов. Дело осложнялось тем, что некоторые объекты имели весьма сложную траекторию движения, по ходу исполнения самой анимации направление движения могло измениться на противоположное.
Тем не менее проблема была решена с помощью if own==blabla: own=bla. Я просто разбил участки работы проперти CHASSY на интервалы, внутри которых менялись скорости и направления движения. Пока, на мой взгляд, строчек многовато, но в будущем, надеюсь, и здесь часть строчек будет убрана. Пока работает и ладно. Что мне и нужно было. Таким образом, анимации в файлах модели больше нет. Совсем.
Возможно, что в 2.78 баг с проигрыванием анимации уже поправили,  но пока не проверял. Да и по большому счету, уже пока и не нужно.
Есть еще кое-какие фишки в Блендере, до которых пока руки не доходят. Так, есть возможность смешения текстур (декали, бортовые номера, имитация повреждений), но все это пока перекрывается тем, что объекты, добавленные в сцену через addObject, имеют одинаковый материал, поэтому и выглядеть будут одинаково...
В общем, пока вот так. Джейсон прочно утвердился в проекте (оказывается, есть еще кое-какие возможности, надо их изучить), анимация в модели и в кабине полностью убрана (это привело к уменьшению файлов примерно на 20 процентов), кабина отлажена полностью, за исключением прицела (не в кого пока целиться, Ла-17 взял из старого проекта, заодно выяснил, откуда там прозрачность и вывернутость полигонов - в настройках материала не отключил Transparency), работает оружие, механизация, стрельба и взрывы. Надо готовить мишени и на них отрабатывать до конца модель повреждений и воздействие оружия, плюс прицел и сенсоры.

суббота, 13 августа 2016 г.

Фредди против Джейсона. Ужасы перестройки.

Похоже, давать постам громкие заголовки, используя названия фильмов (особенно, когда сказать нечего по существу), становится у меня традицией. Почему "Ужасы перестройки"? Да потому что использование json (который я уже обозвал Джейсоном) несет массу неожиданностей. Почему "Фредди против Джейсона"? А потому, что такой фильм был. Даже два - у меня на самом первом компе завалялся огрызок этого фильма - файл был поврежден, поэтому какой маньяк с каким маньяком там воевал установить не удалось. Фредди - это, конечно Федя Крюгов (Фредди Крюгер) - так я с некоторых пор именую свой старый стиль программирования для выставления проперти юнитам (смотрим посты ниже с длинными рядами строк присвоения пропертей). Подобная простыня с многими отступами и занимающая больше 500 строчек способна вогнать в ужас любого мало-мальски сведущего в программировании человека (я имею в видк тех, кто знает не только if own: own, в отличие от меня). Джейсон (json) дает возможность не в пример быстрее и понятнее загрузить данные, правда, с некоторыми оговорками.
Дело в том, что, как мне объяснил dron, проперти в Блендере написаны на языке Си, а метод attrDict вообще-то дает питоновские проперти и, разумеется, вся система мгновенно рушится. Так что сам метод не виноват в том, что у меня ручки кривые (Питон и Си - языки вообще-то разные, а валить все на разработчиков Блендера  не стоит, хотя иногда знатные подарки они таки преподносят, как с проигрыванием анимации).
Поэтому был найден еще один способ раздать проперти, читая файл

jsonINIweaponFile = open(bge.logic.expandPath('//Weapon/'+pathObj+'/'+pathObj+'.json'),'r')
#Читаем файл с проперти
weaponINI = json.load(jsonINIweaponFile
 #Выдаем проперти со значениями и закрываем файл
  for key in weaponINI:
        if key not in newObject:
              newObject[key] = weaponINI[key]

Не сказать, что это меня сильно обрадовало, но, по крайней мере, пока срабатывает. Этот кусочек кода предназначен для выдачи проперти ракетам, бомбам, пилонам подвески - всему тому, что составляет внешнюю подвеску на самолете. Кстати, точно так же раздавались проперти двигателю самолета, с одной поправочкой - в последней строчке отсутствовало not, поскольку проперти я заранее там выставил в блоке редактирования логики, проставив нулевое значение.  И тут неожиданно всплыло еще одно препятствие...
Сначала вообще-то надо прочесть файл старта миссии - названия юнитов, их координаты, лриентация, сторона, способ управления и оружие. Чтение json по данным, не относящимся к проперти юнита проходило отлично - никаких отклонений, ошибок и прочего. Однако, стоило мне присвоить значение проперти target(сторона) и bot(способ управления), как все пошло кувырком после чтения второго json  с остальными проперти (маневренность, тяга, высотность, скорость). Такое впечатление, что повторное вмешательство в проперти опять же с чтением json вызывает какой-то сбой и в этом опять же виноваты особенности проперти в БГЕ.
В итоге на данный момент с помощью хитроумных извращений удалось сделать загрузку всех проперти юнита, сделать генерацию оружия с заменой мешей и работающей системой объектов, зависящей от требуемой детализации, но стартовую загрузку миссии все же оставить в формате txt. Со всеми вытекающими - чтнеием по маркерам-разделителям, резкой и преобразованием текстовых блоков и тд. Это не есть хорошо - Фредди прочно пока удерживает последние позиции (лишь бы не получилось, как с сериалом "Кошмар на улице Вязов" - серия 9 - "Фредди мертв. Последний кошмар", серия 10 "Фредди жив! Новый кошмар"), потому что к хорошему быстро привыкаешь и полста строчек кода вместо полтыщи выглыдят куда привлекательнее. К тому же удалось более-менее выстроить стройную систему генерации вооружения и начать клепать файлы с данными для конкретной ракеты или бомбы. Файлики небольшие, читать и понять их гораздо проще и легче. Посмотрим, не подведут ли меня эти методы генерации и раздачи свойств для оружия. Пуски-то и сброс я пока закомментил...
В результате всего этого произошел переход на 2.77, пусть в нем и присутствует баг с анимацией, но выправлены некоторые другие, да и анимация почти исчезла даже для кокпита - ее заменила ориентация стрелок, как выяснилось, надо просто правильно подобрать коэффициенты. Кроме того, как выяснилось, в БГЕ есть возможность рисовать текст на экране без использования текстовых объектов. Смотрим функцию blf в АПИ Блендера. Тексту можно давать цвет, размеры и расположение на экране. В свое время dron как-то делал GUI еще на старом сайте БлендерУкраина. Я же сделал вывод, что совершенно необязательно делать извращения с заменой мешей с текстурой текста или создавать еще одну оверлейную сцену с текстовыми объектами (есть в Блендере очень старый баг с невидимостью некоторых объектов при взгляде через текстуру с альфа-каналом - об этом, кажется писал еще O.din13 на БУ). Так что текст, скорее всего будет именно рисованным через blf, обновлять его придется либо при повороте камеры либо при поступлении новых данных, что будет происходить относительно редко, так что много сожрать не должно.
В общем перестройка после "термидориансокого переворота"  пусть и со страшным скрипом, но идет вперед.

воскресенье, 31 июля 2016 г.

Переворот 12 Термидора. Великая Анимационная КОНТРРеволюция.

В названии этого поста скрывается грустная шутка. В свое время я писал посл "Вкликая Анимационная Революция", в котором подробно описал способ массового проигрывания анимации у объектов-потомков в БГЕ. К сожалению, иногда разработчики Блендера делают ляпы, из-за которых стройная и уже отлаженная система дает сбой. Тем более обидно, когда этот сбой проявляется в новой версии, вынуждая откатываться на старую и лишая тем самым новых плюшек. Так и получилось с версией 2.77. Проигрывание анимации при помощи скрипта там возможно лишь при 2прерывистом" состоянии сенсора - пульсация там должна быть равна 1, как минимум. Постоянно проигрываться анимация не в состоянии - происходит "рваный" переход от начального кадра к конечному по истечении некоторого времени. И если для работы приборов этого вполне хватит, то вод для проигрывания анимаций механики - никакне подходит (и какого художника я перешел со старой доброй ХР на "семерку", сидел бы себе на 2.75 и Виндовс ХР?!).
В свое время denis8424 как-то подначивал меня, задавая вопрос насчет того, когда же я буду просто вращать детали самолетов, не используя анимацию? Тогда я отбрехался сложной траекторией движения и всем таким. Однако жить захочешь - не так раскорячишься. Поэтому решение все же было найдено. Окончательно оно оформилось сегодня, после успешного добавления и отработки функции шасси. По республикансокму календарю сегодня 12 Термидора, получается прямо Термидорианский переворот ("Я тебя породил, я тебя и убью!"(С)). Для начала я принялся "искривлять" или, точнее, "перекашивать" детали самолета. Дело в том, что тормозные щитки, к примеру, расположены на том же Миг-23 не прямо, а под некоторым углом, то же касается деталей крыла. Поэтому я брал деталь, крутил ее, так чтобы передняя кромка становилась "прямой" и потом уже жал Ctrl+A и - Rotation, затем "доворачивал", чтобы деталь укладывалась обратно на место, но ее стартовый угол был уже ненулевым. Таким образом я получил возможность крутить деталь только по одной оси, а не по трем, как в анимации. Далее требовалось понять, как все это безобразие отклонить на нужный угол... Отклонить на нужный угол можно путем отсчета проперти, которое "гуляет" в строго отведенном ему промежутке от минимума до максимума. А ишшо нужна переменная, которая отслеживает, куда проперти идет - вправо или влево. Решалось это путем создания уже привычного словаря свойств и сравнения текущего значенияпроперти со словарным. И когда нужно - эти значения выравнивались. Сама команда н6а поворот детали осуществляется строчкой типа  obj.applyRotation([три цифрычереззапятую],True). Теперь предстояло понять, как эти трицифрычереззапятую выдавать. В Блендере поворот дается в радианах, что несколько неудобно, но справиться с этим можно. Я выбрал основной величиной полградуса. Градус при переводе в радианы давал 0.0174444444444444, я это число укоротил до первой четверки, полградуса выдается 0.087. И вот тогда началось самое интересное. Для наглядности приведу кусочек кода для тормозных щитков.
Команда для тормоза:
if keyboard.events[bge.events.AKEY] == JUST_ACTIVATED:
           
                if self['localDict']['AIRBRAKE'] == self['AIRBRAKE']:
                    self['AIRBRAKE'] += 1
Как видим, клавиша все  время дает прирост проперти на 1. Чтобы не возиться, потому как у разных самолетов времени анимации тормоза может отличаться. А вот как выглядит исполнение "поворотом":
#Псевдоанимации тормозных щитков               
def airbrake():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    #Направоение перекладки крыла
    airbrakes = 0
   
    #Ограничения по максимальному и минимальному значению проперти
    if own['AIRBRAKE'] > 100:
        own['AIRBRAKE'] = 99
    elif own['AIRBRAKE'] < 0:
        own['AIRBRAKE'] = 0
   
    #Выставление напрaвления перекладки и убывания-возрастания проперти
    if own['localDict']['AIRBRAKE'] < own['AIRBRAKE']:
        airbrakes = 1
        own['AIRBRAKE'] += 1
    elif own['localDict']['AIRBRAKE'] > own['AIRBRAKE']:
        airbrakes = -1
        own['AIRBRAKE'] -= 1
   
    if own['levelsDetails'] == 0:       
        #Собственно, движение тормозных щитков
        if 0 < own['AIRBRAKE'] < 90:
            own.childrenRecursive['ArbUL_'].applyRotation([-0.0087*airbrakes,0.0,0.0],True)
            own.childrenRecursive['ArbUR_'].applyRotation([-0.0087*airbrakes,0.0,0.0],True)
        if 0 < own['AIRBRAKE'] < 80:
            own.childrenRecursive['ArbDL_'].applyRotation([0.0087*airbrakes,0.0,0.0],True)
            own.childrenRecursive['ArbDR_'].applyRotation([0.0087*airbrakes,0.0,0.0],True)
           
    #Остановка псевдоанимации на кадрах со значением 0,100  
    if own['AIRBRAKE'] in [0,100]:
        own['localDict']['AIRBRAKE'] = own['AIRBRAKE']
       
Прикол тут в том, что в начале функции выставлены ограничители - которые "отбрасывают" проперти в рамки от 0 до ста. Внутри этого промежутка проперти НЕ ОСТАНАВЛИВАЕТСЯ, пока не упрется в границу. как видно из кода, переменная airbrake имеет значения 1 (выпуск тормоза) или -1(уборка тормоза). Оно-то и загнано в строчку applyRotation вместе с величиной разового поворота.
Таким же образом теперь работают практически все детали, кроме основных стоек шасси и гидравлики при этих самых стойках. Хотя с течением времени можно заменить и их. Плохо то, что их - 16 штук на МиГ-23/27, но это меркнет на фоне F-5. Там вообще кошмар. Поэтому я принял решение пока сохранить анимации для такой мелочи, но постепенно по возможности отказаться и от нее.
И пока еще есть нерешенная проблема - со стабилизаторами. Кроме флаперонов, этот вид деталей имеет двойное назначение - они работают синхронно при тангаже и ножницами при крене. Примерно как это решить, я знаю, попробую в ближайшее время с этим справиться...
Помимо войны с анимацией занимался поиском решений по генерации юнитов. Тут впечатления двойственные. С одной стороны кое-как освоил загрузку через json файлы для стартовых установок проперти (которых на двиагтеле почти не осталось - только два таймера). с другой  - хотелось бы иметь к примеру юнит-модель типа МиГ-29 и в папке с ним набор материалов с текстурами разных стран - при генерации присвоить модели нужный материал и вперед. увы. dron, который с некоторых пор натаскивает меня по некоторым аспектам программирования (надо признать, что ученик ему попался туповатый, увы и ах), долго бился над этой проблемой, нашел много чего интересного в бленд-файлах попутно, найти оптимального решения не смог, все это дело можно решить через код на Си плюс, но это уже придется забираться в основы самого кода Блендера. в общем, пока со сменой материалов дело не пойдет. Блендер, как объяснил мне Андрей, не может импортировать независимые от материалов меши (во всяком случае я так понял). Жаль, но отрицательный результат - тоже результат. Попутно dron написал класс для проигрывания звука, к которму я так, к стыду своему по-настоящему еще не подступался - все анимации, блин (хотя гильотина там поработала уже, да (черный юмор)).
В общем пока идет переформатирование уже имеющегося. JSON файлы гораздо лучше читаются и понимаются (в том числе и их создателем, который спустя некоторое время смотрит на свой текстовый файл с палочками и мучительно пытается вспомнить, что означает вот эта четвертая слева цифра). На JSON, скорее всего перейдет вся или почти вся стартовая генерация объектов.
Сама модель создания юнита также претерпела резкие изменения. Замена мешей резкао сократилась. Теперь смена уровней детализации идет путем добавления -удаления родителей с потомками. В принципе, все так и осталось - ведь ранее у меня импортировалась система-скелет для замены мешей, но там тоже были свои группы объектов и они также добавлялись-удалялись. После исчезновения почти всей анимации (шасс в расчет особо можно не брать - оно используется за игру пару раз, а чаще - так и вовсе не используется).отпал смысл в импорте скелета (я не хотед набирать в игровой файл большое количество абсолютно одинаковых анимаций). В общем, новая система ЛОДов - компромисс между продвинутой системой замены мешей, о которой я тут распространялся и системой из первой версии, в которой замены мешей не было. Опять таки цитата:"На всякое придыхательное пыльношлемие мы ответим самым суровым булкохрустом" - разумный консерватизм вполне оправдан. А революции часто или почти всегда заканчиваются контрревоюциями...
Ну вот, хоть одна запись в юле все-таки появилась.

понедельник, 27 июня 2016 г.

И снова о сенсорах...

Так вот получилось, что уже на следующий день после публикации предыдущего поста пишу вновь. Так сказать, мысли вслух...

01_120_60_1.2_1_15 - так выглядит запись в текстовом файле для БРЛС истребителя МиГ-23МЛАЭ2. Для ее расшифровки сначала приведу список проперти юнита - в данном случае - летательного аппарата.
target - "краные" (0) или "синие" (1)
targetScan - тип сканирования - "земля" (1) или "воздух" (0)
sensBRLS - проперти бортовой РЛС (как раз первая строчка из цифр).
sensTP - проперти теплопеленгатора
sensLD - проперти лазерного дальномера
sensTV - проперти телевизионного прицела

А теперь расшифруем символы в трочке, перемежаемые подчеркиваниями.

01 - способность к отслеживанию целей, в данном случае - как воздушных, так и наземных (может быть так, что сенсор приспособлен исключительно для работы по земле или небу - 1 или 0 соответственно).

120 -  дальность действия - 120 км.

60 - угол обзора - в данном случае - 60 градусов

1.2 - пауза между сканирующими импульсами, иначе БГЕ может зависнуть. Величина идет скорее "на глазок, меньше секунды точно не будет.

1 - количество подсвечиваемых целей - означает возможность обстреливать одну или несколько целей сразу ракетами с РЛ ГСН (например МиГ-31 и F-14 долгое время в мире были единственными, способными на такое).

15 - это "мертвая зона"  радара. во многом эта величина взята от балды и нуждается в дополнительной проработке. Пока что мертвая зона вычисляется таким образом - сначала берется высота самого перехватчика, делится на то число и полученный результат сравнивается с текущей высотой цели над данным участком местности (не высотой над нулевой отметкой!!!). Но, скорее всего, придется внести коррективы - не абсолютная высота перехватчика, а относительная, как и у цели. Ну, здесь можно сильно поизвращаться... Главное, чтоб движок выдержал.

Вообще-то "мертвая" зона вводится именно для радара, причем только в режиме прицеливания "воздух". Для ракет же существует своя мертвая зона - по абсолютной максимальной высоте и текущей минимальной высоте над землей. К примеру, старые ракеты типа Р-98 на высоте меньше 2 км уже теряли цель в любом случае, что на фоне земли, что на фоне неба - достаточно было атакуемому "нырнуть" пониже к земле (но в данном случае это было не слишком опасно, от Р-24 увернуться было уже проблематично - требовалось опуститься на высоту ниже 25 метров).
Сейчас идет подгонка формата файлов под новые обстоятельства. Учитывая, что я не слишком сильно заморачивался этим в свое время не успел наплодить кучу юнитов, процесс переделки(доработки) будет не таким сложным, как казалось...