Показаны сообщения с ярлыком 3D models. Показать все сообщения
Показаны сообщения с ярлыком 3D models. Показать все сообщения

суббота, 24 июля 2021 г.

Игра "Морской бой. След торпеды" Альфа-версия.

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


И вот подумалось, а почему бы не попробовать сделать то же самое в БГЕ. Пусть и мертв ныне этот движок, в смысле, не занимаются им больше создатели Блендера, но уж такое-то должен он потянуть.

Сказано- сделано. Альфа версия


Простенькая игра. "Морской бой. след торпеды" Создана в BGE, для работы игры испольуйте Blender 2.79b.Управление - перископ влево-вправо - стрелочки влево-вправо, выстрел - - пробел. При переходе на сцену подведения итогов - выход из игры - стрелочка вниз, новый круг - стрелочка вверх.
Длительность игрового сеанса - 3 минуты, боезапас - 20 торпед. После первого сеанса в разделе "Лучший результат будут нули, затем идет сравнение и результат улучшается в первую очередь по количеству побед за сеанс, во-вторых, если количество текущих побед совпало с лучшим результатом, идет сравнение с количеством израсходованного боезапаса, чем меньше израсходовано торпед на одну цель, тем лучше.

Можете поэкспериментировать с дальностью до генерируемых кораблей - открывайте скрипт Submarine_Control.py и ищите строчку почти в самом конце randomXY = random.randrange(200, 1400). В скобках эта самая дальность, можете расширить ее пределы, но не более 4500, хотя сомневаюсь, что на такой дистанции удастся вообще когда-нибудь попасть. А вот уменьшить, скажем до 800-1000 - улучшит количество попаданий. Отрицательные цифры ставить нельзя, и меньше 100 тоже.

Объем упакованного файла - 49 Мб, распакованного проекта - 74. Запускаемый файл - бленд SeaBattle>blend. Скрипт, о котором я говорил, находится в нем же.

Альфа версия. Только один тип мишени - незатекстуренный, тонет при попадании без всяких дополнительных эффектов, движение слева-направо. Предполагаю следующие изменения/дополнения:
1) Еще пара-тройка моделей кораблей.
2) Все модели с текстурами.
3) Смена текстуры неба случайным образом при старте (для разнообразия).
4) Возможность появления кораблей с другой стороны, но движение все равно будет только в одну сторону (типа конвой в море)
5) Если БГЕ не будет сильно возмущаться - добавить эффект разрушения - пожар на тонущем корабле с дымом.

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

https://drive.google.com/file/d/1cfOjeJLvPIUFGBjxt1e19u2ubN6V5XBh/view?usp=sharing
https://yadi.sk/d/nqPDWcWfxCe93Q


Скрины:

Старт


Сама игра.

Подведение итогов



воскресенье, 4 августа 2019 г.

Четвертая операция.

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

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

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

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

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

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

пятница, 8 сентября 2017 г.

Бюрократия. Ревизионизм. Реанимация. Тестирование. Су-25. И снова про blf - избавление от кракозябр.

Следующим после МиГ-27К к реанимации был назначен Су-25. Работа выдалась вполне рутинной. Сначала. А вот потом вдруг всплыла нерешенная еще в пераой версии проблема - кабина Су-25, которую я перенес во вторую версию, была меньше требуемых размеров. Произошло это из-за ошибки в моделировании Су-25 в первой версии. Тогда я вывернулся с оверлейной сценой и благополусно забыл об этом. во второй версии из-за ряда новых нюансов подобная халява не прокатила и пришлось кабину "увеличивать". А вместе с ней и остальные детали - индикаторы, стрелочки и тд. Также пришлось подгонять названия деталей под единый стандарт и править текст скрипта работы приборов. Попутно выяснил, что можно UV-скроллинг задавать из единого центра, называя имена объектов в скрипте - все равно мешей у них в списке всего один. по-видимому, придется все кабины зачистить от лишней логики работы курвиметров и вообще потихоньку оптимизировать скрипты, перечислив газвания стрелочек в "шапках" модулей. Ну, ладно, не в первый раз оптимизирую.
Пришлось повозиться с json для Су-25. Постоянно выплывала ошибка в строке с данными сенсора. В конце концов мое терпение лопнуло и я ее заменил на строчку из другого json, подкорректировав цифры. Ошибка пропала. Что это было я так и не понял.
Установил в кабине прицел АСП-17, про который я писал уже в своем блоге здесь, но пока толком не тестировал.
Большую работу пришлось проделать с меню. Серией различных ухищрений мне удалось достичь того, что теперь текст может более-менее точно располагаться на кнопке, а не только рядом с ней, он может быть русским или английским, иметь определенный цвет и прозрасность. К сожаленпию, смена текстур в 2.78 была таки сломана, да и видеотекстура теперь работает странно... Так что смена заставок в меню была сделана через замену мешей, зато раотает железно.
Наконец, с подсказки dron-а, были ликвидированы жуткие кракозябры вместо русского текста. Понятно, что во всем была виновата кодировка. преодолевается это так:

 with open(bge.logic.expandPath('//Menu/StartMenu.json'), 'r', encoding = 'utf-8') as directMenu:
        JSONmenu = json.load(directMenu)

В данном случае encoding = 'utf-8' - это убийца кракозябр. Русский текст после этого нормально воспринимается и читается в БГЕ и можно его пропечатать на экране хоть в текстовом объекте, хоть в blf. Кстати о последнем. Я уже писал о выведении текста на экран. У меня работают текстовые метки целей (наконец-то получилось отсечь цели позади активной камеры),  но надо было обязательно сделать меню справку о клавишах и командах. Приводить весь текст json, пожалуй, не буду, он длинный и однообразный. Приведу строчку с разъяснением структуры, хотя, там скорее всего и так будет более-менее поянтно.

"str1":{"az":35,"buki":45,"vediR":1.0,"vediG":1.0,"vediB":1.0,"vediA":1.0,"strX":0.01,"strY":0.975,
        "textRus":"Команды","textEng":"Option"},
"str2":{"az":35,"buki":45,"vediR":1.0,"vediG":1.0,"vediB":1.0,"vediA":1.0,"strX":0.01,"strY":0.95,
        "textRus":"+ - увеличение тяги двигателя","textEng":"+ - Engine power plus"}

Хотя нет, не все. Тут еще дело упирается в мой специфический юмор. Недолго думя, я обозвал переменные сами видите как. az и buki - это размер текста. vedi с заглавными буквами - это цвет и прозрасность текста - RGBA, strX-Y- координаты начала строчки на экране. Далее понятно - русский и английский текст. Дело в том, что я постепенно наращивал число ключей в словаре json, не зная толком, что понадобится в работе - в итоге вот так и получилось. Хотя можно и упростить.
Как бы то ни было, меню стало более вменяемым и происходит его сборка-пересборка при смене разделов. у меня не было ни малейшего желания громоздить кучу сцен ради меню, надеюсь, удастся итоговую сцену тоже не делать, а возвращаться в сцену с меню.
И да, наконец удалось ввести в сцену террайна наземную цель - бункер, взаимодействие которого при попадании оружия еще надо как-то отработать - обрушение, разлет обломков там...
Пока меню имеет пять миссий - для МиГ-23МФ и МиГ-29  это перехват Ф-16 и Ф-15 соответственно. Для Су-25, МиГ-23БН и МиГ-27К  я противников из воздуха убрал, но оставил в качестве цели бункер. Надо попробовать ввести МЗА и СЗРК. В первой версии МЗА "Эрликон" был, надо перетаскивать. Плюс "Стрела-1" и БРДМ-2, но их текстурить надо. Когда-то я сделал модель БМП-1. Ее надо избавить от высокополигональных катков и гусениц, посмотреть, что можно упростить и туда же, тем более, она была затекстурена...
В общем, продолжим...
Ниже скрины - нельзя же совсем ез картинок - Су-25 и его кабина. Подвешены по две пары блоков С-8 и бомб ФАБ-250, плюс пара ракет Р-60М.


суббота, 24 июня 2017 г.

Цветомузыка СПО. Возрождение МиГ-29. Код и картинки...

После успешного завершения работы над кабиной МиГ-23 и восстановлением систем РЭБ, прицеливания и наведения встал вопрос о восстановлении кабины МиГ-29. Кроме него надо было привести под общий стандарт F-5E и F-15C. Дабы оппоненты были более разнообразными. Это было проделано, правда половинчато. У "Тайгера" надо дописывать скрипт псевдоанимаций шасси (что меня не приводит в восторг из-за его сложности, точнее, многословности). Для МиГ-29 были сменены "шкурки" камуфляжа, а потом еще и кабины.

Дело в том, что Блендер начал ругаться на потерю способа компрессии dds, как я это понял из сообщений консоли. По совету Андрея (aka dron) я применил компрессию типа dxt1 после установки аддона для dds в ГИМПе, причем конверитровал картинки типа png. Пока ругань консоли ограничивается моими ошибками в коде, что радует. В сущности, кабина МиГ-29 особо не изменилась, если смотреть на текстуры. А вот ИЛС, работа приборов и СПО претерпели некоторые изменения. Особенно много возни было с системой предупреждения об облучении СПО-15. В отличие от СПО-10 в ней на порядок больше ламп и гореть должны только нужные.Первая версия СПО-15 двухлетней давности, если мнен не изменяет память, была мною здесь выложена, она длинная и выполнена методом "против лома нет приема" с помощью "дао дятла" - методичное построчное перебирание ламп, пусть и с некоторыми исключениями части кода с помощью условий. Меня не радовала перспектива  повторить сей подвиг, тем более, что вводные данные изменились и перебирать строчки в поисках ненужных - да ну на фиг...
Вместо этого я обратился к спискам и сравнению списков. Я разбил имена объектов или сами объекты по спискам, к примеру лампы дистанции до угрозы, лампы вектора на угрозу, и тд. Некоторые списки содержат в именах объектв для сравнения например LampThreat60km. смотрим на последние символы в названии (в действительности оно немного другное, но суть та же). Режем имя методом split, чтобы "вышелушить" число 60. Чтобы строку превратить в число применяем int и умножаем на 1000. Все. Если в данных дистанция до угрозы больше 60000, зажигаем лампу с этим названием, если меньше - гасим.
С вектором на угрозу пришлось поступить хитрее. Кроме списка ламп для индикации вектора на угрозу был создан список,э-э, секторов угрозы вида  [[-0.1,0.1],[-0.5,0.5]],[опять цифры во вложенном списке]. Список содержит примерно 10 элементов, в которых есть два вложенных списка - это вектор на угрозу справа и слева и вектор на угрозу спереди и сзади. На входе получается вектор на угрозу и проверяется на "укладывание" в некоторые условия, заданные этим списком с квадратными скобочками. Если данные вектора ПОЛНОСТЬЮ соответствуют вложенным спискам, то лампа горит, иначе - гаснет А прикол в том, что список секторов угрозы по последовательности соответствует списку лампочек. Все делает цикл. Ламп примерно дюжина, так что БГЕ несильно напрягается, если учесть, что СПО работает раз в пару секунд. В итоге код резко "съежился".
А теперь код для СПО-15. На лишние списки внимания не обращать - они для других функций.
 import bge
import random
import mathutils
import  CONTROL_gamer

A_X = 0
#Далее идет блок клавиатурных команд
keyboard = bge.logic.keyboard

JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
INPUT_ACTIVE = bge.logic.KX_INPUT_ACTIVE 

scene = bge.logic.getCurrentScene()
sceneCockpit = scene

pitchListDigital = [scene.objects["pitchILS0"],
                    scene.objects["pitchILS1"]]
           

spo15lampGrad = ["SPO-15grad10R","SPO-15grad30R","SPO-15grad50R","SPO-15grad90R",
                 "SPO-15grad10L","SPO-15grad30L","SPO-15grad50L","SPO-15grad90L",
                 "SPO-15LampLzps","SPO-15LampRzps","SPO-15PPS"]

spo15lampGreen = ["SPO-15green10R","SPO-15green30R","SPO-15green50R","SPO-15green90R",
                 "SPO-15green10L","SPO-15green30L","SPO-15green50L","SPO-15green90L",
                 "SPO-15greenLzps","SPO-15greenRzps","SPO-15PPS"]
                

spo15Grad = [[[0.01, 0.1],[0.0, 1.1]],[[0.1, 0.3],[0.0, 1.1]],[[0.3, 0.55],[0.0, 1.1]],[[0.55, 1.0],[0.0, 1.1]],
             [[-0.1, -0.01],[0.0, 1.1]],[[-0.3, -0.1],[0.0, 1.1]],[[-0.55, -0.3],[0.0, 1.1]],[[-1.0, -0.55],[0.0, 1.1]],
             [[-1.0, 0.0],[-1.1, 0.0]],[[0.0, 1.0],[-1.1, 0.0]],[[-0.01, 0.01],[0.0, 1.1]]]

spo15lampDist = ["SPO-15Lamp65km","SPO-15Lamp60km","SPO-15Lamp55km","SPO-15Lamp50km",
                 "SPO-15Lamp45km","SPO-15Lamp40km","SPO-15Lamp35km","SPO-15Lamp30km",
                 "SPO-15Lamp25km","SPO-15Lamp20km","SPO-15Lamp15km","SPO-15Lamp10km",
                 "SPO-15Lamp5km","SPO-15Lamp1km"]
                
spo15typeThreat = ["SPO-15AirObj","SPO-15ZRKBD","SPO-15ZRKSD","SPO-15ZRKMD","SPO-15DRLOW","SPO-15DRLOZ"]
                

textFuel = [scene.objects["fuel0"],
            scene.objects["fuel1"],
            scene.objects["fuel2"],
            scene.objects["fuel3"]]

textSpeedTarget = [scene.objects["targetSpeed0"],
                   scene.objects["targetSpeed1"],
                   scene.objects["targetSpeed2"],
                   scene.objects["targetSpeed3"]]
           
textHeightTarget = [scene.objects["targetHeight0"],
                    scene.objects["targetHeight1"],
                    scene.objects["targetHeight2"],
                    scene.objects["targetHeight3"],
                    scene.objects["targetHeight4"]]
                   
textSpeedOwn = [scene.objects["ownSpeed0"],
                scene.objects["ownSpeed1"],
                scene.objects["ownSpeed2"],
                scene.objects["ownSpeed3"]]
           
textHeightOwn = [scene.objects["ownHeight0"],
                 scene.objects["ownHeight1"],
                 scene.objects["ownHeight2"],
                 scene.objects["ownHeight3"],
                 scene.objects["ownHeight4"]]


def spo15(own):
   
    """ 
    bge.logic.globalDict['SPODATA'][0] = own.getDistanceTo(threat)
    bge.logic.globalDict['SPODATA'][1] = threat.LockOn
    bge.logic.globalDict['SPODATA'][2] = threat.PR
    bge.logic.globalDict['SPODATA'][3] = threat.avto
    bge.logic.globalDict['SPODATA'][4] = own.worldPosition[2] - threat.worldPosition[2]
    bge.logic.globalDict['SPODATA'][5] = own.getVectTo(threat)[2][0]
    bge.logic.globalDict['SPODATA'][6] = own.getVectTo(threat)[2][1]
    bge.logic.globalDict['SPODATA'][7] = threat.unitName
    bge.logic.globalDict['SPODATA'][8] = threat.subTypeUnit
    """
   
    if bge.logic.globalDict['SPODATA'] != [0.0, 0, 0, 0, 0.0, 0.0, 0.0, "", ""]:
        for element in spo15Grad:
            #print(bge.logic.globalDict['SPODATA'][5],bge.logic.globalDict['SPODATA'][6])
            if element[0][0] < bge.logic.globalDict['SPODATA'][5] < element[0][1] and element[1][0] < bge.logic.globalDict['SPODATA'][6] < element[1][1]:
                scene.objects[spo15lampGrad[spo15Grad.index(element)]].visible = 1
                if bge.logic.globalDict['SPODATA'][2] == 1:
                    scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 1
                else:
                    scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 0
               
            else:
                scene.objects[spo15lampGreen[spo15Grad.index(element)]].visible = 0
                scene.objects[spo15lampGrad[spo15Grad.index(element)]].visible = 0
       
        CONTROL_gamer.SPOaudio()
       
        #Распознавание типа угрозы
        for typeThreat in spo15typeThreat:
            if bge.logic.globalDict['SPODATA'][8] in typeThreat:
                scene.objects[typeThreat].visible = 1
            else:
                scene.objects[typeThreat].visible = 0
       
        if bge.logic.globalDict['SPODATA'][1] == 1:
            scene.objects["SPO-15alarm"].visible = 1
            if bge.logic.globalDict['SPODATA'][4] > 0:
                scene.objects["SPO-15alarmUp"].visible = 1
                scene.objects["SPO-15alarmDown"].visible = 0
            else:
                scene.objects["SPO-15alarmUp"].visible = 0
                scene.objects["SPO-15alarmDown"].visible = 1
        else:
            scene.objects["SPO-15alarm"].visible = 0
            scene.objects["SPO-15alarmDown"].visible = 0
            scene.objects["SPO-15alarmUp"].visible = 0
           
        #Дистанция до угрозы
        for distObj in spo15lampDist:
            #if distObj in scene.objects:
               
                if int(distObj.split("SPO-15Lamp")[1].split("km")[0])*1000 < bge.logic.globalDict['SPODATA'][0]:
                    scene.objects[distObj].visible = 1
                    #print(scene.objects[distObj].visible)
                elif int(distObj.split("SPO-15Lamp")[1].split("km")[0])*1000 > bge.logic.globalDict['SPODATA'][0]:
                    scene.objects[distObj].visible = 0

    #При обнулении угрозы все лампы выключаются
    else:
        scene.objects["SPO-15alarm"].visible = 0
        scene.objects["SPO-15alarmDown"].visible = 0
        scene.objects["SPO-15alarmUp"].visible = 0
       
        for obj in spo15lampGrad:
            scene.objects[obj].visible = 0
        for obj in spo15lampGreen:
            scene.objects[obj].visible = 0
        for obj in spo15lampDist:
            scene.objects[obj].visible = 0
        for obj in spo15typeThreat:
            scene.objects[obj].visible = 0
           
