Главная › Форумы › Blender Game Engine (BGE) › Курс по BGE от drdotmom
Помечено: BGE, bge phyton, Blender, Игровая графика, игры
По многочисленным просьбам (Одной. От пользователя @edik). Пишу урок(?) по BGE.
Небольшое отступление.
Я люблю компьютерную графику за её гибкость и возможность даже под самые кривые руки подобрать инструмент. Если вы не можете рисовать кисточкой в фотошопе — вы можете взять мягкую в Krita. Если и с этим беда — вы можете взять какой-нибудь Moho и «строить» аккуратные кривые в векторе. В этом чудесном мире всё упирается в ваше желание постигать новое и творить, передавать свои мысли и настроение без каких-либо преград и ограничений.
Компьютерная игра — это как возможность показать сюжет или самоутвердиться, выплеснув навыки в самых разных сферах — так и способ заработка. Но получается это не у всех и далеко не у всех потому, что контент на продажу от «only read» отличает тот самый талант, который отличает исскуство от рисунка маленькой девочки, или хотя-бы, ценность квадрата Малевича от ценности работы действительно хорошего, но безизвестного художника.
Заранее прошу прощения за ошибки и скучную подачу. Я далеко не писатель =\
В любом случае может эта инструкция поможет вам лучше ориентироваться в BGE и игровых движках в целом. Это затянется на долго потому, что я люблю писать развёрнуто и, к вашему сожалению — не люблю вставлять картинки (тем более что для этого сайта нужно что-то там придумывать с левыми сайтами для создания прямой ссылки).
В некоторых моментах я могу ошибаться, а в некоторых и вовсе писать откровенный бред. Прошу строго не ругать за это, а ещё лучше — помочь, подсказать, поправить потому, что я обучался в порядке хобби и многое могу упустить, неправильно понять или вовсе забыть.
Собственно — как с 0 начать пользоваться BGE, не имея вот вообще базовых знаний? Ну, это вопрос с подвохом т.к. основные приёмы и функционал Blender 3D знать всё-таки нужно. Так же бонусом будет начальные знания кода (ну, хотя-бы примерное понимание работы и навык чтения), примерное представление о работе игр и их «внутрянки».
Инструкцию пишу для чайников ибо не редко сам натыкался на обрывки, огрызки и прочий неполноценный контент в духе «рисуем кружочек, а потом доводим его до портрета Путина».
Весь «курс» будет в основном опираться на пост с игрушкой Rally Cross, а потому я советую заранее скачать файлик и осмотреть его. На ней я планирую показать применение различных технологий и «костылей», да и в целом рассказать подробно о процессе создания этой игры.
Глава 1. Игровой объект.
О полигональных моделях.
Между двумя точками можно создать грань, между тремя гранями — полигон.
Растовый рисунок состоит из пикселей, 3D модели из полигонов, а потому сразу запишем в наш словарик:
1. Полигон. Это поверхность, созданная между тремя и более точками (вершинами) соединёнными гранями. Компьютер воспринимает только треугольники, а потому все виды многоугольников триангулируются автоматически всегда и все прочие виды полигонов локальны и дальше вьюпорта не уходят. Это просто нужно знать.
2. Backface — дословно это обратнолицо. Являет собой обратную сторону полигона. В Blender 3D обозначаются тёмно-синим цветом, в игровом движке отсекаются (называется эффект backface culling). Так что у всех игрровых моделей следует соблюдать направление нормалей полигонов (в разговорном «выворачивать нормали наружу»). Да и вообще всегда ибо это может вызвать баги даже у моделей для рендеров, не отсекающих обратные стороны.
3. Нормаль. Нормаль — это направление, перпендикулярное поверхности. Так, например, нормаль от поверхности вашего стола смотрит вверх (отбросим кривые ножки, погрешности помещения и прочее).
4. Mesh, «меш». Это полигональная сетка. Произнося это слово обычно указывают на саму кучку полигонов без материалов, текстур и анимации, но в разговорных вариантах это может быть и просто сокращение слова «трёхмерная модель»
5. LoD или Level of Detail. Дословно — Уровень детализации. Для экономии ресурсов для одного и того же объекта может использоваться несколько разных мешей, которые переключаются в зависимости от расстояния до камеры. Обычно делют 3-4 разных меша на главные объекты и 2-3 под второстепенные типа бочек, ящиков и т.п.
6. Collision, коллизия. Это «плотность» объекта. Или нет. В любом случае это эффект, отвечающий за столкновения объектов. Стоит знать, что бы не говорить «проваливается сквозь текстуры». Этот эффект называется «нарушением коллизии».
7. Collider, коллайдер, коллижн-модель. Это уже более полезное слово. Коллижн модель — это специальный меш, скрытый от глаз игрока и отвечающий за форму объекта, для которого рассчитываются столкновения. На деле это позволяет серьёзно экономить на физике. Так, например, имея модель в 20к полигонов — мы можем сделть ей коллижн модель в 2к полигонов практически не потеряв в качестве физики и не нагружая компьютер. Можно для этого использовать один из мешей лодов, что будет ещё дешевле!
Растовая графика.
Касаемо всего, что создаёт графоний кроме 3D моделей вы можете узнать тут:
1. Растеризация или рендеринг. Процесс, в котором векторная 3D графика переводится в понятные монитору пиксели (растовая графика)
1.1 AA, Anti-Aliasing. Это метод для растеризации, позволяющий сгладить ступенчатость наклонных линий. Имеет регулировку в виде кол-ва проходов.
2. Материал. Материал отвечает за всё, что касается внешнего вида полигонов, на которые этот самый материал оказывает влияние. Это позволяет не только упростить создание игровой модели, но и сильно оптимизировать игру, если аналогичный материал используется другими мешами. Это некая локальная ячейка, содержащая в себе информацию о шейдерах, текстурах и методах наложения текстур.
3. Текстура. Обычное изображение, от специально нарисованного под конкретную модель до фотографии вашей бабушки. «Натягивается» на 3D модель по развёртке или другими методами, которые указываются в материале.
4. Шейдеры. Шейдеры бывают трёх видов. // в этой части я могу ошибаться т.к. шайдеры — штука очень сложная. Если вы нашли ошибку — укажите её , пожалуйста, в комментариях.
4.1. Вершинные шейдеры. Работают непосредственно с вершинами. Используются тогда, когда нужно работать с вершинами 3D модели. Например анимация через ключи формы или динамическое сминание кузова автомобиля.
4.2. Геометрические шейдеры . Аналогично с вершинными шейдерами, но работают с целыми частями геометрии. Такие шейдеры могут создать вам из отдельного меша проэкцию (как например тени в GTA SA) или разбить модель на треугольники и разбросать их, буд-то это обломки после взрыва.
4.3. Пиксельные шейдеры. Работают после растеризации и находятся на финишной прямой перед глазами игрока. Отвечают эти шейдеры за наложение текстур, карты нормалей, блики, освещение — всё-всё-всё.
Виды текстур.
Для текстур существуют карты (что тоже в какой-то степени является текстурой). Карты могут быть самыми разными — от области влияния текстуры до ложного объёма.
1. Текстура. Изображение. см пункт 3 «Растовая графика».
2. Карта Specular, SM. Карта, которая отвечает за силу блика на разных частях модели. Так, например, для железного прибора, у которого местами стёрлась краска и появились царапины — блик основной краски и голого металла будет отличаться по силе. Очень важная карта, про которую все забывают!
3. Карты, позволяющие разными способами создавать ощущение объёма.
3.1. Bump. Карта Bump это ч\б изображение, которое даёт информацию о высоте деталей текстуры. Так, например, с помощью бампа можно «выдавить » в кирпичной стене швы между кирпичами. Слово «выдавить» в кавычках потому, что карта не работает с формой объекта. Только определет влияние теней и шейдеров, создовая иллюзию объёма, из-за чего её часто путают с картой нормалей, смещения и паралаксом
3.2. Карта нормальей, Normal map, NM. Тот же бамп, но использует для показания формы 3 цветовых канала. Технически это добавляет к модели информацию о нормалях, которых нет при рассчёте шейдеров. Таким образом увеличивается точность и качество ложной формы.
3.3. Parallax. Это технология, которая в прямом уже смысле выдавливает на модели рельеф независимо от полигонов. Иначе говоря — в отличии от двух верхних рельеф виден даже под углом.
4. Карта смещения, Displacement. Карта, которая отвечает за высоту вершин. Возволяет уже в самом настоящем виде серъёзно влиять на форму объекта, но требует большого кол-ва полигонов для правильной и качественной работы.
Вообще картами можно регулировать огромное количество параметров материала. Это и ограничение области влияния шейдеров, и ограничение области влияния текстур, и какие-то изменения цвета\блика\затенения на определённых участках модели.
Анимация.
ну-ка подними!
Про анимацию много говорить не получится:
1. Ключи формы, Shape Keys. Прямой контроль над положением вершин в объекте. Позволяет надувать шарики и часто используется для лицевой анимации.
2. Скелетная анимация. Для перемещения вершин используется некий «скелет», который привязывается к вешинам и с разной силой контролирует их. По-сути «ручаги», для удобства влияния на группы вершин.
3. Объектная. Фактически это скелетная анимация без самого скелета, где вся группа вершин (а точнее весь объект) перемещается в пространстве или вращается.
4. Процедурная. Процедурая анимация создаётся из информации. Например вращающийся винт самолёта в зависимости от значения переменной рычага тяги или направление глаз персонажа от позиции объекта.
5. Интерактивная. Анимация, которая создаётся в реальном времени. Например отскочивший от пола мячик силами физического движка.
4 и 5 пункты относятся к первым трём.
Логика.
If, Else, туда топай.
Логикой объекта называется его поведение, описанное кодом. В логику входит всё, что имеется у игрового объекта и при этом не статично, а хоть как-то регулируется. От перемещения при нажатии на кнопку до воспроизведения анимации.
Теперь соберём всё вместе и получим такой вот набор:
>Lod0>меш для Lod0>материал>шейдеры>тектура0
>Lod1>меш для Lod1>материал>шейдеры>тектура1
>LodN>меш для LodN>материал>шейдеры>тектураN
>Анимация.
>Логика, которая включает в себя настройки для шейдеров под LoD.
>коллижн-модель.
Всё вместе взятое является полноценным игровым объектом.
В следующей части я пробегусь по работе кода и более конкретно о BGE. Частота выхода новых частей примерно раз в 3-6 дней.
Глава 2. Логика.
Очень много букв!
Код — это сценарий, читая который программа получает необходимые инструкции.
Хотя если точнее — это чистая логика, описанная понятным языком для компьютера. Игровой движок это некоторе объединение звукового, физического и графического движков. Смысл в том, что игровой движок не только объединят все эти движки предоставляя к ним удобный доступ на одном языке, но и ускоряет процесс написания игрушки за счёт объединения нескольких часто используемых функций в одну.
Как обьяснить компьютеру, чего ты от него хочешь или «вход, обработка, выход».
Плиточки Blender Game Engine (они же ноды).
По-умалчанию блендер делит эти ноды на сенсоры, контроллеры и актуаторы.
Сенсоры это входящяя информация. Она может быть любой — запуск игры, переменная, клавиша клавиатуры.
Контроллеры — определяют, как читать эти сенсоры для взаимодействия с актуаторами. Так, например, имея переменную равную 100 мы можем запустить актуатор тогда, когда переменная станет меньше или больше 100.
Актуаторы — определяют действие, которое случится после того, как контроллер обработает сенсор и даст «добро» на выполнение при соблюдении условий.
Эти базовые ячейки заменяют стандартную конструкцию проверки:
«Если условие выполнено, то происходит действие»
Их минусом явлеятся узкий функционал как в количестве актуаторов, так и в их качестве.
Так, например, имея актуатор «Sound» — мы можем настроить для звука параметр «pitch» и «volume» перед запуском игры. Но как быть, если эти параметры нам нужно будет менять в процессе игры для, например, звука двигателя?
Тут уже необходимо использовать скрипт на bge phyton или, по-разговорному — код.
Весь код упирается в написание инструкции из предоставленных движком функций.
Игровой движок — как конструктор. Он содержит необходимые блоки, которых, скорее всего, будет достаточно для ваших целей.
Из этих блоков вы и конструируете игру вместо того, что бы забивать информацию о том, на какую ногу процессора подть ток, а с какого убрать и когда.
Первая мысль, которая проскакивает в голове человека, решившего написать свой первый код — «как мне написать то, что мне нужно на выбранном мной языке?»
Синтаксис.
Что такое синтаксис?
Синтаксис — это правила, которые регулируют написание кода. Как мы руководствуемся правилами при написании писем в реальном мире — так и в компьютерном должны следовать правилам. Причём если человек поймёт «превед медвед» — компьютер решит, что вы больны.
Операции.
Операции это действия, которые мы можем выполнить над функциями и переменными.
Так, например, складывая скорость с положением в пространстве — мы получим в лобешник за эту дичь воспользуемся арифметическим оператором «+»
Арифметические операторы:
1. «+» — сложение
2. «-» — вычитание
3. «*» — умножение
4. «/» — деление
5. «%» — возвращает только остаток от деления (например при делении 11 на 4 — вернёт остаток — 3)
6. «//» — цельночисленное деление(например при делении 11 на 4 — вернёт 2)
7. «**» — возведение в степень.
Операторы сравнения:
1. «==» — сравнение. Выражение 5==5 вернёт «true».
2. «!=» — не равно. Выражение 5!=5 вернёт «false».
3. «<» и «>» — меньше или равно \ больше или равно.
4. «<=» и «>=» — меньше или равно \ больше или равно.
Операторы присваивания:
1. «=» — присваивает значение правого операнда левому. (например x=5)
2. «+=» / «-=» / «*=» и т.д. — присваивание с арифметической операцией. Так, например, выражение x+=y вернёт резултат сложения и присвоит его переменной «x»
Логические операторы:
1. «if» — «если». (если x=y — выполняется действие)
2. «else» / «or» — «Иначе» / «или» (Если x = y — выполняется действие 1, иначе — действие 2)
Разберём небольшой кусочек кода bge:
import bge
player = bge.logic.getCurrentController()
step = player.sensors ["KeyboardW"]
sound = player.actuators ["Step_Sound"]
if step.positive:
player.activate(sound)
else:
player.deactivate(sound)
Указываем player как локальное имя(т.е. только для этого скрипта)для текущего контроллера (объекта, на котором запускается скрипт)
Указываем step как локальное имя для сенсора с именем «KeyboardW»
Указываем sound как локальное имя актуатора «Step_Sound»
Если сенсор был активирован:
то запускается актуатор «Step_Sound»
Иначе:
отключается актуатор «Step_Sound»
Таким образом в игре мы будем включать звук только когда персонаж двигается.
Что касается логического оператора.
Если вы задаёте вопрос — должен быть ответ. Ответов может быть несколько и все они должны содержаться строчкой ниже и с отступом в одну табуляцию относитьно логического оператора.
#
if x=y:
функция;
if t!=s:
функция;
функция;
функция;
#
if g<=4:
высунуть язык;
if g<0:
присесть;
моргнуть;
else:
съесть бревно;
else:
пролить чай;
#
Если g меньше 4х, то персонаж высовывает язык. Если при этом g<0 — то персонаж высунув язык приседает и моргает. Если g меньше 4, но больше 0 — персоаж высунув язык съедает бревно. Если g больше 4х — персонаж проливает чай.
Теперь о самом важном — о применении.
import bge // Открывает библиотеку функций игрового движка
cont = bge.logic.getCurrentController //текущий контроллер
own = cont.owner //переменные текущего контроллера
sound = cont.actuators ["Sound_onlow"] //актуатор текущего контроллера с именем "Sound_onlow"
gear = own['gear'] //переменная текущего контроллера с именем "gear"
sound.pitch = (own["rpm"]*0.01)+ (gear / 50) // для актуатора "Sound_onlow" параметр pitch будет равен переменной "rpm" умноженной на 0.01 плюс результат деления переменной gear на 50.
cont.activate(sound) // для текущего контроллера выполняется актуатор ["Sound_onlow"] cо значением pitch, зависящим от оборотов и передачи.
Переменная оборотов и переменная текущей передачи регулирует уровень pitch для звука двигателя по формуле (rpm*0.01)+(gear/50). Формула для правильного звучания двигателя определялась опытным путём.
При присвоении вы можете использовать любые имена для читабельности кода. Так, например, вы можете bge.logic.getExitKey()(узнать клавишу выхода) присвоить переменной с именем edgjluvbhe, но зачем?
Ссылка на Blender Phyton References, в котором вы найдёте все функции bge phyton с описанием, дополнительной информацией и примерами.
В целом синтаксис приходит с опытом и потому нужно активнее читать скрипты на bge phyton и других яп. Искать общую логику, характерные особенности. То, что я вам сейчас описал — не позволит вам прямо сейчас начать писать GTA6, но поможет изучить bge phyton или, как минимум — начать его читать.
Надеюсь, был полезен.
В следующей главе планирую разобрать физику машинки из Rally Cross по строчкам.
Глава 3.
Физика в Rally Cross. Или «учимся читать» + продолжение к теме логики.
Загнул я про построчное чтение физики машинки — не нужно это. Чуть позже поймёте почему.
Физика Rally Cross базируется на небольшом примере.
Собственно — к первичному варианту мало чего добавилось — лишь корректировки переменных да всякие мелочи вроде нормальной работы сцепления с дорогой, адекватной управляемости через лёгкий занос и более-менее приятной динамики ускорения (а с недавних пор ещё и зависимость мощности от «оборотов» (тык по RallyCross в этой главе — на ЯД исходник(ну, или тут)). Сразу скажу, что это только тестовая версия обновления и она не содержит трассы и интерфейса!
Ну, не суть. Сейчас-то речь идёт о физике.
Вам потребуется записать новое слово:
Констреинт, Constraint. Это физическая связь объектов. Лучший пример такой связи — две сосиски в одной оболочке. Та сосиска, которая будет дочерней — вращается свободно, но только относительно «главной» сосиски. Констреинт же не только создаёт эту связь, но и ограничивает её. Констреинт автомобиля, о котором идёт речь ниже — значительно сложнее и включает в себя несколько связей + подвеску.
Итак, вся логика машинки находится в трёх скриптах:
1. CarSetup. Этот скрипт определяет положение колёс, их размеры, направение и т.д.
2. Suspension. Этот скрипт, как понятно из названия — по подвеске. В нём находятся параметры, отвечающие за жёсткость подвески на сжатие, усилие отбоя и поперечную стабилизацию.
3. Powertrain. Содержит в себе кучу регулировок — там и мощность двигателя, и сцепление колёс, и клавиши управления — всё-всё-всё, чего нет в двух прошлых скриптах.
Все переменные упаковываются в аккуратные функции, превращаясь в аргументы для одной единственной функции — bge.constraints.getVehicleConstraint
Да, вот так просто. Фактически эта функция — это здоровенная такая кнопка «сделать красиво».
Всё, что перечисленно в этих трёх скриптах — является упаковкой аргументов для красивого вида кода и удобного редактирования.
Все «упаковки» можно определить по заголовку «def имя_функци()»
На примере Tire_Objects():
def Tire_Objects(): //создаём функцию
frontDriver = "FLWheel" //Переднее левое колесо
frontPassenger = "FRWheel"//
rearDriver = "RLWheel"//
rearPassenger = "RRWheel"//
scene = bge.logic.getCurrentScene()// Сокращение функции "текущая сцена"
objList = scene.objects// Сокращение функции "получить объекты из текущей сцены
tire_0 = objList[frontDriver]//присвоение объекта-колеса из сцены переменной tire_0
tire_1 = objList[frontPassenger]//
tire_2 = objList[rearDriver]//
tire_3 = objList[rearPassenger]//
return (tire_0, tire_1, tire_2, tire_3)//Перечисление. Эти переменные (в полном виде типа bge.logic.getCurrentScene().objects['имя колеса1'] ) функция отправит тогда, когда её вызовут.
На самом деле можно было бы и сократить до:
def Tire_Objects(): Создаём функцию
scene = bge.logic.getCurrentScene() Текущая сцена
objList = scene.objects Объекты в текущей сцене
tire_0 = objList["FLWheel" ] Присвоить переменной tire_0 объект из текущей сцены с именем FLWheel
tire_1 = objList"FRWheel"]
tire_2 = objList"RLWheel"]
tire_3 = objList"RRWheel"]
return (tire_0, tire_1, tire_2, tire_3) Перечисляем то, что функция вернёт при вызове.
Но, мысль кодера сложна и он почему-то решил, что будет круто создать 4 лишних переменных.
Иначе говоря — мы можем получить все 4 колеса всего одной функцией — Tire_Objects()
Например:
objects=Tire_Objects()
objects.localScale=[2,2,2]
Таким образом одновременно увеличив размер всех колёс. (правда в самом скрипте это не работает ищ-ща определённых особенностей констреинта)
Далее — что такое vehicle ID?
vehicleID указывает на тип констреинта и объект, на который этот констреинт накладывается.
Иначе говоря vehicleID это сокращение bge.constraints.getVehicleConstraint(bge.constraints.createConstraint(bge.logic.getCurrentController().owner.getPhysicsId(), 0, 11).getConstraintId())
Что переводится как «Констреинт автомобиля (создать констреинт для(текущего контроллера).получив его физический идентификатор и применив аргументы 0 и 11, которые являются то ли типом констреинта, то ли ограничениями, то ли осями (фиг его разберёт короче. Я так и не смог найти на это ответа )»
Фактически чувак просто взял и грамотно, чисто и красиво подготовил переменные для установки оных как аргументов к констреинту. Местами, правда, перестарался и получилась фигня, как с Tire_Objects(). Бонусом лишняя регулировка поворачивающих колёс в CarSetup, которая не работает.
Почему во многих функциях присутствует точка? Что это и зачем?
Я до знакомства с bge python пописывал на gml и общий принцип работы кода знал, но эти точки сожгли мой стул. Без хорошего примера тут определённо не обойтись:
Мы, допустим, имеем большой дом. В доме есть коридор, который ведёт к разным дверям. Разные двери открывают разные комнаты, в каждой из которых свои элементы. Чайник, ванная, кровать, окно и т.д.
Таким образом, что бы использовать определённый элемент нам нужно пройти к нему.
коридор.комната.кухня.чайник
Как-то так.
Для того, что бы сослаться на определённый актуатор — нам нужно пройти довольно большой путь:
bge.logic.getCurrentController().actuators ["Актуатор1"]
bge.logic.getCurrentController().actuators ["Актуатор2"]
Мы можем это дело исравить
#1
qwerty=bge.logic
qwerty.getCurrentController().actuators ["Актуатор1"]
qwerty.getCurrentController().actuators ["Актуатор2"]
#2
cont=bge.logic.getCurrentController()
cont.actuators ["Актуатор1"]
cont.actuators ["Актуатор2"]
#3
act = bge.logic.getCurrentController().actuators
act["Актуатор1"]
act["Актуатор1"]
#
Все 3 варианта равнозначны. Другое дело — целесообразность и удобство использования\чтения.
т.к. писать много мне лень, да и не получилось с физикой машинки ибо всё слишком просто — опишу как пользоваться API документацией.
Использование документации.
В Game Engine Modules вы найдёте все функции, работающие чисто с bge.
Game Types (bge.types) — тут имеются все актуаторы, сенсоры и контроллеры. Чисто технически они сами по себе вам не нужны, однако в них можно найти функции актуаторов, что уже значительно полезнее.
Так, например, для актуатора Action (BL_ActionActuator(SCA_IActuator)) вы можете увидеть список всех регулировок вроде начального кадра, конечного, текущего и т.д.
Для актуатора Sound( KX_SoundActuator(SCA_IActuator)) — pitch, громкость, пауза и т.д.
Использовать эти функции просто:
envSound = cont.actuators ["Sound_wind"]
envSpeed = (speed*0.02)+.3
envSound.pitch = abs(envSpeed)
if impulse.positive:
cont.activate(envSound)
Эта функция активирует актуатор Sound с именем ‘Sound_Wind’, присваивая в процессе значение pitch, в зависимости от скорости автомобиля (abs превращает любое значение скорости в положительное)
Аналогично можно и с любым другим актуатором:
action = bge.logic.getCurrentController().actuators ["FGHY"]
action.frame=35
action.activate
Установит Action актуатору с именем FGHY 35 кадр.
Иначе говоря — если вы видете регулироку у актуатора и хотите её поменять кодом или вовсе поставить зависимость и менять в процессе игры — вы можете найти её в интересующем вас Актуаторе в разделе Game Types.Более того — там можно найти даже то, чего нет в настройках кирпичиков.
Game Logic (bge.logic)
В первую очередь в логике нас интересуют:
bge.logic.getCurrentController()
bge.logic.getCurrentScene()
первая функция управляет текущим контроллером (объектом, на котором был запущен скрипт). Предоставляет доступ ко всему — от сенсоров-актуаторов до переменных и настроек физики объекта.
Вторая функция предостовляет нам доступ к объектам и настройкам всей сцены. Через неё мы можем как связаться с любой информацией от другого контроллера, так и изменить активную камеру, настройки освещения и, даже, создавать копии объектов.
В остальном функция огромная и может менять количество проходов физики\логики за шаг, записывать анимацию, закрывать игру или открывать из других файлов. Ех, славные были времена, когда я с GML возился, где каждая функция означала одно единственное действие =\
Rasterizer (bge.render). Как понятно (или не очень) из названия — содержит в себе все функции, касающиеся растеризации (см Часть1). Это и тип рендера (GLSL или Multitexture), и разрешения\V-Sunc\AA\mipmapping — вся вот эта вот лабуда. Может также создавать скриншоты в файл, показывать фпс и дэбаг переменных\физики, что просто незаменимая вещь, если вы хотите сделать менюшку с настройками перед игрой.
Video Texture (bge.texture) содержит в себе функции, касающиеся чисто анимированных текстур. Если вам хочется добавить в игру видеоролик, написать свой шейдер, использующий текстуру или изменить текстуру объекта в процессе игры — вам сюда.
Game Keys (bge.events) отвечает за обработку событий клавамыши.
Пример:
Wkey=bge.events.WKEY
if Wkey.positive:
действие
Действие произойдёт по нажатию на клавишу. вроде всё просто)
Physics Constraints (bge.constraints) — это констреинты. Функции, содержащиеся в этом разделе помогут вам правильно настроить физические связи между объектами (например открывающиеся двери авто после аварии или дверь, которая будет открываться брошеным в неё камнем)
Application Data (bge.app) поможет вам, если вы хотите проверить версию блендера соседа, открывшего вашу игрушку(если я правильно понял), определить, подключен ли джойстик.
Ну, на этом пока всё. Меня пугает эта тишина. Вы хоть отпишите, что эту фигню хоть кто-то читает) А то вдруг я пишу просто так) Да и интересно — может есть какие пожелания к следующим главам, способе подачи или ещё чего.
Спасибо за труды!) Всё на понятном языке.
Прочитал всё на одном дыхании. Однако — полезно и интересно.
Механика в целом понятна. Хотелось бы в будущем разбор простых примеров, как этот тыц. С чего начать и как не запутаться.)
А то я заглянул в справочник Blender Phyton… а там огромная гора информации, и не знаю с какой стороны подойти)
Работа с Phyton, лично для меня, нечто новое. Последнее что я писал это сайт на html, и немного джавы))
В любом случае, как бы то ни бы-ло, спасибо и на этом))
Ну, на этом пока всё. Меня пугает эта тишина. Вы хоть отпишите, что эту фигню хоть кто-то читает) А то вдруг я пишу просто так) Да и интересно — может есть какие пожелания к следующим главам, способе подачи или ещё чего.
Я специально молчу, чтобы не загаживать тему :)
Всегда приятнее открыть тему и читать вот такие посты, а не стандартные Помогите/Спасибо… В общем не останавливайся и пиши сколько душа желает. Осмелившихся разобраться с Python будет очень мало, но они есть и труд 100% не пропадет ;)
Я так же как и другие читаю твои посты. Продолжай в том же духе писать уроки. Специально суда ничего не писал, что бы мои посты не мешались в данной теме.
Думаю, намного лучше будет по итогам вообще удалить этот пост и скопировать всё в новый т.к. много грамматических ошибок, которые нужно бы поправить + картиночки хоть вставить ибо текстом-то оно хорошо, но со схемами проще. Но пока нет кабеля для граф.планшета, а с мышки рисовать не хочу — это как после икры и раков пустыми макаронами давиться)
Обсуждения между главами очень даже важны — если что-то не понятно, мало информации или любые другие вопросы, появившиеся в процессе чтения — лучше отписать. Я занесу эти изменения в документик с пометкой и при правке\копировании темы дополню недостающей информацией или в конце сложной главы добавлю «вопрос>ответ» в зависимости от вашего вопроса. До тех пор смогу ответить прямо в комментариях т.к. понимание моего текста — важная часть понимания всего курса в целом. Речь будет не только о программировании на bge python. Надеюсь, что в конце вы сможете не просто создать задуманное, но и чётко понимать смысл своих действий и процесса для других игр в любом жанре и практически на любом движке.
Постараюсь в будущем показать процесс «от идеи к реализации» с пошаговым объяснением всех действий от «подумал, как оно должно быть и придумал вот что:» до «а вот тут я забыл что и как и поэтому подсмотрел тут:». Думаю, это будет достойным ответом на большинство вопросов, возникших в процессе чтения курса и освоения BGE как первого, так и альтернативного движка для написания игрушек.
Приятно видеть, что курс читается. Теперь хоть знаю, что мои труды нужны, а это очень важная часть мотивации =)
Важно понимать, что вы не просто читаете курс, но и являетесь эдакими бета-тестерами. В мечтаниях этот курс должен охватить всё, что касается базы BGE на доступном языке и кидаться ссылкой как ответ =)
Вопросов много)) Но это по не знанию.
На данный момент (ну лично для себя) хотелось бы подробно узнать о синтаксисе bge.
Я так понимаю, это же и есть правила написания кода. Функции, операции, констреины… а как этим оперировать, что за чем следует, и т.д.
Что то в таком духе — в начале мы указываем то, присваиваем такие то переменные, в результате получаем это)
Глава 4.
Подробнее про синтаксис и сам процесс написания скриптов на bge python.
Как я понял — синтаксис был затронут слишком поверхностно. Попробую рассказать о нём подробно.
Подготовка пространства Blender 3D для работы с BGE
Во-первых — стоит заменить таймлайн на редактор логики. Таймлайн в процессе создания игрушки вам врядли пригодится. Вообще я настоятельно рекомендую создавать все объекты в другой сессии блендера. Это позволит вам сосредоточиться строго на объекте и не путаться в сцене.
Далее — окно 3D вида стоит разделить и в одном из получившихся открыть Text Editor для редактирования скриптов.
При желании можно так же потянуть за уголок, но с зажатым шифтом для того, что бы вместо разделения текущего окна создалось отдельное, которое можно в случае чего свернуть.
Тип рендера следует переключить на Blender Game, в n-панели 3D вида во вкладке Shading установить режим материала на GLSL.
В редакторе текста нажать Ctrl+T (появится T-панель) и во вкладке Proporties включить Line Numbers (нумерация строк), Word Wrap (перевод строк) и Syntax Highlight (подсветка синтаксиса)
Заповеди bge python.
1. Всё, что вы имеете в скрипте — можно присвоить переменной.
Это и значения, и имена объектов, и функции. При присвоении используется оператор ‘=’. При этом значение правой части присваивается переменной в левой части
x = y \\ x принимает значение y
color = [1.0, 1.0, 1.0] \\ color принимает набор значений
object = scene.objects [‘Empty’] \\ object принимает значение объекта Empty становясь «ссылкой» на него.
debug = bge.render.showProperties \\ debug принимает значение функции, показывающей переменные. Теперь для её активации достаточно написать debug = True для активации или debug = False для деактивации.
2. Всегда соблюдай табуляцию.
В текстовом редакторе Blender табуляция (Tab) по-умолчанию ровняется четырём пробелам, однако смысловая разница есть.
Табуляция используется под каждым логическим оператором.
##########################################
if x == t:
y = 4
elif x != t and y<1:
y = 2
else:
y = 6
##########################################
if x == t:
if x == 3:
y = 1
if x == 4:
y = 3
else:
y = 0
else:
y = 2
#########################################
Табуляция — способ разделения условия от операции, производимой при соблюдении этого самого условия.
Кстати говоря — вы можете записать операции и в одну строку, разделив их знаком ;
if x == t: y = 4; g = 2; e = 0
Но это уже вопрос читабельности — вариант с записью в одну строку читать намного труднее. Я мог бы и промолчать про эту возможность для того, что бы вы так не делали, но это было бы не проффесионально. Так что знайте, но не используйте, пожалуйста =)
3. Помни про регистр.
«String» не равен «string»!
4. Держи скрипт в чистоте и порядке.
Это не обязательно и на работу скрипта не влияет, но очень даже желательно. Даже если никому не собираешься показывать код — тебе самому, возможно, придётся в нём ползать, искать ошибки и добавлять \ менять функции.
Два скрипта ниже абсолютно одинаковые по функциям, но со вторым работать приятнее
5. Не путай сравнение и равенство!
Для логических операторов используется только «==»
Для присваивания только «=»
if x==y:
t=q
Также не забываем про двоеточие в конце вопроса.
6. Если код не работает — виноват сам код.
Лично у меня за всё время ползанья по bge (а это чуть больше 2х лет) ни разу не случалось ошибок bge. Только мои собственные. Если и есть шанс бага блендера — он настолько мизерный, что его можно не брать в расчёт и в любом случае начинать с проверки кода.
Глава 5.
Процессируем в Text Editor на пальцах.
Код, указанный выше довольно простой, а потому хорошо подойдёт для разогрева.
Итак, по коду — он управляет освещением автомобиля. Это стоп-сигналы, лампы заднего хода и фары. Альтернатива — только анимация, что намного жирнее по ресурсам выйдет. Бонусом придётся забить кирпичиками ноды всех объектов и страдать при попытке что-то подрегулировать.
Посмотреть полную версию скрипта можно в последнем обновлении.
О процессе создания.
Этап первый — подготовка.
Первым делом было написано :
import bge \\ импортируем библиотеку BGE
cont = bge.logic.getCurrentController() \\ сокращаем получение текущего контроллера
own = cont.owner \\ сокращаем доступ к объекту, на котором находится контроллер
scene = bge.logic.getCurrentScene() \\ сокращаем получение информации от текущей сцены
Это что-то вроде базовых функций, которые используются чаще всего. Их стоит знать в глаза.
Грубо говоря я на этом этапе заранее утаптывал почву для того, что бы начать строить скрипт.
Этап второй — планирование.
Самое время задуматься о том, что требуется от скрипта.
Из обязательного — включение\выключение ламп заднего хода и тормоза без использования анимации.
Смотрим на наши возможности — открываем python reference и вводим в поиск «light»
Она нам выдаёт внушительный список, но нас интересует только bge.
В списке в первой позиции имеется bge.types.KX_Scene.lights — читаем и видим A list of lights in the scene, (read-only).
Это нам не подходит т.к. функция позволяет лишь получить список ламп в сцене. Возвращаемся в поиск.
Листаем ниже и находим bge.types.KX_LightObject. Под ним находим переменные этого LightObject — дистанция, энергия, цвет, тип источника света — то, что нужно. Значит, это функция отвечает непосредственно за источник освещения.
Смотрим в пример:
import bge
co = bge.logic.getCurrentController()
light = co.owner
light.energy = 1.0
light.color = [1.0, 0.0, 0.0]
Из примера становится ясно, что управляется источник света через Объект.функция света = (значение)
Потому как bge.logic.getCurrentController().owner равнозначно текущему объекту. Это хорошо потому, что вместо текщего объекта можно поставить любой другой. Значит, можно уместить код на одном объекте без сложностей указав лампы из кода и накинуть скрипт куда-нибудь на пустышку, объединяющую все лампы.
Наличие переменной цвета нам очень даже пригодится — мы можем те же задние лампы использовать и в качестве ламп заднего хода, что хорошо — сэкономим на кол-ве источников света.
Этап третий — пишем код.
Если мы хотим расположить скрипт на одном объекте — нужно указать все лампы.
База у нас уже была, а потому дописываем:
lamp1 = scene.objects['StopL']
lamp2 = scene.objects['StopR']
Мы присвоили этим двум переменным значение объектов-ламп тормоза.
Теперь можно задуматься о способе включения ламп.
Из очевидного — включать лампы когда нажата клавиша S, но это не то т.к. лампы будут работать и при заднем ходе. Идём в объект-автомобиль и попутно открываем Powertrain. В планах переключать переменную только тогда, когда констреинт будет использовать торможение.
Находим в Powertrain.py блок, отвечающий за торможение — def Brakes()
В нём нас интересует только # brake и # no brakes потому, что # emergency это ручной тормоз, а # emergencyD — копипаста ручника при смерти, которая клинит колёса.
сам скрипт я немного модифицировал т.к. старый не работал. Он проверял разные состоряния нажатия клавиши тормоза (brake.positive == True) и клавиши занего хода (reverse.positive == False), хотя это была одна клавиша — ‘S’.
Я добавил проверку скорости и состояния клавиши газа. Таким образом, тормоза срабатывали [при скорости выше 0 и при нажатом тормозе] или [при скорости ниже 0 и при нажатом газе ]
Конструкция проверки уже имеется и нам остаётся только дописать под функциями присвоение нашей переменной.
Состояние записывается в пееременную BrakeBool (True или False). Записана она в виде «own[‘BrakesBool’]» потому, что находится в N-панели редактора логики и, соответственно, сперва приходится указывать объект, на котором скрипт уже будет искать переменную.
Нам осталось только получить состояние этой переменной в скрипте освещения.
Всё, что было сделано в Powertrain.py — чисто для определения состояния тормоза. ну, за исключением правки скрипта. Таким образом на выходе мы имеем переменную, которая имеет значение <True или False в зависимости от состояния тормоза.
Содаём объект-пустышку и родительской связью привязываем его к фонарям. У пустышки создаём через редактор логики сенсор Always (всегда), включаем обновление (кнопочка с тремя точками сверху) и соединяем с контроллером «python» с указанием нашего скрипта.
Далее идём в скрипт и собираем всю необходимую информацию от объекта-автомобиля:
car = scene.objects['Cube'] \\ сокращаем получение информации от объекта "Cube" (коллайдер автомобиля, на котором "держится" основная часть логики машины)
brake = car['BrakeBool'] \\ присваиваем переменной brake состояние переменной BrakeBool от объекта-автомобиля
rear = car.sensors['Reverse'] \\ присваиваем переменной rear состояние сенсора "Reverse"(по-сути просто нажатие клавиши S)
speed = car['speed'] \\ присваиваем переменной speed переменную speed из объекта автомобиля.
Далее просто собираем всю информацию в код.
if brake == True: \\ если тормоз включён
stop1.energy = 8.0 \\ энергия первой лампы = 8
stop2.energy = 8.0
stop1.color = [1.0,0.0,0.0] \\ цвет первой лампы красный (значения указываются по палитре RGB [r,g,b])
stop2.color = [1.0,0.0,0.0]
elif rear.positive and speed < 0: \\ иначе если нажата клавиша S и скорость меньше 0 (задний ход)
stop1.energy = 1.0 \\ энергия первой лампы равна 1
stop2.energy = 1.0
stop1.color = [1.0,1.0,1.0] \\ цвет первой лампы белый
stop2.color = [1.0,1.0,1.0]
else: \\ иначе (если машина не тормозит и не включён задний ход)
stop1.energy = 0.0 \\ энергия первой лампы = 0
stop2.energy = 0.0
По тормозам вроде всё. Для наполнения скрипта я ещё добавил фары и в кирпичики поставил переключение boolean переменной при нажатии клавиши H
front1 = scene.objects['FrontL'] \\ присваиваем переменной front1 объект-лампу
front2 = scene.objects['FrontR']
FrontLight = own['FrontLight'] \\ присваиваем переменной FrontLight переменную с таким же названием, но созданную в редакторе логики (которая регулируется кирпичиками)
if FrontLight == True: \\ если переменная имеет значение True
front1.energy = 2.0 \\ энергия фары1 = 2
front2.energy = 2.0
else: \\ иначе (т.е. если переменная имеет значение False)
front1.energy = 0.0 \\ энергия = 0
front2.energy = 0.0
Весь скрипт в основном состоит из той самой конструкции проверки:
Если условие выполнено:
выполняем действие
Спасибо, очень интересно. Только можно как-то скрины выложить на хостинг, который поддерживает всплывающие скрины с разрешением побольше. А то нифига кода не видно.
Только можно как-то скрины выложить на хостинг, который поддерживает всплывающие скрины с разрешением побольше. А то нифига кода не видно.
Правой кнопкой по картинке > Открыть картинку в новой вкладке
И будет все о-о-очень большим :)
Спасибо, познавательно. Ждём продолжения.
Правой кнопкой по картинке > Открыть картинку в новой вкладке
И будет все о-о-очень большим :)
Да, очевидное решение. :)
Прошу, можешь выделить отдельную главу на подробное описание того, как надо работать с api блендера.
Прям вот оч надо.
И если не сложно, можно следующую главу о внедрении GLSL кода в игру (если можно)
Лично мне интересно то, как можно взаимодействовать с материалом через GLSl.
Например та же самая простая имплементация PBR состоит в том, что текстура на шероховатости не блюрится тяжелыми алгоритмами блюра, а просто уменьшается в размерах (ташемта https://www.shadertoy.com/view/4slGWn ) Так вот, код, как видно, никакуший — по времени уменьшается качество изображения, но вот как сделать так чтобы, допустим:
У нас есть этот код и есть группа нодов, нам надо маскировать часть меша при помощи grayscale изображения так, чтобы белые пиксели маски значили -0% уменьшения изображения а черные — 100% уменьшение (как тут, постепенное уменьшение качества https://www.shadertoy.com/view/lsfGWn по градиенту). Но, допустим, нам надо изменить эту grayscale нодами при помощи рампа, так вот как заставить код обрабатывать новые данные? Или это уже сложно?
@stkopp, если бы я умел писать GLSL шейдеры — я бы не подрабатывал сейчас автомехом=)
В любом случае я обязательно посмотрю, что там и как, но ничего не обещаю ибо мне все эти vector3+vector2 — тёмный лес короче.
Бонусом я пока ещё не до конца разобрался с bge python. Нужно ещё много сказать и переоформить сам курс, выделив отдельно всё, что касается питона и с более жёсткой структурой.