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

суббота, 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, надо только решить, как реализовать погодные условия и время суток. Наверное, придется отказаться от сложных и сильно затратных шейдеров, а также от масштабных батальных сцен, потому что у БГЕ есть свой предел и через него не перепрыгнешь, хотя и стоит максимально постараться оптимизировать работу игры. Локальные конфликты малой интенсивности - вот на что будет способен проект. Ну и ладно. Лишь бы работало. Да и сам проект я хочу закончить скорее из-за чисто иррационального упрямства - жалко бросать так далеко продвинувшееся дело...

понедельник, 1 апреля 2019 г.

Большой Апдейт. И прочие новости...

Не писал я довольно давно. Тут причин мног7о, но главная, просто не знал, о чем писать...
БГЕ таки мертв, будут ли из него делать форки, помимо известных мне upBGE, неясно... С другой стороны, мне все ж стало жалко своих трудов на ниве быдлокодерства, и, помявшись, походив вокруг да около, я все же решил начать Большой Апдейт. Так сказать, прощальный аккорд для БГЕ. Плюс, таким образом я рассчитываю не забыть совсем уж навыки работы с Питоном, а заодно отработать кое-какие приемчики, могущие мне пригодиться в работе уже с Юнити (о нем чуть позже).
Правда, я не знаю, к чему и когда все это приведет, потому как назревает уже четвертая по счету операция для моего глаза (вполне ожидаемая вторичная катаракта), на какое время и когда мне придется "заткнуть свой фонтан" (С), пока непонятн6о, но и сидеть, сложа ручки, тоже неохота, а водку и прочие средства расширения сознания я не уважаю категорически.
Данный пост должен сыграть (и я надеюсь, сыграет), роль своеобразного пинка самому себе, дабы подьянуть расщатавшуюся за последнее время самодисциплину и хотя бы частично прогнать лень.
Итак.
В чем сущность Большого Апдейта (данное понятие следует рассматривать сугубо с иронической точки зрения)?
Сущность его в том, чтобы довести проект до хоть какой-то завершенности. Здесь основной задачей является упорядочивание структуры проекта и обретения новых навыков работы с файлами. А именно - новые методы поиска и загрузки моделей в игру, создание редактора миссий и кампаний и введения в состав доступных игроку юнитов зенитно-ракетных комплексов, вертолетов и танков. Хотя бы и в единственном экземпляре, причем необходимо повысить гибкость проекта и сделать его открытым для добавления новых юнитов (точнее, сохранить открытость). Это основная задача.
Вспомогательная задача - избавиться от завышенных ожиданий, ввести ограничения в проект, превращающий его в, гм... в симулятор конфликтов малой интенсивности (поскольку БГЕ не любит большого количества объектов). Например, для редакторов миссий и кампаний ограничить число одновременно находящихся в воздухе самолетов и вертолетов, скажем, не больше 20 с обеих сторон (в "Ил-2", кстати, ЕМНИП максимум можно было выставить 32 самолета с разных сторон). Скорее всего, онлайн-версию делать не буду (а так, не знаю, что там получится, зарекаться не буду). Приведу в порядок код, в соответствии с новыми знаниями, почерпнутыми мною из всевозможных наставлений и инструкций. Пейзаж покрасивше сделаю (кроме БГЕ, у меня есть и другие планы, но о них я пока помолчу ввиду неопределенности с кое чем - см. выше).

Теперь немного кода. Меня весьма раздражала выстроенная мною система подгрузки объектов в игру из блендов и необходимость долго и нудно расставлять разного рода слеши и двоеточия в файлах json. Почитав учебники и форумы, а также примеры по Питону и воспользовавшись подсказкой dron-a, я наваял очередной блок кода, в котором происходит поиск и установление всех путей ко всем файлам проекта и их сортировка. Пути к файлам Питона добавляются в json файл, лежащий рядом с пусковым блендом проекта. По задумке, игра должна сама смотреть нужные ей пути и тащить в себя необходимые бленды, а не пользоваться готовеньким и разжеванным заранее, что должно сильно сократить как сам код загрузки объектов, так и объем файлов json, в которых не надо будет прописывать пути.

import bge
import sys
import os
import json

#Функция создания файла json с путями к файлам всего проекта
def startPath():
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner

    #Создаем переменные - список путей и пустую строку для путей
    pathList = []
    k = ""
   
    #Циклом перебираем файлы, директории и папки, создаем и сшиваем строки в пути
    for d, dirs, files in os.walk(bge.logic.expandPath("//")):
        for f in files:
           
            #Обрабатываем вид путей, убирая слеши и название стартовой директории
            k = os.path.join(d,f)
            k = k.split("BlendSim_2.1")[1]
            k = k.replace("\\", "/")
           
            #Питон-файлы добавляем в sys.path, остальные в список путей
            if ".py" not in k:
                pathList.append("/"+k)
            else:
                sys.path.append("/"+k)
                   
    #Открываем файл json  с путями (если его нет, то он создается) и сбрасываем туда список путей
    with open(bge.logic.expandPath('//NodePath.json'), 'w') as NODEPATH:
        json.dump(pathList, NODEPATH)
   
    #Это просто проверка   
    proverka(own, scene)
 
def proverka(own, scene):
    missile = None

    with open(bge.logic.expandPath('//NodePath.json'), 'r') as READPATH:
        JSONREADPATH = json.load(READPATH)
       
        for ob in JSONREADPATH:
            if "R-24R.blend" in ob:
                print(ob)
                bge.logic.LibLoad(bge.logic.expandPath(ob), 'Scene', load_actions = True)
                missile = scene.addObject("R-24R", own)
       
Ну, я думаю, с комментариями будет все ясно, для чего все это надо. Проверка в виде появления на сцене ракеты Р-24Р прошла успешно и неоднократно...

Теперь о Юнити. В свое время я скачал и Юнити и Анрил Энжин 4. Второй движок ощутимо мощнее и совершеннее первого, да вот беда, пользоваться им смогут не все, я - пока точно нет. Его недостатком является отсутствие большого количества документации, в отличие от Юнити, к тому же, на форуме b3d.ua мне много раз говорили, что Си Шарп относительно простой в освоении язык (но, к сожалению, я тугодум и с налету его понять не могу, изображая обезьяну, повторяющую поведение человека, а именно, создавая код по примерам и образцам, не понимая, для чего и ка, впрочем, при освоении Питона начиналось так же). В Юнити для меня еще остались кое-какие нюансы, лежащие за пределами моего понимания, например, как присвоить материал дереву и его листьям (перетаскивание порой сильно раздражает, проше было бы набрать на клавиатуре). Но все-таки, я добавил в Юнити многострадального подопытного кролика - МиГ-23МФ из моего же бленд-проекта и попробовал для начала помахать крылышками. Не сразу, но это получилось. Код, наверное, я пока приводить не буду - он состоит из занудного объявления публичных переменных и всего-то из десятка строчек кода для крыльев. Пока я не столько работаю в Юнити, сколько пытаюсь уложить в свою голову хотя бы азы Сишарпа. И прежде всего - взаимодействие скриптов между собой, минуя Инспектор Юнити. У меня он ассоциируется с логическими кирпичами БГЕ, которые необходимо использовать только там, где это действительно нужно. А многократно переделывать  еще и переменные в инспекторе - нет уж. Лучше сразу научиться хотя бы по минимуму работать с кодом, а не таскать переменные и оббъекты с одно края экрана на другой (да и окошечки там маленькие, с моим зрением сейчас...)
Ну ладно, будем считать, что стартовый шаг сделан (точнее, самопинок) и блогспот не накроется медным тазом, как Гуглплюс (ходили т акие слухи).

пятница, 2 февраля 2018 г.

Классы-наследники для оружия. Доводка напильником.

На некоторое время работа над прпоектом как бы подзависла. Причиной тому было продолжавшееся"дробление" скриптов, кстановление взаимосвязи между ними, шлифовка кода и его "притирка". Долгое время никак не удавалось отработать поражающее действие оружия на юнитах - из-за того, что когда я ломал старую схему, в импортируемом новом коде сдублировал функции. После чего корректная работа кода нарушилась. Также я долго и безуспешно пытался понять причину странного поведения ракет Р-27Р - после пуска с МиГ-29 одна обязательно взрывалась почти сразу (самоликвидация), а вторая шла в цель. Позже выяснилось, что причина просто в некорректном разрешении на пуск - олно выдавалось за пределами дальности, поэтому придется еще пошерстить скрипт сеносрра юнитов, чтобы этот баг устранить.
Но сначала я грешил на сам класс оружия и такие основания у меня были. Хотя я вроде как и освоил классы, и даже классы наследники, но не все так просто. Когда идет мутация объекта - при создании класса, то данные в самом классе меняются для всех объектов, которые ранее этот самый классс использовали. Иначе я никак не могу объяснить феномен поиска деталей МиГ-29 у Ф-15 (а именно такую ошибку мне выадвала консоль) или деталей МиГ-23 у Ф-16. Это безобразие продолжалось ровно до тех пор, пока я не ввел наследование классов для юнитов. В итоге подобные вещи прекратились, потому что классы МиГ-29 и Ф-15, а также МиГ-23 и Ф-16 более не пересекаются. Это отдельные классы, хотя и имеющие схожую структуру.
Весьма раздраженный поведением Р-27Р и еще не ведая об истинных причинах, я приступил к созданию классов наследников для оружия. По-видимому, я все же набил руку, потому что сделал это практически с первого залпа. Отработав технику на Р-27Р, принялся терпеливо дописывать json  с данными для оружия и для оружия же писать скрипты класса. Папок было довольно много, поэтому делал я это в несколько приемов, затратив пару дней. Несложно, но одноообразно и надо быть внимательным, чтобы не проскочили символы типа плюс-минус в заголовке класса - консоль тут же выразит свое возмущение...
Теперь превращение 30-мм снаряда в 152-мм во время выстрела и его полета к цели исключено. Схема выглядит так.
Запускаем скрипт в игровом файле - тащим из нужной папки класс оружия, название питоновского файла обычно выглядит так - R27R.py.