Ободренный успехом с СПО-15, я принялся за ИЛС. В сущности, досточно стандартный набор вращений, замены мешей и так далее.

Вот только элементов много. Для цифр со сменой мешей пришлось ввести ограничение по исполнению - раз в 19 тиков, чтобы не утруждать БГЕ.

Из нововведений отмечу отклонение вверх вниз в некотором диапазоне указателя тангажа на ИЛС относительно линии горизонта, что показывает, вверх или вниз отклонен нос самолета. Кроме того рядом с линией горизонта на ИЛС появился цифровой указатель тангажа, замеряющий оный в пределах от минус до плюс 15 градусов. Также был введен более реалистичный монитор радара. По сути - это дубль ИЛС с индикатором крена и бегающей по экрану меткой цели. Круговой радар я делать непосредственно в кабинах не буду - нет смысла. Хватит и карты обстановки (которую тоже надо делать).
В итоге Ф-15 стали уверенно поражаться ракетами Р-27Р, СПО весело мигает лампочками и начинает орать при малейшем поводе, цифры на ИЛС мелькают, стрелочки крутятся, пушка тарахтит, ловушки отстреливаются с шипением и дымом...
Как общий результат - получена вторая более менее работоспособная миссия, отреставрирован МиГ-29 и не только он, изничтожено некоторое количество багов и получен задел на будущее. Дело в том, что СПО-15 стоит на МиГ-23МЛД, Су-33, Су-27. Су-25, Су-17М4, МиГ-27М/Д/К, а ИЛС МиГ-29 идентична ИЛС Су-27 и части его модификаций, вроде того же Су-33...

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

Теория классовой борьбы-2. Метод мавра.

В прошлом посте я делился новообретенными знаниями о создании класса и изощрялся в раздаче данных в этом самом классе. И все бы хорошо - все работало, самолет летал, пускались ракеты, работал кокпит... Словом все было замечательно. Однако тут  при добавлении оппонентов вышла неувязочка. Сначала я к своему удивлению, обнаружил упорное нежелание модели МиГ-23МФ добавлять высокодетализированные меши. Погтом вдруг обнаружилось, что кабины нет и управления, кстати, тоже нет. И при всем этом консоль упорно выдавало отсутствие какой-либо ошибки. Еще раз вспомнив бессметрное изречение O.din13: "Боендер не тупит - тупят люди", я приступил к исследованию сего бага. В конце концов, пришлось признать, что дал маху. И весьма ощутимого. Ошибка была в том, что в БГЕ создается ОДИН класс на ВСЕ юниты типа "летательный аппарат". Но при этом его данные меняются в масштабах ВСЕЙ сцены, а не одного конкретного объекта. В итоге вместо пары МиГ-23МФ и пары Ф-16 мы получаем 4 самолета одного типа - чья сточка с данными будет прочитана последней. И это был Ф-16. Но вот добавление "основы" юнита у меня идет независимо от значения в классе, в итоге получаем Ф-16 с нормально работающей моделью и Миг-23 с ЛОД-основой, но тоже вроде как Ф-16! Прикол, ничего не скажешь...
Пришлось некоторое время поломать голову. В итоге, следуя дао дятла и с остервнением пробившись сквозь дебри своего же кода, получил работающую модель. Надо заметить, что у меня идет подключение и использование скриптов-модулей в некоем "коммутаторе". Чтобы совсем уж не ломать существующую структуру с неясным исходом, я применил старый испытанный прием - "против лома нет  приема".

Класс у меня стал простым и суровым:
import bge

class air(bge.types.KX_GameObject):
   
    def __init__(self, old_owner):
       
        self.nameIndex = str(id(self))
        self.WINGS = -1
        self.Temp_WINGS = -1
        self.FLAPS = 0
        self.Temp_FLAPS = 0
        self.SLATS = 0
        self.Temp_SLATS = 0
        self.CANOPY = 0
        self.Temp_CANOPY = 0
        self.AIRBRAKE = 0
        self.Temp_AIRBRAKE = 0
        self.CHASSY = 0
        self.Temp_CHASSY = 0
        self.ROLL = 0
        self.Temp_ROLL = 0
        self.YAW = 0
        self.Temp_YAW = 0
        self.PITCH = 0
        self.Temp_PITCH = 0
        self.ROLLwings = 0
        self.Temp_ROLLwings = 0
        self.avto = 0
        self.Temp_avto = 0
        self.PR = 0
        self.Temp_PR = 0
        self.LockOn = 0
        self.enemy = 0
        self.weapon = -1
        self.Temp_weapon = -1
        self.crash = 1.0
        self.Temp_crash = 1.0
        self.correctSpeedAngleOfAttack = 0.0
        self.levelsDetails = 3
        self.Temp_levelsDetails = 3
        self.angleOfAttack = 0.0
        self.ownType = 0
        self.sensEyesscanTimer = 300
        self.rotatX = 0.0
        self.rotatY = 0.0
        self.rotatZ = 0.0
        self.rollSelf = 0
        self.pitchSelf = 0
        self.yawSelf = 0
        self.antiForce = 0.0
        self.massFuel = 0.0
        self.massChild = 0.0
        self.massFuelTank = 0.0
        self.ownAntiForce = 0.0001
        self.lovushki = 0
        self.correctRotat = 1.0
        self.correctSpeed = 1.0
        self.slideRotat = 0.0
        self.speedTemp = 0.0
        self.typeShtopor = "NULL"
        self.Temp_BK = 0
        self.BK = 0
        self.typeSensor = 0
        self.targetType = 0
        self.timerImpulse = 0
        self.sonicBoom = 0
        self.idTarget = []
        self.PuskSbros = 0
        self.Temp_PuskSbros = 0
        self.indexRadarDist = 0
        self.timerShoot = 0
        self.classWeapon = ""
        self.nameWeapon = ""
        self.Temp_typeSensor = 0
        self.localTargetList = []
        self.threatWeapon = []
        self.sbrosInterval = 0
        self.ochered = 0
        self.Temp_ochred = 0
        self.dictWeapon = {}
       
        self.controlUnit = ""
        self.keyDict = ""
        self.bortNumber = 0
        self.target = -1
       
        self.typeMissions = ""
        self.Marshrut = []
        self.ownGroup = ""
        self.targetGroup = []
        self.escortGroup = []
        self.statusDict = {}
        self.ownBase = ""
        self.levelsSkill = 100
               
        self.THREAT = {"UnitAir":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]},
                       "UnitGround":{'RL':[],'TP':[],'LD':[],'RD':[],'TV':[],'MG':[],'SN':[],'EL':[]},
                       "tnreatWeapon":[]}
       
        self.unitName = ""
        self.unitNation = ""
        self.unitModule = ""
        self.speedMax = 0.0
        self.heightMax = 0
        self.speedVert = 0
        self.startSpeed = 1.1
        self.limitRoll = 1.1
        self.limitPitch = 1.1
        self.alarmAngleOfAttack = 0.0
        self.globalRotatPitch = 0.005
        self.speedStall = 0.0
        self.angX = 0.0
        self.angY = 0.0
        self.angZ = 0.0
        self.rotatDyn = 0.0
        self.dvigDyn = 1
        self.MaxPower = 0.0
        self.maxFuelSpeed = 0.0
        self.massFuelOwn = 0.0
        self.massOwn = 0.0
        self.antiECM = 0.0
        self.stealth = 0.7
        self.maxVisibleDist = 0
        self.ownECM = 0.0
        self.lovushki = 0
        self.Temp_lovushki = 0
        self.sensorList = ["Eyes"]
        self.WINGS = -1
        self.Temp_WINGS = -1
        self.dvig = 0
        self.dvigRight = 0
        self.dvigLeft = 0
        self.Temp_dvig = 0
        self.Temp_dvigRight = 0
        self.Temp_dvigLeft = 0
        
        self.etalonDvig = 0
        self.typeManeur = ""
        self.bot = 0           
             
      
def mutate(cont):
    air(cont.owner)

В нем происходит появление всех атрибутов с исходными значениями. Сообсветственно, все юниты при рождении - братья-близнецы. Потом они получают свои качества...
Повозившись с открытиями-закрытиями json, я махнул рукой и стал при появлении юнитов цеплять к ним 2репер", хотя его можно назвать и "ярлыком" (на княжение, стало быть, угу). В этом ярлыке командой типа yarlyk["startDict"]= {} я создавал стартовый словарь и ссыпал туда из json-ов данные. Не абы как, чтобы потом можно было разобраться. В итоге юнит уже имел потомка при появлении, а впотомке было записано все, что нужно. Мне пришлось ввести еще пару бриков для стартовой инициализации юнита - разовая операция по чтению словаря потомка приводила к появлению у объектов одного класса разного набора характеристик. Ничего сложного - просто используем setattr или __dict__.
#Сборка атрибутов юнита
def startInit():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    for key in own.childrenRecursive["confaUnit"]["confaUnit"]["startProp"]:
        setattr(own, key, own.childrenRecursive["confaUnit"]["confaUnit"]["startProp"][key]) 
   
    setattr(ArbitrGame, own.keyDict, own.childrenRecursive["confaUnit"]["confaUnit"])
   
    with open(bge.logic.expandPath(own.childrenRecursive["confaUnit"]["confaUnit"]["unitPath"]), 'r') as directClass:
        JSONtechnic = json.load(directClass)
        for key in JSONtechnic["listPropertys"]:
            setattr(own, key, JSONtechnic["listPropertys"][key])
            #print(getattr(own, key))
   
    if own.controlUnit == "Gamer":
        gamerConfig = ["a_x","a_y","a_z","Temp_a_x","Temp_a_y","Temp_a_z"]
        for key in gamerConfig:
            setattr(own, key, 0.0)
           
        setattr(own, "timerFuel", 0)
        setattr(own, "colorHUD", 0) 
           
        sensor1 = scene.objects['SENSOR1']
        sensor2 = scene.objects['SENSOR2']
        sensor3 = scene.objects['SENSOR3']
        #Расстановка сенсоров пустышек для отслеживания ориентации в пространстве
        sensor1.setParent(own, False,False)
        sensor1.worldOrientation = own.worldOrientation
        sensor1.localPosition = [0.0, 1.0, 0.0]
        sensor2.setParent(own, False,False)
        sensor2.worldOrientation = own.worldOrientation
        sensor2.localPosition = [1.0, 0.0, 0.0]
        sensor3.setParent(own, False,False)
        sensor3.worldOrientation = own.worldOrientation
        sensor3.localPosition = [0.0, 0.0, 1.0]

        if 'Cockpit' not in bge.logic.getSceneList():
            bge.logic.addScene('Cockpit',1)
            unit = own.unitName + own.unitNation
            typeCockpit = "//Aircraft/" + unit + "/Cockpit_" + unit + "/Cockpit_" + unit + ".blend"
            bge.logic.globalDict["cockpitPath"] = typeCockpit
   
    elif own.controlUnit == "Statist":
        own.ownType = 1
   
    #Убирем более ненужный реперный объект
    own.childrenRecursive["confaUnit"].endObject()              
   
    #Обязательно заносим юнит в списки "лагерей", сортируя по ходу дела
    ArbitrGame.UNITS[1].append(own)
    ArbitrGame.UNITS[0][own.ownType][own.target-1].append(own)  
    #print(ArbitrGame.UNITS)
   
Судьба репеного объекта соответствует известному изречению:"Мавр сделал свое дело..."
Такая вот поправочка вышла.
Кстати, аналогично я поступил и с оружием. Мне только не хватало, чтобы пущенная мною Р-24Р вдруг трансформировалась в "Сайдуиндер".
Сейчас идет возня с оживлением ботов - с их ИИ и возвращению сенсоров. с сенсорами придется попотеть - во-первых, изменились некоторые названия, во-вторых, где-то скрывается ошибка, возможно, опечатка, сейчас я переписываю скрипт, заодно убирая ставшие ненужными моменты.
Удалось также отладить камеру с ее переключениями - имеется в виду внешний обзор, а также восставноить текстовую маркировку юнитов - по их названиям, лагерю и дистанции.


вторник, 20 декабря 2016 г.

Освоение новых возможностей. Работа с blf.

В первой версии была у меня система текстовых меток и подсказок. И хотя результат меня худо-бедно устраивал, наличие большого количества дополнительных объектов и второй оверлейной сцены меня несколько напрягало.Но тогда у меня было стремление любой ценой довести проект хотя бы до промежуточного результат и я не стал искать других путей. А они были, между прочим...
Есть в составе инструментов Блендера весьма полезная функция blf, правда, чтобы ей пользоваться, никак не обойтись без дао обезъяны, а именно - для начала иметь хоть какой-то исходный код, который можно сплагиатить, извратить переработать. Такой код с выводом текст а"Хелло Ворлд" в АПИ Блендера имелся.
Однако тут воспоследовали проблемы. Сам текст, понятно, как сформировать, но надо его запихнуть в нужное место экрана, а потом еще и дать ему нужный цвет для удобоваримого чтения. После трехдневной возни с подсказками dron-a, на свет божий явился промежуточный результат. Скрин приводится ниже.



На ИЛС присутствует надпись F-16_цифры. Синего цвета. Это говорит о том, что радар взял на сопровождение F-16C "синих", плюс до него указывается дистанция. В перспективе охота получить возможность маркировать все цели в округе, как свои, так и чужие, плюс иметь возможность отключать или включать это маркирование. Пока вариант весьма сырой, хотя и работающий, поэтому приведу код. Часть первая - скрипт ClassHud с функциями чтения и отсчета экранных координат цели с возможностью вывода положения метки в пределах ИЛС.

import bge

#МЕТКИ ЦЕЛИ НА КОЛЛИМАТОРЕ - ОБЕСПЕЧИВАЕТСЯ ЕЕ ПЛАВНОЕ ПЕРЕМЕЩЕНИЕ ВСЛЕД ЗА ЦЕЛЬЮ  
#Чтение экранных координат    
def readCoord():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    camera = bge.logic.getCurrentScene().active_camera
   
    targetMarka = scene.objects['targetMetka']
    #collimatorRight = scene.objects['collRight']
    #collimatorLeft = scene.objects['collLeft']
    targetLabel = scene.objects['targetMarka']
   
    camera = scene.active_camera
   
    #Метка цели работает только при включенном радаре и выставленной дальности
    try:
       
        targetMarka.worldPosition[0] = bge.logic.globalDict['targetCoord'][0] * 20.0
        targetMarka.worldPosition[1] = -bge.logic.globalDict['targetCoord'][1] * 11.3
       
       
        plusX = targetLabel['limX'] #collimatorRight.worldPosition[0]
        minusX = -targetLabel['limX'] #collimatorLeft.worldPosition[0]
        plusY = targetLabel['limY'] #collimatorRight.worldPosition[2]
        minusY = -targetLabel['limY'] #collimatorLeft.worldPosition[2]
       
        targetLabel.worldPosition[0] = targetMarka.worldPosition[0] - 10.0
        targetLabel.worldPosition[2] = targetMarka.worldPosition[1] + 5.65
       
        if targetLabel.worldPosition[0] < minusX:
            targetLabel.worldPosition[0] = minusX
        elif targetLabel.worldPosition[0] > plusX:
            targetLabel.worldPosition[0] = plusX
        if targetLabel.worldPosition[2] < minusY:
            targetLabel.worldPosition[2] = minusY
        elif targetLabel.worldPosition[2] > plusY:
            targetLabel.worldPosition[2] = plusY
    
    except:
        targetLabel.worldPosition = [0.0, 0.0, 0.0]
        
    import RenderText
    RenderText.init()
   
       
#Передача экранных координат   
def writeCoord():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner

    try:
        target = scene.objects.from_id(int(own['idTarget']))
       
        camera = scene.active_camera

        #if camera.getVectTo(target)[2][2] > 0:
        bge.logic.globalDict['targetCoord'] = camera.getScreenPosition(target)
        #print(target['unitName'], bge.logic.globalDict['targetCoord'])
       
      
        winXcoord = bge.render.getWindowWidth(target)*camera.getScreenPosition(target)[0]
        winYcoord = bge.render.getWindowHeight(target)*camera.getScreenPosition(target)[1]
        infoTarget = target['unitName'] + str(int(own.getDistanceTo(target)/1000))
        bge.logic.globalDict['targetPixel'] = [winXcoord, winYcoord, infoTarget]
       
        targetDistance = own.getDistanceTo(target)
        targetSpeed = target.localLinearVelocity[1]
        targetHeight = target.worldPosition[2]
       
        bge.logic.globalDict['targetData'] = [targetDistance, targetSpeed, targetHeight]
       
    except:
       
        bge.logic.globalDict['targetPixel'] = [0, 0, '']
        bge.logic.globalDict['targetCoord'] = [0, 0]
        bge.logic.globalDict['targetData'] = [0, 0, 0]
       


И второй скрипт - тот самый, для отрисовки текста. Изменения коснулись прежде всего вывода самого текста и его положения на экране. Думаю, здесь понятно, откуда берутся данные - из глобального словаря. В тексте присутствуют следы неудачных проб и ошибок в виде неиспользуемых переменных. Скрипт будет подвергаться дальнейшей доработке.

# import game engine modules
from bge import render
from bge import logic
# import stand alone modules
import bgl
import blf

winXcoord = logic.globalDict['targetPixel'][0]
winYcoord = logic.globalDict['targetPixel'][1]
infoText = logic.globalDict['targetPixel'][2]


def init():
    logic.font_id = 0 #blf.load(font_path)
    scene = logic.getCurrentScene()
    scene.post_draw = [write]
    #print(logic.globalDict['targetPixel'][2])
   

