|
DWD-009 |
|
Меню Сайта |
 |
|
 |
|
 |
__________________
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
____________________
|
 |
|
 |
|
 |
|
 |
_______________
|
 |
|
 |
|
 |
|
 |
|
 |
_______________________
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
|
 |
___________________
|
 |
|
 |
_________________
|
 |
|
 |
|
 |
|
 |
|
 |
|
|
 |
ToP Sites
http://mo3del.ru/
http://mir3d.3dn.ru/
http://sw-in.narod.ru/
|
--Спрайты и Tекстуры---
Урок 6. Спрайты и текстуры
6.1. Создание, загрузка спрайтов.
Спрайты - простые плоские прямоугольники, состоящие из двух треугольников. В отличие от других объектов (entities), они не имеют каркасной модели (mesh). По умолчанию спрайт создается в координатах 0,0 и простирается из точки -1, -1, в точку +1,+1.
У спрайта есть два значительных преимущества. Первое - он состоит только из двух полигонов - это означает, что Вы можете использовать большое их количество за раз. Это делает их незаменимыми в создании разного рода эффектов на основе частиц. Второе - спрайтам можно назначить режим их отображения используя функцию SpriteViewMode. По умолчанию этот режим равен 1, что означает, что спрайт всегда повернут к камере. То есть, не имеет значения какое положение камера занимает по отношению к спрайту, вы никогда не будете подозревать, что спрайт плоский; если же вы назначите ему сферическую текстуру, то он вообще не будет отличаться от обычной сферы.
Созданием спрайтов в языке Blitz3D занимается функция CreateSprite. У этой функции (команды) есть опциональный параметр parent, означающий, что Вы можете назначить родительский объект для Вашего спрайта. Таки образом, если родительский объект будет двигаться, то и зависимые от него спрайты, тоже будут двигаться вместе с ним. Но связь это односторонняя - если двигать спрайты, это никоим образом не повлияет на родительский объект.
Надо заметить, что спрайты имеют свои команды для вращения и масштабирования.
Давайте создадим простейший спрайт и посмотрим из чего он состоит. Создайте новый проект и введите следующий код:
Graphics3D 640,480
cam = CreateCamera()
MoveEntity cam, 0, 0, -5
sp = CreateSprite()
RotateSprite sp,20
While KeyDown(1) <> True
If KeyHit( 17 )
wire=Not wire
WireFrame wire
EndIf
RenderWorld
Flip
Wend
End |
После запуска этой программы на экране должен появиться слегка повернутый белый четырехугольник. Если мы нажмем клавишу "W" на клавиатуре, что увидим что собой представляет наш спрайт без "shaded-in" областей, то есть каркас. Как и ожидалось - спрайт состоит из двух треугольников. Функция WireFrame языка BlitzBasic - переключает отображение текущей сцены в каркасный ((проволочный) wire) режим, в котором мы может четко отследить из чего состоит модель нашего мира. Этот режим хорошо использовать для режима отладки, чтоб видеть насыщенность сцены полигонами и если что, применять некоторые шаги по оптимизации объектов.
Чтоб загрузить уже готовый спрайт в игру, нужно воспользоваться функцией BlitzBasic - LoadSprite, которая имеет следующие параметры:
text_file$ - имя файла с картинкой
tex_flag (опционально) - флажок текстуры:
1: Color - (по умолчанию) обычная текстура
2: Alpha - эта текстура является альфа-каналом
4: Masked - это прозрачный спрайт (обычно с черным фоном)
8: Mipmapped - (специальная технология см. 1 урок)
16: Clamp U - U фиксация
32: Clamp V - V фиксация
64: Spherical reflection map - сферическая отражающая текстура
parent - родительский объект
Давайте загрузим спрайт с текстурой дерева, причем фон текстуры должен быть прозрачным. Используем флажок 4 функции LoadSprite для того чтоб задействовать режим прозрачности.
Сохраните текстуру в папку проектов для урока №6:

И слегка модифицируйте программу, которая была приведена выше, следующим образом:
Graphics3D 640,480
cam = CreateCamera()
MoveEntity cam, 0, 0, -5
sp = LoadSprite("tree5.png",4)
While KeyDown(1) <> True
If KeyHit( 17 )
wire=Not wire
WireFrame wire
EndIf
RenderWorld
Flip
Wend
End |
Вы видите как легко можно сделать деревья, не прибегая к их моделированию. Безусловно деревья в виде моделей будут более реалистичны, но если нужно множество деревьев в сцене, допустим на некотором удалении от игрока то модели вполне можно заменит спрайтами и их трудно будет отличить от их многополигональных аналогов!
6.2. Вращение и масштабирование
Для получения различного рода эффектов может понадобится вращение спрайтов, а для реалистичности подбора размеров спрайтов - их масштабирование.
Сохраните файл, который представлен ниже в папку с проектом для урока 6 и запустите слегка видоизмененный файл проекта:

Graphics3D 640,480
cam = CreateCamera()
MoveEntity cam, 0, 0, -5
sp = LoadSprite("spark.png",4)
ang = 0
size# = 0
While KeyDown(1) <> True
RotateSprite sp,ang
ScaleSprite sp, size, size
ang = ang + 50
size = size+0.01
RenderWorld
Flip
Wend
End |
Запустив этот пример, Вы увидите как спрайт из маленькой точки превращается в большой вращающийся шар. Заметьте, что переменная size объявлена как переменная с плавающей точкой, потому что у нас приращение масштаба (size = size+0.01) происходит на небольшую дробную величину. Кроме того спрайт каждое обновление экрана поворачивается на угол, указанный в переменной ang. За вращение и масштабирование спрайтов отвечают такие функции языка Blitz3D как RotateSprite, ScaleSprite. Так как спрайт может вращаться только вокруг одной оси, то и параметр угла у функции RotateSprite тоже один. Что касается функции масштабирования ScaleSprite, то она очень похоже на уже знакомую нам функцию ScaleEntity, но так как спрайт - плоская фигура то и координат масштабирования будет всего две.
6.3. Режимы отображения спрайта
Режимы отображения спрайта изменяют его ориентацию относительно камеры. Некоторые из этих режимов позволяют вести себя спрайту будто он больше чем двухмерный объект.
Функция SpriteViewMode в Blitz3D служит для изменения этого режима и ей могут быть переданы следующие аргументы:
sprite - идентификатор спрайта (возвращаемый функциями LoadSprite, CreateSprite)
view_mode - режим отображения спрайта
1: фиксированный, спрайт всегда обращен "лицом" к камере (по умолчанию)
2: свободный, спрайт не зависим от камеры
3: upright1 (sprite always faces camera, but rolls with camera as well, unlike mode no.1)
4: upright2 (sprite always remains upright. Gives a 'billboard' effect. Good for trees, spectators etc.) |
Давайте используем нашу текстуру с деревом из пункта 6.1. нашего текущего урока и попробуем создать программу по движению камеры относительно спрайта с этой текстурой, меня режим его отображения:
Graphics3D 640,480
cam = CreateCamera()
Global player=CreateSphere()
PositionEntity player,0, 0, -5
cam=CreateCamera(player)
Global Floortex=LoadTexture("floor.jpg")
Floor1=CreatePlane()
PositionEntity Floor1,0,-1,0
EntityTexture Floor1, floortex
sp = LoadSprite("tree5.png",4)
While KeyDown(1) <> True
If KeyDown(200) MoveEntity player, 0, 0, 0.2
If KeyDown(208) MoveEntity player, 0, 0, -0.2
If KeyDown(203) TurnEntity player, 0, 2, 0
If KeyDown(205) TurnEntity player, 0, -2, 0
If KeyDown(2) SpriteViewMode sp, 1
If KeyDown(3) SpriteViewMode sp, 2
If KeyDown(4) SpriteViewMode sp, 3
If KeyDown(5) SpriteViewMode sp, 4
RenderWorld
Flip
Wend
End |
Используйте текстуру пола, из урока 4. Пол нам нужен только для того чтоб видеть смещения камеры относительно дерева, т.е. для пространственного эффекта. Нажимайте клавиши 1,2,3,4 на клавиатуре для изменения режима отображения спрайта. Режимы 3 и 4 ведут себя практически идентично режиму 2. Если вы используете режим отображения спрайта 2 , то наверняка заметите, что спрайт виден только с одной стороны, если приближаться к нему с "тыла" то он просто не будет отображаться.
Технически все 4 режима можно описать так. При перемещении камеры:
1: Спрайт меняет значения yaw, pitch, чтоб всегда быть повернутым к камере, но не меняет свое значение roll.
2: Спрайт не меняет значения yaw, pitch, roll.
3: Спрайт меняет значения yaw, pitch, чтоб всегда быть повернутым к камере, и меняет свое значение roll, чтоб подстраиваться под камеру
4: Спрайт меняет значения yaw, чтоб всегда быть повернутым к камере, изменяет свое значение roll.
Если Вы еще не в курсе pitсh, yaw, roll значения означают углы спрайта относительно X,Y,Z осей.
6.4. Создание травы
Часто в играх можно встретить ландшафты покрытые растительностью. Это создает довольно реалистичную картинку местности, посему мы не моги обойти эту тему в наших уроках. Но так как ландшафты мы еще с Вами не изучали, то будем покрывать травой ровный участок местности.
Для начала запасемся текстурами травы:

и земли:

Следующая программа изобразит на нашем экране густую траву слегка колеблющуюся под порывами небольшого ветра:
Graphics3D 640,480
Global windwave#
Global grasscount
Global player=CreateSphere()
PositionEntity player,0, 1, -5
cam=CreateCamera(player)
light=CreateLight()
Global groundtex=LoadTexture("ground.jpg")
Ground=CreatePlane()
PositionEntity Ground,0,-1,0
EntityTexture Ground, groundtex
ramp=CreateMesh()
surf=CreateSurface( ramp )
segs=1
width#=2
For k=0 To segs
x#=Float(k)*width/segs-width/2
u#=Float(k)/segs
AddVertex surf,x,1,0,u,0
AddVertex surf,x,-1,0,u,1
Next
For k=0 To segs-1
AddTriangle surf,k*2,k*2+2,k*2+3
AddTriangle surf,k*2,k*2+3,k*2+1
Next
EntityFX ramp, 16 Or 1
grasstex=LoadBrush("grass.png",4)
PaintMesh ramp, grasstex
sp = LoadSprite("tree5.png",4)
ScaleSprite sp, 2,3
Dim grass(1000)
Dim grass_dir#(1000)
grasscount=0
For j#=-10 To 10 Step 1
For i#=-10 To 10 Step 1
grass(grasscount)=CopyEntity(ramp)
x#=(i+Rnd(5,5))
z#=(j+Rnd(5,5))
y#=0;
PositionEntity grass(grasscount),x,y,z
grass_dir(grasscount)=Rand(-45,45)
grasscount=grasscount+1
Next
Next
HideEntity ramp
While KeyDown(1) <> True
If KeyDown(200) MoveEntity player, 0, 0, 0.2
If KeyDown(208) MoveEntity player, 0, 0, -0.2
If KeyDown(203) TurnEntity player, 0, 2, 0
If KeyDown(205) TurnEntity player, 0, -2, 0
wind(1)
UpdateWorld
RenderWorld
Flip
Wend
End
;--------------------
Function wind(speed#)
For i=0 To grasscount-1
x#=EntityX(grass(i))
z#=EntityZ(grass(i))
RotateEntity grass(i),0,grass_dir(i),Cos(windwave+z)*0.5
Next
windwave=(windwave+speed#)
End Function |
Для того чтоб трава была видна с любой стороны, но не вела себя как спрайт мы используем динамически создаваемый mesh и затем рисуем текстуру травы на его поверхности.
Ниже приведенный участок кода как раз отвечает за создание такого каркаса (mesh). Надо заметить, что этот кусок куда универсален, так как мы можем создать наш mesh из любого количества сегментов, за что отвечают две переменные (segs и width). Функция CreateMesh
создает основу нашего объекта mesh , а функция CreateSurface генерирует поверхность, к которой в дальнейшем при помощи команд (функций) AddVertex добавляются вершины наших будущих треугольников. После создания вершин создаются треугольники, за что отвечает функция AddTriangle.
Увеличивая значение переменной сегментов ( segs ) можно добиваться большей детализации поверхности объекта mesh. В нашем же случае, когда травы много, лучше поберечь ресурсы компьютера и принять число сегментов равным 1.
Далее команда EntityFX со вторым аргументом 16 Or 1 позволяет добиться отображения объекта mesh с любой стороны, и выбрать яркую его подсветку. (см. флаги передаваемые во втором аргументе этой команды во встроенной помощи Blitz3D)
Ну и напоследок команда PaintMesh рисует ранее загруженную текстуру (в данном примере, текстура выступает в роли кисти для рисования по поверхности, причем кисть имеет прозрачные участки, LoadBrush со вторым аргументом 4) на поверхности созданного объекта mesh.
ramp=CreateMesh()
surf=CreateSurface( ramp )
segs=1
width#=2
For k=0 To segs
x#=Float(k)*width/segs-width/2
u#=Float(k)/segs
AddVertex surf,x,1,0,u,0
AddVertex surf,x,-1,0,u,1
Next
For k=0 To segs-1
AddTriangle surf,k*2,k*2+2,k*2+3
AddTriangle surf,k*2,k*2+3,k*2+1
Next
EntityFX ramp, 16 Or 1
grasstex=LoadBrush("grass.png",4)
PaintMesh ramp, grasstex |
Отрисовка травы при помощи клонирования объекта mesh производится в коде приведенном ниже. Массив grass содержит копии объекта mesh, а массив grass_dir служит для хранения углов поворота объекта mesh. Угол поворота задается случайным числом при помощи функции Rand, а именно: (grass_dir(grasscount)=Rand(-45,45))
Dim grass(1000)
Dim grass_dir#(1000)
grasscount=0
For j#=-10 To 10 Step 1
For i#=-10 To 10 Step 1
grass(grasscount)=CopyEntity(ramp)
x#=(i+Rnd(5,5))
z#=(j+Rnd(5,5))
y#=0;
PositionEntity grass(grasscount),x,y,z
grass_dir(grasscount)=Rand(-45,45)
grasscount=grasscount+1
Next
Next
HideEntity ramp
|
В дальнейших наших уроках, мы научимся создавать траву на участках поверхности любой формы.
6.5. Имитация выстрела в комнате
Давайте вспомним наш Урок №4 и загрузим один из созданных нами проектов (например room7.bb). Сохраним его под другим именем (к примеру shoot.bb) и попробуем реализовать алгоритм выстрелов в комнате. Для ощущения большего эффекта, стрелять будем шариками (сгустками плазмы) для чего весьма пригодится уже использованный ранее нами рисунок (spark.PNG)

Так как плазменных шариков, которые мы будем испускать может быть больше чем один в данный конкретный момент времени, то средствами языка Blitz3D создадим пользовательский тип шариков и его константное описание для определения дальнейшего взаимодействия со стенами и дверьми.
Добавьте в начало программы следующие строки:
Const TYPE_BULLET = 4
Global bull_sprite=LoadSprite( "bluspark.bmp" )
ScaleSprite bull_sprite,3,3
EntityRadius bull_sprite,1.5
EntityType bull_sprite,TYPE_BULLET
HideEntity bull_sprite
Type Bullet
Field rot#,sprite,time_out
End Type
Collisions TYPE_BULLET,TypeWall, 1, 1
|
Как вы и догадались, сперва мы загружаем спрайт с изображением нашего плазменного шарика, а затем создает пользовательский тип Type Bullet, с полями rot - угол поворота спрайта, sprite - копия объекта спрайта, time_out - время жизни спрайта в игре. После создания, спрайт прячется с экрана (HideEntity bull_sprite).
Создание спрайтов после выстрела можно оформить в виде функции, представленной ниже:
Function CreateBullet.Bullet( player )
b.Bullet=New Bullet
btime_out=150
bsprite=CopyEntity( bull_sprite,player )
EntityParent bsprite,0
Return b
End Function |
В этой функции вначале создается пустой объект спрайта. Затем инициализируются поля типа - время жизни, сам объект со спрайтом. Копирование объекта осуществляется с указанием второго аргумента - родительского объекта, которым в данном случае является наш игрок (player). Таким образом вновь созданный шарик будет иметь координаты игрока.
Для того чтоб отображать в игре все созданные нашим выстрелом шарики, напишем функцию UpdateBullet, которая будет менять координаты каждого созданного шарика, следить за пересечениями и временем жизни, а также вращать шарик на определенный угол.
Вот эта функция:
Function UpdateBullet( b.Bullet )
btime_out=btime_out-1
If (btime_out=0) Or EntityCollided( bsprite,TypeWall )
FreeEntity bsprite
Delete b
Return
EndIf
brot=brot+30
RotateSprite bsprite,brot
MoveEntity bsprite,0,0,0.2
End Function |
При выполнении условия столкновения со стеной (EntityCollided( bsprite,TypeWall )) и обнулением счетчика жизни спрайта, он удаляется из игры.
Вставьте вызов этой функции где-нибудь в главном цикле программы:
For b.Bullet=Each Bullet
UpdateBullet( b )
Next |
Этот цикл ведет перебор всех экземпляров типа Bullet и вызывает нашу функцию UpdateBullet для вышеописанных изменения состояний объектов наших шариков.
Чтоб инициировать выстрел напишем следующий кусочек кода, который, тоже вставим в главный цикл нашей программы:
If KeyHit(56)
CreateBullet( player )
EndIf |
При нажатии на клавишу Alt (код клавиши 56 ), шарик плазмы улетает вперед от камеры и исчезает при столкновении со стенами. Для тренировки Вы можете чуть чуть изменить функцию
UpdateBullet для того, чтоб учитывать и столкновения шарика с дверьми. Сейчас же шарик пролетает сквозь двери, ввиду того, что мы заведомо опустили контроль их столкновений.
Полный код этого примера можно скачать здесь.
6.6. Создание эффекта снега
При создании игры, рано или поздно придется столкнуться с программированием тех или иных погодных явлений. Давайте представим, что наступила зима и пошел мелкий снежок.
Для создания снега, на основе спрайтов, нам понадобится картинка снежинки, к примеру эта:

и немного смекали. Так как снежинок у нас будет некоторое приличное количество измеряемое не одной сотней, то нам понадобится тип (структура), описывающая этот объект. Давайте за основу нашего мира возьмем пример со стрельбой в комнате и к нему будем добавлять функции и объекты описывающие поведение снега. Переименуйте предыдущий проект к примеру в snow.bb и сохраните в папку с проектами для 6 урока. Туда же поместите картинку снежинки. Пример объекта описания снежинки приведен ниже:
Type flakes
Field x#
Field y#
Field z#
Field spr
End Type
Global flake.flakes
Const TOTALFLAKES=400 |
Как вы наверно догадались, снежинка описывается тремя координатами в пространстве и самой картинкой, что и отражено в полях нашего объекта. Положим, что снежинок у нас будет 400 штук.
Теперь загрузим картинку снежинки в глобальную переменную:
Global snowpic = LoadSprite("snow.bmp",4) |
Для большей правдоподобности можно убрать потолок в нашей комнате. Закомментируйте следующие строчки в нашей программе:
;Global skytex=LoadTexture("ceil.jpg")
;ceiling=CreatePlane()
;PositionEntity ceiling,0,1.5,0
;EntityTexture ceiling, skytex
;TurnEntity ceiling, 0,0,180 |
Теперь давайте создадим наши 400 снежинок и поместим в игру. Предположим, что снег идет только в нашей первой комнате, а над остальной территорией есть какой-то навес. Поэтому площадь высыпания снега предположим 10 квадратов:
Function InitFlakes()
For x = 1 To TOTALFLAKES
flake.flakes = New flakes
flakespr = CopyEntity( snowpic )
ScaleSprite flakespr,0.3,0.3
flakex#=Rnd(0,20)
flakey#=Rnd(-1.5,1.5)
flakez#=Rnd(0,20)
Next
End Function |
Приведенная нами функция InitFlakes в цикле от 1 до 400 создает объекты снежинок. Далее она же копирует в поле spr картинку снежинки для каждого созданного объекта, а затем инициализирует переменные координат x,y,z с таким условием, что по координате y диапазон изменения координат не превышает -1.5 до 1.5 (это как раз координаты пола и гипотетического потолка). По x и z переменные варьируются в пределах от 0 до 20.
Нам понадобится еще одна функция (назовем ее UpdateFlakes), которая будет отвечать за поведение снежинок в игре:
Function UpdateFlakes()
For flake.flakes = Each flakes
If flakey#<-1.5 Then
flakex#=Rnd(0,20)
flakey#=1.5
flakez#=Rnd(0,20)
End If
dir#=Rnd(-0.02,0.02)
flakex#=flakex#+dir+.005
flakey#=flakey#-0.02
PositionEntity flakespr, flakex#, flakey#, flakez#
Next
End Function |
Единственное условие этой функции - если снежинка долетает до уровня пола, то она переносится на уровень потолка с случайными координатами x и z. Таким образом имитируется непрерывность падения снега. Также с помощью генерируемых случайным образом переменных создается видимость хаотического перемещения снежинки.
Не забудьте перед главным циклом вызвать функцию InitFlakes(), в внутри главного цикла - UpdateFlakes()
Программу целиком вы можете увидеть тут.
Для разминки Вы можете поменять координаты точек охвата выпадения снега, также увеличить/уменьшить количество снежинок, изменить скорость снега, размеры снежинок, хаотичность движения. Попробуйте привязать снег к координатам игрока, чтоб, где бы не находился игрок снег выпадал равномерно (подсказка: координаты игрока x и z должны быть в пределах случайного число для координат x и z снега, а получить координаты объекта player можно с помощью функций EntityX, EntityZ).
6.7. Анимационные текстуры
Зачастую в игре необходимо производить анимацию каких-то картинок. Например: сменяющаяся реклама на досках объявлений, водная поверхность, эффекты взрывов и горения и т.д. Для реализации этих задумок не всегда полезно использовать модели. Иногда вполне достаточно применить последовательность заранее нарисованных картинок. Blitz3D располагает очень удобной функцией для загрузки последовательности анимационных кадров - LoadAnimTexture. Рассмотрим подробнее передаваемые ей аргументы:
file$ - имя файла содержащего анимационную последовательность
флаги текстуры (опционально)
1: Color (по умолчанию) - обычная цветная текстура, что видим, то и рисуем.
2: Alpha - если картинка содержит такого рода информацию, то она может использоваться для создания прозрачных участков. С alpha-map более темные участки рисунка получатся более прозрачными, а более яркие - менее прозрачными
4: Masked - все участки рисунка с цветом 0,0,0 - не будут отображаться на экране.
8: Mipmapped - текстуры с низким разрешением могут быть использованы на удаленном расстоянии от камеры, что не приводит к порче изображения, а оно получается сглаженным
16: Clamp U - Любая часть текстуры лежащая за пределами U координат 0-1 не будет отображаться. Это служит для предотвращения "враппинга" (переноса) текстур.
32: Clamp V - то же самое для V координаты
64: Spherical reflection map - одна из форм пространственного наложения текстуры. Берется одна текстура и накладывается на объект типа mesh, что приводит к эффекту отражения этой текстуры в объекте.
128: Cubic environment map - почти то же самое что 64, только берутся 6 текстур - левая, правая, задняя передняя, верхняя и нижняя. (многие старые видео карты не поддерживают эту способность)
256: Store texture in vram - сохранение текстур в видеопамяти, позволяет быстрее проводить такие ресурсоемкие операции, как рисование на кубической пространственной текстуре и т.д.
512: Force the use of high color textures - использование текстур с большей глубиной цвета в режимах с более низкой глубиной цвета. Иногда заметно улучшает качество изображений.
frame_width - ширина каждого фрейма (кадра внутри файла последовательности картинок)
frame_height - высота каждого анимационного фрейма
first_frame - номер первого фрейма
frame_count - всего фреймов для анимации
Для наложения анимационной текстуры на объект служит давно знакомая нам команда (функция) EntityTexture третий аргумент которой как раз и является номером кадра из анимационной последовательности, который нужно отобразить в данный момент времени.
Для примера, давайте создадим куб и обтянем его анимационной текстурой водной поверхности, которую можно взять тут. Первый кадр этой последовательности можно увидеть на рисунке ниже:

Сама программа приведена ниже:
Graphics3D 640,480
SetBuffer BackBuffer()
camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0
cube=CreateCube()
PositionEntity cube,0,0,5
; Load anim texture
anim_tex=LoadAnimTexture( "wateranim.jpg",1,124,124,0,23 )
While Not KeyDown( 1 )
frame=MilliSecs()/30 Mod 23
EntityTexture cube,anim_tex,frame
pitch#=0
yaw#=0
roll#=0
If KeyDown( 208 )=True Then pitch#=-1
If KeyDown( 200 )=True Then pitch#=1
If KeyDown( 203 )=True Then yaw#=-1
If KeyDown( 205 )=True Then yaw#=1
If KeyDown( 45 )=True Then roll#=-1
If KeyDown( 44 )=True Then roll#=1
TurnEntity cube,pitch#,yaw#,roll#
RenderWorld
Flip
Wend
End |
В строке: anim_tex=LoadAnimTexture( "wateranim.jpg",1,124,124,0,23 ) в описанную выше функцию передается название файла (wateranim.jpg), флажок 1 (отображение как есть), ширина и высота одного кадра, в нашем случае - 124, и начальный и конечный кадр последовательности - 0 и 23 соответственно.
Интерес представляет строка: frame=MilliSecs()/30 Mod 23, суть которой назначить скорость смены кадров (30 - это смена кадров каждые 30 миллисекунд, 23 - кадров всего (начиная с нулевого))
Пример можете открыть и здесь.
6.8. Нанесение множества текстур
Графические карты поддерживают мультитекстурирование с очень небольшим уменьшением производительности. Для большинства карт это две, начиная с GeForce3 - четыре текстуры для одного объекта, которые могут быть применены одновременно. Если же вы попытаетесь применить больше текстур Blitz будет самостоятельно эмулировать этот эффект за счет увеличения числа одного и того же объекта и текстур связанных с ним. Однако это уже приведет к ощутимым потерям производительности.
Для примера создадим шарик и покроем его двумя текстурами, одна из которых будет пространственной (environment) сферической текстурой, вторая - обычной.
Сохраните следующие текстуры в каталог с вашим проектом:
 |
 |
и перенесите ниже приведенный код в новый проект:
Graphics3D 640, 480
SetBuffer BackBuffer()
AmbientLight 0,0,0
logo=LoadTexture( "mtex.png" )
env=LoadTexture( "spheremap.png",64)
sphere=CreateSphere(20)
EntityTexture sphere,logo,0,0
EntityTexture sphere,env,0,1
camera=CreateCamera()
PositionEntity camera,0,0,-4
light=CreateLight()
TurnEntity light,45,45,0
LightColor light,255,0,0
TextureBlend logo,1
TextureBlend env,3
While Not KeyHit(1)
off#=off#-.01
PositionTexture logo,off,0
TurnEntity sphere,.1,.2,.3
UpdateWorld
RenderWorld
Flip
Wend
End |
Для наложения множества текстур применяется знакомая Вам функция EntityTexture, Единственное отличие в использовании этой функции - это передаче в качестве 4-го аргумента порядкового номера накладываемой текстуры. Наслоение текстур (Texture Blending) осуществляется следующим образом - Blitz3D использует текстуру с самым большим индексом для смешивания с текстурой под ней и так далее до смешивания самой нижней с полигонами объекта, на которые они натягиваются.
Функция TextureBlend использует 3 флага:
0 - нет смешивания
1 - нет смешивания или текстура загружена с alpha-флажком (не рекомендуется для
мультитекстурирования)
2 - Multiply (этот флаг используется по умолчанию)
3 - Add (добавить)
Применительно к видеокартам сам термин альфа-смешивания: Alpha-Blending (альфа-смешение) - создание полупрозрачных объектов, возможность задать изображению или отдельному пикселю специальный атрибут, определяющий как будет выглядеть изображение: сплошным (не пропускает свет), невидимым (прозрачным), или полупрозрачным. Текстура, наносимая на объект, может содержать помимо информации о цвете (Red,Green,Blue), информацию о прозрачности (Alpha). В зависимости от величины коэффициента Alpha разные части объекта приобретают различную степень прозрачности, т.е. при использовании совместно с полигонами, альфа-смешение позволяет создавать стекло, воду или другие виртуально прозрачные элементы. Как правило смешивание цветов перекрываемого объекта и полупрозрачного объекта (с альфа прозрачностью) происходит по следующей формуле: (alpha) * (значение цвета объекта с прозрачностью) + (alpha-1) * (значение цвета покрываемого объекта) при 0<=alpha><=1. >.
Попробуйте поварьировать флажками функции TextureBlend и посмотрите на полученные результаты.
6.9. Обычно открывающиеся двери
В наших предыдущих примерах мы рассматривали двери, которые открываются скольжением в ту или иную сторону. Наверно у Вас возникал вопрос, как сделать двери, которые открываются традиционным образом, как у нас в домах и квартирах - поворачиваясь на петлях.
Для того, чтоб дверь вращалась с центром вращения смещенным к одной из своих боковых сторон - необходимо создать пустой объект (pivot) а саму дверь привязать к этому пустому объекту со смещением. Давайте создадим также пару стенок и дверные ручки:
Global wall_left=CreateCube()
ScaleEntity wall_left,4,4,0.1
PositionEntity wall_left,-6,0,0
EntityColor wall_left,100,100,50
Global wall_right=CreateCube()
ScaleEntity wall_right,4,4,0.1
PositionEntity wall_right,6,0,0
EntityColor wall_right,100,100,50
Global roof_bar=CreateCube()
ScaleEntity roof_bar,2,1,0.1
PositionEntity roof_bar,0,3,0
EntityColor roof_bar,100,100,50
Global hinge=CreatePivot()
PositionEntity hinge,-2,0,0
Global door=CreateCube(hinge)
ScaleEntity door,2,3,0.2
PositionEntity door,2,-1,0
EntityColor door,50,100,0
Global door_handle1=CreateSphere(8,hinge)
PositionEntity door_handle1,3.5,-1,-0.3
ScaleEntity door_handle1,0.2,0.2,0.2
Global door_handle2=CreateSphere(8,hinge)
PositionEntity door_handle2,3.5,-1,0.3
ScaleEntity door_handle2,0.2,0.2,0.2 |
Сперва мы создадим стенки и верхнюю перегородку в виде соответствующим образом отмасштабированных кубов (переменные wall_left, wall_right, roof_bar). Затем, как мы и собирались - создаем пустой объект (pivot) - переменная hinge (англ. "петля") И наконец создаем саму дверь, как "потомок" объекта hinge со смещением 2 по оси Х. Почему 2? Потому что - мы предварительно отмасштабировали ее в два раза по оси Х функцией ScaleEntity.
Дверные ручки сделаем в виде сфер (переменные - door_handle1 и door_handle2)
Объявим также две переменных, которые определяют текущее состояние двери:
Global door_open=0; 0 = дверь не двигается, 1 = дверь закрывается, 2 = дверь открывается
Global door_status=0 |
В главном цикле программы при нажатии клавиши пробел заставим нашу дверь открываться и закрываться:
If KeyHit(57) Then
If door_status=0 Then
door_open=1
ElseIf door_status=1 Then
door_open=2
End If
End If
If door_open=1 Then
open_door()
ElseIf door_open=2 Then
close_door()
End If |
Реализация функций open_door() и close_door() приведены ниже:
Function open_door()
RotateEntity hinge,EntityPitch(hinge),EntityYaw(hinge)+1,EntityRoll(hinge)
If EntityYaw(hinge)>=90 Then
door_status=1
door_open=0
End If
End Function
Function close_door()
RotateEntity hinge,EntityPitch(hinge),EntityYaw(hinge)-1,EntityRoll(hinge)
If EntityYaw(hinge)<1 Then
door_status=0
door_open=0
End If
End Function |
Как можно заметить, при открытии двери к аргументу функции RotateEntity отвечающему за вращение вокруг оси У добавляется единица, при закрытии - единица отнимается. Функция EntityYaw возвращает текущий угол двери относительно оси У.
Выглядит наш пример следующим образом:

Код программы полностью приведен здесь (door.bb)
Что будет далее:
Следующей урок будет полностью практическим. На основе всех ранее полученных знаний мы создадим простую космическую стрелялку в стиле Slordax (ID Software).
уже 16003 посетителей!
|
|