Приветствую Вас Гость | Зарегистрироваться | Вход | RSS

Поиск по сайту:
Поиск в интернете:
Главная » Статьи » Учебники по Blitz3d

Учебник для начинающих по Blitz3d (Часть 1_2)
Полный учебник по Blitz 3D. Часть 1.
Этот раздел представляет не просто урок, а полный учебник по Blitz3D. В учебнике рассмотренны, практически, все команды и все аспекты программирования игр на языке Blitz3D.

Создание плоскости и операторы текстурирования

Итак, как я уже говорил, нам не с чем сравнивать положение нашего объекта, поэтому он так странно двигается. Для этого урока нам понадобится: код из прошлого урока, и текстура. Что такое текстура? Для тех кто не знает попробую объяснить. Текстура – это картинка в любом формате (самые популярные это .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. Понравилось? Теперь будет намного интереснее. Конечно при условии, что Вы старательно изучали все ранее изложенные главы, на которые потратили лучшие минуты своей жизни и большую часть поняли. Что же пора заняться тем, о чём я обещал – простенькой игрушкой!!!

Категория: Учебники по Blitz3d | Добавил: blitz3d-portal (08.Декабрь.04) E W
Просмотров: 4411 | Комментарии: 1 | Рейтинг: 5.0/1
Всего комментариев: 1
1 kery@98  
0
[color=orange] tongue cheesygrin Прикольно!)))

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]