def write():
    width = render.getWindowWidth()
    height = render.getWindowHeight()
  
   
    bgl.glMatrixMode(bgl.GL_PROJECTION)
    bgl.glLoadIdentity()
    bgl.gluOrtho2D(0, width, 0, height)
    bgl.glMatrixMode(bgl.GL_MODELVIEW)
    bgl.glLoadIdentity()
    
    font_id = logic.font_id
    blf.position(font_id, logic.globalDict['targetPixel'][0], logic.globalDict['targetPixel'][1], 0)
    blf.size(font_id, 25, 36)
   
    bgl.glColor3f(0.0,0.0,1.0)
    #bgl.glColor4f
   
    bgl.glDisable(bgl.GL_LIGHTING)
    blf.draw(font_id, logic.globalDict['targetPixel'][2])
   
   
   

вторник, 13 декабря 2016 г.

Цветомузыка СПО.

СПО - сокращение от "Системы Предупреждения об облучении". Ныне эти системы весьма распространены, в том числе и среди автолюбителей, называясь "антирадаром" (чтобы заранее узнать, где сидит злой гаишник, готовый выписать штраф за превышение скорости)...
На самолетах военных же СПО предназначена для идентификации угрозы со стороны вражеских машин, использующих радары. И именно радары - именно на них реагирует СПО. Впрочем, также успешно СПО реагирует на облучение радаров ЗРК, если известна их частота 9в первые дни войны Судного Дня израильтянам здорово досталось от "Кубов",  частоты работы которых, в отличие от ЗРК С-75 и С-125, израильтянам известны не были и СПО их самолетов на работу станций ЗРК "Куб" не реагировали).
Так что СПО не спасет от внезапного выстрела из ПЗРК или неожиданно пущенной в хвост ракеты с ТГСН, которую враг запустил, используя теплопеленгатор. Но вот внезапного пуска ракеты с радийной головкой самонаведения не выйдет в принципе, если СПО зафиксировала работу радара. Причем радар выдает себя на гораздо большем удалении, нежели может охватить - отраженные импульсы с увеличением дистанции теряют силу,  не доходя до приемника, а вот излученные импульсы вполне способны СПО "потревожить" на дистанции, с которой противник просто не сможет принять отреженный сигнал.
все это - небольшое лирико-техническое отступление. В игре СПО только одна - и находится она у самолета игрока, точнее, на оверлейной сцене кабины. Ее единственное назначение - сообщить игроку об угрозе со стороны противника. Причем во второй версии СПО угрозы селектирует по степени опасности. тут играет свою роль не только дистанция до противника, но и его готовность открыть огонь или уже выпустившего ракету.
Ниже приведен скрин работающей СПО-10 "Сирена-3" в кабине истребителя МиГ-23МФ.
 Панель СПО - в белом прямоугольнике.


 Устройство включает в себя по четыре жельые и красные лампы, расположенных по периферии круглой панели и одну большую красную лампу посередине на изображении самолета. На скрине она потушена. Горят одна жельая лампа (правый передний квадрант - угроза спереди справа) и четыре красные лампы - показываюшие расстояние до источника угрозы - в пределах 20-30 км.
Мне неизвестно точно в деталях, как работает СПО-10, поэтому в соответствии работы красных лампочек я не уверен - тут я исходил из индикации работы СПО-15, на которой имеется ряд лампочек, отвечающих за отображение  дистанции до угрозы. Да и индикация СПО-15 тоже куда как более продвинута, о ней я напишу потом, когда до нее доберусь.
Пришлось повозиться со звуками, потому что СПО выдает не только световые, но и звукаовые сигналы земмером, меняющим тон и частоту звучания в соответствии с обстановкой.
А теперь немного кода.Сначала я дописал небольшую функцию для передачи в кабину данных об угрозах.

def SPODATA(own, target):
 
    indexOwn = str(id(own))                                        #Индекс перехватчика
    #indexTarget = str(id(target))                                  #Индекс цели
    distance = own.getDistanceTo(target)/1000                #Дистанция от источника угрозы
    LockOn = own['LockOn']                                   #Режим работы сенсора - облучение, захват, подсветка
    typeSensor = own['localDict']['sensList'][own['typeSensor']]   #Тип сенсора
    targetWectOwn = target.getVectTo(own)[2]                       #Ориентация цели относительно перехватчика
    #ownVectTarget = own.getVectTo(target)[2]                        #Обратная ориентация
    heigtUpDown = own.worldPosition[2] - target.worldPosition[2]   #Показатель превышения-принижения перехватчика перед целью
    PR = own['PR']
    AVTO = own['avto']
   
    #Список харакетирисимкм угрозыдля работы СПО оверлейной сцены
    SPODATA = [indexOwn, AVTO, PR, LockOn, distance, typeSensor, targetWectOwn, heigtUpDown]
   
    #Глобальный словарь
   
    bge.logic.globalDict['SPODATA'].append(SPODATA)

Думаю, в комментариях к коду понятно, ради чего все это нужно. Через определенные промежутки времени идет обнуление данных - многомерный список "пустеет", чтобы не засорять память. А "на том конце провода" идет селекция угроз из списка и работает индикация.  Звкеи работают в другом модуле, его я приводить не буду, поскольку ЕМНИП, я уже выкладывал тут его код. Саму функцию выбора звука я чуть доработал, но принцип все тот же - выбирается актуатор по НАЗВАНИЮ, задаваемый проперти audioProp.
Ну, в этой функции и задана работа ламп индикации по принципу "видно- не видно". СПО-10 система относительно простая, даже примитивная, гораздо сложнее ее СПО-15, правда, там индикация более подробная.
Итак, код работы СПО-10.

#SPODATA = [indexOwn, AVTO, PR, LockOn, distance, typeSensor, targetWectOwn, heigtUpDown]

def spo10():   
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
   
    #Это списки по степени угрозы
    listAVTO = []
    listPR = []
    listLockOn = []
    listNotThreat = []
   
    SelectThreatList = bge.logic.globalDict['SPODATA']
   
    #Список ламп СПО-10
    quad1 = scene.objects['SPOquad1']
    quad2 = scene.objects['SPOquad2']
    quad3 = scene.objects['SPOquad3']
    quad4 = scene.objects['SPOquad4']
    SPOalarm = scene.objects['SPOalarm']
    SPOdist5km = scene.objects['SPOdist5km']
    SPOdist10km = scene.objects['SPOdist10km']
    SPOdist15km = scene.objects['SPOdist15km']
    SPOdist20km = scene.objects['SPOdist20km']
   
    Cockpit = scene.objects['Cockpit']
   
    lampQuadrant = [quad1,quad2,quad3,quad4]
    lampDistance = [SPOdist5km,SPOdist10km,SPOdist15km,SPOdist20km]
   
    SPODATA = bge.logic.globalDict['SPODATA']
   
    #Если есть что  обрабатывать
    if len(SPODATA) > 0:
        for threat in SPODATA:
           
            #Если просто облучают
            if threat[1] == 0:
                listNotThreat.append(threat)
           
            #Если взяли на сопровождение
            elif threat[1] == 1:
                listNotAVTO.append(threat)
               
                #Если смаолет игрока захвачен и противник готов применить оружие
                if threat[2] == 1:
                    listPR.append(threat)
               
                #Если противник уже выпустил ракету и подсвечивает ее
                if threat[3] == 1:
                    listLockOn.append(threat)
                   
    #Теперь селектируем приоритетную угрозу в списке SelectThreatList                  
    #Идет сортировка по степени угрозы, выбирается сначала самая опасная
    if len(listLockOn) > 0:
        SelectThreatList = sorted(listLockOn, key = lambda obj:obj[4])
    else: 
        if len(listPR) > 0:
            SelectThreatList = sorted(listPR, key = lambda obj:obj[4])
        else: 
            if len(listAVTO) > 0:
                SelectThreatList = sorted(listAVTO, key = lambda obj:obj[4])
            else: 
                if len(listNotThreat) > 0:
                    SelectThreatList = sorted(listNotThreat, key = lambda obj:obj[4])
                else: 
                    SelectThreatList = []  
   
    #Если угрозы отсутствую, гасим все лампы на панели СПО       
    if len(SelectThreatList) == 0:
        for lampQuadrants in lampQuadrant:
            lampQuadrants.visible = 0
        for lampDistances in lampDistance:
            lampDistances.visible = 0      
    else:
        #А иначе, начинаем обработку обстановки
       
        #Лампа ракетной атаки противника
        if SelectThreatList[0][3] == 1:
            SPOalarm.visible - 1
        else:
            SPOalarm.visible = 0
       
        #Лмапы дистанции до угрозы   
        if SelectThreatList[0][4] > 30:
            lampDistance[0].visible = 0
            lampDistance[1].visible = 0
            lampDistance[2].visible = 0
            lampDistance[3].visible = 0
       
        elif 20 < SelectThreatList[0][4] < 30:
            lampDistance[0].visible = 1
            lampDistance[1].visible = 1
            lampDistance[2].visible = 1
            lampDistance[3].visible = 1
           
        elif 10 < SelectThreatList[0][4] < 15:
            lampDistance[0].visible = 1
            lampDistance[1].visible = 1
            lampDistance[2].visible = 1
            lampDistance[3].visible = 0
           
        elif 5 < SelectThreatList[0][4] < 10:
            lampDistance[0].visible = 1
            lampDistance[1].visible = 1
            lampDistance[2].visible = 0
            lampDistance[3].visible = 0
           
        elif SelectThreatList[0][4] < 5:
            lampDistance[0].visible = 1
            lampDistance[1].visible = 0
            lampDistance[2].visible = 0
            lampDistance[3].visible = 0
           
        #Лампы квадрантов
       
        #Слева спереди
        if SelectThreatList[0][6][0] < 0 and SelectThreatList[0][6][0] > 0:              
            lampQuadrant[0].visible = 0
            lampQuadrant[1].visible = 1
            lampQuadrant[2].visible = 0
            lampQuadrant[3].visible = 0
      
        #Справа спереди
        elif SelectThreatList[0][6][0] > 0 and SelectThreatList[0][6][0] > 0:              
            lampQuadrant[0].visible = 1
            lampQuadrant[1].visible = 0
            lampQuadrant[2].visible = 0
            lampQuadrant[3].visible = 0
          
       
        #Справа сзади
        elif SelectThreatList[0][6][0] > 0 and SelectThreatList[0][6][0] < 0:              
            lampQuadrant[0].visible = 0
            lampQuadrant[1].visible = 0
            lampQuadrant[2].visible = 1
            lampQuadrant[3].visible = 0
           
        #Слева сзади
        elif SelectThreatList[0][6][0] < 0 and SelectThreatList[0][6][0] < 0:              
            lampQuadrant[0].visible = 0
            lampQuadrant[1].visible = 0
            lampQuadrant[2].visible = 0
            lampQuadrant[3].visible = 1

        #Звуковая сигнализация
        if len(SelectThreatList) == 0:
            Cockpit['audioProp'] = 'NULL'
            Cockpit['volume'] = 0.0
            Cockpit['pitchSound'] = 0.0
        else:
            if SelectThreatList[0][3] == 1:
                Cockpit['audioProp'] = 'InfoLaunch'
                Cockpit['volume'] = 1.0
                Cockpit['pitchSound'] = 2.0
            else:
                if SelectThreatList[0][2] == 1:
                    Cockpit['audioProp'] = 'InfoLock'
                    Cockpit['volume'] = 1.0
                    Cockpit['pitchSound'] = 3.0
                else:
                    if SelectThreatList[0][1] == 1:
                        Cockpit['audioProp'] = 'InfoLock'
                        Cockpit['volume'] = 1.0
                        Cockpit['pitchSound'] = 1.0
                    else:
                        Cockpit['audioProp'] = 'InfoLock'
                        Cockpit['volume'] = 1.0
                        Cockpit['pitchSound'] = 0.0

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