forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   FAQ (http://forum.boolean.name/forumdisplay.php?f=15)
-   -   Создаём свой FPS (first person shooter) (http://forum.boolean.name/showthread.php?t=15)

impersonalis 04.09.2005 06:40

Баг-фикс
Я всё-таки почти человек, поэтому ничего странного, что забыл дописать ещё вот что:
Код:

Function update_bot()
        v#=0.3
        For a.bot=Each bot
 PointEntity a\entity,user
 MoveEntity a\entity,0,0,v#
        ;физика
 TranslateEntity user,0,-G#,0
        Next
End Function

теперь ботов будет притягивать вниз :)

impersonalis 04.09.2005 06:40

Чит-мод :)

Попробуйте включить в функцию обработки игрока строку
Код:

If MouseDown(2) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0)
И стрелять, используя удержание правой кнопки мыши :)

impersonalis 04.09.2005 06:42

Релиз

В аттаче полностью рабочая версия описанной здесь программы. Со всеми ресурсами :grins


http://boolean.name/archive/code/Example%2...0FPS%20v1.0.rar

impersonalis 04.09.2005 06:44

Уход от полного перебора

Как и обещал, расскажу, как улучшить цикл обработки выстрелов.
В Блитце есть недокументированные команды Handle и Object. Благодаря команде Handle можно получить
"указатель" на объект типа (использование термина "указатель" весьма спорно для Блитца). Например:
Код:

P.Player=New Player
pHANDLE=Handle(P)

а затем, используя сохранённое значение (здесь в переменной pHANDLE) получить доступ к элементу типа:
Код:

P.PLAYER=Object.PLAYER(pHANDLE)
*******
Модифицируем функцию create_bot
Код:

Function create_bot(x#,y#,z#)
        b.bot=New bot
        b\entity=CreateSphere()
        EntityType b\entity,BOTT
        PositionEntity b\entity,x#,y#,z#
        NameEntity b\entity,Handle(b)
        Return True
End Function

Командой NameEntity присвоим объекту B\entity имя - в данном случае, совпадающее с хендлом элемента типа,
содержащего данный объект.
Теперь модифим кусок функции update_shot()
Код:

Function update_shot()
        vs#=1
        max_dist#=1000
        For a.shot=Each shot
 MoveEntity a\entity,0,0,vs#
 a\dist#=a\dist#+vs#
 bot_h=EntityCollided(a\entity,BOTT)
 If a\dist#>max_dist#
        FreeEntity a\entity
        Delete a
 ElseIf EntityCollided(a\entity,TERRT)<>0
        FreeEntity a\entity
        Delete a
 ElseIf bot_h<>0
        FreeEntity a\entity
        Delete a
        bhandle=EntityName(bot_h)
        bc.bot=Object.bot(bhandle)
        Delete bc
        FreeEntity bot_h
        create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100))
 EndIf
        Next
End Function

Здесь мы получаем указатель на элемент типа из имени бота (имени объекта, который его визуализирует - сферы)
Код:

bhandle=EntityName(bot_h)
далее удалям запись об этом боте (удаляем соответствующий элемент типа)
Код:

        bc.bot=Object.bot(bhandle)
        Delete bc

И, наконец, удаляем сам объект (используя его хендл)
Код:

FreeEntity bot_h
Таким образом, мы избавились от цикла и сохранили прежнюю функциональность, повысив при этом
производительность.

impersonalis 04.09.2005 06:46

Уход от привязки к FPS
Frames Per Second
В текущей версии программы события (а именно: обновление ботов, игрока, выстрелов) будут происходить с частотой вызова соответствующих

ф-ций. Т.е. (как видно из записи главного цикла) с частотой прорисовки - ФПС. А значит напрямую будут зависеть от производительности

системы.
Избавиться можно 2мя способами:
:?? 1) Производить обновление N раз в секунду - что приведёт к принудительному занижению высоких ФПС и скачкообразной анимации.
:?? 2) Рассчитывать переменные скорости, ускорения и проч. в соответствии с дельтой времени. Что сейчас и сделаем.

Так как в следующих версиях алгоритма или уже в этой ( при большом кол-ве ботов) время начала обработки первого и последнего ботов

