четверг, 28 апреля 2016 г.

Горячая пора-4. Кувалдой и напильником обретешь ты право свое...

Столь вычурным названием этот пост обязан  долгим и не сказать что удачным поискам решения по загрузке и расстановке юнитов, а также раздаче им стартовых характеристик. После серии обломов пришел к тому, что теперь юниты грузятся путем просмотра многомерного списка типа:
generationList = [['2','UnitAir','red','Yes','MiG-23MF','Libya','FuelTank-3_R-23R_R-13M','Intercept','4','300.0_200.0_1500.0','0.0'],
                      ['2','UnitAir','red','Not','MiG-23MF','Libya','FuelTank-3_R-23R_R-13M','Intercept','4','1000.0_200.0_1500.0','0.0']]

Часть данных, судя по всему придется либо трансфорировать, либо просто выкинуть, но девять десятых из этого списка останется, наверное.
Из этого мегасписка, который будет выглядеть куда как пострашнее (а ведь здесь всего-то два самолета), извлекаются циклом по отдельности вложенные списки, которые я обозвал кластерами (надо же как-то выпендриться). Далее из кластера извлекаются и используются данные, причем иногда их надо преобразовать. Если учесть, что эта операция с небольшой натяжкой - одноразовая и используется только лишь при старте игры, тог тормозить она не должна (при старте игры и так есть закономерное подвисание при подгрузке ландшафта (что я наблюдаю во вполне "фирменных" играх тоже).
Далее я долго ломился в открытую дверь... Изощрялся в вызовах функций, модулей, классов, плохо в этом соображая и не понимая, что это совсем необязательно. В конце концов после отработки генерации на подвесках всего, что требуется (прошлый пост), пришел к более простому и универсальному способу. В каждую папку с юнитом суется файл с названием типа MiG-23MF_Libya_INI.txt, где содержится записб на первый взгляд непонятная, однако если читать скрипт, который занимается извлечением и применением данных из этого файла, то не такая уж и непонятная. Сам файл имеет вид типа:
#Технические характеристики
T|694|17500|220|0.8|1.3|0.3|1.0|0.14|4|12.5|1.8|4090.0|400|10000.0|E_|7400|FLG_|1.1|1.1|W\n
#Характеристики БРЭО
E|45|60|1.8|1|0.75|0.1|0.3|15|W\n
#Размещение камеры летчика в кабине
D|0.0|3.0|0.56|W\n
#Размещение пилота
O|PilotRus|0.0|2.85|0.25|W\n
#Бортовые номера
N|b_|6901,6902,6903,6904,6905,6906,6907,6908,6910,6911,6912,6913,6914,6915,6916|W\n
#Один топливный бак
P|0|FuelTank-1|FLG_FuelTank_Center_|0.0|-1.13|-1.04|0.8|0.05|W\n
#Пара топливных баков
P|0|FuelTank-2|FLG_FuelTank_Wing_|-2.67|-1.76|-0.1|0.8|0.05|W\n
P|0|FuelTank-2|FLG_FuelTank_Wing_|2.67|-1.76|-0.1|0.8|0.05|W\n
#Три бака
P|0|FuelTank-3|FLG_FuelTank_Center_|0.0|-1.13|-1.04|0.8|0.05|W\n
P|0|FuelTank-3|FLG_FuelTank_Wing_|-2.67|-1.76|-0.1|0.8|0.05|W\n
P|0|FuelTank-3|FLG_FuelTank_Wing_|2.67|-1.76|-0.1|0.8|0.05|W\n
#ПЕРВАЯ ПАРА ПОДВЕСОК
#Ракеты Р-24Р
P|1|R-24R|FLG_APU23_|0.005|W\n
P|1|R-24R|R-24R|-1.428|-2.0|0.03|-0.035|0.0|0.0|1|0.02|W\n
P|1|R-24R|R-24R|1.428|-2.0|0.03|-0.035|0.0|0.0|1|0.02|W\n
#Ракеты Р-24РМ
P|1|R-24RM|FLG_APU23_|0.005|W\n
P|1|R-24RM|R-24RM|-1.428|-2.0|0.03|-0.035|0.0|0.0|1|0.02|W\n
P|1|R-24RM|R-24RM|1.428|-2.0|0.03|-0.035|0.0|0.0|1|0.02|W\n

В файле перечислены варианты подвесок самолета, его пилоны для этих подвесок, координаты и повороты подвешиваемых объектов, а также всякие мелочи вроде лобового сопротивления, веса горючего в баках  и так далее. Но перед этим расписаны технические характеристики самолета, его особенности, вроде типа используемого шасси или названия семейства. Плюс положение летчика, положение камеры в кабине, характеристики бортовой электроники и так далее. Все это буду расписывать в следующих постах, по мере "введения в строй" бортсистем. Добавлю только, что предусмотрены "мертвые зоны" для РЛС (БРЛС "Фантомов", "Томкэтов", МиГ-23М, МиГ-21МФ например весьма плохо видят цели на фоне земли и не могут их захватить, зато радары МиГ-23МЛД, Ф-15, МиГ-29, не говоря уж о Ф-22 и "Тайфунах", отлично видят и сопровождают низколетящие цели), параметры устойчивости к помехам и так далее. в перспективе думаю возможно реализовать так называемую "уводящую помеху" для УРВВ с РГСН, до которой дело так и не дошло в первой версии, как и до контейнеров РЭБ, которые и будут эту помеху устраивать. Вообще во второй версии имитацию электроники юнитов постараюсь сделать гораздо более продвинутой.
Ладно, это погтом, и вообще, "гладко ьыло на бумаге", вернемся к нашим баранам...
В игровой сцене есть такой объект Generator, который является "внуком" двигателя. "Внучок" проживает недолгую, но очень насыщенную жизнь, прочитав файл с текстовыми данными и ударными, прямо-таки стахановскими методами генерируя и развешивая на своего "папу" (потомка двигателя, так сказать, в первом поколении) плейны, меши которых меняются на меши пилонов, ракет, бомб и баков. Заодно он "наставляет на путь истинный" своего "деда", выставляя и подправляя ему вевозможные проперти вроде угловых скоростей, высотности, угла обзора радара и так далее. Такой вот сознательный товарищ. после чего исчезает.
Выглядит это примерно так:
#Открываем файл инициализации юнита       
        directClass = open(bge.logic.expandPath('//Aircraft/' + typeUnit + '/' + typeUnit + 'INI' + '.txt'),'r')
       
        #то заглядываем в файл
        for string in directClass:
             #Срез строки по маркеру |
             config = string.split('|')
             #Эта строка, с начальным символом # - комментарий и ее читать не надо
             if config[0] == '#':
                 continue
            
             #Эта строка - технические данные летательного аппарата
             elif config[0] == 'T':
                 own.parent.parent['speedMax'] = int(config[1])                  #Максимальная скорость
                 own.parent.parent['heightMax'] = int(config[2])                 #Максимальный потолок
                 own.parent.parent['speedVert'] = int(config[3])                 #Максимальная вертикальная скорость
                 own.parent.parent['angX'] = float(config[4])                    #Максимальная скорость тангажа
                 own.parent.parent['angY'] = float(config[5])                    #Максимальная скорость крена
                 own.parent.parent['angZ'] = float(config[6])                    #Максимальная скорость рыска
                 own.parent.parent['startSpeed'] = float(config[7])              #Стартовый коэффициент скорости
                 own.parent.parent['rotatDyn'] = float(config[8])                #Угловое ускорение
                 own.parent.parent['dvigDyn'] = int(config[9])                   #Динамика(приемистость) двигателя

Сам скрипт вроде и длинный и преобразования строковых величин опять же. Да, xml, наверное было бы получше, но, как сказать... Из-за тегов возрастет объем, а если учесть, что таковых текстовых данных очень много... Проще уж повозиться со скриптом в самой игре, чем подгонять друг под друга новые и новые xml, да и данные в txt четко укладываются в строчки, не занимая много места.
И самый убийственный довод - c TXT я работать умею, а с xml - нет, увы... Пока, надеюсь... мне надо было сдвинуть с места застопорившую было работу и это все-таки удалось.
Из этого способа генерации меня пока все устраивает, осталось доделать массу для подвешиваемых объектов (она у меня играет большую роль, сильно влияя на маневренность), а также продумать, каким образом надо производить отстрел ловушек и стрельбу из пушки. Для сброса бомб и пуска ракет все более-менее понятно, а вот для пушки и ЛТЦ надо придумать новый способ.
Кстати о массе. В конце концов я привел все массовые характеристики к тоннам. Вес самолета, топлива, оружия - если в кг - загромождение идет большими цифрами. А так - максимум - сотнями приходится опернировать, а не сотнями и десятками тысяч.
В общем после грубой подгонки началась более тонкая работа - шлифовка и "достраивание" для летательных аппаратов. Выясяется, какие проперти и для чего нужны, как их использовать и так далее. Постепенно надо изобретать способ формирования стартового мегасписка и уточнять, какие параметры нужны во вложенных списках.
Результат  последних дней и часов работы - МиГ-23МФ с тремя ПТБ, бортовым номером, ракетами и летчиком в кабине (заодно привел к единому стандарту кокпиты МиГ-23БН и МФ):

  

воскресенье, 17 апреля 2016 г.

Горячая пора-3. Танцы с бубном.

Начнем с цитаты. Она подходит к нынешнему положению дел.
"""– Интересная работа?
– А разве бывает неинтересная работа?
– Да, конечно… И чем же вы занимаетесь?
– Я занимаюсь структурным анализом. Но учтите, Саул, я отрешился от земного. Давайте я расскажу вам еще что-нибудь про тахоргов.
– Да нет, благодарю вас, про тахоргов не надо. Лучше расскажите, как вы работаете.
– Саул, я же сказал, что отрешился.
– Ну как же это так – отрешился? Что же, вы теперь совсем не думаете о работе?
– Наоборот. Все время думаю. Я всегда думаю о той работе, которой занят в данный момент. Сейчас я суперкарго и второй пилот – это на тот случай, если у Антона вдруг случится отложение солей. Впрочем, об этом я, кажется, уже… Так вот, мне сейчас очень хочется пойти и немножко поводить «Корабль».
– Да вы еще успеете поводить! И потом я прошу рассказать не о сущности вашей работы, а о внешней форме, так сказать… Вот вы приходите на работу. Обычные трудовые будни…
– Хорошо. Будни. Я ложусь на вычислитель и думаю.
– Ну-ну… Постойте – на вычислитель? Ну да, понимаю. Вы лингвист, и вы ложитесь на… И что же дальше?
– Час думаю. Другой думаю. Третий думаю…
– И наконец?..
– Пять часов думаю, ничего у меня не получается. Тогда я слезаю с вычислителя и ухожу.
– Куда?!
– Например, в зоопарк.
– В зоопарк? Отчего же в зоопарк?
– Так. Люблю зверей.
– А как же работа?
– Что ж работа… Прихожу на другой день и опять начинаю думать.
– И опять думаете пять часов и уходите в зоопарк?
– Нет. Обычно ночью мне в голову приходят какие-нибудь идеи, и на другой день я только додумываю. А потом сгорает вычислитель.
– Так. И вы уходите в зоопарк?
– При чем здесь зоопарк? Мы начинаем чинить вычислитель. Чиним до утра.
– Ну, а потом?
– А потом кончаются будни и начинается сплошной праздник. У всех глаза на лоб, и у всех одно на уме: вот сейчас все застопорится, и начинай думать сначала.
– Ну, ладно. Это будни. Однако же нельзя все время работать…
– Нельзя, – сказал Вадим с сожалением. – Я, например, не могу. В конце концов заходишь в тупик, и приходится развлекаться.
– Как?
– Как придется. """
А. и Б. Стругацкие "Попытка к бегству"

Дорого бы я дал за такую возможность валяться на вычислителе и думать. Имеется в виду, конечно квазибиологический комп с мыслеуправлением. Глаза не напрягать, на скрипящем и кряхтящем раздолбанном кресле не сидеть...
Иногда я бываю непробиваемо тупым. Особенно, если начинаю освоение чего-нибудь нового. Потом, как обезъяна, начинаю копировать и ставить что-то уже наработанное, но не понимаю до конца, как оно работает. Потом следует "щелчок" и новая порция навыков укладывается в москХ. При этом я вполне способен вынести мозг тем, кто меня наставляет на путь истинный. Те, кто читает эти строки, наверняка поймут, о чем речь...
В прошлом посте я выложил куски кода по стартовой загрузке семейства МиГ-23/27. Осталось дело за малым - научиться не только генерить объекты, но и менять им меши. После серии брани со стороны консоли об отсутствующих мешах и неправильных путях и опечатках, наконец свершилось страшное. Стали генериться и появляться подвесные баки. Повернутые на 90 градусов. Долго мне еще будут икаться "проклятые Блендером меши". При импорте файла в формате обж внешне все выглядит благопристойно, но нельзя забывать, что в графе "поворот по Х" стоит 90 градусов. То же самое случилось с пилонами. Исправил. Баки есть, бомб и остального нет. Опять начал танцевать с бубном. Принтом вывожу список потомков. Есть. Пути к мешам (блендам)? Правильно, все на месте. Где оружие?! Принт в файл контроля юнита на игровой сцене. Что за хрень? Только баки! открываю скрип генерации объектов в "самолетном файле". Диагноз серьезен, прогноз печален. Припаренчивание мешей ракет и бомб идет к самому генератору, который исчезает, выполнив свою миссию. а надо цеплять к родителю генератора - фюзеляжу. Добавляю .parent к self. Открываю игру, запускаю. ура! Все на месте. Но... А где левый блок с НАР? Ну да, конечно, там парент не проставлен... Исправил. Снова запуск и я вижу МиГ-23БН в полном обвесе - три ПТБ, по паре Б-8, ОФАБ-20 и ФАБ-100. Слава Высокому Небу!
Вообще-то я рассчитывал генератор объектов сделать единым в игровой сцене, но при вызове класса там встречаются свои нюансы - имя класса надо формировать с помощью модуля sys. А кроме этого, есть еще функция самого класса. Вчерашний вечер был убит на попытки изощриться и сделать подобное. Заодно чуть не был сведен с ума мой добровольный помощник (да и я сам чувствовал себя не очень ... комфортно). В итоге пока сделал загрузку оружия старым способом через генератор в самом самолетном файле. Есть у меня подозрения, где я мог лопухнуться, но пока еще не проверял. может и не лопухнулся, а просто честно ломился в открытую дверь (так уже бывало - смотрим посты по разметке квадратов ландшафта).
Ну а пока что имеем  полный обвес, и способ генерации, по-видимому, будет изменен еще раз. Путем загрузки данных из тестового фала со всеми координатами, поворотами, массой, лобовым сопротивлением... вот так вот рождаются и мгновенно устаревают файлы. Результат этого устаревшего файла:

суббота, 16 апреля 2016 г.

Горячая пора - 2. Записки бюрократа.

Пару дней назад сдуру удалил файл МиГ-23БН. Теперь вот думаю, а может не сдуру. Как сказал Диего Марадона, забив мяч англичанам рукой: "Мою руку направлял сам Бог". Учитывая, что совсем недавно перед этим матчем Аргентина огребла от британцев на фолклендских, то бишь Мальвинских островах, кое-какие основания для этих слов у него были (обида аргентинцев не прошла с тех пор). Ну, может быть, мою руку направляла Высокое Небо (читаем Валентинова). В том файле содержался начатый мною стартовый класс для семейства МиГ-23 и МиГ-27. Обычно я всегда проверяю содержимое корзины перед удалением, но тут почему-то не стал. Может, к лучшему.
Позавчера я этот файл восстановил, вчера написал класс FLG_, то бишь FLOGGER_, в котором помимо расстановки вооружения, указал конкретные технические характеристики для разных модификаций. Какие-то модификации почти не отличаются, какие-то отличаются сильно. я бы не сказал, что файл полностью закончен, потому что в нем пока отсутствуют характеристики БРЭО, вроде дальности действия радара, его угла обзора, времени сканирования и пр.
Тем не менее, прописывание всего этого заняло весь день, потому что везде надо было менять own на self, прописывать другие имена и тому подобное. В несколько приемов я с этим справился. Та же участь ждет остальные модули первой версии для МиГ-29, Су-25, F-15, F-16 и F-5.  Про модули для новых машин я не говорю, потому что добавляться они будут с новыми машинами. вообще же класс FLG_ выглядит примерно так:
стартовые характеристики:
class FLG_(bge.types.KX_GameObject):   
    def __init__(self, old_owner):
        if self.parent.parent['unitName'] in ['MiG-23MF','MiG-23M']:
           
            #Если этот юнит управляется игроком, то выставляем камеру пилота в кабине в нужную позицию
            if.self.parent.parent['bot'] == 0:
                Gor = scene.objects["Gor"]
                Gor.localPosition = [0.0, 3.0, 0.56]
           
            if 'PilotObj' not in bge.logic.globalDict['listLoadObj']:
                #Вызов и установка фигуры пилота
                bge.logic.LibLoad('//Aircraft/PilotObj.blend', 'Scene', load_actions = False)
                bge.logic.globalDict['listLoadObj'].append('PilotObj')
           
            pilot = scene.addObject('PilotRus', self.parent)
            pilot.setParent(self.parent, False, False)
            pilot.localPosition = [0.0, 2.85, 0.25]
           
            #Пушка ГШ-6-30 для МиГ-27
            if 'MiG-27' in self.parent.parent['unitName']:
                pushka = scene.addObject('UniversalMesh', self.parent) 
                pushka['meshes'] = 'GSh630'     
                pushka.setParent(self.parent, False, False)
                pushka.localPosition = [0.0,1.4,-0.675]
           
            #Вначале выдаем ограничения по маневренности, высотности, скорости и прочим ТХ
            self.parent.parent['speedMax'] = 694 #максимальная скорость
            self.parent.parent['heightMax'] = 17500 #потолок
            self.parent.parent['MaxPower'] = 12.5 #максимальная  тяга двигателя
            self.parent.parent['speedVert'] = 220 #максимальная вертикальная скорость
            self.parent.parent['angX'] = 0.8 #максимальная угловая скорость тангажа
            self.parent.parent['angY'] = 1.3 #максимальная угловая скорость крена
            self.parent.parent['angZ'] = 0.3 #максимальная угловая скорость рыска
            self.parent.parent['rotatDyn'] = 0.1 #динамичность угловых скоростей
            self.parent.parent['dvigDyn'] = 4 #динамичность двигателя
            self.parent.parent['speedDyn'] = 5 #динамичность скорости
            self.parent.parent['startSpeed'] = 1.0 #стартовый коэффициенот скорости
            self.parent.parent['maxFuelSpeed'] = 1.8 #максимальный расход топлива кг в секунду
            self.parent.parent['massFuelOwn'] = 4090 #внутренний запас топлива
            self.parent.parent['unitChassy'] = 'E_' #тип шасси
            self.parent.parent['ownECM'] = 0.1 #встроенная система РЭБ - эффективность
        elif self.parent.parent['unitName'] == 'MiG-23MS':
            #Вначале выдаем ограничения по маневренности, высотности, скорости и прочим ТХ
            self.parent.parent['speedMax'] = 694 #максимальная скорость
            self.parent.parent['heightMax'] = 17500 #потолок
            self.parent.parent['MaxPower'] = 12.5 #максимальная  тяга двигателя
            self.parent.parent['speedVert'] = 220 #максимальная вертикальная скорость
            self.parent.parent['angX'] = 0.8 #максимальная угловая скорость тангажа
            self.parent.parent['angY'] = 1.3 #максимальная угловая скорость крена
            self.parent.parent['angZ'] = 0.3 #максимальная угловая скорость рыска
            self.parent.parent['rotatDyn'] = 0.12 #динамичность угловых скоростей
            self.parent.parent['dvigDyn'] = 4 #динамичность двигателя
            self.parent.parent['speedDyn'] = 5 #динамичность скорости
            self.parent.parent['startSpeed'] = 1.0 #стартовый коэффициенот скорости
            self.parent.parent['maxFuelSpeed'] = 1.8 #максимальный расход топлива кг в секунду
            self.parent.parent['massFuelOwn'] = 4590 #внутренний запас топлива
            self.parent.parent['unitChassy'] = 'E_' #тип шасси
            self.parent.parent['ownECM'] = 0.05 #встроенная система РЭБ - эффективность
        elif self.parent.parent['unitName'] == 'MiG-23BN':
            #Вначале выдаем ограничения по маневренности, высотности, скорости и прочим ТХ
            self.parent.parent['speedMax'] = 694 #максимальная скорость
            self.parent.parent['heightMax'] = 18500 #потолок
            self.parent.parent['MaxPower'] = 12.5 #максимальная  тяга двигателя
            self.parent.parent['speedVert'] = 220 #максимальная вертикальная скорость
            self.parent.parent['angX'] = 0.8 #максимальная угловая скорость тангажа
            self.parent.parent['angY'] = 1.3 #максимальная угловая скорость крена
            self.parent.parent['angZ'] = 0.3 #максимальная угловая скорость рыска
            self.parent.parent['rotatDyn'] = 0.13 #динамичность угловых скоростей
            self.parent.parent['dvigDyn'] = 4 #динамичность двигателя
            self.parent.parent['speedDyn'] = 5 #динамичность скорости
            self.parent.parent['startSpeed'] = 1.0 #стартовый коэффициенот скорости
            self.parent.parent['maxFuelSpeed'] = 1.8 #максимальный расход топлива кг в секунду
            self.parent.parent['massFuelOwn'] = 4590 #внутренний запас топлива
            self.parent.parent['unitChassy'] = 'E_' #тип шасси
            self.parent.parent['ownECM'] = 0.05 #встроенная система РЭБ - эффективность

Здесь отнюдь не все модификации - а то слишком много места уйдет. А теперь кусочек кода по вооружению:

#Генерация вооружения
            if self.point9 != 'pusto':
                #Добавление топливных баков
                if self.point0 in ['FuelTank-1','FuelTank-3']:
                    ptbCnt = scene.addObject('UniversalMesh', self)
                    ptbCnt['meshes'] = 'FLG_FuelTank_Center_'
                    ptbCnt['fuel'] = 800.0
                    ptbCnt.setParent(self.parent, False, False)
                    ptbCnt.localPosition = [0.0,-1.13,-1.04]
                if self.point0 in ['FuelTank-2','FuelTank-3']:
                    #Левый подвесной бак
                    ptbL = scene.addObject('UniversalMesh', self)
                    ptbL['meshes'] = 'FLG_FuelTank_Wing_'
                    ptbL['fuel'] = 800.0
                    ptbL.setParent(self.parent, False, False)
                    ptbL.localPosition = [-2.67,-1.76,-0.1]
                    #Правый подвесной бак
                    ptbR = scene.addObject('UniversalMesh', self)
                    ptbR['meshes'] = 'FLG_FuelTank_Wing_'
                    ptbR['fuel'] = 800.0
                    ptbR.setParent(self.parent, False, False)
                    ptbR.localPosition = [2.67,-1.76,-0.1]
           
            #Генерация вооружения
            if self.point1 != 'pusto':
               
                #Добавление оружия
                #Ракеты средней дальности для МиГ-23
                if self.point1 in ['R-23R+R-23T','R-24R+R-24T','R-24RM+R-24T','R-24R','R-23R','R-24RM','R-24T','R-23T']:
                    #Пилон стандартный для всех вариантов
                    pilon1 = scene.addObject('UniversalMesh', self)
                    pilon1.setParent(own, False, False)
                    pilon1['meshes'] = 'FLG_APU23_'
                    #Ракет две штуки всегда, расположение - одинаковое
                    weaponMesh1newObject1 = scene.addObject('UniversalMesh', self)       
                    weaponMesh1newObject1.setParent(self, False, False)
                    weaponMesh1newObject2 = scene.addObject('UniversalMesh', self)       
                    weaponMesh1newObject2.setParent(self, False, False)
                    weaponMesh1newObject1.localPosition = [-1.428,-2.0,0.03]
                    weaponMesh1newObject1.applyRotation([-0.035,0.0,0.0],True)
                    weaponMesh1newObject2.localPosition = [1.428,-2.0,0.03]
                    weaponMesh1newObject2.applyRotation([-0.035,0.0,0.0],True)
                   
                    if self.point1 in ['R-24R','R-23R','R-24RM','R-24T','R-23T']:
                        #Для первой ракеты значение проперти и меша по-любому стандартны
                        weaponMesh1newObject1['weapon'] = 1
                        weaponMesh1newObject1['meshes'] = self.point1
                        #При симметричной подвеске нет вопросов и по второй ракете
                        weaponMesh1newObject2['weapon'] = 1
                        weaponMesh1newObject2['meshes'] = self.point1
                       
                    #Смешанная подвеска   
                    elif self.point1 == 'R-23R+R-23T':
                        #Для первой ракеты значение проперти и меша по-любому стандартны
                        weaponMesh1newObject1['weapon'] = 1
                        weaponMesh1newObject1['meshes'] = 'R-23R'
                        #При смешанной подвеске вторая ракета получает другие значения проперти и меша
                        weaponMesh1newObject2['weapon'] = 3
                        weaponMesh1newObject2['meshes'] = 'R-23T'
                    elif self.point1 == 'R-24R+R-24T':
                        #Для первой ракеты значение проперти и меша по-любому стандартны
                        weaponMesh1newObject1['weapon'] = 1
                        weaponMesh1newObject1['meshes'] = 'R-24R'
                        #При смешанной подвеске вторая ракета получает другие значения проперти и меша
                        weaponMesh1newObject2['weapon'] = 3
                        weaponMesh1newObject2['meshes'] = 'R-24T'
                    elif self.point1 == 'R-24RM+R-24T':
                        #Для первой ракеты значение проперти и меша по-любому стандартны
                        weaponMesh1newObject1['weapon'] = 1
                        weaponMesh1newObject1['meshes'] = 'R-24RM'
                        #При смешанной подвеске вторая ракета получает другие значения проперти и меша
                        weaponMesh1newObject2['weapon'] = 3
                        weaponMesh1newObject2['meshes'] = 'R-24T'
       
       
                #Ракеты Р-3Р, Р-3С, Р-13М и Р-13М1      
                elif self.point1 in ['R-3R','R-3S','R-13M','R-13M1']:
                    weaponMesh1newObject1 = scene.addObject('UniversalMesh', self)       
                    weaponMesh1newObject1.setParent(self, False, False)
                    weaponMesh1newObject1['weapon'] = 1
                    weaponMesh1newObject1['meshes'] = self.point1
                    weaponMesh1newObject2 = scene.addObject('UniversalMesh', self)       
                    weaponMesh1newObject2.setParent(self, False, False)
                    weaponMesh1newObject2['weapon'] = 1
                    weaponMesh1newObject2['meshes'] = self.point1
                    #Это расположение ракет на пилонах МиГ-23
                    if 'MiG-23' in self.parent.parent['unitName']:
                        weaponMesh1newObject1.localPosition = [-1.428,-1.57,0.027]
                        weaponMesh1newObject1.applyRotation([-0.0175,0.0,0.0],True)
                        weaponMesh1newObject2.localPosition = [1.428,-1.57,0.027]
                        weaponMesh1newObject2.applyRotation([-0.0175,0.0,0.0],True)
                        pilon1 = scene.addObject('UniversalMesh', self)
                        pilon1.setParent(self, False, False)
                        pilon1['meshes'] = 'FLG_APU13W'
                    #Расположение ракет на МиГ-27 чуть отличается плюс другой пилон   
                    elif 'MiG-27' in self.parent.parent['unitName']:
                        weaponMesh1newObject1.localPosition = [-1.428,-1.57,-0.02]
                        weaponMesh1newObject1.applyRotation([-0.0175,0.0,0.0],True)
                        weaponMesh1newObject2.localPosition = [1.428,-1.57,0.02]
                        weaponMesh1newObject2.applyRotation([-0.0175,0.0,0.0],True)
                        pilon1 = scene.addObject('UniversalMesh', self)
                        pilon1.setParent(self, False, False)
                        pilon1['meshes'] = 'FLG_APU3W_'

Несколько длинновато, ну ладно, пускай на всякий случай будет.
Как видно из написанного, в обязательном порядке добавляется фигура летчика, а для МиГ-27 еще и пушка ГШ-630. Кроме того, с течением времени добавятся блоки выброса помех, контейнеры РЭБ, и другое вооружение, вроде никак не доделываемых мною УРВЗ Х-25.
Все это - одноразовая функция, исполняемая объектом Generator, с коротким временм жизни, двойной parent - это родитель его родителя (дедушка, сатло быть, хе-хе) - сам двигатель юнита. вот, значит, внучок, дедушку жизни учит... Проставляя ему свойства, прописанные в классе. Вся эта бюрократия, хотя и весьма объемна, но необходима. могу только сказать, что в том же WoE подобных перечислений на порядок больше. Хотя, может стоит брать все данные из текстовых файлов?  я просто не знаю, открытый, а потом закрытый текстовый файл загружает память или нет...
В общем, пока так обстоят дела. Картинок пока новых нет, так что остается только текст.
P.S. Сейчас видел забавное поведение вороны, прилетевшей на огород - попить ей, видите ли захотелось. Четыре ведра, одно закрытое. По очереди отпила из КАЖДОГО ОТКРЫТОГО. Видимо, думала, что в разных ведрах вода разной степени вкусности. Потом некоторое время поизучала закрытое и улетела. Видимо, решила, что там та же самая вода...

среда, 13 апреля 2016 г.

Горячая пора. Или опять "проклятые Блендером" меши.

Как-то незаметно подошло время подвешивать оружие. Пока не стреляющее. макеты, если угодно. Некоторое время пришлось потратить на то, чтобы уяснить алгоритм генерации оружия на подвесках и сами пилоны. Само оружие и пилоны, хотя и не имеют много поликов, но все же не плейны и тратить время на просчет невидимых с большого расстояния мелких объектов не хочется. Также не хотелось оставлять висящей мертвым грузом одноразовые кирпичи логики. Сначала было искушение сделать некий единый для всех генератор, но при импорте класса, насколько я понимаю, фокус с формированием имени импортируемого модуля не пройдет, во всяком случае, рисковать я не стал.
Я просто сделал объект с названием типа Generator_MiG-23BN_ в самом подгружаемом файле, поместив плейн на неактивный слой. Жить ему недолго - по окончании загрузки он удаляется. Но перед этим он добавляет и цепляет к своему родителю ("центру юнита", он же "бортовой номер")  плейн. Так называемый "универсальный меш" - и название у него такое же, только на инглиш. Заодно генератор добавляет мешу свойство weapon, если это оружие и общее для все таких объектов проперти meshes. В этом проперти указывается название меша, котороый должен проявиться при высоком уровне детализации. Заодно внес изменения в скрипт контроля состояния детализации юнита - с учетом нового имени UniversalMesh. После "подчистки" скрипта генерации от ошибок и опечаток был обруган консолью. Опять выползла та самая ошибка в мешах МиГ-23. Опять придется проводить лечение экспортом-импортом в формате obj. Вроде бы это последние 2осколки" от старого "зараженного" файла. Который, очевидно, за что-то был проклят Блендером и БГЕ.
Теперь у двигателя самолета присутствует лишь одно свойство для подвесок - pointWeapon, оно строковое и имеет вид типа: PTB-3_B-8_OFAB-250_FAB-100_. При помощи функции split('_')[индекс число]  происходит сортировка данных в проперти и мы получаем список вооружения для юнита. И вообще все подвесок. Первое значение зарезервировано  для топливных подвесных баков, далее идут уже названия подвесок вооружения на пилонах. Сами пилоны жестко прописаны в классе генерации, в самом подгружаемом бленде юнита, что исключает ошибки после отлаживания скрипта при его использовании, заодно освобождает от лишних проперти типа pilon.
Скорее всего, свойство pointWeapon я перенесу на сам генератор, а на движке самолета проперти уберу. Сам принцип пуска будет отличаться от ранее принятого. Не будет проперти pusk, команда на отсоединение и запуск нужного оружия будет будет подаваться прямо на нужный объект-потомок. Принцип формирования списков имеющегося оружия по значению класса weapon останется, этот метод себя вполне оправдал в первой версии. Информация об используемом оружии будет также передаваться на оверлейные сцены напрямую - непосредственно текстовому объекту - нет смысла держать в глобальном словаре "малоподвижные" элементы.
Заодно выяснилось, что сделанные мною зеркала не кушают много логики, если задать разрешение отрендериваемой видеотекстуре 128. Даже на 256 в принципе - терпимо - 25 процентов. При 128 - вообще 6 процентов. Так что пока зеркала в кабине оставлю в покое.
Остается дописать скрипт генерации и расстановки объектов, используя ранее написанные модули для самолетов в первой версии - это касается всех машин. Стартовые позиции и углы наклона для ракет и бомб уже давно прописаны, что сэкономит время.
В заключение - скрин МиГ-23БН с подвешенными блоками Б-8. Серый пилон подвески и стал той причиной, которая заставила ругаться консоль. Будем лечить...

понедельник, 11 апреля 2016 г.

Пейзаж после погрома.

Лучшее - враг хорошего. Иногда это утверждение верно, иногда - нет. Вышедший недавно blender 2.77a заставил меня предположить, что если в названии версии уже присутствует буква, то ее можно юзать и радоваться улучшениям. Увы, мои предположения оказались несколько наивными и преувеличенными. Меня ожидал мощный облом там, где я этого совсем не ждал (каламбурчик, однако). Не далее как пару постов назад я писал о "Великой анимационной революции" и разливался соловьем насчет плавности работы как анимации, так и физики.  Новая версия мои ожидания обломала и весьма жестко.
Прикол заключается в новом толковании строчки obj.playAnimation(spisok). Длинный список аргументов этой функции я приводить не буду, скажу только, что строчки я переделывал в соответствии с новыми правилами, аргументов там стало 12, а 1, как написано в новом API 2.77. Но анимации не работают, как положено. Они отражают лишь начальный и конечный кадр. А у меня эти кадры задаются значением проперти. Хоть ты тресни - крыло 2перескакивает" с первого на 200-ый кадр по завершении изменения проперти, аналогично с 201 на 400 и обратно. И вообще все анимации перестали понимать эту строчку с playAction.
Сначала я грешил на анимацию, созданную в 2.75, однако, созданный мною ради эксперимента файл с анимациями "чисто" в 2.77 показал тот же самый результат. А еще один прикол заключается в том, что если я задействую актуатор Action и в нем проставлю анимацию изменением проперти, то все работает!
Можно, конечно, придать всем объектам этот самый актуатор, но для этого им надо еще добавить проперти и логику. Объектов не сказать, чтобы много, но сама мысль о том, что придется вводить логику там, где без логических кирпичей можно прекрасно обойтись (было, скажем так, возможно обойтись) восторга у меня не вызывает. Если кто-нибудь читает эти строчки и хорошо знает английский, то пусть попробует на своем компе проделать этот фокус с анимацией - логическим блоком и скриптом. И если у вас получится такой же облом с командой playAction, отпишите багрепорт на сайт разработчиков. У меня сейчас запарка и просто нет времени.
А времени у меня нет еще по той причине, что польстившись на 2.77а и прочитав, что поддержка Виндовс ХР более не работает, поставил Виндовс 7. Хотя отношение у меня к этой и более поздним версиям Винды неоднозначное. Кроме того, думаю все-таки поставить рядом с виндой и Линукс. Сначала, правда, придется затвердить правила установки рядом с Виндовс - есть несколько хорших статей на эту тему, а потом еще надо выяснить, сколько трафика у меня осталось на Мегафоне, потому как Линукс с Билайном не дружат. Плюс проверить версию Линукса в работе, потому что версия 12.04 люто била по глазам - вроде бы и работала, но монитор "мигал" каждые 10 секунд. Тогда-то меня и остановило это обстоятельство. В прошлом году я на всякий случай скачал 14.04 и проверил ее, не устанавливая, запуском с DVD - экран не мигал, Мегафон работал, что порадовало. Однако тогда еще вовсю работала ХР, да и рассчитывал я на установку нового винчестера. Однако грянувший могучий ремонт дворовых объектов и самого дома несколько поумерил аппетиты, да еще впереди еще кое-какие дела, так что пока неясно с этим винчестером. Но учиненный мною погром с установкой "семерки" привел к тому, что откладывать установку Линукса вроде как уже неудобно - диск порядком очищен от старых файлов, места полно, вирусня еще набежать не успела - надо решаться. что плохо - не удается пока восстановить джаббер.
Тем временем, несмотря на столь резкий поворот событий, проект все же продолжает свое движение вперед. Был сделан скриншотер экрана. Вообще-то он был и в первой версии, но этот - совершеннее. Его новшество в том, что теперь сохраняются скрины старых сеансов игры, раньше каждый новоый запуск игры и фотографирование процесса на мониторе приводило у ничтожению старых скринов. Это не есть хорошо, и эту проблему, немного повозившись, я решил.
Кусок кода:

#Скриншот экрана
                if key == bge.events.F12KEY:
                    #Открываем текстовый файл и смотрим номер последнего снимка
                    screenNull = open(bge.logic.expandPath('////Screenshot/screen.txt'),'r')
                    kadrNull = screenNull.read()
                    #Увеличиваем номер на 1 и закрываем файл
                    kadrNew = int(kadrNull) + 1
                    screenNull.close()
                    #составляем новое имя для нового снимка
                    indexScreen = 'BlendSim' + str(kadrNew)
                    #Делаем снимок и отправляем в папку
                    bge.render.makeScreenshot("//ScreenShot/" + indexScreen)
                    #А теперь опять открываем текстовый файл с номером снимка и пишем туда уже номер нового скрина
                    kadrWrite = open(bge.logic.expandPath('//Screenshot/screen.txt'),'w')
                    kadrWrite.write(str(kadrNew))
                    #Опять закрываем файл
                    kadrWrite.close()

В папке со скриншотами находится текстовый файл. в нем записано число. Это число предыдущего снимка - если угодно - число последнего срабатывания скриншотера. В коде есть пояснения и вы разберетесь, как  открывается, читается и закрывается файл, как формируется имя нового снимка, а затем файл с номером открывается и в него записывается новое значение. Главное, чтобы строчка со словом close - "закрыть", присутствовала там, где должна быть...
Параллельно с этим начал работу по добавлению объектов-декораций на игровую сцену. Первым (и пока единственным) стал аэродром. Собственно, это не один объект. Это группа объектов - ангаров, бункеров и зданий, припаренченных к взлетной полосе. Прикол в том, что здесь я опять применил замену мешей. В "первозданном" виде все эти объекты представляют собой параллелепипеды разных размеров, имеющие названия типа BunkerGreen_01 или AirbaseBuild_12. "Изюминка" заключается в том, что размеры этих объектов подогнаны под размеры "исходников" - которые существуют в виде маленьких блендов, содержащих объекты с теи же названиями, что и выше показанные, только без цифр. И меши этих многочисленных декораций меняются на подгруженные из блендов нужных мешей. Просто имя объекта "режется" по символу "_". В игре новые меши появляются только тогда, когда камера находится вблизи них, все остальное время меши заменяются на плейн, независимо от названия. Функция "декоратор":

#Эта функция отвечает за работу замены мешей в декорациях - города, растительность, аэродромы и прочее и прочее
def dekorator():
    #Объект - подгружаемые из других блендов объекты, сцена - Scene, слой 1
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner 
   
    #камера внешнего обзора
    cameraWorld = scene.objects["CameraWorld"]
    #Камера в кокпите юнита игрока
    cameraPilot = scene.objects["CameraPilot"]
   
    sens = cont.sensors[0]           
   
    #идет просчет уровня требуемой детализации
    if  sens.positive:
       
        #Перехват клаиватуры
        keyboard = bge.logic.keyboard
        JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
       
        #Перехват команды на переключение камер - сброс таймера ЛОД-уровня объекта
        if keyboard.events[bge.events.F1KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F2KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F3KEY] == JUST_ACTIVATED:
            own['timerLOD'] = 2.0
        if keyboard.events[bge.events.F4KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F5KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F6KEY] == JUST_ACTIVATED:
            own['timerLOD'] = 2.0  
        if keyboard.events[bge.events.F7KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F8KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F9KEY] == JUST_ACTIVATED:
            own['timerLOD'] = 2.0
        if keyboard.events[bge.events.F10KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F11KEY] == JUST_ACTIVATED or keyboard.events[bge.events.F12KEY] == JUST_ACTIVATED:
            own['timerLOD'] = 2.0       
       
        #Таймер - идет проверка
        if own['timerLOD'] > 1.5:
            #Если хотя бы одна из камер находится на расстоянии меньше 20 км, то уровень детализации - высокий
            if own.getDistanceTo(cameraPilot) < 20000 or own.getDistanceTo(cameraWorld) < 20000:
                #При этом еще и уровень детализации ранее - низкий, то идет его изменение
                if own['lodOwn'] == 0:
                    for obj in own.childrenRecursive:
                        obj.replaceMesh(obj.name.split('_')[0], True, False)
                    own['lodOwn'] = 1
            #Иначе - просто сброс значения проперти и замна всех мешей-потомков на плейны       
            elif own.getDistanceTo(cameraPilot) > 20000 and own.getDistanceTo(cameraWorld) > 20000:
                if own['lodOwn'] == 1:
                    for obj in own.childrenRecursive:
                        obj.replaceMesh("UniversalMesh", True, False)
                    own['lodOwn'] = 0
            own['timerLOD'] = 0.0

Ну, здесь еще и таймер есть, чтобы не "проспать" приближение камеры к объекту. А висит эта функция на родителе группы потомков. Это в данном случае - взлетная полоса, но может быть центр города, завода, базф, группы деревьев...
И, кроме всего этого, сегодня я сделал еще одну "революцию". Посмотрев свой кокпит МиГ-23БН и пробежавшись по названиям объектов, я вдруг понял (надо было раньше быть сообразительным), что можно радикально переделать скрипт кокпита, уничтожив если не всю, то практически всю логику. Поскольку сейчас кокпиты у меня изолированы, то есть возможность каждый скрипт кокпита, который находится в бленде со всеми приборами, стеклами, панелями, подогнать под конкретный случай. Все дело в том, что имена объектов в сцене кокпитов уникальны и совершенно не фиг постоянно писать own.childrenRecursive, достаточно просто написать scene.objects[название] и все. Также совершенно необязательно на все стрелочки вешать логику с функцией анимации - анимацию можно прописать наряду с работой лампочек и индикаторов в одной функции. Поскольку анимированных объектов у меня не так-то много, а индикаторы с их состоянием виден-не виден и раньше-то много не кушали, я решил рискнуть. Заодно индикация стала более продвинутой - особенно это касается работы топливной системы - теперь работает индикация расходования топлива из баков - как внутренних, так и подвесных. Пока до конца  скрипт еще не дописан - мне осталось прописать ориентацию (не анимацию!) указателей крена-тангажа-рыска, и подключить стрелки перегрузки и угла атаки, плюс вариометр. Тем не менее, после устранения тройки опечаток, кокпит заработал. К моему удивлению, он стал кушать еще меньше, точнее, почти перестал кушать. По всему выходит, что сейчас он потребляет где-то 1% против двух ранее (в первой версии - все 25-30). Так что кокпиты для других машин будут делаться по этому образцу.
Кокпит Миг-23БН.



вторник, 5 апреля 2016 г.

"Случай в квадрате 36-80 или "Операция Святой Януарий". Вторая серия, однако...

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

import bpy
#print('xxx') # пoтому что камасутра чистой воды
scene = bpy.context.scene

listCoord = [-49,-47,-45,-43,-41,-39,-37,-35,-33,-31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11,-9,-7,-5,-3,-1,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49]

listIndex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49]

blockX = 0
blockY = 0

cursorX = 0
cursorY = 0

for obj in scene.objects:
    if '.' in obj.name: # у тебя должно быть другое имя
        obj.select = True
        X, Y, Z = obj.location
        obj.data.name = obj.name
        bpy.ops.object.origin_set(type = 'ORIGIN_GEOMETRY', center = 'MEDIAN')
      
        for coordX in listCoord:
            if (coordX-1)*10000 < X < (coordX+1)*10000:
                blockX = listCoord.index(coordX)
                scene.cursor_location[0] = coordX*10000
              
        for coordY in listCoord:
            if (coordY-1)*10000 < Y < (coordY+1)*10000:
                blockY = listCoord.index(coordY)
                scene.cursor_location[1] = coordY*10000
      
        scene.cursor_location[2] = 0.0
        bpy.ops.object.origin_set(type = 'ORIGIN_CURSOR')
      
        newName = str(blockX) + '_' + str(blockY)
        obj.name = newName
        obj.data.name = obj.name
        print(obj)
        obj.select = False

Смысл всего этогог заключается в том, что каждый блок получает конкретное название, состоящее из слова "блок" и индекса квадрата, в котором он находится. в коде, думаю, ясно, как формируются индексы квадратов по Х и Y. Такое же название получает и меш блока. Теперь не надо создавать текстовый файл с длиннющим списком названий блоков, и их координатами. КООРДИНАТЫ каждого блока ВШИТЫ в его имя. с определенной поправкой, правда. У меня нет блоков с отрицательными значениями типа -35 или -2. Все индексы блоков заключены в интервале от 0 до 49.
А теперь приведем код подгрузки блоков ландшафта в новом варианте. Он пока еще не полон, осталось ввести список блоков района боевых действий для наземки, чтобы та не теряла опору под собой. Я не буду приводить "шапку" и окончание класса для экономии места

        listCoord = [-49,-47,-45,-43,-41,-39,-37,-35,-33,-31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11,-9,-7,-5,-3,-1,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49]
        listIndex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49]
       
        #Камера ТВ-прицела - вычисление нужного квадрата ее местонахождения
        targetTV = scene.objects["CameraWorld"]
        targetTVquadroX = int((int(targetTV.worldPosition[0]/1000)+ 490)/20)
        targetTVquadroY = int((int(targetTV.worldPosition[1]/1000)+ 490)/20)
       
        #Индексы "догружаемых" в сцену блоков - для этой камеры хватит и 9
        targetTVblockX = str(listIndex.index(targetTVquadroX))
        targetTVblockY = str(listIndex.index(targetTVquadroY))
        targetTVblockX0 = str(listIndex.index(targetTVquadroX+1))
        targetTVblockY0 = str(listIndex.index(targetTVquadroY+1))
        targetTVblockX1 = str(listIndex.index(targetTVquadroX-1))
        targetTVblockY1 = str(listIndex.index(targetTVquadroY-1))
       
        #Жестко зафиксированный список для 9 блоков - "догружаемых" к камере ТВ-прицела
        targetTVtempIndex = [('block_' + targetTVblockX + '_' + targetTVblockY + '_'),
                             ('block_' + targetTVblockX0 + '_' + targetTVblockY0 + '_'),
                             ('block_' + targetTVblockX1 + '_' + targetTVblockY1 + '_'),
                             ('block_' + targetTVblockX1 + '_' + targetTVblockY + '_'),
                             ('block_' + targetTVblockX + '_' + targetTVblockY1 + '_'),
                             ('block_' + targetTVblockX0 + '_' + targetTVblockY + '_'),
                             ('block_' + targetTVblockX + '_' + targetTVblockY0 + '_'),
                             ('block_' + targetTVblockX1 + '_' + targetTVblockY0 + '_'),
                             ('block_' + targetTVblockX0 + '_' + targetTVblockY1 + '_')]
       
        for block in targetTVtempIndex:
            if block not in scene.objects:
               
                blockTerrain = scene.addObject(block, self)
                blockTerrain.worldPosition[2] = 0.0
               
                blockX = blockTerrain.name.split('_')[1]
                blockY = blockTerrain.name.split('_')[2]
               
                blockTerrain.worldPosition[0] = int(blockX)*20000 - 490000
                blockTerrain.worldPosition[1] = int(blockY)*20000 - 490000
       
        #Вычисление нужного квадрата, центрального блока ландшафта под камерой, целочисленные значения
        #20 - размер блока в км, 490 - поправка сдвиг влево и вниз на "начало отсчета" от левого нижнего угла
        #1000 - чтобы было меньше возни, отбрасываем метры, нас интересуют целые числа в диапазоне от 0 до 49
        activeCam = scene.active_camera
        quadroX = int((int(activeCam.worldPosition[0]/1000)+ 490)/20)
        quadroY = int((int(activeCam.worldPosition[1]/1000)+ 490)/20)
       
        #Вычисление индексов "огружаемых" блоков к камере игрока на ЛА - 25 блоков
        blockX = str(listIndex.index(quadroX))
        blockY = str(listIndex.index(quadroY))
        blockX0 = str(listIndex.index(quadroX+1))
        blockY0 = str(listIndex.index(quadroY+1))
        blockX1 = str(listIndex.index(quadroX-1))
        blockY1 = str(listIndex.index(quadroY-1))
        blockX2 = str(listIndex.index(quadroX+2))
        blockY2 = str(listIndex.index(quadroY+2))
        blockX3 = str(listIndex.index(quadroX-2))
        blockY3 = str(listIndex.index(quadroY-2))
       
        #Жестко зафиксированный список для 25 блоков - "догружаемых" к камере игрока на ЛА
        tempIndex = [('block_' + blockX + '_' + blockY + '_'),
                     ('block_' + blockX0 + '_' + blockY0 + '_'),
                     ('block_' + blockX1 + '_' + blockY1 + '_'),
                     ('block_' + blockX1 + '_' + blockY + '_'),
                     ('block_' + blockX + '_' + blockY1 + '_'),
                     ('block_' + blockX0 + '_' + blockY + '_'),
                     ('block_' + blockX + '_' + blockY0 + '_'),
                     ('block_' + blockX1 + '_' + blockY0 + '_'),
                     ('block_' + blockX0 + '_' + blockY1 + '_'),
                     ('block_' + blockX2 + '_' + blockY + '_'),
                     ('block_' + blockX2 + '_' + blockY0 + '_'),
                     ('block_' + blockX2 + '_' + blockY1 + '_'),
                     ('block_' + blockX2 + '_' + blockY2 + '_'),
                     ('block_' + blockX2 + '_' + blockY3 + '_'),
                     ('block_' + blockX3 + '_' + blockY + '_'),
                     ('block_' + blockX3 + '_' + blockY0 + '_'),
                     ('block_' + blockX3 + '_' + blockY1 + '_'),
                     ('block_' + blockX3 + '_' + blockY2 + '_'),
                     ('block_' + blockX3 + '_' + blockY3 + '_'),
                     ('block_' + blockX0 + '_' + blockY2 + '_'),
                     ('block_' + blockX1 + '_' + blockY2 + '_'),
                     ('block_' + blockX + '_' + blockY2 + '_'),
                     ('block_' + blockX0 + '_' + blockY3 + '_'),
                     ('block_' + blockX1 + '_' + blockY3 + '_'),
                     ('block_' + blockX + '_' + blockY3 + '_')]
                    
        for block in tempIndex:
            if block not in scene.objects:
               
                blockTerrain = scene.addObject(block, self)
                blockTerrain.worldPosition[2] = 0.0
               
                blockX = blockTerrain.name.split('_')[1]
                blockY = blockTerrain.name.split('_')[2]
               
                blockTerrain.worldPosition[0] = int(blockX)*20000 - 490000
                blockTerrain.worldPosition[1] = int(blockY)*20000 - 490000
           
        #Проверка на наличие лишних блоков и их уборка  
        for terrain in scene.objects:
            if 'block_' in terrain.name:
                if terrain.name not in tempIndex:
                    if terrain.name not in targetTVtempIndex:
                        terrain.endObject()

Суть в том, что есть два жестко фиксированных списка - один для камеры игрока на ЛА и вообще активной камеры в игре. Он рассчитан на 25 блоков, чтобы не было "пустот" на горипзонте (скорости-то большие). Второй список  рассчитан на 9 блоков и нужен он для камеры ТВ прицела, передающей изображение на мониторы в кабине. Вроде бы он и лишний, но в то же время могут произойти такие события, когда он понадобится. Обратите внимание на длинные списки переменных для вычисления индексов сопутствующих блоков. Названия, наверное, не вполне удачные, а списки имен блоков я сделал "напрямую", без циклов и перебора значений в списке. Зато это работает и ничто не мешает в будущем это дело изменить. Как вычисляются координаты блоков по Х и Y, исходя из их имен, думаю, тоже понятно. Там просто вводится поправка по причине отсутствия отрицательных значений - так сложилось.
в конце идет проверка на наличие лишних блоков и их удаление. Перед тем, как задействовать этот алгоритм я пытался действовать через замену мешей террайна. Однако он кушал лишние 10 процентов логики, потому что объектов в сцене было ну очень много. При проверке работоспособности данного скрипта выяснилось, что кушает он меньше процента при частоте обновления раз в секунду (против 70 процентов при частоте обновления раз в три секунды). Зарекусь пока говорить о полной и безоговорочной победе, чтоб не сглазить - об этом можно будет уверенно сказать при работающих миссиях и кампаниях. Тем не менее, это явный шаг вперед, по сравнению с предыдущими вариантами. И да, ландшафт имеет 640 килополиков в сумме, вылетов БГЕ пока не отмечено, игровой процесс пока кушает  4 процента.
Картинок пока не будет - поскольку изменения были внутренние, а не внешние.
Теперь необходимо переходить к созданию меню с возможностью выбора оружия и редактирования миссий и кампаний. К тому же меню еще и будет завязано на выбор района БД, погоды и 2декораций", типа городов и авиабаз.
В заключение о декрациях. что-то мне не улыбается добавлять построчно каждый объект на тот же аэродром при его вызове. Гораздо проще сделать вызов группы объектов - ВПП и ее потомков - ангаров, башен и прочего. И произвести замену мешей. Опять будут задействованы имена объектов с "отсечением" лишнего типа _021. И вообще сильно мне полюбилось менять меши, да. Недавно тестил новые эффекты взрывов с помощью замены мешей-кадров, а не вызова плейнов-объектов, как раньше. выяснилось что две сотни одновременных взрывов кушают всего-то 5 процентов логики. Для движка это большое облегчение. Чуть позже напишу и об этом.