def Weapon(cont):
    own = cont.owner
   
    nameWeapon = ""
    nameGeneral = own.parent.name
    nameSysPath = ""
    #print(own.parent, own.childrenRecursive)
    if hasattr(own.parent, "dictWeapon") == True:
        if "WPU" in own.parent.dictWeapon:
            nameGeneral = own.parent.dictWeapon["WPU"]["gunBullet"]
    if hasattr(own.parent, "parent") == True:
        #print(own.parent.parent)
        if hasattr(own.parent.parent, "parent") == True:
            #print(own.parent.parent.parent)
            if hasattr(own.parent.parent.parent, "dictWeapon") == True:
                if "bulletGen" in own.parent.parent.parent.dictWeapon:
                    #print("True")
                    nameGeneral = own.parent.parent.parent.dictWeapon["bulletGen"]
                    #print(nameGeneral)
           
    nameSysPath = "//Weapon/"+nameGeneral   
   
    sys.path.append(bge.logic.expandPath(nameSysPath))
    with open(bge.logic.expandPath("//Weapon/"+nameGeneral+"/"+nameGeneral+".json"), 'r') as directWeaponClass:
        JSONweaponClass = json.load(directWeaponClass)
        nameWeapon = JSONweaponClass["ownClass"]
   
    unit_module = __import__(nameWeapon)
    #print(nameWeapon)
    unit_class = getattr(unit_module, nameWeapon)
    #print(nameGeneral)
    x = unit_class(own)
   
    #import ClassWeapon
    #own = cont.owner
    #x = ClassWeapon.typeWeapon(own)
   

А потом уже тащим класс-наследник. Он совсем маленький.

import bge

from ClassWeapon import typeWeapon

class R27R(typeWeapon):
    def __init__(self, old_owner):
        typeWeapon.__init__(self, old_owner)
   
def mutate(cont):
    R27R(cont.owner)

А этот класс сначаа тащит стандартный класс оружия, который лежит в папке со скриптами для оружия.

import bge
import json

with open(bge.logic.expandPath('//Weapon/WEAPON_CLASS.json'), 'r') as directWeapon:
    JSONweapon = json.load(directWeapon)

class typeWeapon(bge.types.KX_GameObject):   
   
    def __init__(self, old_owner):
        self.__dict__.update(JSONweapon)
       
def mutate(cont):
    typeWeapon(cont.owner)


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


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

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

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

Дао скриптов. Перманентный погром, как оптимизация.

"Лев Революции", товарищ Троцкий (ставший потом Иудушкой после изменения политичекой еонъюктуры) как-то провозгласил: "Есть у революции начало и нет у революции конца". Это изречение вполне применимо к моему проекту, хотя, если честно, мне гораздо больше нпавится другое его изречение: "И пусть наши враги знают - на всякую принципиальность с их стороны мы ответим абсолютной беспринципностью!" Но что есть - то есть.
В прошлом посте я писал, по поводу "дробления" огромных кусков кода на удобочитаемые скрипты относительно небольшого объема. Что ж, схема обретает свои очертания. Итак, сначала мы используем священное заклинание import sys. А потом перечисляем требующиеся нам пути к папкам с новыми скриптами.
import bge

import sys
for pathFolder in ["Scene_Scripts","UnitAir_Scripts", "UnitGround_Scripts", "Weapon_Scripts"]:
    sys.path.append(bge.logic.expandPath("//Scripts/" + pathFolder))

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

1. Первый уровень, видимый в пусковом бленде и там же находящийся. Я его обозвал скрипт-нод. Например CONTROL_UnitAir. Он отвечает за поведение летательного аппарата, безразлично от того, летит ли он или стоит на аэродроме, изображая мишень. Теперь от огромного скрипта остались только пара десятков строчек. Непрервыно в этом скрипте работает лишь система детализации. При условии, что ЛА летит, из папки со скриптами вызывается скрипт UnitAir_engine.

def UnitAir():
    cont = bge.logic.getCurrentController()
    own = cont.owner
   
    if own.controlUnit != "Statist":
        UnitAir_engine.engine(own)
       
    #Работа уровня детализации идет ВСЕГДА
    UnitAir_LODes.LODes(own)

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

3. Уровень три. так сказать, уровень нюансов. Здесь скриптов гораздо больше и лни помельче. Это, к примеру, описание работы сенсоров самолета (РЛС, теплопеленгатор и тд), описание анимации подвижных деталей (рули высоты, элероны и тд), описание поведения самолета при штопоре, модель разрушения и так далее. В свою очередь, некоторые из этих скриптов ведут все дальге и вниз. На четвертый уровень, если надо.

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

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

пятница, 20 октября 2017 г.

Опытный кролик-2. Новые методы использования классов.

В прошлом посте я писал о своих опытах с МиГ-23БН, который был моим подопытным кроликом в деле отработки новых методов использования классов.
Тогда речь шла всего-навсего о создании класса-наследника с целью "развести" и не допустить "перекрещивания" данных при создании юнитов.
однако совет dron-a по поводу использования методов в самом классе заставил меня задуматься... когда я только начинал осваивать классы, я уже, сам того не зная, писал в них методы, но не знал, как правильно их использовать. видимо, для осознания новых возможностей и способов требуется время и с "лету" схватывать я таки не способен. Но все-таки медленно, но учусь. Для начала приведу пример с использованием методов теле класса на примере класса уже очень опытного виртуального колика - многострадального МиГ-23БН.

