|
Курсы
Регистрация
Методика
Оглавление
Для учащихся

Статьи
Инструменты
Контакты |
Урок
9.
Создание серьезных уровней
9.1. Ознакомление
с
редактором
Cartography Shop
Для создания внутренних
лабиринтов существует десяток редакторов, которые в той или ной степени
позволяют проектировать карты в формате совместимом с
Blitz3D. Но
так как
Blitz3D понимает форматы
.X, .3DS, .MD2 и собственный
.B3D и уровень при создании в
сторонних редакторах проектируется как неделимое целое, то это не очень удобное
решение. Поясним на примере: допустим мы создали лабиринт из нескольких комнат в
редакторе
MilkShape и экспортировали его в формат
.3DS или .X.
Загрузив уровень в нашу игру, сделанную на
Blitz3D мы сможем двигаться в
этом лабиринте, но это будет целостный лабиринт, а потому разные объекты
интерьера нам придется грузить отдельно. Если же мы создадим объекты интерьера в
этом редакторе, то будучи экспортированными в
.3DS или .X
формате они будут частью уровня и мы никак не осуществим с ними
взаимодействия. А если в нашей игре стоит задача, допустим, щелкнуть мышкой на
шкаф и достать оттуда конверт, затем щелкнуть на стул и сдвинуть его с места?
Увы, мы будем не в состоянии это осуществить! У
Blitz3D правда еще есть
возможность грузить уровни от игры
Quake3 в формате
.BSP, но в этом случае, понадобится
сама игра
Quake3, чтоб брать нужные текстуры, и кроме
того понадобится нахождение конвертеров для
созданных уровней, потому что
Blitz3D не всегда может понять
.BSP файл, созданный в разных редакторах.
Разрешение этого конфликта нашлось и имя ему -
Cartography Shop! Нельзя
сказать, что это идеальное средство, этот редактор еще в стадии разработки (на
сегодняшний момент стабильной является версия 4.0 доступна также 4.1). Но сама
идея редактора, его удивительная простота и гибкость, заставляет его уважать с
самого начала ознакомления с этой программой.
Скачать этот редактор можно, зайдя на сайт разработчика:
http://www.leadwerks.com/
иля прямо на страничку редактора:
http://www.leadwerks.com/index.php?page=mapeditor.htm
Редактор имеет возможность экспорта в формат .Х для версии 4.0 и в форматы
.B3D и
Half-Life .MAP в версии 4.1.
Но самое интересное для нас то, что автор редактора, привел фрагмент кода,
благодаря которому в программы, написанные на
Blitz3D можно загружать родной
формат редактора .CSM. Надо заметить, что
это открывает большие перспективы для программиста, ведь
несколько модифицировав код автора, можно получить доступ не только к отдельным
объектам созданной карты, но и создавать описания своих объектов непосредственно
в редакторе, а потом уже читать их параметры в программе. Теперь уже карты не
представляется в виде одного многополигонального целого, а разбита на объекты.
Например, если в редакторе мы назначим кубу имя, Дверь, то в программе, щелкнув
на нем мышью, мы прочитаем его имя и будем работать с ним как с дверью!
Если вы уже успели загрузить редактор и запустили его, то увидите перед собой 4
экрана, три из которых проекции, а четвертая 3-х мерный вид нашей карты.

На рисунке выше
- один из примеров, которые
поставляются вместе с редактором. Некоторые
объекты антуража уже есть в редакторе, такие как кровать, кресло, столик, шкаф и
тумбочка, и т.д. так что вам не нужно будет ломать голову обставляя комнату.
Для создания новой карты, выберите пункт меню:
File->New или нажмите
комбинацию клавиш
Ctrl+N. Присмотритесь к
меню
Objects редактора. В нем есть
различные разделы, такие как:
Primitives, Splines, Bedroom, Living Room, Space
и
Lights. Сразу оговоримся,
заметив, что в это меню можно добавлять свои объекты. Например, если вы в
редакторе создадите свой объект, вы можете сохранить его в одну из папок в
каталоге
Prefabs редактора и этот
объект будет уже доступен для ваших дальнейших карт. Например вы очень
потрудились и создали красивую башню для крепости, теперь остается только
сохранить ее в каталог
Prefabsв папку, например "Fortress"
и ваша башня будет всегда под рукой в любом из дальнейших
проектов.
Давайте создадим простейший объект в редакторе - например куб. Для этого
выберите их меню
Objects->Primitives->Box и
далее на одной из проекций прижмите левую кнопку мыши и растягивайте пунктирный
прямоугольник до необходимого размера:

Если вас удовлетворяют размеры будущей фигуры, отпустите клавишу мыши и цвет
пунктирного прямоугольника поменяется. Кроме того добавятся еще квадратики для
последующего изменения размеров. Если вы не намерены больше модифицировать
размеры, то смело нажимайте клавишу
Enter и фигура будет создана.

Этот принцип справедлив для добавления любого объекта в окно редактора, начиная
с куба и заканчивая каким-нибудь сложным объектом из существующих или созданных
вами.
Теперь когда объект реально создан, его можно изменять следующим образом.
Щелкните на значке х, который обозначает цент нашего объекта. Должны
появиться белые квадратики по всему периметру объекта:

В этом режиме доступно изменение размеров объекта. Если вы щелкните на значке
х второй раз, то квадратики сменятся на кружки:

В этом режиме доступно вращение объекта. Третье нажатие на значке х
приведет к смене режима на следующий:

Это режим сдвига или перекоса.
Перемещение самого объекта целиком с места на место, можно осуществить если
прижать его в районе все того же значка х и тащить в нужном направлении.
При этом объект должен быть выделенным.
На панели быстрых кнопок редактора есть целая их группа, дающая быстрый доступ к
элементам меню редактора. Так например кнопки:
Wireframe, Solid, Textured меняют
режимы отображения карты в окне 3-х мерной проекции. Кнопки
Select object, Select surface, Select vertex
дают возможность выделять целый объект, одну из его поверхностей
или группу его вертексов соответственно.
Очень полезными являются кнопки сгруппировать объекты или разгруппировать объект
(Group, Ungroup).
Так например, кровать
Bed в группе
Bedroom после разгруппировки
состоит из двух объектов для каждого из которых можно задать свои параметры.
В версии 4.1 редактора появились кнопки
Carve и Hollow и
если функциональность первой так и не удалось проверить, то вторая исправно
делала дырки в объектах, главное было задать толщину стенок.
Также можно найти довольно интересными кнопки
Flip и Mirror. Первая
позволяет инвертировать поверхности объекта. Иногда это
помогает для объектов которые созданы в других редакторах и импортированы в
Cartography Shop и при переносе
могли потерять информацию о правильной ориентации поверхностей. Вторая кнопка
дает возможность инвертирования всего объекта относительно одной из выбранных
координат.
А теперь изюминка редактора: кнопка
Calculate Lights. Эта функция
просчитывает карты освещения (естественно после добавления нами в карту
источников освещения) и мы можем их использовать в игре. В окне трехмерной
проекции после расчета освещения тут же появляются правильные тени от
расставленных объектов. Карта освещения (lightmap)
сохраняется в формате .CSM и мы ее считываем в
нашу игру.
Теперь немного о текстурах. Текстуры хранятся в каталоге
Textures в подкаталогах Brick,
Fabric, Floor и т.д. но вам никто не запретил добавлять свои папки и
файлы в эту директорию. Все ваши текстуры тут же будут доступны из меню
Textures редактора или в обозревателе текстур (Texture
Browser) активируемого клавишей "T".
Применить текстуру к выбранному объекту или поверхности можно используя
правую панель
Cartography Shop:

Кроме простого наложения текстуры, вы можете точно подобрать масштаб текстуры,
ее расположение на поверхности и даже угол поворота текстуры! Все это доступно
из меню на правой поверхности, когда вы войдете в режим
Select surface:

На рисунке ниже, мы применили к одной из поверхностей куба другую текстуру,
отмасштабировали ее, уменьшив в два раза и развернули на 45 градусов:

Заметим, что после загрузки в игру все будет выглядеть аналогично.
Давайте для примера сделаем пол, установим на него кровать из доступного набора
объектов и создадим источник освещения, а потом просчитаем карту освещения.
Итак полом у нас будет служить куб (Objects->Primitives->Box)
мы лишь слегка растянем его в плоскости
XZ. Затем добавим кровать (Objects->Bedroom->Bed).
Выровняйте ее хорошенько относительно пола для
достоверности и теперь добавьте источник освещения (Objects->Lights
->Light) Позиционируйте его в нужном вам месте и нажмите
Enter как вы делали и для
других объектов. Допустим у вас получилась следующая картина:

Теперь выберите кнопку
Calculate Lights в панели
быстрого доступа или нажмите
Ctrl+L. После просчета и наложения карты освещения (lightmap) получаем следующую
картинку:

Теперь попробуйте поупражняться в освоении редактора, создавая различные
архитектурные композиции и интерьеры из доступных элементов. Думаю даже новичку
будет легко и удобно пользоваться этим гибким инструментом.
9.2. Создаем
лестницы и арки, используем конвертер
Без всякого сомнения, чтоб подниматься с этажа на
этаж нашему персонажу кроме лифта может понадобится лестница. А так как этого
объекта нет в редакторе, то построим ее из кубов - самого простого подручного
материала, который можно найти в
Cartography
Shop.
Поставим в ряд 9 кубов один выше другого, как это показано на рисунке ниже:

Если у вас талант дизайнера, то можете добавить резные перила и витые решетки
:). Мы же просто попросим вас выделить все поверхности кубов лестницы, которые
составляют ступени и лежат в плоскости
ZY. и затем поменять для них
текстуру. После этого можете сгруппировать все составляющие объекта (после
выделения их всех, естественно)и сохранить наш труд в папку
"Misc" (создайте ее предварительно)
каталога
"Prefabs" редактора
Cartography Shop под именем
Stair.csm.

Теперь эта лестница содержится в списке готовых объектах и доступна в любой
момент и для любой карты из меню редактора:
Objects->Misc->Stair
Теперь давайте создадим простейшую арку:
Воспользуемся нашим известным строительным материалом - кубом (box)
. Создайте 6 кубов, установленных друг на друге в
проекции XY:

Теперь выделите все кубы при помощи левой кнопки мыши щелкая на значок х
и удерживая клавишу Ctrl пока
все они не выделятся красным цветом. Теперь перейдите в режим
"Select vertex" (кнопка на панели быстрого доступа
редактора). После чего у нас
появляется возможность изменять наши объекты, растягивая их форму при
перемещении точек (вертексов).
Однако для более точного позиционирования можно уменьшить шаг сетки. Нажмите
клавишу "["
(левая скобка).
Теперь поменяйте координаты вертексов при помощи мыши пока не
получите следующее:

Теперь вернитесь к режиму выделения объектов "Select
object" (кнопка на панели быстрого доступа редактора)
и скопируйте объект (Ctrl+C).
Вставьте объект (Ctrl+V)
- он вставится на тоже место, что и копируемый.
Оттащите его чуть правее и поверните вокруг оси
Х, при помощи кнопки "Mirror" .
Состыкуйте объекты до получения целостной арки.

Сгруппируйте и можете сохранить как Prefab для
использования в последующих проектах. Трехмерная проекция должна выглядеть
примерно так:
Если вы уже достаточно освоились в редакторе
Cartography Shop, то наверняка заметили, что в числе
предопределенных объектов типа куб, конус, цилиндр отсутствует весьма
необходимый - сфера. Чтоб не особенно напрягаться создавая его в
Cartography Shop можно легко и просто
создать его в MilkShape3D и
потом экспортировать в .3DS для
конвертирования в .CSM при
помощи конвертера PrefabMaker, который
вы также можете найти на сайте разработчика
Cartography Shop -
http://www.leadwerks.com/ из
раздела Tools. Или по прямой
ссылке:
Download
Если вы, к примеру создали шар в редакторе MilkShape3D
и экспортировали его в файл
sphere.3ds,

то после запуска конвертера (файл
convert.exe)
и выбора вышеуказанного файла, получаем конвертированный
файл sphere.csm, который можно
сохранить в Prefabs для
дальнейшего использования. Как видите у нас уже накопилось 3 объекта, которые мы
создали самостоятельно:

9.3. Загрузка
уровней в Blitz3D
Как мы уже
сообщали, разработчик редактора Cartography Shop
во встроенной помощи к своему продукту привел код для загрузки уровня в
Blitz3D непосредственно из .CSM
файла.
Данный код, который вы должны добавить к дальнейшим нашим проектам содержится
ниже:
;=================================================
;=================================================
Function
LoadCSM(file$,texturepath$=".\")
f=ReadFile(file)
If
Not
f Return
ChangeDir
FileDir(file)
lightmapbank=CreateBank()
texturebank=CreateBank()
;Version - this will
load CShop 4.0 and CShop 4.1 maps
version=ReadInt(f)
If
version<>4 And
version<>5
CloseFile
f
Return
EndIf
map=CreatePivot()
;Groups
DebugLog
groupcount+" groups"
groupcount=ReadInt(f)
For
n=1 To
groupcount
flags=ReadInt(f)
group=ReadInt(f)
Properties$=readstringn(f)
r=ReadInt(f)
g=ReadInt(f)
b=ReadInt(f)
Next
;Visgroups (new in
4.1)
If
version=5
visgroupcount=ReadInt(f)
For n=1
To
visgroupcount
name$=readstringn(f)
flags=ReadInt(f)
r=ReadInt(f)
g=ReadInt(f)
b=ReadInt(f)
Next
EndIf
;Lightmaps
lightmapcount=ReadInt(f)
DebugLog
lightmapcount+"
lightmaps"
For
n=1 To
lightmapcount
w=ReadInt(f)
h=ReadInt(f)
texture=CreateTexture(w,h)
TextureCoords
texture,1
ResizeBank
lightmapbank,BankSize(lightmapbank)+4
PokeInt
lightmapbank,BankSize(lightmapbank)-4,texture
LockBuffer
TextureBuffer(texture)
For ty=0
To
h-1
For tx=0
To
w-1
hue=ReadInt(f)
WritePixelFast
tx,ty,hue,TextureBuffer(texture)
Next
Next
UnlockBuffer
TextureBuffer(texture)
Next
;Meshes
meshcount=ReadInt(f)
DebugLog
meshcount+" meshes"
For
n=1 To
meshcount
flags=ReadInt(f)
group=ReadInt(f)
properties$=readstringn(f)
r=ReadInt(f)
g=ReadInt(f)
b=ReadInt(f)
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
If
version=5 visgroup=ReadInt(f)
facecount=ReadInt(f)
DebugLog
facecount+" surfaces."
mesh=CreateMesh(map)
NameEntity
mesh,properties
PositionEntity
mesh,x,y,z
;Surfaces
For
s=1 To
facecount
flags=ReadInt(f)
texturefile$=readstringn(f)
lightmapindex=ReadInt(f)
offsetu#=ReadFloat(f)
offsetv#=ReadFloat(f)
scaleu#=ReadFloat(f)
scalev#=ReadFloat(f)
rotation#=ReadFloat(f)
vertexcount=ReadInt(f)
DebugLog
vertexcount+"
vertices"
trianglecount=ReadInt(f)
DebugLog
trianglecount+"
triangles"
linecount=ReadInt(f)
surf=CreateSurface(mesh)
brush=CreateBrush()
texturefile=Lower(texturefile)
texture=retrievetexture(texturepath+texturefile,texturebank)
If
texture BrushTexture
brush,texture
If
lightmapindex>0 And
lightmapindex*4<=BankSize(lightmapbank)
lightmap=PeekInt(lightmapbank,(lightmapindex-1)*4)
If
lightmap
BrushTexture
brush,lightmap,0,1
BrushFX
brush,1
EndIf
EndIf
PaintSurface
surf,brush
FreeBrush
brush
;Vertices
For
v=0 To
vertexcount-1
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
nx#=ReadFloat(f)
ny#=ReadFloat(f)
nz#=ReadFloat(f)
r=ReadInt(f)
g=ReadInt(f)
b=ReadInt(f)
u0#=ReadFloat(f)
v0#=ReadFloat(f)
w0#=ReadFloat(f)
u1#=ReadFloat(f)
v1#=ReadFloat(f)
w1#=ReadFloat(f)
TFormPoint
x,y,z,0,mesh
AddVertex
surf,TFormedX(),TFormedY(),TFormedZ(),u0,-v0
VertexColor
surf,v,r,g,b
VertexTexCoords
surf,v,u1,-v1,0,1
VertexNormal
surf,v,nx,ny,nz
Next
;Triangles
For
t=0 To
trianglecount-1
a=ReadInt(f)
b=ReadInt(f)
c=ReadInt(f)
AddTriangle
surf,a,c,b
Next
For
l=0 To
linecount-1
a=ReadInt(f)
b=ReadInt(f)
Next
Next
Next
;Point Entities
entitycount=ReadInt(f)
DebugLog
entitycount+"
entities"
For
n=1 To
entitycount
visgroup=ReadInt(f)
; used to be
flags, but wasn't really used
group=ReadInt(f)
properties$=readstringn(f)
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
entity=CreatePivot(map)
NameEntity
entity,properties
PositionEntity
entity,x,y,z
Next
;Free textures
For
n=0 To
BankSize(lightmapbank)-1
Step
4
FreeTexture
PeekInt(lightmapbank,n)
Next
FreeBank
lightmapbank
For
n=0 To
BankSize(texturebank)-1
Step
8
FreeBank
PeekInt(texturebank,n)
FreeTexture
PeekInt(texturebank,n+4)
Next
FreeBank
texturebank
CloseFile
f
Return
map
End Function
;Read a
null-terminated string
Function
ReadStringN$(f,maxlength=0)
Repeat
ch=ReadByte(f)
If ch=0
Return
t$
If maxlength
If
Len(t$)=maxlength
Return
t$+Chr(ch)
EndIf
t$=t$+Chr$(ch)
Forever
End
Function
;Return a loaded
texture
Function
RetrieveTexture(file$,bank)
For
n=0 To
BankSize(bank)-1
Step
8
namebank=PeekInt(bank,n)
s$=""
For b=0
To
BankSize(namebank)-1
s=s+Chr(PeekByte(namebank,b))
Next
If s=file
Return
PeekInt(bank,n+4)
Next
ResizeBank
bank,BankSize(bank)+8
namebank=CreateBank(Len(file))
For
b=0 To
BankSize(namebank)-1
PokeByte
namebank,b,Asc(Mid(file,b+1))
Next
DebugLog
"Loading texture "+file
PokeInt
bank,BankSize(bank)-8,namebank
texture=LoadTexture(file)
If
Not
texture DebugLog
"Failed to load texture "+Chr(34)+CurrentDir()+file+Chr(34)
PokeInt
bank,BankSize(bank)-4,texture
Return
texture
End
Function
;Get the file part of
a file path
Function
FileName$(file$,ext=1)
file=Replace(file,"/","\")
Repeat
p=Instr(file,"\")
If
p
file=Right(file,Len(file)-p)
Else
Exit
EndIf
Forever
If
Not
ext
p=Instr(file,".")
If
p file=Left(file,p-1)
EndIf
Return
file
End
Function
;Get the directory of
a file path
Function
FileDir$(file$)
file=Replace(file,"/","\")
oldp=1
Repeat
p=Instr(file,"\",oldp)
If
p
oldp=p+1
Else
file=Left(file,oldp-1)
Exit
EndIf
Forever
Return
file
End Function
;Parsing function
Function
Piece$(s$,entry,char$="
")
While
Instr(s,char+char)
s=Replace(s,char+char,char)
Wend
For
n=1 To
entry-1
p=Instr(s,char)
s=Right(s,Len(s)-p)
Next
p=Instr(s,char)
If
p<1
a$=s
Else
a=Left(s,p-1)
EndIf
Return
a
End
Function
;Function for
retrieving entity properties
;[ "light"=KeyValue(entity,"classname") ]
Function
KeyValue$(entity,key$)
properties$=EntityName(entity)
key$=Lower(key)
Repeat
p=Instr(properties,Chr(10))
If
p test$=(Left(properties,p-1))
Else
test=properties
testkey$=Piece(test,1,"=")
testkey=Trim(testkey)
testkey=Replace(testkey,Chr(34),"")
testkey=Lower(testkey)
If
testkey=key
value$=Piece(test,2,"=")
value$=Trim(value$)
value$=Replace(value$,Chr(34),"")
Return
value
EndIf
If
Not
p Return
properties=Right(properties,Len(properties)-p)
Forever
End
Function |
Сохраните данный фрагмент кода в
виде шаблона (например под именем
load_csm.bb), он Вам понадобится не один раз.
Сразу можно смело сказать, что его придется
модифицировать, так как простая загрузка уровня нас никак не может устроить. Нам
понадобится контролировать столкновения с объектами, добавлять свои объекты и
получать информацию о них в программе и многое другое.
Давайте создадим простейший уровень в редакторе
Cartography Shop и загрузим его в программу на
Blitz3D. Начните новый проект и
добавьте в него какие-либо объекты по Вашему усмотрению, например так:

Сохраните ваш уровень под именем mymap.csm в папку
с проектами для урока №9 и туда же поместите каталог TEXTURES
из папки, куда вы поставили Cartography Shop,
так как текстуры понадобятся для загрузки в программу. Файл mymap.csm
можно скачать тут.
Совет: Все текстуры в Cartography Shop
содержатся в формате *.bmp что не очень экономично. Вы
можете конвертировать их всех в формат *.JPG и
далее уже создавать карты с этими текстурами. Экономия места будет очень
существенной.
Теперь напишем небольшой стандартный фрагмент кода для перемещения камеры и
загрузки созданного Вами уровня:
Graphics3D
640,480
SetBuffer
BackBuffer()
Global player=CreateSphere()
PositionEntity
player,0,0,0
TurnEntity
player,0,0,0
EntityRadius
player, 100
cam=CreateCamera(player)
map=LoadCSM("mymap.csm","textures\")
While
Not
KeyHit(1)
If KeyDown(200)
MoveEntity
player, 0, 0, 5
If KeyDown(208)
MoveEntity
player, 0, 0, -5
If KeyDown(203)
TurnEntity
player, 0, 2, 0
If KeyDown(205)
TurnEntity
player, 0, -2, 0
RenderWorld
UpdateWorld
Flip
Wend
End
|
Как видите, загрузка уровня
сводится лишь к вызову функции
LoadCSM
и передаче ей
двух параметров - имени файла карты и имени каталога с загружаемыми текстурами.
Добавьте этот код к коду чтения карты и выполните программу. Безусловно сразу
бросается в глаза, то что мы проходим сквозь все объекты. Для устранения этого
недоразумения, введите две константы, определяющие тип объектов, как мы уже
делали ранее:
|
Const
TypePlayer = 1, TypeWall = 2 |
Первая определяет тип игрока,
вторая любую поверхность с которой мы будем сталкиваться. Теперь опишем коллизии
и назначим тип объекта нашему игроку:
EntityType
player, TypePlayer
Collisions
TypePlayer, TypeWall, 2, 1 |
Теперь найдите раздел
Meshes в коде загрузки уровня и после строчек, затененных у нас
в следующем фрагменте серым цветом вставьте следующую строку.
;Meshes
.......
.......
mesh=CreateMesh(map)
NameEntity mesh,properties
PositionEntity mesh,x,y,z
EntityType
mesh, TypeWall
; Added |
Этой строкой мы указываем
программе, что любой объект загруженной карты принимает тип содержащийся в
константе TypeWall.
Запустите программу и убедитесь, что теперь объекты карты
перестали быть проходимыми для игрока.
9.4. Свободное
вращение камеры
Следующим нашим заданием будет реализация управления
камеры при помощи мыши. Как вы знаете, во всех современных
3D-шутерах от первого лица
управление камеры осуществляется мышью. Все это вполне по силам реализовать в
Blitz3D. Давайте воспользуемся
редактором
Cartography Shop и создадим
более продвинутую карту. Наша цель создать по крайней мере два этажа соединенных
между собой лестницей. Как вы помните, лестница уже нами создана и остается ее
лишь вставить в нужную позицию. Этажи конструируются из объектов
Box. Кое-где расставим мебель
из стандартной поставки редактора. Даже для новичка в картостроении это не
должно занять более получаса времени.
Тем не менее для экономии ваших 30 минут на создание уровня можно скачать его по
этой ссылке. Распакуйте его в каталог, где ранее
находился ваш тестовый уровень
mymap.csm. Распакованный файл носит имя -
house.csm. Напоминаем, что в том
же каталоге должна находиться папка с текстурами редактора
Cartography Shop.
Воспользуйтесь шаблоном
load_csm.bb и
добавьте к нему (естественно выше)
следующий код:
Const
TypePlayer = 1, TypeWall = 2
Graphics3D
640,480
SetBuffer
BackBuffer()
Global
camera,campitch#,camyaw#,mvx#,mvy#,mvz#
player=CreateSphere()
PositionEntity
player,420, 70, 170
TurnEntity
player,0,0,50
EntityRadius
player, 60
EntityType
player, TypePlayer
camera=CreateCamera(player)
CameraFogMode
camera,1
CameraFogColor
camera,100,200,255
map=LoadCSM("house.csm","textures\")
ScaleEntity
map, 0.5,0.5,0.5
Collisions
TypePlayer, TypeWall, 2, 3
While
Not
KeyHit(1)
mxspd#=MouseXSpeed()*0.2
myspd#=MouseYSpeed()*0.2
MoveMouse
GraphicsWidth()/2,GraphicsHeight()/2
campitch=campitch+myspd
If
campitch<-89
Then
campitch=-89
If
campitch>89
Then
campitch=89
RotateEntity
player,campitch,EntityYaw(player)-mxspd,0
If
KeyDown( 203 )=True
Then
mvx=mvx-1.5
If
KeyDown( 205 )=True
Then
mvx=mvx+1.5
If
KeyDown( 200 )=True
Then
mvz=mvz+1.5
If
KeyDown( 208 )=True
Then
mvz=mvz-1.5
mvy=mvy-0.1
If
EntityCollided(player,TypeWall)
mvy=mvy+0.1
EndIf
mvx=mvx/1.2
mvy=mvy/1.2
mvz=mvz/1.2
MoveEntity
player,mvx,0,mvz
TranslateEntity
player,0,mvy,0
RenderWorld
UpdateWorld
Flip
Wend
End
|
После стандартного
уже создания игрока, камеры и загрузки уровня, в главном цикле
представляет интерес функции Blitz3D
работы с мышью. Вначале вызываются функции
MouseXSpeed
и MouseYSpeed
которые
определяют разницу между
тем где был указатель мыши и где он находится теперь.
MoveMouse
позиционирует
указатель
мыши в центре экрана. Далее осуществляются проверки на не превышение угла
подъема камеры
более 89 градусов, чтоб наш игрок не сломал себе шею, рассматривая пол и
потолок :) После чего осуществляется поворот нашего игрока в зависимости от
рассчитанных параметров скорости движения указателя мыши.
Простейшая проверка на коллизии по координате У, заставляет нашего игрока
прижиматься к полу, а не свободно висеть в воздухе, при спуске и подъеме по
лестницам и объектам расставленным в комнатах.
mvy=mvy-0.1
If
EntityCollided(player,TypeWall)
mvy=mvy+0.1
EndIf |
Функция
TranslateEntity
используется
для учета текущей координаты У независимо
от ориентации объекта player.
Полный текст программы на всякий случай приведен
здесь.
Теперь поставим перед собой задачу отображения информации об объектах нашей
карты при наведении на них курсора мыши. Откройте карту
house.csm в редакторе
Cartography Shop. Выделите понравившийся объект, например
пол и нажмите клавишу "P"
на клавиатуре.

В открывшемся окне, нажмите кнопку
"Entity properties..."
В диалоговом окне:

Введите ключ (поле Key)
-
name, и
значение
(поле Value) -
"это пол".
Вы можете добавить еще несколько значений, какие вам понадобятся. Все
они, в дальнейшем могут быть считаны в процессе игры!
Внимание: Некоторые объекты (например кровать, дверь и т.д.) на
самом деле являются группами объектов и потому для того чтоб ввести считываемые
параметры в игре, их нужно разгруппировать. (Пометьте объект и нажмите
Ctrl+U или выберите пункт меню
Tools->Ungroup) После разгруппировки можно задать
ключевые параметры для каждого из объектов слагающих основной.
Задайте значения еще нескольким объектам и сохраните карту.
Теперь найдите раздел
Meshes в коде загрузки уровня и после строчек, затененных у нас
в следующем фрагменте серым цветом вставьте следующую строку.
;Meshes
.......
.......
mesh=CreateMesh(map)
NameEntity mesh,properties
PositionEntity mesh,x,y,z
EntityType
mesh, TypeWall
; было добавлено ранее
EntityPickMode
mesh, 2 |
Эта строка указывает программе, что объект может быть
выбран мышью по полигональной поверхности. Теперь прямо после ключевого слова
while главного
цикла нашей программы вставим следующий код:
ent =
CameraPick(camera,MouseX(),MouseY())
If ent
CName$ = KeyValue(ent,"name")
Else
CName$ = ""
EndIf |
а между
UpdateWorld и
Flip поместим следующую
строку:
|
Text 10, 10,
"Info: "+CName |
Теперь при наведении курсора мыши на объект, строковую
информацию о котором мы задали в редакторе, в углу экрана отобразится эта
строка. Как вы понимаете, это открывает огромные перспективы перед
программистом, так как мы теперь может задавать стартовые значения любых
объектов карты и по идентификаторам объектов манипулировать ими в программе.
Текст программы можно увидеть тут.
9.5. Создание
кубических отражений
Предположим, что перед нами стоит
задача касательно создания эффекта отражения окружающих предметов от поверхности
воды. В
Blitz3D для этих целей
предусмотрена функция SetCubeFace, которая может
принимать следующие параметры:
texture
- идентификатор текстуры
face - поверхность куба. Аргумент может содержать
одно из следующих значений:
0: левая поверхность (отрицательная X)
1: передняя поверхность (положительная Z) - по
умолчанию.
2: правая поверхность (положительная X)
3: задняя поверхность (отрицательная Z)
4: верхняя поверхность (положительная Y)
5: нижняя поверхность (отрицательная Y)
Для примера воспользуемся нашим предыдущим проектом с двухэтажным домом и для
создания эффекта кубических отражений, зальем первый этаж водой по щиколотку.
Если вы помните, для создания эффекта воды мы применяли объект
mesh в формате .3DS.
В данном примере мы поступим таким же образом, только текстура воды должна, по
замыслу, отражать антураж комнаты первого этажа.
Сохраните предыдущий проект под другим именем в ту же папку. Загрузите
mesh водной
поверхности или возьмите из урока № 8.
Теперь немного теории: Для того чтоб отражать в воде объекты комнаты, а
также е стены и потолок, необходимо создать вторую камеру и периодически
переключать управление на нее. Камера создается в точке, где находится игрок и
имеет все координаты игрока, кроме координаты
Y, которую можно опустить до
уровня воды (нашего объекта
mesh)
Созданную камеру мы направляем вправо, влево, вперед,
назад и вверх от этой базовой точки ее расположения и делаем моментальные снимки
дублирующего буфера экрана (BackBuffer()), которые
потом можно копировать в заранее созданную текстуру воды при помощи функции
CopyRect() . Функция
SetCubeFace()
рассмотренная выше применяется все пять раз, получая в качестве аргументов
текстуру воды и номер поверхности.
Добавьте глобальные переменные в программу, загрузите
mesh воды и
опишите функции добавления объекта воды следующим кодом:
Global
WaterCamera,water,WaterMapSize,WaterMapTexture
WaterMapSize = 256
water=LoadMesh("water.3ds")
MoveEntity
water,100,6,1100
WaterMapTexture=CreateTexture(WaterMapSize,WaterMapSize,128+256+48)
EntityTexture
water,WaterMapTexture
EntityColor
water,100,200,255
EntityColor
water,512,512,512
EntityAlpha
water,0.7
ScaleEntity
water, 5,5,5
AddEntity(water)
Type
Vertices
Field
x#
Field
y#
Field
z#
End Type
surf=GetSurface(water,1)
Dim
Vertex.Vertices(CountVertices(surf))
For
i=0 To
CountVertices(surf)-1
Vertex(i) = New
Vertices
Vertex(i)\x#=VertexX#(surf,i)
Vertex(i)\y#=VertexY#(surf,i)
Vertex(i)\z#=VertexZ#(surf,i)
Next
Type entity
Field ent
End Type
;----------------------------------------------------------------------------------
Function
AddEntity(ent)
e.entity=New
entity
e\ent=ent
End Function
|
Как видите текстура создается
внутри программы и имеет размеры 256 х 256 пикселей. Загрузку
mesh воды и заполнение
массива его вертексов мы уже проходили. Теперь остается написать функцию
рендеринга водной поверхности на основе теории приведенной выше:
Function RenderWater()
CameraProjMode
camera,0
CameraProjMode
WaterCamera,1
PositionEntity
WaterCamera,EntityX(player),EntityY(water),EntityZ(player)
;Left view
SetCubeFace
WaterMapTexture,0
RotateEntity
WaterCamera,0,90,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
;forward view
SetCubeFace
WaterMapTexture,1
RotateEntity
WaterCamera,0,0,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
;right view
SetCubeFace
WaterMapTexture,2
RotateEntity
WaterCamera,0,-90,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
;backward view
SetCubeFace
WaterMapTexture,3
RotateEntity
WaterCamera,0,180,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
;up view
SetCubeFace
WaterMapTexture,4
RotateEntity
WaterCamera,-90,0,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
CameraProjMode
WaterCamera,0
CameraProjMode
camera,1
;Animate Water
Mesh
s=GetSurface(water,1)
Freq#=MilliSecs()/4
For
i=0 To
CountVertices(s)-1
Vertex(i)\y#=Sin(freq+Vertex(i)\x#*500+Vertex(i)\z#*300)*1.2
VertexCoords
s,i,Vertex(i)\x#,-Vertex(i)\y#,Vertex(i)\z#
Next
End Function
|
Нужно сразу оговориться, что
данный код съедает изрядно процессорного времени и на слабых компьютерах может
изрядно тормозить. Для оптимизации алгоритма и убыстрения рендеринга сцены можно
разнести текстурирование отдельных видов. То есть - за один цикл визуализации
сцены - текстурировать один вид. Перепишем функцию
RenderWater() чуть
по другому:
Function RenderWater()
CameraProjMode
camera,0
CameraProjMode
WaterCamera,1
PositionEntity
WaterCamera,EntityX(player),EntityY(water),EntityZ(player)
;Left view
If
water_side = 0
SetCubeFace
WaterMapTexture,0
RotateEntity
WaterCamera,0,90,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
Endif
;forward view
If
water_side = 1
SetCubeFace
WaterMapTexture,1
RotateEntity
WaterCamera,0,0,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
Endif
;right view
If
water_side = 2
SetCubeFace
WaterMapTexture,2
RotateEntity
WaterCamera,0,-90,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
Endif
;backward view
If
water_side = 3
SetCubeFace
WaterMapTexture,3
RotateEntity
WaterCamera,0,180,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
Endif
;up view
If
water_side = 0
SetCubeFace
WaterMapTexture,4
RotateEntity
WaterCamera,-90,0,0
RenderWorld
CopyRect
0,0,WaterMapSize,WaterMapSize,0,0,BackBuffer(),TextureBuffer(WaterMapTexture)
Endif
water_side = water_side + 1
If
water_side > 4
water_side = 0
EndIf
+
CameraProjMode
WaterCamera,0
CameraProjMode
camera,1
;Animate Water
Mesh
s=GetSurface(water,1)
Freq#=MilliSecs()/4
For
i=0 To
CountVertices(s)-1
Vertex(i)\y#=Sin(freq+Vertex(i)\x#*500+Vertex(i)\z#*300)*1.2
VertexCoords
s,i,Vertex(i)\x#,-Vertex(i)\y#,Vertex(i)\z#
Next
End Function |
Определите глобальную переменную
water_side где-то до главного цикла
программы и подправьте код, как показано выше. Разнос рендеринга отдельных
отражений по разным циклам визуализации сцены не приводит к значительному
ухудшению получаемой картинки, но очень существенно повышает скорость игры.
Полный текст программы приведен здесь.
9.6. Расстановка пользовательских объектов
Очень полезным свойством редактора
Cartography Shop является возможность
расстановки пользовательских объектов не связанных с геометрией уровня. Нельзя
сказать, что эта функция является документированной в помощи редактора, но тем
не менее она существует и мы можем ее использовать. Предположим, что мы создали
уровень и теперь желаем разместить монстров в нужных позициях. Весьма неудобно
будет размещать их на глаз, грузить уровень и потом подгонять их позиции, раз за
разом меняя координаты монстров и снова просматривая правильность их
позиционирования в игре.
Однако если мы создадим контрольные токи размещения
этих монстров в редакторе Cartography Shop,
то нам останется лишь считать их в игре и ни о чем больше не заботиться как о
создании модели монстра в этих координатах.
Более того, кроме координат, задающихся в редакторе
автоматически при визуальном размещении этого объекта, мы можем добавлять ему
некоторые параметры, как мы делали это уже с другими геометрическими объектами в
редакторе.
Итак для добавления объекта найдите папку
Entities в каталоге, где вы установили
Cartography Shop и создайте в нем файл
с расширением .def , например
user_obj.def.
Отредактируйте файл добавив в него такие строки:
Pointclass Monster : [50,255,50] : [32,32]
{
"name"="Toad"
"color"="255 255 255" : color()
"style"="1" : choices("active","passive")
"flags"="1" : flags("live","dead")
} |
PointClass - стандартное
ключевое слово редактора Cartography Shop
и мы лишь указываем имя класса -
Monster
[50,255,50] - это цвета создаваемого объекта в формате
RGB (в нашем случае - зеленый)
[32,32] - размеры объекта (в нашем случае это кубик 32х32)
name - имя монстра (или его тип), которое мы можем анализировать в игре.
остальные параметры символизирует его статус и живой он или нет.
Безусловно это слишком мало характеристик, которыми
можно описать монстра в современной игре, поэтому Вы
можете добавить другие, создающие полную картину для
начальной инициализации монстра.
Если вы находитесь в редакторе, то выйдите из него и снова войдите для
вступления изменений в силу. Теперь в меню
Objects появилось подменю
user_obj, где находится созданный вам класс
Monster. Если поместите его на карту, то он будет отображаться у вас в
виде зеленого квадратика на проекциях и в виде зеленого
куба на трехмерном виде:

Свойств объекта будут доступны при нажатии в редакторе клавиши
"P" при выделенном объекте:

Само собой разумеется, что мы можем добавить другие свойства, какие нам
необходимы.
Теперь давайте посмотрим, как это работает в при загрузки карты в
программу на
Blitz3D.
Добавьте пол к нашему уровню, чтоб кроме пользовательского объекта
Monster на уровне было хоть что-то
еще. Сохраните карту под именем addobj.csm.
Воспользуйтесь шаблоном
load_csm.bb и добавьте в него функции загрузки
уровня, управления камерой (вобщем, все то что мы уже не раз проделывали для
демонстрации загрузки и навигации по уровню .CSM)
Теперь модифицируем секцию Point Entities в коде
загрузки карты, следующим образом:
;Point
Entities
entitycount=ReadInt(f)
DebugLog entitycount+" entities"
For n=1 To entitycount
visgroup=ReadInt(f); used to be flags, but wasn't really used
group=ReadInt(f)
properties$=readstringn(f)
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
entity=CreatePivot(map)
NameEntity entity,properties
PositionEntity entity,x,y,z
; Added Code!!!
MonsterName$ = KeyValue$(entity,"name")
If
MonsterName = "Toad"
Monster =
CreateSphere()
ScaleEntity
Monster, 5,5,5
PositionEntity
Monster,x,y,z
EndIf
Next
|
Как вы уже
догадались, все добавленные пользователем объекты попадают в раздел
Point Entities
где их можно анализировать и
предпринимать определенные действия. В данном случае мы проанализировали
свойство name объекта и
когда программа нашла соответствие на заданное в редакторе имя (в нашем случае
Toad)
создаем обычный объект сфера, для индикации местоположения
монстра. Безусловно в игре у вас будет гораздо большее количество объектов.
Поэтому изначально нужно хорошо продумать их типизацию, все начальные свойства и
параметры. Однако загрузка их и анализ в программу будет универсален. На месте
сферы, безусловно мог быть добавлен любой объект и модель монстра в том числе.
Карта и программа для загрузки этого примера - (карта,
программа).
Что будет далее:
Наш следующий урок будет посвящен сетевому взаимодействию и как его можно
реализовать в Blitz3D
|
|