вероятно будет отличаться на целое кол-во мс и так же потому, что глобальные переменные делают программу, имхо, менее удобочитаемой и

переносимой, а так же плодят логические ошибки (из-за ошибок при вводе имени) - для каждого бота сделаем собственное время последнего

обновления.
Код:

Type bot
        Field entity
        Field time
End Type

Код:

Function update_bot()
        v#=0.03
        For a.bot=Each bot
        ;=
 If a\time=0 a\time=MilliSecs()
 new_time=MilliSecs()
 delta_t=new_time-a\time
 a\time=new_time
        ;=
 PointEntity a\entity,user
 MoveEntity a\entity,0,0,v#*delta_t
        ;физика
 TranslateEntity user,0,-G#*delta_t,0
        Next
End Function

Ёлки-палки :o :oops - только сейчас заметил, что боты притягиваются с коэффицентом 1 а не G
Переменная v# должна получиться универсальным значением для всех ПК (однако Вы можете откалибровать её по-точнее).
Проделаем операцию ухода... для выстрелов:
Код:

Type shot
        Field entity
        Field dist#
        Field time
End Type

Код:

Function update_shot()
        v#=0.1
        max_dist#=1000
        For a.shot=Each shot
        ;=
 If a\time=0 a\time=MilliSecs()
 new_time=MilliSecs()
 delta_t=new_time-a\time
 a\time=new_time
 vs#=v*delta_t
        ;=
 MoveEntity a\entity,0,0,vs#
 a\dist#=a\dist#+vs#
 bot_h=EntityCollided(a\entity,BOTT)
 If a\dist#>max_dist#
        FreeEntity a\entity
        Delete a
 ElseIf EntityCollided(a\entity,TERRT)0
        FreeEntity a\entity
        Delete a
 ElseIf bot_h0
        FreeEntity a\entity
        Delete a
        bhandle=EntityName(bot_h)
        bc.bot=Object.bot(bhandle)
        Delete bc
        FreeEntity bot_h
        create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100))
 EndIf
        Next
End Function

Обратите внимание здесь рассчитанная по дельте скорость используется не идиножды - сохраняем в отдельную переменную.
Теперь сам игрок.
Поскольку мультиплеером в игре и не пахнет - игрок реализован не типизированным объектом и он один. Заведём для его обновления глобальную

переменную:
Код:

Global user_time
Код:

Function update_user()
;=
        If user_time=0 user_time=MilliSecs()
        new_time=MilliSecs()
        delta_t=new_time-user_time
        user_time=new_time
;=
        V#=0.02*delta_t
        u#=70;предельный угол
        TurnEntity camera,MouseYSpeed(),0,0
        TurnEntity user,0,-MouseXSpeed(),0
        If KeyDown(203)=1 Then MoveEntity user,-V#,0,0
        If KeyDown(205)=1 Then MoveEntity user,V#,0,0
        If KeyDown(200)=1 Then MoveEntity user,0,0,+V#
        If KeyDown(208)=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
       
        If MouseHit(1) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0)
        If MouseDown(2) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0)
;физика
        TranslateEntity user,0,-G#*delta_t,0
End Function

Осталось откалибровать коэффициент гравитации.
Код:

Const G#=0.1
Пока поставил такое значение.

impersonalis 04.09.2005 06:47

Небесная сфера
Вопреки названию, будем создавать не сферу, а SkyBox -

коробку, так как для неё проще изготовить текстуру и

смотрится, имхо, она красивее.
Возьмём стандартную функцию создания из примеров блитца

(\Samples\Blitz 3D Samples\AGore\GrassDemo):
Код:

Function MakeSkyBox( file$ )

        m=CreateMesh()
