Главная » Статьи » Blitz3D » Для новичков

Учебник по Blitz3D для начинающих. Часть 1
Введение в создание игр
Итак, у Вас есть прекрасная возможность почувствовать себя богом, который создаёт вселенную. Но сначала давайте разберёмся, из чего же она будет состоять. А состоит она из разных объектов. Большая часть – это так называемые 3D объекты (Mesh). Что это такое? 3D объекты – это просто модели. Цилиндр, куб, шар, Ваша собственная модель – всё это 3D объекты. Они состоят из вершин – это просто точки в пространстве, и треугольников, состоящих из трёх соединённых вершин. Но это можно пока не запоминать – к этому мы подойдём чуть позже. Но, кроме 3D объектов, к объектам относятся: свет (Light), камеры(Camera), и пивоты (Pivot) или центры. Пивоты – это невидимые точки в пространстве, которые очень помогают в процессе работы. Далее, из двухмерного мира к нам пришли спрайты (Sprite). Спрайты - это просто плоские объекты, состоящие из четырёх вершин и двух треугольников, т.е. это просто плоская картинка. Потом плоскость (Plane) – тот же спрайт, только бесконечный. Их обычно используют для создания неба, воды – всего, что должно быть плоским и бесконечным. И, наконец ландшафт (Terrain), но его мы затрагивать не будем. Далее, вселенная - трёхмерная, так что у неё есть 3 оси – X, Y, Z. Ось X – это ось ширины, направленная слева направо. Ось Y – высота, идущая снизу вверх. И ось Z – как, бы длина, направленная вперёд! Эти оси существуют не только у всей вселенной, но и у каждого объекта тоже, но и об этом тоже по порядку. Это была вводная часть в 3D мир, без которого нельзя было обойтись, чтобы знать, что вообще мы собираемся делать. Но, более - менее мы всё поняли, так что идём дальше! Пора уже приступать к программированию – зайдите в Blitz3D, и наберите следующий код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Repeat
Until KeyHit(1)
End