import bge

from ClassUnitAir  import UnitAir

class MiG23BN_Libya_(UnitAir):
    def __init__(self, old_owner):
        UnitAir.__init__(self, old_owner)
    #Летательный аппарат
    def is_AIR(self):
        return True
    #Подтип ЛA - вртолет, самолет (реактивный или внитовой)
    def is_AIRTYPE(self):
        return "JET"
    #Пилотируемый или БПЛА
    def is_DRON(self):
        return False
   
    #Одно- или многодвигательный
    def is_MULTIENGINE(self):
        return False
    def is_CHASSY(self):
        return True
   
    #######################################
    def is_CANOPY(self):
        return True
    def is_CANOPY_Device(self):
        CANOPY_Device = [
                        ["Cnp_",[0.0087,0.0,0.0],"rotat"]
                        ]
        return CANOPY_Device
    def is_POINT_CANOPY(self):
        reper = [0, 114]
        return reper
    ########################################
    def is_FLAPS(self):
        return True
    def is_FLAPS_Device(self):
        FLAPS_Device = [
                        ["FlpR_",[-0.0087,0.0,0.0],"rotat"],
                        ["FlpL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return FLAPS_Device
    def is_POINT_FLAPS(self):
        reper = [-50, 0, 100]
        return reper
   
   
    ######################################
    #Наличие-отсутствие предкрылков
    def is_SLATS(self):
        return True
    def is_SLATS_Device(self):
        SLATS_Device = [
                        ["SltR_",[-0.0087,0.0,0.0],"rotat"],
                        ["SltL_",[-0.0087,0.0,0.0],"rotat"]
                        ]
        return SLATS_Device
    def is_POINT_SLATS(self):
        reper = [0, 40]
        return reper
   
    #####################################
    #Наличие-отсутствие крыла изменяемой стреловидности
    def is_SWINGWING(self):
        return True
    def is_POINT_WINGS(self):
        reper = [0, 300, 550]
        return reper
   
    def is_WINGS_Device(self):
        WINGS_Device = [
                        ["WngR_",[0.0,0.0,-0.00174],"rotat"],
                        ["WngL_",[0.0,0.0,0.00174],"rotat"]
                        ]
        return WINGS_Device
   
    ####################################
    #Наличие-отсутствие воздушных тормозов
    def is_AIRBRAKE(self):
        return True
    def is_AIRBRAKE_Device(self):
        AIRBRAKE_Device = [
                           ["ArbUR_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbUL_",[-0.00783,0.0,0.0],"rotat"],
                           ["ArbDR_",[0.00696,0.0,0.0],"rotat"],
                           ["ArbDL_",[0.00696,0.0,0.0],"rotat"]
                          ]
        return AIRBRAKE_Device
   
    def is_POINT_AIRBRAKE(self):
        reper = [0, 100]
        return reper

             
def mutate(cont):
    MiG23BN_Libya_(cont.owner)

Смотрим на функции типа def is_чегго-то-там. Это не что иное, как некие константы - например наличие или отсутствие крыла изменяемой стреловидности, принадлежности к воздушным юнитам, но эти константы, например, могут нести такую ценную информацию, как скорость поворота крыла и его название. Первоначально я хотел ставить не многомерные списки, а словари. Однако, после многократной ругани со стороны консоли БГЕ о невозможности прочесть из функции класса метод с этим самым словарем, решил все же поставить многомерные списки. Посмотрим, мрожет потом я все-таки пойму, где ошибался и поставлю словари.
Что это дает? Это дакт возможность пока для стандартных анимаций (точнее псевдоанимаций, потому как идет просто движение деталей через скрипт) загнать их работу в единый скрипт, избавив бленды моделей от лишнего кода по тем же перемещениям КИС для МиГ-23. Экономия существенная.
Посмотрим, как это работает. Для начала команда на перекладку крыла из скрипта для игрока CONTROL_gamer.

#Смена стреловидности - для самолетов с КИС
    if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED or keyboard.events[bge.events.TABKEY] == JUST_ACTIVATED:
        #Проперти wing у самолетов с КИС имеет минимальное значение 0. При смене стреловидности вызывается класс юнита
        #и проводится смена значений максимальных значений скорости и маневренности
        if own.FLAPS == 0:
            if own.Temp_WINGS == own.WINGS:
                if keyboard.events[bge.events.QKEY] == JUST_ACTIVATED:
                    if own.is_SWINGWING():
                        own.WINGS += 1

Сначала при нажатии клавиши идет проверка наличия крыла изменяемой стреловидности у данного юнита. смотрим is_SWINGWING  в коде выше.Если там стоит True, то команда проходит и начинается самое  интересное. Функция перекладки в скрипте CONTTROL_UnitAir универсальна для всех типов самолетов с КИС. Вообще всех. Такова особенность построения моделей и использования в них названий.
#Псевдоанимации крыльев
def wings(own):
    #Направоение перекладки крыла
    wings = 0
   
    #Смена стреловидности возможна лишь при убранных закрылках
    if own.WINGS > own.is_POINT_WINGS()[-1]:
        own.WINGS = own.is_POINT_WINGS()[-1]
    elif own.WINGS < 0:
        own.WINGS = 0
   
    #Выставление напрaвления перекладки и убывания-возрастания проперти
    if own.Temp_WINGS < own.WINGS:
        wings = 1
        if own.WINGS-own.Temp_WINGS == 5:
            AudioEmitter = scene.addObject('AudioEmitter', own, 220)
            AudioEmitter.setParent(own, False, False)
            AudioEmitter["audioProp"] = "FLAPS"
           
        own.WINGS += 1
   
    elif own.Temp_WINGS > own.WINGS:
        wings = -1
        own.WINGS -= 1
        if own.WINGS-own.Temp_WINGS == -5:
            AudioEmitter = scene.addObject('AudioEmitter', own, 220)
            AudioEmitter.setParent(own, False, False)
            AudioEmitter["audioProp"] = "FLAPS"
       
    #Собственно, движение крыльев - это действие не зависит от уровня детализации
    for key in own.is_WINGS_Device():
        if key[2] == "rotat":
            own.childrenRecursive[key[0]].applyRotation([key[1][0]*wings,
                                                         key[1][1]*wings,
                                                         key[1][2]*wings
                                                         ],True)
   
    #Остановка псевдоанимации на кадрах со значением 0,300,550   
    if own.WINGS in own.is_POINT_WINGS():
        own.Temp_WINGS = own.WINGS

Здесь используются данные о реперных точках положения крыла - там , где должна прекратиться анимация и жестко выставлены ограничения слева и справа. Это is_POINT_WINGS.  А еще используются данные из многомерного списка. я думаю, понятно, как - название детали, скорость поворота, флаг - "поворот".

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

пятница, 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М.


понедельник, 5 июня 2017 г.

Время собирать камни...

Перестройка под классы в общем-то, закончена. Была создана отключаемая РЭБ -при наличии таковой ее можно включить или выключить. Было переделано все, что касается обмена данными об угрозах, что обеспечило стабильную работу СПО. Были доведены до рабочего состояния самонаводящиеся ракеты. интересно, что в версии 2.78 физика динамических объектов изменилась. Если раньше при наличии "толкающей" силы или линейной скорости по оси Х или Y объект спокойно двигался по горизонтали без опоры и падать не собирался, то теперь без опоры он просто падает. Поэтому для ракет пришлось физику отключать совсем, оставив динамические свойства лишь для бомб и подобных им объектов. Для чего сие нововведение понадобилось - тайна скрытая мраком. Во всяком случае пару недель на осознание этой "фичи" с поиском решения ушло...
Была создана тестовая версия под все эти вещи - ссылку можно найти на b3d.ua. Здесь я ее не даю, поскольку вещь весьма сырая и представляет интерес разве что с точки зрения истории проекта. тем более, что и весит архив около 450 Мб.
Сейчас идет работа по восстановлению того, что было в версии 2015 года, а именно - подключение юнитов, пока только самолетов. Планируется сделать пару-тройку тестовых миссий  с возможностью выбора из меню. Кстати, меню я частично реанимировал, но его практическая ценность пока околонулевая. Для создания тестовых миссий необходимо восстановить и поправить F-5E, F-15C, F-16C, МиГ-29А и МиГ-21бис. В сущности, F-15/16 готовы, все дело упирается в файлы загрузки подвесок. Дело муторное и нудное, хотя и несложное. "Пятерка" же по имени "Тайгер" , как и МиГ-29 пока нуждается в доводке - сама модель и скрипт псевдоанимаций. МиГ-21 вообще новый юнит, в версии 2015 его не было даже близко. Его модель готова, но пока нет кабины и опять же скрипт псевдоанимаций с файлами загрузки оружия. В основном, сейчас все идет по написанию json, хотя пилоны и баки для МиГ-21 были распиханы по папкамс названиями и туда же вставил json  с их массами и лобовым сопротивлением.
По мере доводки и гготовности поланирую создать раздел миссий "Одноклассники". Суть в том, что в воздухе встречаются самолеты примерно "одной весосвой категории", список спарринг-партнеров примерно такой: Ф-5:МиГ-21, Ф-4:МиГ-23, Ф-16:МиГ-29, Ф-15:Су-27. Аналогично в перспективе Ф-104:Су-15, Ф-100:МиГ-19, Ф-86:МиГ-15, Ф-18:Су-33. Но это пока только планы. Делать прогнозы не берусь.
К тому же необходимо заняться доработкой ландшафта и освещения, шейдером неба и звуковым классом. Много чем заняться...

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

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

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

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

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

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

среда, 8 февраля 2017 г.

Как не надо кодить. Создание и ликвидация бага своими руками.

Я не раз замечал за собой особенность создавать такие ситуации, которые, по-видимому, кроме меня никто не создаст. Во всяком случае в здравом уме, трезвом состоянии и твердой памяти. По-видимому, это у меня такой талант.
Продолжая работу над проектом, я внезапно столкнулся с одной очень специфической проблемой. Как известно, если самолет ударяется об землю, он взрывается. Дальше все зависти от фантазии автора проекта - появляется ли черный дымщийся остов или самолет просто исчезает. В любом случае юнит "убирается с доски". Так вот, неожиданно выяснилось, что если об землю разбивается самолет игрока, то после нажатия кнопки Esc немедленно следует вылет Блендера. Причем было непонятно, что это за баг. dron написал файл для запуска файла через блендерплейер с выводом ошибок в файл error, но ситуацию это не прояснило. Файл оставался пустым.
Баг крайне непрятный и его надо было ликвидировать любой ценой. Но и понять, в чем причина было сложно. я рассуждал так: если вылет следует после исчезновения самолета игрока, значит, это как-то связано с управлением. спешно переделал код управления клавишами - уже тогда у меня появились подозрения, что в этом виновато исезновение оверлейной сцены кабины. Запустил - то же самое. Хорошо, тогда почистим скрипт управления камерами. Дело в том, что я по жадности своей и лени не стал, как в первой версии делать обмен данными между сценами через глобальный словарь, а запускал цикл проверки списка сцен с выбором сцены кабины и дальше уже традиционно обращался к scene.objects с названием объекта. Как говорится: "Жадность фраера сгубила." Однако и после зачистки скрипта камер веселье продолжилось. У меня оставался один-единственный скрипт, где еще существовал вызов списка сцен. А теперь смотрим...

def controlUnit():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = bge.logic.getCurrentScene()
    sensor = cont.sensors["unit"]
    kontakt = cont.sensors["kontakt"]
   
    #Название пропенрти для механики ведомого - при условии
    #что юнит - ведущий, при выпуске тормоза, крыла, шасси и тп он передает команду на ведомого для выполнения такой же операции
    mechanicWingman = ''
   
    #словарь свойств - для анимации, применения оружия и смены подвесок, плюс отслеживание повреждений
    if 'localDict' not in own:
        controlUnitStart()
       
    #Активная камера в сцене
    activeCam = scene.active_camera
       
    #Камера в кокпите юнита игрока
    cameraPilot = scene.objects["CameraPilot"]
   
    #Камера внешнего обзора, она же камера телевизионного прицела
    cameraWorld = scene.objects["CameraWorld"]
   
    #Центр юнита - опорный объект для всх потомков, он же нужен для смены меша на меш номера, объект присутствует изначально, это потомок
    #двигателя, причем неубираемый, также он нужен для анимации проворота фюзеляжа при выпуске-уборке шасси, поскольку на земле многие
    #машины не стоят ровно - как правилог их нос приподнят - больше или меньше
    CntAircraft = own.childrenRecursive['CntAircraft']
   
    #Это - собственно, полет
    if sensor.positive:
       
        #Контакт с ландшафтом
        if kontakt.positive: 
            #столкновение с землей сбитого самолета или вертолета
            if own['crash'] < 0.2 or own['CHASSY'] < 98:
                if 'CameraPilot' in own.childrenRecursive:
                    own.childrenRecursive['CameraPilot'].removeParent()
                    #Убираем оверлейную сцену
                    sceneList = bge.logic.getSceneList()
                    for sceneGame in sceneList:
                        if sceneGame.name == 'Cockpit':
                            sceneGame.end()
               
                CrashPropertyAir()
           
                import ControlScene
                ControlScene.nearUnitExplode()
                #Убираем сам объект
                own.endObject()
       
       
        if own['bot'] == 0:
            #Вызов управления юнита игрока с помощью клавиатуры, вызов идет постоянно
           
            bge.logic.globalDict['idTarget'] = own['idTarget']
            import ClassGamer
            ClassGamer.control()
        elif own['bot'] > 0:
            #Вызов искусственного интеллекта - необходимо доработать, поскольку own['bot']=1 - взлет, 2 - посадка, 3 - полет по маршруту,
            import ClassBotAir
            ClassBotAir.control()
       
        #Пока ЛА исправен, он может лететь
        if own['crash'] > 0 and own['bot'] > -1:
            import ClassEngineAir
            ClassEngineAir.control()
       
        #Падение по балиистической кривой для сбитого
        elif own['crash'] < 0.1 and own['bot'] > -1:
            unit_Crash = __import__(own['unitModule'])
            unit_Crash.CRASHscenery()
            #Для самолета игрока убираем сцену оверлей и камеру пилота
            if own['bot'] == 0:
                if 'sceneryCrash' in own:
                    if own['sceneryCrash']['timerCrash'] == 1: 
                        if 'CameraPilot' in own.childrenRecursive:
                            own.childrenRecursive['CameraPilot'].removeParent()
                    #Убираем оверлейную сцену
                    sceneList = bge.logic.getSceneList()
                    for sceneGame in sceneList:
                        if sceneGame.name == 'Cockpit':
                            sceneGame.end()        
            import ClassEngineAir
            ClassEngineAir.traectoryCrash()
         
        #Просчет уровней детализации
        #Высокий - детализированная анимированная модель
       
        if own.getDistanceTo(activeCam) < 200:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 0
        #Средний - детализированная неанимированная модель
        elif 200 < own.getDistanceTo(activeCam) < 3500:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 1
        #Низкий - малодетализированная модель
        elif 3500 < own.getDistanceTo(activeCam) < 10000:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 2
        #Малый - модели, как таковой, нет
        elif own.getDistanceTo(activeCam) > 10000:
            if activeCam.getVectTo(own)[2][2] < -0.8 and abs(activeCam.getVectTo(own)[2][1]) < 0.4 and abs(activeCam.getVectTo(own)[2][0]) < 0.24:
                own['levelsDetails'] = 3
       
       
           
        unit_module = __import__(own['unitModule'])
        unit_module.correctData()
       
        ####################################
        #Смена уровня детализации
        if own['localDict']['levelsDetails'] != own['levelsDetails']:
            ClassOperation.LOD()
            own['localDict']['levelsDetails'] = own['levelsDetails']
           
        if own['localDict']['dvig'] != own['dvig']:
            ClassOperation.dvig()
            own['localDict']['dvig'] = own['dvig']
           
        if own['localDict']['crash'] != own['crash']:
            import ClassCrash
            ClassCrash.CrashModel()
            own['localDict']['crash'] = own['crash']
       
        #Пробитие звукового барьера  
        if own['localDict']['sonicBoom'] != own['sonicBoom']:
            ClassOperation.sonicBoom()
            own['localDict']['sonicBoom'] = own['sonicBoom']
       
        if own['localDict']['startLO'] != own['startLO']:
            for generatorLO in own.childrenRecursive:
                if generatorLO.name == 'generatorLO':
                    generatorLO['startLO'] = own['startLO']
            own['localDict']['startLO'] = own['startLO']
       
        if own['levelsDetails'] == 0:          
            #Работа проперти рысканья              
            if own['rotatZ'] != 0 or own['localDict']['YAW'] != own['YAW']:
                unit_module.yaw()
            #Работа проперти тангажа              
            if own['rotatY'] != 0 or own['localDict']['ROLL'] != own['ROLL']:
                unit_module.roll()
            #Работа проперти тангажа              
            if own['rotatX'] != 0 or own['localDict']['PITCH'] != own['PITCH']:
                unit_module.pitch()
           
        #Стандартная операция - отсчет кадров с помощью проперти
        #Шасси
        if own['localDict']['CHASSY'] != own['CHASSY']:
            unit_module.chassy()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['CHASSY'] > own['CHASSY']:
                if own['localDict']['CHASSY'] == own['CHASSY'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
            if own['localDict']['CHASSY'] < own['CHASSY']:
                if own['localDict']['CHASSY'] == own['CHASSY'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
       
        #Тормоз
        if own['localDict']['AIRBRAKE'] != own['AIRBRAKE']:
            unit_module.airbrake()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['AIRBRAKE'] > own['AIRBRAKE']:
                if own['localDict']['AIRBRAKE'] == own['AIRBRAKE'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'AIRBRAKE'
                    audioUnit()
            if own['localDict']['AIRBRAKE'] < own['AIRBRAKE']:
                if own['localDict']['AIRBRAKE'] == own['AIRBRAKE'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'AIRBRAKE'
                    audioUnit()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['AIRBRAKE'] - own['AIRBRAKE']) == 2:
                    mechanicWingman = 'AIRBRAKE'
                    controlUnitWingman(own, mechanicWingman)
                   
           
        #Перекладка крыла - для самолетоа с КИС
        if own['localDict']['WINGS'] != own['WINGS']:
            unit_module.wings()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['WINGS'] > own['WINGS']:
                if own['localDict']['WINGS'] == own['WINGS'] + 2:
                    own['localDict']['audioLife'] = 230
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            if own['localDict']['WINGS'] < own['WINGS']:
                if own['localDict']['WINGS'] == own['WINGS'] - 2:
                    own['localDict']['audioLife'] = 230
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['WINGS'] - own['WINGS']) == 2:
                    mechanicWingman = 'WINGS'
                    controlUnitWingman(own, mechanicWingman)
             
        #Закрылки
        if own['localDict']['FLAPS'] != own['FLAPS']:
            unit_module.flaps()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['FLAPS'] > own['FLAPS']:
                if own['localDict']['FLAPS'] == own['FLAPS'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
            if own['localDict']['FLAPS'] < own['FLAPS']:
                if own['localDict']['FLAPS'] == own['FLAPS'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'FLAPS'
                    audioUnit()
           
           
        #Предкрылки
        if own['localDict']['SLATS'] != own['SLATS']:
            unit_module.slats()
            #Передача команды ведомому срабатывает только один раз
            #Заодно и передается название проперти, которое надо подтянуть
            if own['statusDict']['listWingman'][0] != '1':
                if abs(own['localDict']['SLATS'] - own['SLATS']) == 2:
                    mechanicWingman = 'SLATS'
                    controlUnitWingman(own, mechanicWingman)
           
        #Фонарь кабины
        if own['localDict']['CANOPY'] != own['CANOPY']:
            unit_module.canopy()
            #Стандартная опреация для проигрывания звука
            if own['localDict']['CANOPY'] > own['CANOPY']:
                if own['localDict']['CANOPY'] == own['CANOPY'] + 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
            if own['localDict']['CANOPY'] < own['CANOPY']:
                if own['localDict']['CANOPY'] == own['CANOPY'] - 2:
                    own['localDict']['audioLife'] = 100
                    own['localDict']['audioProp'] = 'GEAR'
                    audioUnit()
       
        ######################################################
        ######################################################
        ######################################################
       
        #Смена подвесок - одиночный вызов функции из скрипта ClassOperation, он изначально вызван в этом скрипте
        if own['localDict']['weapon'] != own['weapon']:
            own['PR'] = 0
            own['sbros'] = 0
            #Запускаем функцию работы по отсоединению подвесок и использованию оружия
            ClassOperation.weaponsUnit()
            #Корректируем значения словаря юнита
            own['localDict']['weapon'] = own['weapon']
           
        #Стрельба - одиночный вызов функции из скрипта ClassOperation, он изначально вызван в этом скрипте
        if own['localDict']['sbros'] != own['sbros']:
            if own['sbros'] == 1:
                ClassOperation.shooting()
            own['localDict']['sbros'] = own['sbros']
       
        #Это - определение целей при смене текущй цели или выборе типа прицеливания
        if own['localDict']['enemy'] != own['enemy'] or own['localDict']['targetType'] != own['targetType']:
            if len(own['localDict']['localTargetList']) > 0:
                try:
                    own['idTarget'] = own['localDict']['localTargetList'][own['enemy']]
                    if 'CameraPilot' in own.childrenRecursive:
                        own.childrenRecursive['CameraPilot']['idTarget'] = own['idTarget']
                except:
                    own['enemy'] = 0
                    own['idTarget'] = own['localDict']['localTargetList'][0]
                    #При ошибке убираем индекс из списка
                    #if own['idTarget'] in own['localDict']['localTargetList']:
                       #own['localDict']['localTargetList'].remove(str(id(target)))
                   
           
            own['localDict']['enemy'] = own['enemy']
            own['localDict']['targetType'] = own['targetType']

        #Сортировка списка угроз
        if own['timerThreat'] > 3.0:
            threatSorted()
           
       
        #Работа сенсоров - вызов функции из скрипта ClassSensor, он вызывается через разные промежутки времени, минимум раз в секунду
        #и только при "живом юните", время паузы между сканированием регулируется - оно задается при переключении сенсоров
        if own['crash'] > 0.1 and own['bot'] > -1:
            #if own['limTimerScan'] > own['scanTimer']:
               
                sensorUnit = own['localDict']['sensList'][own['typeSensor']]
                ClassSensor.SensorDef()

Для облегчения понимания я подчеркнул место своей логической ошибки. Получается, что при получении нулевого уровня исправности происходит повторный вызов списка сцены и требование убрать уже несуществующую! Причем эта операция осуществляется за один тик - в один проход функции. Во всяком случае, я такой вывод сделал, скажем так. Из этого следует вывод, что так быдлокодить нельзя. Питон, конечно терпелив и высвечивает программеру-кодеру его ошибки в консоли, но и у него иногда лопается терпение. Может быть, кто-то из гораздо более сведущих в программировании людей посчитает этот вылет багом самого Питона, в котором не выполняется исключение перехвата ошибок. Однако подобный прискорбный случай говорит и том, что никто, кроме меня, подобную ситуацию еще не создавал...
В общем, проблема была решена путем удаления лишнего обращения к функции. я жестко указал в коде, что операция по поиску и удалению оверлейной сцены должна происходить только при получении проперти crash = 0. И неважно, чем это вызвано - стокновением с землей, сбитием, столкновением с другим юнитом. Меняется проперти crash? Оно равно нулю? Вызываем функцию CrashModel. Только ОДИН раз, и в ней же проверяем - есть в потомках камера пилота? Тогда убираем оверлейную сцену. ВСЕ!
После переделки кода последовал запуск-тест. специально гробанул многострадального "кролика" подопытного МиГ-23 об землю. Выхожу из игрового режима. Все в норме. Еще раз! все чисто. Но, товарищи, так кодить нельзя.
правда, попутно еще выловил и устранил баг с пушкой, которую я сломал в ходе своих "оптимизаций" довольно давно, почистил код. К тому же, действуя по принципу: "Коль пошла такая пьянка, режь последний огурец", в очередной раз устроил погром в скрипте кабины. Собирался я это сделать давно, просто удачно под руку подвернулось. Многие оперции в кабинном скрипте стандартны, как и имена объектов сцены. к примеру, лампы индикации подвесок, закрылков, тормоза, шасси. Нет смысла из файла в файл плодить одни и те же функции с одинаковыми названиями и одинаковым до буквы кодом. Поэтому в игровом файле-стартере я создал еще один скрипт ClassCockpit, куда и сбросил все стандартные функции из кабинногог кода. Теперь в коде кабины работают только стрелки приборов да те вещи, которые уникальны для того или иного самолета. Например индикация топливной системы.
Буквально на днях Андрей предложил переделать скрипт загрузки сторонних бленд-файлов через асинхронизацию и дал образец-пример. Надеюсь, сумею разобраться и применить относительно своего файла, потому что собирался это делать в любом случае, к тому же в способе dron-a есть возможность вставить красивую картинку, а не черный плейн, как у меня. Будем продолжать движение.