|
Записки начинающего программиста (часть 1)
Blitz3d я начал изучать недавно, других языков не знаю, так что строго прошу не судить.
Думаю, эта статья хоть немного кому-нибудь поможет сделать первый шаг в написании своих программ. После того, как мы закончим, у нас получится готовый движок для игр типа 3D-Action. Поэтому, пока не будем наталкивать в программу детали конкретной игры. Это всё будет потом. Сначала нам нужно научить игрока ходить, бегать, прыгать и приседать (или ложиться).
Итак, начнем:
Graphics3D 1024,768,32,1; настройка графического режима
SetBuffer BackBuffer()
lit=CreateLight(2); точечный источник света
RotateEntity lit,90,0,0
;Это пока обычные команды, я на них не буду останавливаться, так как это всё
; хорошо описано в ;руководствах для начинающих.
; Иногда бывает полезно включить рассеянный свет в дополнение к основному
AmbientLight 200,200,200;установить рассеянный свет
;Определим подключаемые файлы:
Include "CONST.bb"; константы
Include "COLLISIONS.bb"; коллизии
Include "GLOBAL.bb"; глобальные переменные
;Когда ваша программа станет большой, такое подключение файлов облегчит вашу ;работу.
;*********** НАЧАЛО ИГРЫ ********************
Setup(); начальные установки - это наша функция
period=1000/FPS; это для стабилизатора
time=MilliSecs()-period; для стабилизатора
;*********** Главный цикл *******************
While Not KeyHit(1)=1; выход по нажатию клавиши Esc
Include "FPS.bb"; программа для измерения FPS, она нужна только для отладки и
;работает независимо от стабилизатора скорости.
; дальше идет продолжение стабилизатора. Более подробно можно прочитать в журнале
; Blitz et Cetera, N6.
Repeat
elapsed=MilliSecs()-time
Until elapsed
ticks=elapsed/period
;tween#=Float(elapsed Mod period)/Float(period); Когда начал делать оптический прицел,
; эту строчку я отключил, мешала работать.
For k=1 To ticks
time=time+period
If k=ticks Then CaptureWorld
Update_Game(); обновление игрового блока
UpdateWorld(); определение столкновений
Next
RenderWorld ; tween; отключено см. Выше
; Выводим на экран всю нужную нам информацию
Text 0,0,"user x="+EntityX(user,1); положение игрока относительно мировых коордионат
Text 0,15,"user y="+EntityY(user,1)
Text 0,30,"user z="+EntityZ(user,1)
Text 0,45,"centr y="+EntityY(centr,1)
Text 0,90,"Треугольников рендерится: "+TrisRendered(); сколько полигонов на экране
Text 0,105,"fps: "+Framecounter_framerate ; контролируем кадры в секунду (FPS)
Flip
Wend
;************ Конец главного цикла ***************
ClearWorld(); очищает всю память при выходе, хотя при завершении программы это
; происходит автоматически. Но лучше держать всё под контролем
End
;************* КОНЕЦ ИГРЫ **************************
;Здесь размещаем все функции. Следующие две главные, остальные будут вызываться из этих.
Function Setup() ; Здесь будут выполняться все начальные настройки игры
Function Update_Game() ; из этой функции будем контролировать поведение всех объектов |
Такое построение программы намного облегчает её понимание и написание. Всё будем делать в функциях, так легче программировать и переносить программы из одного проекта в другой.
Начнем с функции Setup()
Function Setup()
;установим текущий шрифт для вывода вспомогательной информации
font=LoadFont("Arial Cyr",16,True,False,True)
SetFont font
Color 255,0,0
create_ground(); наша функция создания земли
sky=MakeSkyBox( “sky\” ); наша функция создание неба, файлы в папке sky
ScaleEntity sky,20,20,20
create_user(); наша функция создание игрока
End Function
;******** вспомогательные функции *******************
Function create_ground(); создание земли
terrain=CreatePlane()
tertex=LoadTexture("textures\grass.jpg")
ScaleTexture tertex,10,10
EntityTexture terrain,tertex ; текстурируем нашу землю (пока просто плоскость)
FreeTexture tertex
EntityType terrain,TERRT ; тип коллизий для земли
EntityPickMode terrain,2 ; пригодится при приседаниях и прыжках
End Function
Function MakeSkyBox( file$ ); создание неба в виде куба
m=CreateMesh()
; передняя поверхность
b=LoadBrush( file$+"front.jpg",49 )
s=CreateSurface( m,b )
AddVertex s,-1,+1,-1,0,0:AddVertex s,+1,+1,-1,1,0
AddVertex s,+1,-1,-1,1,1:AddVertex s,-1,-1,-1,0,1
AddTriangle s,0,1,2:AddTriangle s,0,2,3
FreeBrush b
; правая поверхность
b=LoadBrush( file$+"right.jpg",49 )
s=CreateSurface( m,b )
AddVertex s,+1,+1,-1,0,0:AddVertex s,+1,+1,+1,1,0
AddVertex s,+1,-1,+1,1,1:AddVertex s,+1,-1,-1,0,1
AddTriangle s,0,1,2:AddTriangle s,0,2,3
FreeBrush b
; задняя поверхность
b=LoadBrush( file$+"back.jpg",49 )
s=CreateSurface( m,b )
AddVertex s,+1,+1,+1,0,0:AddVertex s,-1,+1,+1,1,0
AddVertex s,-1,-1,+1,1,1:AddVertex s,+1,-1,+1,0,1
AddTriangle s,0,1,2:AddTriangle s,0,2,3
FreeBrush b
; левая поверхность
b=LoadBrush( file$+"left.jpg",49 )
s=CreateSurface( m,b )
AddVertex s,-1,+1,+1,0,0:AddVertex s,-1,+1,-1,1,0
AddVertex s,-1,-1,-1,1,1:AddVertex s,-1,-1,+1,0,1
AddTriangle s,0,1,2:AddTriangle s,0,2,3
FreeBrush b
; верхняя поверхность
b=LoadBrush( file$+"up.jpg",49 )
s=CreateSurface( m,b )
AddVertex s,-1,+1,+1,0,1:AddVertex s,+1,+1,+1,0,0
AddVertex s,+1,+1,-1,1,0:AddVertex s,-1,+1,-1,1,1
AddTriangle s,0,1,2:AddTriangle s,0,2,3
FreeBrush b
; нижнюю поверхность не создаём, т.к. её всё равно не будет видно, но принцип тот же
ScaleMesh m,100,100,100
FlipMesh m
EntityFX m,9
EntityOrder m,10
Return m
End Function
Function create_user(x#=-0,y#=4,z#=0); создание игрока в позиции y>0 нужно для того
;чтобы он не проваливался под действием гравитации ниже поверхности.
user=CreateSphere(); создать сферу
EntityRadius user,3,3; задать радиус для коллизий. Насколько я понял, сначала задается
;размер по y, а потом по x.
EntityAlpha user,0; сделать сферу прозрачной
EntityType user,USERT; назначить тип для коллизий
centr=CreatePivot (user); вспомогательный пивот
MoveEntity centr,0,2.5,0
camera=CreateCamera(centr); создаем камеру ("глаза" игрока) в позиции centr
CameraRange camera,0.01,10000; дистанция камеры мин. мах.
microphone=CreateListener(camera,.2,1,.2); создать микрофон ("уши"), чтобы слышать 3D-звуки.
PositionEntity user,x#,y#,z#; установить позицию появления игрока
End Function
;Игрок у нас получился высотой и шириной 6 единиц, camera-centr(глаза) на высоте 5,5
;Когда игрок будет ложиться, будем менять его размеры и опускать камеру(в функции ;update_user() ) |
Теперь напишем функцию Update_Game(), которая вызывается из главного цикла при каждом его проходе и в ней должны анализироваться текущие параметры каждого предмета в игровом мире, а так же проверяться все взаимодействия между ними, вследствие их порождаться новые объекты или уничтожаться старые, происходить перемещемия объектов по каким-то правилам и т.д. Здесь у нас пока есть только один важный активный объект- игрок и мы должны предусмотреть в этой функции разные ситуации и действия, а так же обработать нажатие клавиш на клавиатуре. Земля и небо в обработки не нуждаются. Земля, правда, участвует в столкновениях (коллизиях), но не требует каких-либо функций для их обработки. Небо, вообще, просто декорация.
Так как игрок будет управляемым, то и функции для него получаются сложнее (вернее, больше), чем для других объектов, ведь Вы можете поставить его в самые размые ситуации, например, загнать в узкое место и попытаться там прыгать и т.п. Все эти ситуации нужно предусмотреть, тогда получится почти готовый движок для любой игры типа 3D-Action.
Function Update_Game()
update_user(); обновление игрока
; здесь будут вызываться функции обработки других объектов, но их пока нет
End Function
;напишем функцию update_user()
;Обратите внимание : все переменные (например us, kcam, ncam) изначально равны 0,
;поэтому их значение можно заранее не указывать.
Function update_user(); обновление игрока
If KeyHit(46) us=us+1; лечь-встать , клавиша "C"
If us=2 us=0; переменная us имеет только два значения или 0 или1
if us=1 And kcam=0; лечь
MoveEntity centr,0,-0.15,0
If EntityY (centr,1)=<EntityY(user,1) Then kcam=1:ncam=1:V1#=0.05: EntityRadius user,2,3; задать радиус по осям y,x
EndIf
;здесь мы переместили камеру и уменьшили размер игрока при помощи пивота centr.
;Когда я пробовал делать это напрямую с камерой, в системе возникало нарушение
;координат, особенно при наклоне камеры, когда игрок ложится.
If us=0 And kcam=1 ; встать
PositionEntity user,EntityX(user),EntityY(user)+1.8,EntityZ(user),1
;здесь мы немного приподняли игрока для его увеличения и чтобы не мешали коллизии
; иначе он сразу провалится под землю
EntityRadius user,3,3
PositionEntity centr,EntityX(user),EntityY(user)-1.8,EntityZ(user),1; переместили камеру
kcam=0
EndIf
If kcam=0 And ncam=1
MoveEntity centr,0,0.15,0
If EntityY (centr,1)=>EntityY(user,1)+2.5 Then ncam=0:V1#=0.2
EndIf
;восстановили начальные размеры игрока и положение камеры
;Самая большая проблема была вот в чем:
;Движение игрока зависит от гравитации, радиуса коллизии, скорости игрока, наклона
;плоскости (вы же не хотите лазить по отвесной стене), а те алгоритмы прыжков, которые
;мне попадались, работают только при малой гравитации. Пришлось делать свой
;алгоритм, ну а часть я взял из статьи ”Создаём свой First Person Shooter” в журнале Blitz et Cetera ;выпуск 5..
;контроль отрыва игрока от земли
;++++++++++++++++++++++++++++
pick_ent=LinePick(EntityX(user),EntityY(user),EntityZ(user),0,-3.6,0)
If pick_ent
jump_bool=False ;на земле
Else
jump_bool=True;в воздухе
EndIf
If KeyHit(57) And jump_bool=False And ncam=0 Then jmp#=0.7: j#=EntityY(user,1) ;прыжок
; меняя значения jmp#, меняем скорость прыжка. В момент нажатия пробела
;считываем Y-коордионаты игрока в переменную j#.
If EntityY (user,1)=>j#+8 Then jmp#=0;высота прыжка 8 единиц
MoveEntity user,0,jmp#-G#,0 ; гравитация
;Плавность прыжка зависит от разницы между G# и jmp#
If KeyDown(42)=1; ускорение
V2#=V1#+V1#*0.5
Else
V2#=V1#
EndIf
If KeyDown(30)=1 MoveEntity user,-V2#,0,0 ; клавиша "A"- влево
If KeyDown(32)=1 MoveEntity user,V2#,0,0 ; клавиша "D"- вправо
If KeyDown(17)=1 MoveEntity user,0,0,+V2# ;клавиша "W" - вперед
If KeyDown(31)=1 MoveEntity user,0,0,-V2# ;клавиша "S" - назад
TurnEntity camera,MouseYSpeed()*0.5,0,0
TurnEntity user,0,-MouseXSpeed()*0.5,0
If KeyDown(30)=1 Then MoveEntity user,-V#,0,0
If KeyDown(32)=1 Then MoveEntity user,V#,0,0
If KeyDown(17)=1 Then MoveEntity user,0,0,+V#
If KeyDown(31)=1 Then MoveEntity user,0,0,-V#
MoveMouse GraphicsWidth()*0.5,GraphicsHeight()*0.5
If Abs(EntityPitch#(camera))>u# RotateEntity camera,u#*Sgn(EntityPitch#(camera)),0,0
Return
End Function |
Дальше идет содержимое дополнительных подключаемых файлов. В принципе, их можно и не делать, а все строки из них вписать в начале основной программы, но это сделано на будущее. Когда основная программа станет слишком большой будет содержать много функций и переменных, лучше отделить те из них, которые уже отлажены и проверены, чтобы они не загромождали код, ведь его то и дело приходится перематывать то вперед, то назад, что-то искать и т.п. Списки переменных, а особенно типов только мешают. Удобнее держать их в отдельном файле и открывать его в отдельном окне.
; Содержание файла CONST.bb - здесь все константы нашей игры
;CONST
Const G#=0.4;гравитация
Const u#=70;предельный угол поворота камеры (чтобы не заглянуть за спину)
Const USERT=1; тип коллизий для игрока
Const TERRT=2; тип коллизий для земли
Const FPS=50; желательные кадры в секунду |
; Содержание файла COLLISIONS.bb - здесь определим все коллизии между разными объектами
;COLLISIONS
Collisions USERT,TERRT,2,3; между игроком и землей |
; Содержание файла GLOBAL.bb - здесь все глобальные переменные
;GLOBAL
Global centr; для камеры
Global camera
Global user; игрок
Global V1#=0.2;скорость
Global j#; ось Y игрока
Global jmp#; прыжок
Global kcam; камера вверху
Global ncam; внизу
Global us; лечь-встать |
; Содержание файла FPS.bb - вычисляет кадры в секунду
;FPS
Framecounter_counter=Framecounter_counter+1
If Framecounter_time=0 Then Framecounter_time=MilliSecs()
If Framecounter_time+1001 <MilliSecs() Then
Framecounter_framerate=Framecounter_counter
Framecounter_counter=0
Framecounter_time=MilliSecs()
EndIf |
Пока всё. Может быть, это и не самый лучший пример программирования, но всё сделано самостоятельно, во многих местах, по-моему, лучше, чем у других (особенно прыжки и приседания) и, самое главное, СТАБИЛЬНО РАБОТАЕТ.
Если Вы не хотите всё это писать или копировать туда-сюда, можете скачать готовый архив (173 кб)
2 часть >>>
3 часть >>>
Ковалёв Анатолий, Алтайский край, Романово, 23.06.2007.
|
|