Момент, когда Вы это запустите, можете считать историческим, и отмечать его, как день рождения - Вы только что создали собственный мир! Пусть там ничего не видно – просто он пока пустой! Разберём все команды по порядку: Graphics3D 640,480,16,1 – эта строчка инициализирует Direct3D, ну, и создаёт вселенную. Синтакс такой: Graphics3D ширина, высота, глубина цвета, режим. Ширина, высота – размер создаваемого экрана (в пикселях). Глубина – глубина цвета – 16, 24 или 32 бита на пиксель. Режим: 0 - оконный во время разработки (если возможно), полноэкранный в .exe формате; 1 - всегда полноэкранный
2 - всегда оконный
3 - всегда оконный, с возможностью изменения размеров окна. SetBuffer BackBuffer() - устанавливает буфер на задний буфер. Те, кто знали, те поняли, те же, кто не знали, не поняли вообще. В общем, если хотите, чтобы всё было правильно, пишите эту команду после Graphics3D. Repeat и Until KeyHit(1) - между этими двумя командами, у нас будет происходить сам цикл игры. Перевод таков: Повторять до нажатия клавиши Esc. End – эта команда показывает, что программа завершена. Что, не впечатляет? А зря – вы только что создали окно Direct3D – практически Ваше окно в мир создания компьютерных игр! Создание простейшей 3D программы Вставим следующий кусок кода после инициализации графики: cam=CreateCamera()
cub=CreateCube() В этих строках мы создаём наши первые объекты. cam=CreateCamera() – здесь, мы создаём камеру, (как бы наши «глаза» в виртуальном мире), и помещаем её в переменную cam. cub=CreateCube() – таким же образом создаём 3D-объект – куб, и называем cub. Созданные нами объекты очутятся в начале координат, т.е. в точке с координатами 0, 0, 0. Чтобы камера не была в одной точке с кубом, а смотрела на него со стороны, нам нужно поставить куб в другое место. Для этого можно воспользоваться командой PositionEntity, которую мы расположим после команды создания куба. PositionEntity cub,0,0,5 PositionEntity cub,0,0,5 - ставит объект куб в точку с координатами 0,0,5 – т.е. немного впереди. Синтакс: PositionEntity объект, координата X, координата Y, координата Z. В цикл программы вставим следующие три строчки: UpdateWorld
RenderWorld
Flip Эти команды (точнее последние две) должны присутствовать в Вашей программе, если Вы хотите, чтобы она что-то Вам показывала. UpdateWorld - анимирует все объекты в созданном мире, и проводит проверку на столкновения. RenderWorld - рендерит все объекты. Т.е. создаёт картинку той части мира, которую видит камера(ы). Flip – меняет местами передний и задний буфер. На этом основан способ двойной буферизации, но это знать не обязательно – просто нужно вставлять эту команду, для того, чтобы на экране что-то появлялось. Итак, теперь запустите программу. Что Вы видите? Серый квадрат? Практически да, хотя на самом деле – это куб, просто мы смотрим на него прямо, и видим только одну грань. Хоть какой-то прогресс. Дальше интереснее. Заставим-ка его двигаться! Поставим эту строчку в цикл. Прямо в самое его начало после команды Repeat. TurnEntity cub,.1,.2,.3 TurnEntity cub,.1,.2,.3 – эта функция поворачивает объект cub на 0.1 градус по оси X, 0.2 градуса по оси Y, и 0.3 градуса по оси Z, относительно системы координат объекта. Естественно, здесь можно использовать отрицательные числа и ноль. Например, команда TurnEntity cub,0,.1,0 – будет поворачивать куб просто влево, а TurnEntity cub,0,-.1,0 – вправо. Итак, первая сложность (хотя в общем ничего сложного здесь нет, это наоборот даже удобно). Попробую объяснить: помните, я говорил, что оси X, Y и Z (т.е. система координат) есть не только у самой вселенной, но и у каждого объекта. Сделано это для удобства. Главная – «мировая» система координат – статичная, и нужна нам для того, чтобы узнать где, например, находится объект, а система координат объекта – двигается и поворачивается вместе с самим объектом. Например, у нас есть персонаж, который должен поворачиваться и ходить вперёд, допустим, мы его повернули на какой-то угол. И, если мы захотим сделать так, чтобы он шёл в ту сторону, в которую он направлен через «мировую» систему координат, нам придётся делать вычисления, рассчитывающие то, на какой угол он повёрнут, и насколько должен передвинуться относительно каждой оси, но, к счастью, у него есть собственная система координат, и, где бы он не находился, как бы он не был повёрнут относительно «мировой» системы координат, его ось Z будет указывать направление вперёд - назад (относительно него), Y – вверх-вниз, X – влево-вправо. Иногда нужно наоборот – независимо от того, куда направлен объект, двигать его в какую-то определённую сторону, относительно всего мира. Так, например, действует гравитация – т.е. как бы не был повёрнут объект со своей системой координат, мы просто двигаем его вниз по оси Y по «мировой» системе координат – и он падает. Это было небольшое отступление, чтобы всё было ясно. Сами команды будут позже. Итак, когда Вы запустите эту программу, Вы увидите вращающийся куб. Правда чего-то не хватает? Да! Все его грани одинакового цвета, и мы понимаем, что это куб чисто интуитивно. Что же закрашивать каждую грань разными цветами? Нет до этого мы не доросли, да это и не нужно. То, что нам не хватает – это свет! Именно он должен оживить всю сцену. Да будет свет! lit=CreateLight() Здесь всё также, как и раньше! Эту команду можно поставить в любом месте перед главным циклом, но я обычно создаю свет сразу после создания камеры. Теперь запустите программу. Так намного лучше, неправда ли? Да, кстати, свет – это объект, а это значит, что им тоже можно управлять, как и всеми объектами. Поэтому, чтобы он лучше смотрелся, сразу же после создания повернём его: TurnEntity lit,45,45,0 По-моему так лучше… или у Вас есть другие варианты? А весь код выглядит так:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
lit=CreateLight()
TurnEntity lit, 45,45,0
cub=CreateCube()
PositionEntity cub,0,0,5
Repeat
TurnEntity cub,.1,.2,.3
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, и как Вам? Всего 14 строчек сделали то, для чего в других языках пришлось бы писать несколько страниц! Вот, в общем первая трёхмерная программа. Не бойтесь экспериментировать – попробуйте подставить свои значения углов, или назвать объекты по-другому (тут главное изменить названия переменных во всём коде, а не только в команде создания). А чтобы убедиться, что вы всё хорошо усвоили, попробуйте сделать так, чтобы в цикле поворачивался не только куб, но и свет. В следующем уроке мы научимся управлять объектом сами. Использование клавиатуры для перемещения объекта Можно, конечно, до посинения смотреть на разные там вращающиеся кубы, сферы, конусы, цилиндры, свет, камеры – но когда-нибудь это надоест. Кроме того – игра (а ведь мы именно игры хотим писать а не всякие там трёхмерные абстрактные пейзажи) требует ввода чего- нибудь с клавиатуры. Ну, тогда давайте разбираться с перемещением предмета. Я надеюсь, что предыдущие уроки Вы хорошо освоили, так что начнём сразу с такого шаблона:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
Repeat
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Здесь мы (по порядку) инициализируем графику, создаём камеру, свет, куб и в цикле всё это рендерим и выводим на экран, пока не будет нажата клавиша Esc. Обратите внимание, что здесь мы поменяли позицию камеры (а не куба, как в прошлый раз), и теперь она имеет координаты X=0 Y=5 Z=-10. В итоге, получилось, что мы смотрим на куб сзади, и чуть-чуть сверху. В нашем распоряжении имеется куб. Наша миссия двигать его, в направлении, задаваемом с клавиатуры. Итак, новые команды: KeyDown(сканкод) – (вообще-то не команда, а функция) – проверяет, нажата ли соответствующая клавиша. В скобках нужно указать сканкод клавиши (сканкоды клавиш можно узнать в Help'e Blitz3D, в разделе Command Reference). Мы будем управлять клавишами управления курсора и манипулятором типа мышь. Значит сразу напишу сканкоды (всем запомнить – пригодится): кнопка вверх – 200, вниз – 208, влево – 203, вправо – 205. С мышкой разберёмся позже. MoveEntity объект, перемещение по X, по Y, по Z – перемещает объект относительно своей собственной системы координат. Так, у нас есть команда проверки нажатия клавиш, и есть команда перемещения объекта, осталось только связать их вместе. Как это сделать? Очевидно, просто надо поставить условие (ЕСЛИ НАЖАТА кнопка ВВЕРХ то ПЕРЕДВИНУТЬ ОБЪЕКТ ВПЕРЁД), ну, и так далее. Я надеюсь Вы прочитали учебник по основам Blitz Basic, поэтому команду условия IF я уж объяснять не буду. Вот так это должно выглядеть: If KeyDown(200) MoveEntity cub,0,0,.1 Поставьте это условие в начало цикла, и запустите прогу. Теперь, когда Вы будете нажимать кнопку вверх, куб будет двигаться по направлению оси Z, т. е. вперёд. По аналогии сделаем остальные условия:

If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0

Полностью код выглядит так:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Кубик двигается! Что ещё нужно для счастья? Я думаю достаточно, но Blitz3D предоставляет нам очень много возможностей на этой почве, и в следующих уроках мы постараемся уяснить самые важные. Доработка кода программы Круто, конечно, объект перемещается, но мы попробуем немного модернизировать программу. Две последние строчки проверки нажатия клавиш немного изменим. Теперь, когда мы будем нажимать кнопки ВЛЕВО и ВПРАВО он будет поворачиваться влево и вправо (раньше он перемещался влево и вправо). Итак строчки: If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0 Нужно заменить на: If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0 Вот у нас уже что-то вроде автогонок вырисовывается (точнее кубогонок). Так, команду TurnEntity Вы уже знаете, но обратите внимание, что если мы хотим, чтобы наш объект вертелся именно влево или вправо, мы поворачиваем его по оси Y. Чтобы немного было понятнее, где у него передняя часть, а где боковые, я предлагаю его немного трансформировать. ScaleEntity cub,1,.5,2 Эту команду нужно вставить после команды создания куба (смотрите не вставьте её в цикл ). Синтаксис: ScaleEntity объект, трансформация по X, по Y, по Z. То есть в примере мы оставляем такой же размер по X (ширину) (1 значит, что мы оставляем тот же размер), трансформируем размер по Y (высоту) до 0.5 от его размера (то есть просто уменьшаем в 2 раза) – получается как бы сплющиваем его сверху, и в два раза увеличиваем размер по Z (длину) – то есть растягиваем его в длину. Получился, что-то похожее на кирпич. Вот ездит этот кирпич, ездит, да заезжает за экран, так что мы перестаём его видеть. А как бы сделать так, чтобы не терять его из виду? Здесь нам поможет такая замечательная команда PointEntity объект1, объект2 – эта команда просто поворачивает объект1 в сторону объекта2. То есть если мы хотим, чтобы наша камера всегда следила за нашим кубиком, нам просто нужно вставить эту команду в цикл. Вот как это делается: PointEntity cam,cub Эту команду можно вставить в любое место в цикле, но, чтобы у нас было всё по порядку, поставим её после команд проверяющих нажатия клавиш. Вот полный код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
PointEntity cam,cub
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Опять же столько всего, только в 18 строчках! Если запустить программу, то видно, что камера как бы следит за кубиком, хотя это плохо понятно – такое чувство, как будто он ездит сам как хочет. Всё это потому, что у нас нет ничего, с чем мы могли бы сравнивать его положение.

Создание плоскости и операторы текстурирования
Итак, как я уже говорил, нам не с чем сравнивать положение нашего объекта, поэтому он так странно двигается. Для этого урока нам понадобится: код из прошлого урока, и текстура. Что такое текстура? Для тех кто не знает попробую объяснить. Текстура – это картинка в любом формате (самые популярные это .jpg, .bmp, .tga, .pcx, .png и.т.д.), которой мы закрашиваем какой-нибудь объект. Если объект большой, а текстура – нет, то она накладывается как бы повторяющимися квадратами. Вы наверняка видели в каких нибудь трёхмерных играх повторяющуюся траву, скалы – где-то это видно сразу, где-то сильно скрыто. Когда создаётся объект, он создаётся белым, а когда мы его текстурируем – то получается покрываем его картинкой – текстурой. Ну, будем надеяться, что кто этого не знал – примерно понял. В общем возьмите любую картинку из перечисленных форматов, и поместите её в ту же папку, где у Вас сохранён этот код программы. Сделаем мы так: создадим плоскость, которая будет находиться на одном месте, и относительно неё будет хорошо видно, что наш кубик всё таки движется. pln=CreatePlane() Эта команда, также как и остальные команды создаёт объект, на этот раз плоскость (Plane). Что это такое? Ну, плоскость – это плоскость. Она плоская и бесконечная. Да, один момент – плоскость видна только с одной стороны – с другой она невидимая (как и спрайт, кстати). Поставьте эту команду перед циклом, после создания куба. После создания плоскости загрузим текстуру из файла: tex=LoadTexture("Picture.bmp") Эта команда загружает текстуру из файла Picture.bmp (это у меня картинка называлась Picture.bmp, а Вы можете изменить имя файла, кстати, там можно писать полный путь к файлу, например “C:MyGamePicture.bmp”. Так, плоскость есть, текстура есть – осталось только затекстурировать эту плоскость: EntityTexture pln,tex Синтаксис: EntityTexture объект, текстура – эта команда элементарно покрывает заданный объект заданной текстурой. Да, и ещё одно – эта текстура наверняка будет смотреться мелко, поэтому я советую вставить такую команду после загрузки текстуры: ScaleTexture tex,10,10 Она просто расширяет данную текстуру в10 раз по ширине и в 10 раз по высоте. Вот, вроде, и всё готово! Теперь у нас внизу есть плоскость, а куб как бы ездит по ней! Весь код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,30,30
EntityTexture pln,tex
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
PointEntity cam,cub
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Теперь попробуйте сами затекстурировать куб. Работа с мышью В этот раз я покажу, как можно управлять объектом с помощью мышки. Итак, берём шаблон:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,0
lit=CreateLight()
cur=CreateSphere(8)
EntityColor cur,255,215,0
PositionEntity cur,0,0,10
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
Repeat UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Здесь мы создаём всё, что нам нужно, расставляем и создаём цикл. Новые команды: CreateSphere(количество сегментов) – создаёт сферу, в скобках указываем количество сегментов – 8 = 224 полигона, 16 = 960 полигонов и 32 = 3968 полигонов. Естественно, чем больше полигонов, тем "круглее" наша сфера, и тем больше памяти он занимает. EntityColor объект, красный, зелёный, синий – эта команда закрашивает данный объект цветом RGB, где указывается сколько должно быть красного, зелёного, синего цвета, значения которых могут быть от 0 до 255. Вот, например, 0,0,0 – чёрный цвет, 255,255,255 – белый, 0,255,0 – самый зелёный. Теперь насчёт мышки. Хотя курсор и не показывается на экране, на самом деле он есть – то есть он двигается, если вы двигаете мышку, остаётся только определить на сколько. Просто поставьте эти команды в начале цикла: mx=MouseXSpeed()
my=MouseYSpeed()
MoveMouse 320,240 Что эта всё значит? Поясняю. MouseXSpeed() – это функция, которая говорит нам об изменении X координаты мышки на экране, с момента последнего вызова этой функции. Вобщем, на сколько пикселей её в последний раз передвинули (по оси X). MouseYSpeed() – тоже самое, но по Y (есть ещё MouseZSpeed() – это передвижение колёсика). MoveMouse x,y – устанавливает курсор мышки в точку 320, 240. Итак, мы знаем, на сколько у нас передвигается мышка с каждым кадром, осталось только передвигать сферу, в зависимости от передвижения мышки (данные о передвижении находятся у нас в переменных mx и my): MoveEntity cur,mx,0,-my Теперь можно запускать программу. Шар передвигается с помощью мыши, правда он какой-то гиперактивный, нужно сбавить ему скорость – просто заменим эту строчку на: MoveEntity cur,mx*.1,0,-my*.1 Так будет намного удобнее. Всё – у нас есть трёхмерный курсор. Но мы на этом не остановимся! Мы пойдём дальше! Сделаем так, чтобы камеру можно было вертеть: If KeyDown(203) TurnEntity cam,0,2,0
If KeyDown(205) TurnEntity cam,0,-2,0 Если Вы теперь запустите программу, то обратите внимание, что когда камера повёрнута нормально (как стоит в начале) – то всё как бы нормально – двигаешь мышку влево, сфера двигается влево, двигаешь вперёд – и сфера двигается вперёд… но стоит нам повернуться на 90 градусов влево, как становится совсем неудобно: передвигаешь мышку влево – курсор уходит вперёд, передвигаешь мышку вперёд – курсор уходит вправо. А если повернуться на 180 градусов – то всё вообще становится наоборот. Почему же происходит такое неправильное движение? Вообще-то движение-то правильное – оно как было, так и осталось – просто мы теперь смотрим на это под другим углом. Что же теперь камеру не вертеть что ли? Конечно нет! Настоящие программеры не сдаются. Значит будем думать вместе, хм, это, э-э-э, а может, хотя нет, да! Есть идея! Смотрите: сфера всегда повёрнута прямо. Когда камера повёрнута прямо, получается так, что их оси совпадают по направлению, а когда камера повёрнута налево, то получается что её ось X совпадает с осью Z сферы. Короче, надо сделать так, чтобы их оси совпадали, говоря человеческим языком – чтобы они были направлены в одну сторону. Но как это сделать? Очень просто:

ex=EntityPitch#(cam)
ey=EntityYaw#(cam)
ez=EntityRoll#(cam)
RotateEntity cur,ex,ey,ez

Или так (результат один и тот же):

RotateEntity cur,EntityPitch#(cam),EntityYaw#(cam),EntityRoll#(cam)
Итак новые командосы. EntityPitch#(объект) – функция, возвращающая наклона данного объекта относительно оси X мировой системы координат. EntityYaw#(объект)-по оси Y, EntityRoll#(объект)-по оси Z. RotateEntity объект, X, Y, Z – вобщем похожа на команду TurnEntity (тем, что она поворачивает объект), вот только делает она это относительно мировой системы координат, а не системы координат объекта. Вот мы и научились работать с мышкой и курсором! Родительская зависимость объектов или привязки В этом уроке мне бы хотелось рассказать о том что такое привязки, и что с ними можно делать. Начнём сразу с примера. Да возьмём один из прошлых – помните, когда мы поворачивали кубик?

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
cam=CreateCamera()
PositionEntity cam,0,5,-10
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
PositionEntity pln,0,-1,0
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

В общем-то ничего не изменилось, мы только поставили плоскость внизу, и создали камеру после создания куба. Хорошо, теперь кое-что изменим. Представим, например, что мы делаем какую-нибудь игру с видом от третьего лица. Тогда нам надо, чтобы камера двигалась вместе с кирпичом – вернее сзади него. Сделайте вот что - замените простую строчку создания камеры на: cam=CreateCamera(cub) Какая-то хитрая строчка вроде ничего не изменилось, кроме того, что мы поставили в скобки переменную созданного нами куба. В этом и весь фокус! Когда мы создаём какой-то объект, и в скобках ничего не указываем, объект создаётся свободным, а если мы что-то укажем – объект становится зависимым от другого объекта, указанного в скобках – в данном случае это куб. Немного расскажу об этой зависимости. Значит так, во-первых свободный, непривязанный ни к чему объект создаётся в точке 0,0,0 относительно мировой системы координат. Объект же, привязанный таким образом к другому объекту - родителю, создаётся в той точке, где находится его родитель. Второе - все команды которые писались раньше относительно мировой системы координат – теперь пишутся относительно системы координат родителя т.е. в данном случае если бы камера не была привязана к кубу, команда PositionEntity cam,0,5,-10 означала бы поставить объект cam в точку с координатами 0,5,-10. А так как она привязана к кубу, камера ставится в точку 0,5,-10 относительно куба (т.е. центр куба для камеры считается точкой 0,0,0). Получается, что камера располагается сзади и немного сверху относительно куба. И куда мы этот куб перед этим бы не поставили, как бы не повернули – всё равна камера бы поставилась именно таким образом. И, наконец, самое главное, все движения и повороты, которые применяются к родителю автоматически применяются к зависимым от него объектам – то есть они как будто бы привязаны к родителю. Например, если мы подвинем родителя, все зависимые от него объекты также подвинутся. Если повернём – все зависимые объекты относительно него повернутся. Но не наоборот! Надеюсь понятно объяснил. Хотелось бы только отметить: если всё же нам нужно будет поместить, передвинуть или повернуть зависимый объект относительно мировой системы координат, то это делается очень просто – в конце добавляется True (что значит, что команда совершается глобально – то есть относительно мировой системы координат.). Например, если нам камеру надо будет поставить в точку 5,5,-20 относительно мировой системы координат, а не относительно мировой системы родителя - мы просто пишем PositionEntity cam,5,5, -20,True. И всё. И связь всё равно от этого не потеряется. Запустите программу, и вы увидите, что камера стоит ровно, и следит за кубом. Чтобы показать, что движение зависимых объектов никак не влияет на родителей, добавим ещё две строчки в цикл:

If KeyDown(30) MoveEntity cam,0,0,.1
If KeyDown(44) MoveEntity cam,0,0,-.1

В них, как Вы видите, условие – если нажата кнопка A – двигать камеру вперёд, а если нажата Z – назад. Куб остаётся на месте. Полный код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
PositionEntity pln,0,-1,0
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
If KeyDown(30) MoveEntity cam,0,0,.1
If KeyDown(44) MoveEntity cam,0,0,-.1
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, мы раскрыли секрет игр, сделанных от третьего лица! Но использование привязок не ограничивается «следящей» камерой. О том, что ещё можно делать с привязками, я расскажу в следущем уроке. Фишки с привязками А вот что можно сделать, если правильно использовать технологию привязок. Алгоритм галактики:

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim sp(99)
center=CreateCube()
For i=0 To 99
sp(i)=CreateSphere(8,center)
ScaleEntity sp(i),.2,.2,.2
PositionEntity sp(i),Rnd(-20,20),Rnd(-20,20),Rnd(-20,20)
Next
cam=CreateCamera()
PositionEntity cam,0,0,-40
Repeat
TurnEntity center,.0,.5,.5
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Если Вы запустите этот код, то увидите как шарики совершают поступательное движение вокруг куба по кругу. На самом деле всё проще. Эти шары просто находятся в родительской зависимости от куба, и остаются на месте, а поворачивается только куб – ну, а они вместе с ним. Так, допустим, что нам этот алгоритм понравился, и мы захотели всунуть его в нашу игру, но нас немного смущает этот куб в середине. Конечно, можно его как подобает затекстурировать, и сказать, мол, это наш антигравитационный голографический трансхренолятор, или сделать сферу, и сказать, что это чёрная дыра (и такое в нашей практике бывает, когда лень что-то исправлять), но здесь мы поступим подругому. Помните, в самом начале я перечислял какими бывают объекты, и упомянул о Центрах (Pivots). Так вот – центр – это просто точка в пространстве, она невидимая, но у неё есть (как и всех остальных объектов) своя система координат, а значит – своё направление. Центры – это очень полезные объект, когда дело касается всяких там привязок. Уже догадались, что мы собираемся делать? Неправильно, мы собираемся заменить этот куб в середине на центр. А, Вы так и подумали? Ну, тогда, заменим команду сами знаете что на команду создания центра: center=CreatePivot() А для тех, кто все-таки не понял объясняю: мы меняем команду создания куба (center=CreateCube()) на команду создания центра (center=CreatePivot()). Всё – теперь Вы можете наслаждаться видом крутящегося скопления звёзд без всяких там лишних вещей! Но не обязательно делать такую одинарную привязку - можно привязать один объект к другому, который в свою очередь привязан к третьему – тот к четвёртому, и так далее. Для чего это делать? А вот посмотрите пример:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim prv(2)
prv(0)=CreateCube()
prv(1)=CreateCube(prv(0))
PositionEntity prv(1),5,0,0
prv(2)=CreateCube(prv(1))
PositionEntity prv(2),5,0,0
obj=CreateSphere(8,prv(2))
PositionEntity obj,5,0,0
cam=CreateCamera(0)
PositionEntity cam,0,0,-30
Repeat
For i=0 To 2
TurnEntity prv(i),0,0,1
Next
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Как видите, привязки играют очень большую роль в программировании игр, и они очень облегчают жизнь. Несколько дополнений: 1) Помните - синтаксис команды создания сферы таков: CreateSphere (количество сегментов, [родитель]) – очень частой ошибкой является то, что в поле, где указывается количество сегментов, вместо них указывают родителя. 2) Задать привязку можно не только во время создания объекта, но и после этого – командой EntityParent объект, родитель – эта команда привязывает заданный объект к заданному родителю. Вместо родителя можно поставить 0 (ноль) – и это отвяжет заданный объект от родителя, каким бы страшным этот родитель ни был. Ну, и напоследок – то, что можно сделать, изучив технологию привязок:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim sp(99)
sp(0)=CreateSphere(8)
EntityAlpha sp(0),0
For i=1 To 99
sp(i)=CreateSphere(8,sp(i-1))
PositionEntity sp(i),1,1,1
EntityColor sp(i),250,215,i*2
EntityAlpha sp(i),(100-i)*.01
Next
cam=CreateCamera(0)
PositionEntity cam,0,30,0
Repeat
For i=0 To 99
TurnEntity sp(i),.1,.2,.3
Next
PointEntity cam,sp(99)
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, Вы поняли, как это работает? Ну, давайте – подумайте! Вы же всё-таки программеры как-никак– даже если Вы задумали всю жизнь программировать один, Вам всё равно придётся читать чужие коды – чтобы понять как работает та или иная фишка. А про команду EntityAlpha – посмотрите в Help'е, потому что, даже если Вы совершенно ничего не знаете в английском – в Help придётся обращаться очень часто, поэтому, лучше учиться сейчас. Инициализация столкновений объектов Вот мы плавно подошли к главной составной части физики Blitz3D (не надо только пугаться – никаких формул типа E=mc^2 вспоминать не нужно – здесь Вы их сами будете придумывать), и одного из его самых главных компонентов – проверки на соприкосновение или столкновение. Я думаю, после того, как мы его разберём, Вы будете готовы к тому, чтобы приступить к самой разработке игр! Ну, ладно, начнём – чего время-то терять?

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Const TypePlayer=1,TypeWall=2
Player=CreateSphere()
EntityType Player,TypePlayer
Wall=CreateCube()
PositionEntity Wall,0,0,10
EntityType Wall,TypeWall
Collisions TypePlayer,TypeWall,2,3
cam=CreateCamera()
PositionEntity cam,0,30,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Repeat
If KeyDown(200) MoveEntity Player,0,0,.1
If KeyDown(208) MoveEntity Player,0,0,-.1
If KeyDown(203) TurnEntity Player,0,2,0
If KeyDown(205) TurnEntity Player,0,-2,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Итак, в этой программке представлен основной принцип проверки на прикосновения. Здесь мы создаём шар и куб, затем, стрелками управляем шаром, и, если Вы захотите заехать внутрь куба, у Вас ничего не получится. Ура! Это и есть та самая инициализация столкновений – она не пропускает одни объекты в другие, если, конечно перед этим указать, какие именно объекты, и куда именно не пускать. А теперь разберём все нововведения. Сначала мы создали две константы – TypePlayer, равной единице и TypeWall, равной двум. Зачем мы это сделали? Так это чтобы не запутаться. Я думаю, потом поймёте в чём именно. Ещё, Вы, наверное, заметили, ещё одну новую команду – EntityType объект, тип объекта – присваивает данному объекту данный тип. Тип (в данном случае) - это просто цифры. Здесь мы вместо цифр использовали константы – чтобы не запутаться, да и чтобы легко можно было прочитать – к какому типу этот объект относится, и с чем соприкасается (а то представьте такую ситуацию, у нас есть (какие-то) цифры для главного героя, стен, врагов, ну, и прочей дряни – всё это мы вначале создания проекта распределили, расставили, и благополучно забыли за ненадобностью, но вдруг в середине проекта Вы вспоминаете, что забыли задать проверку на столкновение между врагом и героем, и начинаете судорожно вспоминать какая же цифра у Вас обозначала врага, просматриваете килобайты кода, ну в общем можно поступить намного удобней, задав сразу константы, таким вот образом Тип_Герой = 1, Тип_Стена = 2, Тип_Пол = 3, Тип_Враг = 13, Тип_Враг_Босс = 113. Следуящая команда: Collision первый тип, второй тип, метод, результат – в общем то, что она делает я только что написал, ну а подробней: Первый тип – объект, который будет проверяться на столкновения.
Второй тип – объект, с которым эти самые столкновения и будут происходить. Метод: 1 – соприкосновение сферы со сферой
2 – соприкосновение сферы с полигонами
3 – соприкосновение сферы с параллелепипедом Результат: 1 – остановка
2 – скольжение – полное скольжение
3 – скольжение – защита объекта от скольжения вниз (может я перевёл не правильно, конечно, ну в общем этот метод создан для работы с ландшафтом). Теперь постараюсь обо всём этом, да поподробнее, начнём с методов. Как видите, объект, который будет проверяться на столкновения (первый объект) должен будет иметь «сферу» столкновения – так как методы столкновения бывают только сферы с чем-либо. Величину этой самой сферы проверки можно задать для каждого объекта отдельно – с помощью команды EntityRadius объект, радиус#. Это накладывает некоторые ограничения (зато сама проверка – очень быстро реализована) и как бы Ваша фигура не выглядела – столкновение будет проверяться именно по какой-либо сфере – т.е. допустим, у Вас есть предмет – спичка – здесь Вам придётся делать либо большую сферу, получая довольно приличное расстояние в середине спички между радиусом сферы соприкосновения и радиусом самой спички, либо сделать сферу поменьше, но сверху и снизу спичка будет вылезать из сферы (как вариант – только верхняя или нижняя часть) – т.е. тут уже эта выпирающая часть может залезть в другой объект… ну, со спичкой я утрирую – но, вот модельки людей, например, тоже не похожи на шары – так что рано или поздно, такой вопрос встанет, и о нём лучше подумать заранее – например, можно сделать более приземистых, коренастых юнитов – как в Quake'e первом, например. Далее – насчёт столкновений. 1-е: это самое столкновение происходит только при движении первого объекта внутрь второго. 2-е: столкновение – вещь односторонняя – т.е. если мы, как в примере, хотим, чтобы герой соприкасался с врагами, а те, в свою очередь – с ним, то нам нужно писать две команды - «Коллизион между Типом_Героем и Типом_Врагом» и «Коллизион между Типом_Врагом и Типом_Героем», а так как первый объект должен иметь именно сферу соприкосновения, то единственный доступный метод здесь – первый! Так что к сфере надо будет привыкнуть, и уже начинать думать, что с ними делать, правда, я слышал о том, что разработчики думают сделать соприкосновение по эллипсоиду, но когда это будет? Так, незадолго до того, как я дописал учебник, вышел апдейт 1.82 (поэтому я тут немного дополняю), где эллипсоидный метод столкновения уже реализован, правда удлинять или сплющивать можно только по Y оси, но и это неплохо. Если я не ошибаюсь, новая команда выглядит так: EntityRadius объект, радиус по X и Z, радиус по Y. Вот, в общем-то и вся основа 3D. Понравилось? Теперь будет намного интереснее. Конечно при условии, что Вы старательно изучали все ранее изложенные главы, на которые потратили лучшие минуты своей жизни и большую часть поняли. Что же пора заняться тем, о чём я обещал – простенькой игрушкой!!!

Категория: Для новичков | Добавил: Samarik (15.05.2007)
Просмотров: 11407 | Комментарии: 9 | Рейтинг: 4.1/10
Всего комментариев: 9
9 tex  
0
fdf

8 Батько Тарас  
0
Уважаемые.
Текст этой статьи и есть этот самый учебник)
Вот то, перед вами, - читайте, больше ничего нет)
Разве что вторая часть (валялась где-то неподалёку)

7 Гришка  
0
им
мне можно?gancharuk_grigor@mail.ruпожалста огроменное большушее спасибо !!!!

6 Ruslan  
0
пришлите пожалуйста мне учебник по Blitz3D мне на мыло Rusoverclocker@bk.ru. Заранее спасибо!

5 FaxMod  
0
Пришлите пжл учебник и мне kain9714@yandex.ru
Заранее спасибствую!

4 fatek  
0
Пришлите и мне если можно ,сюда fatek@land.ru
Зарание спасибо!....

3 Саня  
0
Мне можете прислать учебник по Blitz3D на мыло saniakd@ukr.net

2 Серёга  
0
Сори... Забыл E-mail написать. chashenkov@mail.ru пришлите сюда!!!

1 Серёга  
0
Доброго времени суток! Пришлите мне, пожалуйста, учебник по Blitz3D на мыло! Заранее спасибо!

С уважением, Серёга.


Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Наш опрос
Что в гоночных играх самое важное?
Всего ответов: 265
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0