;front face
        b=LoadBrush( file$+"_FR.bmp",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
;right face
        b=LoadBrush( file$+"_LF.bmp",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
;back face
        b=LoadBrush( file$+"_BK.bmp",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
;left face
        b=LoadBrush( file$+"_RT.bmp",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
;top face
        b=LoadBrush( file$+"_UP.bmp",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

и скопируем её в наш код. Вот, кстати, довод за

использование ф-ций: в любой момент вы можете использовать

свои наработки в других проектах.

Переместимся в функцию create_world()
Код:

        create_user()
       
        sky=MakeSkyBox("sky")
       
        c_bot=Input("input Amount")

и скопируем текстуры неба из папки с примером в папку нашеё

программы. Уже сейчас можно посмотреть на результат, правда,

сомнительный.
Из-за недостаточных размеров skybox`а небо ощутимо

квадратное:



Отскалим небо
Код:

ScaleEntity sky,150,150,150
После этого незамысловатого изменения небо исчезнет совсем =(
Вероятно оно за максимальным радиусом ренедера.
глянем в create_user - именно здесь мы обрезали расстония

рендера, правим:
Код:

CameraRange camera,0.1,10000
Проверяем - всё работает =)

impersonalis 04.09.2005 06:48

Релиз

В аттаче полностью рабочая версия (уже 2ая)описанной здесь программы. Со всеми ресурсами :grins

http://boolean.name/archive/code/Example%2...0FPS%20v2.0.rar

impersonalis 04.09.2005 06:49

Живучие боты
Бот, умирающий от одного залпа, - это, кончено весело, но не интересно. Добавим ботам шкалу жизни.
Итак, переходим в объявление типа и правим:
Код:

Type bot
        Field entity
        Field time
        Field Survivability
End Type

В создании бота припишем:
Код:

b\Survivability=100
Теперь надо растянуть смерть бота, а значит переходим в ф-цию update_shot()
Код:

ElseIf bot_h<>0
        FreeEntity a\entity
        Delete a
        bhandle=EntityName(bot_h)
        bc.bot=Object.bot(bhandle)
        bc\Survivability=bc\Survivability-16
        If bc\Survivability<=0
  Delete bc
  FreeEntity bot_h
  create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100))
        EndIf
 EndIf

Если есть столкновение пули с ботом, то удаляем пулю и её информационный элемент, получаем хендл элемента типа для бота, получаем доступ к элементу типа, вычитаем из значения живучести бота число. Если живучесть бота меньше или равна 0, то удаляем информацию о боте, удаляем тело бота, создаём нового бота.
Теперь равновесие сместилось в пользу ботов - за это понизим им скорость передвижения:
Код:

Function update_bot()
        v#=0.01


impersonalis 04.09.2005 06:50

Жизнь в коробке
Рано или поздно, надо чётко обазначить границы игрового мира. После добавления в игру SkyBox, это становится необходимо сделать.
Модифицируем ф-цию создания неба и сохраним её под новым именем (прототип тоже надо оставить в коде):
Код:

Function MakeBox()

        m=CreateMesh()
;front face
        s=CreateSurface( m )
        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
;right face
        s=CreateSurface( m )
        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
;back face
        s=CreateSurface( m )
        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
;left face
        s=CreateSurface( m )
        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
;top face
        s=CreateSurface( m)
        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

        ScaleMesh m,100,100,100
        FlipMesh m
        EntityFX m,9
        EntityOrder m,10
        Return m
       
End Function

Теперь функция возвращает хендл простой неоттекстурированной корбки. Зайдём в create_world():
Код:

border=MakeBox()
;ScaleEntity border,140,140,140
EntityAlpha border,.5

Запустим прогамму. Если некоторое время идти в одном направлении, можно увидеть коробку, ограничивающую уровень.

Теперь поставим этой коробке в соответствие тип для колизии
Код:

Const BORDERT=5;for box
Код:

EntityAlpha border,.5
EntityType border,BORDERT

Код:

Collisions USERT,BORDERT,2,1
Боты могут проходить сквозь эту стену - на случай если игрок будет в крайнем положении, в следствие чего бот создастся за стеной. Поэтому коллизию с типом ботов не ставим. Спрайтам выстрелов это тоже не нужно.
Теперь, если идти в одну сторону, в какой-то момент вы просто упрётесь в стену.
Ставим стене нулевую альфу и растягиваем её.
Код:

border=MakeBox()
ScaleEntity border,140,140,140
EntityAlpha border,0
EntityType border,BORDERT


impersonalis 04.09.2005 06:51

кодим-кодим

Что-то управление больно казуальным вышло. :unsure:
Перенастроим движение на W,S,A,D.
Функция update_user()

Код:

        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#

:glag:

impersonalis 04.09.2005 06:52

Прыжки и реалистичность физики

давно пора было это сделать

Для нормальной реализации кинематики нам понадобится:
1)контроль за отрывом от земли и моментом соприкосновения с ней.
Код:

EntityPickMode terrain,2
теперь к земле можно применить операцию pick.
Код:

Global jump_bool
переменная под логику отрыва от поверхности.
Код:

;физика
jump_bool=True
pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3,0)
If pick_ent jump_bool=False

допишем в функции обновления игрока. И добавим
Код:

Text 10,10,"jump_bool="+jump_bool
Flip

в главном цикле.
Запустим.
И увидим, что программа работает неверно.
jump_bool=1, т.е. условие If pick_ent не срабатывает, т.е. LinePick возвращает 0, т.е. на расстоянии 3 ед. под игроком ничего "не цепляется" командой Pick =(
Вообще в этой точке будет как раз соприкосновение сферы игрока и земли, так что надо попробовать увеличить расстояние - методом калибровки подбираем значение=3.6
Код:

pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0)
Ориентировочное значение три я взял, исходя из ф-ции создания игрока:
Код:

user=CreateSphere()
k#=3
ScaleEntity user,k#,k#,k#

и если был бы внимательнее, посмотрел чуть ниже
Код:

EntityRadius user,k#
Радиус коллизи тоже равен 3, что как раз и объясняет возникшее затруднение. Наше расстояние должно быть немногим больше 3, как оказалось = 3.6 .
Теперь значение jump_bool характеризует состояние игрока - есть или нет сцепления.
2)время невзаимодействия с землёй.
перепишем обновление игрока ещё раз
Код:

;физика
       
        pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0)
        If pick_ent
 jump_bool=False
        Else
 jump_bool=True
        EndIf

время взаимодействия как-таковое нам нужно между обновлениями,а оно у нас уже есть.
3)скорости движения для их динамического изменения.
Код:

Global user_vy#
снова переписываем
Код:

        pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0)
        If pick_ent
 jump_bool=False
 user_vy#=0
        Else
 jump_bool=True
 user_vy#=user_vy#-G#*delta_t;v1=v2-a*t
        EndIf
        TranslateEntity user,0,user_vy#*delta_t,0

4)инициализация события "прыжок".
Код:

TranslateEntity user,0,user_vy#*delta_t,0
;;;
        If KeyHit(57) And jump_bool=False Then user_vy#=50

однако - не работает =(

после некоторых раздумий подправим физику:
Код:

        pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0)
       
        TranslateEntity user,0,user_vy#*delta_t,0,True
       
        If pick_ent
 jump_bool=False
 user_vy=0
 TranslateEntity user,0,-1,0,True
        Else
 jump_bool=True
 user_vy#=user_vy#-G#*delta_t;v1=v2-a*t
        EndIf

;;;
        If KeyHit(57) And jump_bool=False Then user_vy#=1

И гравитацию на 0.001
Однако теперь объект не смещается вниз, пока его не сдвинут вперёд-назад или влево-вправо.
Я думаю, логика игры не пострадает, если предположить, что человек, прыгая вверх чуть-чуть смещается вперёд.
Код:

        TranslateEntity user,0,user_vy#*delta_t,user_vy#*0.01,True
Всё!

impersonalis 04.09.2005 06:53

Релиз

В аттаче обновление кода

http://boolean.name/archive/code/ES_FPS_3_codeUp.rar

Необходимые ресурсы, можно взять из более ранней версии программы
http://boolean.name/archive/code/Example%2...0FPS%20v2.0.rar

:glag:

impersonalis 08.10.2005 13:54

Можно прочитать вышеизложенное и здесь:
http://blitzetc.boolean.name/fps.htm

Brodyaga 08.10.2005 15:15

:o
Что-то нето..
Во время игры, точнее стрельбы второй клавишей Рантайм еррор сообщает что не существует Ентити bot_h

alcosholik 08.10.2005 15:39

Скачал. Все работает.

Только прыжок нужно более реалистичным сделать: чтобы и при движении вверх ускорение было, а не просто мгновенное перемещение по оси Y.


Часовой пояс GMT +4, время: 06:30.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot