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:02

page # 0
Всем привет!
По опыту знаю: большинство хочет сразу после просмотра сэмплов сесть и написать FPS. И не просто какой-нибудь: УБЕЙ_ИХ_ВСЕХ_2010, а что-нибудь революционно-инновационное – с крутейшей графикой, умнейшим ИИ, и возможностью сетевой игры на разных протоколах и в разных игровых режимах, с абсолютной нелинейностью сюжета, причём всё это будет требовать смехотворно мало ресурсов. Если Вы именно такой пользователь THEN goto основы. Так как начинать с подобного рода программ – отбивать себе всякое желание программить на Блитце – попишите пока тетрисы, змейки и проч.

начало статьи (с иллюстрациями, безвозвратно канувшими в Лету, благодаря очередному переезду):
http://blitzetcetera.org/index.php/С...Person_Shooter

impersonalis 04.09.2005 06:03

подготовка
Итак… FPS… конечно же 3D! Запускаем компилятор и смело вбиваем
Код:

Graphics3D 800,600,32
SetBuffer BackBuffer()

чуть ниже организовываем цикл, это будет главный цикл программы MAIN LOOP. Оформляйте его любыми операторами цикла, я, лично, сделаю так:
Код:

While Not KeyHit(1)=1
Wend
end

тут всё должно быть ясно как день:
цикл крутится пока Не ПрерываниеОтКнопки (Escape)
команда END в данном случае – чистый формализм: после инструкции WEND у нас в коде ничего нет, таким образом, после выхода из цикла программа и так завершится. Но всё же я настоятельно рекомендую не пренебрегать подобными формальностями!
Уже можно давить F5 – и любоваться :) … космосом… без звёзд…
Теперь займёмся камерой… даже - лучше сразу игроком ;)
Опять же: я настоятельно рекомендую заключать подобные знаковые участки кода в тела функций. Поэтому где-нибудь повыше напишем:
Код:

Function create_user()
End Function

Если Вы затрудняетесь определить: «где же это самое то место» - в принципе Блитцу всё равно, но я настоятельно рекомендую объявлять все функции перед главным циклом (а так как это не С++, то и писать тела функций там же), а поскольку в функции будут использоваться команды работы с 3d-объектами объявить их надо после строчек
Код:

Graphics3D 800,600,32
SetBuffer BackBuffer()

Надеюсь, с местом определились.
Теперь передаваемые в функцию аргументы… Думаю, удобно будет задавать ими место появления игрока. С другой стороны, практически всегда у нас он появляется в 0,h#,0 – в центре карты на небольшом расстоянии от земли, поэтому пишем следующее:
Код:

Function create_user(x#=0,y#=1,z#=0)
End Function

Теперь подумаем о физической модели игрока… В масштабах этого манула, позволительно допустить: игрока никто не видит, и камера как в FPS. Значит не стоит отвлекаться на фактический внешний вид игрока. У нас должна быть точка опоры и над ней (на некотором расстоянии) камера. А это, имхо, самый простой и надёжно работающий способ решения:
Код:

user=CreateSphere()
camera=CreateCamera(user)
PositionEntity camera,0,1,-0.5

PositionEntity user,x#,y#,z#

набираем мы в функции. Сам пользователь – это будет сфера (та самая точка) к которой прикреплена камера : в координатах 0,1,-0.5 относительно сферы. Камера прикрепляется сразу после создания, т.к. ей задан родитель (парент) в качестве аргумента. Затем происходит её позиционирование, и затем позиционирование самого игрока.
Запускаем… И снова темнота :sad
А как по-вашему: у нас есть описание действия «создать игрока» и пустой цикл.
Для начал надо вызвать функцию создания перед главным циклом:
Код:

create_user()
Затем: ну да – в памяти мы создали и сферку и камеру – а дальше то что? Как минимум надо это всё обработать как геометрические объекты и отрисовать в BackBuffer . Добавим в главном цикле команду:
Код:

RenderWorld()
Ещё одна неувязочка: на экране находится при двойной буферизации FrontBuffer, а менять содержимое буферов мы будем командой
Код:

Flip
После такой перекройки программа должна выглядеть примерно так:
Код:

Graphics3D 800,600,32
SetBuffer BackBuffer()

Function create_user(x#=0,y#=1,z#=0)
 user=CreateSphere()
camera=CreateCamera(user)
PositionEntity camera,0,1,-0.5

PositionEntity user,x#,y#,z#

End Function

create_user()
While Not KeyHit(1)=1
RenderWorld()
Flip
Wend
End

Снова запускаем – и опять темнота… :surpr Но на самом деле всё рисуется – сфера которую вы не видите, потому что «не можете наклонить голову» и… всё – больше в мире пока ничего нет.
Нам нужна земная твердь. И обязательно текстурка для неё. Ведь компьютер без дополнительных инструкций строит идеальные видео образы – абсолютно ровную поверхность земли (на самом деле мелкие неровности пока тоже ничего бы не дали – нет освещения – которое и позволяет видеть объёмную картину мира , без света всё будет казаться «плосокватым») так что вы просто увидите светлый прямоугольник занимающий часть экрана. Для примера – стандартная Блитц-текстура:





Опять же прибегая к упрощениям – земля будет в виде бесконечной ровной поверхности – создадим её плэйном:
Код:

terrain=CreatePlane()
затем загрузим текстурку
Код:

tertex=LoadTexture("terrain1.jpg")
и наложим её на поверхность
Код:

EntityTexture terrain,tertex
ну и для профилактики очистим память из под текстуры – она нам больше не понадобиться:
Код:

FreeTexture tertex
Ах да всё это должно быть набрано после инициализации графики но перед циклом.
И ещё немного подумав, объединим вызов ф-ции создания игрока и создание мира в одну функцию:
Код:

Graphics3D 800,600,32
SetBuffer BackBuffer()


Function create_user(x#=0,y#=1,z#=0)
 user=CreateSphere()
camera=CreateCamera(user)
PositionEntity camera,0,1,-0.5

PositionEntity user,x#,y#,z#

End Function

Function create_world()
terrain=CreatePlane()
tertex=LoadTexture("terrain1.jpg")
EntityTexture terrain,tertex
FreeTexture tertex
create_user()
End Function

create_world()

While Not KeyHit(1)=1
RenderWorld()
Flip
Wend
End

Та-дам! Запускаем, наслаждаемся :D

ПРОДОЛЖЕНИЕ СЛЕДУЕТ :super:

impersonalis 04.09.2005 06:03

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

Graphics3D 800,600,32
SetBuffer BackBuffer()


Function create_user(x#=0,y#=1,z#=0)
 user=CreateSphere()
camera=CreateCamera(user)
PositionEntity camera,0,1,-0.5

PositionEntity user,x#,y#,z#

End Function

Function create_world()
terrain=CreatePlane()
tertex=LoadTexture("terrain1.jpg")
;EntityTexture terrain,tertex
FreeTexture tertex
create_user()
End Function

create_world()

While Not KeyHit(1)=1
RenderWorld()
Flip
Wend
End

Как я и обещал, видим светлый прямоугольник отхапавший часть экрана. :grins Раскомментируем строчку обратно.
Кстати о комментариях: не стоит ими пренебрегать но и объяснять всё подряд не стоит. :o
Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

;создание игрока
Function create_user(x#=0,y#=1,z#=0)
 user=CreateSphere()
camera=CreateCamera(user)
PositionEntity camera,0,1,-0.5

PositionEntity user,x#,y#,z#

End Function

;создание игрового мира
Function create_world()
terrain=CreatePlane()
tertex=LoadTexture("terrain1.jpg")
EntityTexture terrain,tertex
FreeTexture tertex
create_user()
End Function
;функции закончились :)
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
RenderWorld()
Flip
Wend
;----------------------------
End

Надеюсь, вы уже обучили Блитц понимать русский язык? ;) Только по секрету:
http://community.boolean.name/index.php?act=ST&f=4&t=25
Не стоит так же пренебрегать грамотным форматированием кода:
Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

;создание игрока
Function create_user(x#=0,y#=1,z#=0)
        user=CreateSphere()
        camera=CreateCamera(user)
        PositionEntity camera,0,1,-0.5
       
        PositionEntity user,x#,y#,z#
       
End Function

;создание игрового мира
Function create_world()
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        EntityTexture terrain,tertex
        FreeTexture tertex
        create_user()
End Function
;функции закончились :)
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        RenderWorld()
        Flip
Wend
;----------------------------
End

Вот…

impersonalis 04.09.2005 06:06

Озираемся и бегаем
Так-с-с : на поверхность посмотрели – пора двигаться дальше. Будем учить игрока двигаться:
Код:

;обновление игрока
Function update_user()
End Function

Так получается, что к глазам игрока (камере) и его ногам (сфере) будут обращаться вне функции создания игрока. А значит надо сделать хендлы камеры и сферы глобальными. Для этого подготовим для их хранения глобальные переменные:
Код:

Global user
Global camera

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

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

;создание игрока
Function create_user(x#=0,y#=1,z#=0)
        user=CreateSphere()
        camera=CreateCamera(user)
        PositionEntity camera,0,1,-0.5
       
        PositionEntity user,x#,y#,z#
       
End Function

;обновление игрока
Function update_user()
End Function

;создание игрового мира
Function create_world()
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        EntityTexture terrain,tertex
        FreeTexture tertex
        create_user()
End Function
;функции закончились :)
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        RenderWorld()
        Flip
Wend
;----------------------------
End

Так… перейдём к программированию движений: для удобства объявим в нашей функции переменную, отвечающую за смещения по осям (никто не мешает вам сделать разную скорость для движения вперёд, назад и шагов влево-вправо)
Код:

V#=0.5
и кодируем движения нашей сферки (напомню: на ней укреплена камера)
Код:

        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#

Запустим… результат, конечно зависит от производительности вашей машины, но, думаю, он не будет сильно разнится: при движении земля жутко мельтишит под ногами, так что невозможно даже оценить – в какую сторону мы сейчас двигаемся. Для устранения этого надо или понизить коэффициент скорости, или (что мы и сделаем ) растянуть текстурку:
Код:

        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10;вот тута новое
        EntityTexture terrain,tertex

мир по-прежнему имеет весьма сомнительный вид, но уже лучше, чем было :grins
Теперь займёмся обзорами местности.
Будем вращать камеру вверх-вниз в зависимости от смещения мыши по Y-оси и влево-вправо от смещения по X-оси.
Угу: как же ну смотрю я вправо… ещё вправо… опа: курсор упёрся в правый край экрана и всё :sungl . Значит после оценки изменений мы должны вернуть мышь на исходную – самое логичное, в центр экрана. Получить текущий размер экрана в пикселях по X и по Y можно функциями
Код:

GraphicsWidth()
GraphicsHeight()

соответственно. Т.е. задвинуть мышь в центр экрана:
Код:

MoveMouse GraphicsWidth()*0.5,GraphicsHeight()*0.5
А снять перемещение мыши – измерив её скорость по осям:
Код:

MouseYSpeed()
MouseXSpeed()

Но вот ещё загвоздка: если мы будем крутить только камеру, то получается: мы развернули камеру влево и нажали кнопку ВПЕРЁД, но наша опора то как стояла, так и стоит неразвёрнутой – в результате смотрим вбок и прём вперёд, а надо бы идти, туда куда смотрим. С другой стороны вращать только основу мы не можем по схожей причине: Уставились мы в небо и нажали ВПЕРЁД и наша основа полетела тоже в небо (даже если мы включим гравитацию, эффект останется – модуль скорости будет равен проекции вектора скорости на поверхность т.е. если мы задерём голову вверх – cos(90)=0 и мы не сможем двигаться).
Код:

TurnEntity camera,MouseYSpeed(),0,0
TurnEntity user,0,-MouseXSpeed(),0

т.е. после доработок:
Код:

Function update_user()

        V#=0.5
       
        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


End Function

Ндя… :o неприятный осадок всё же остался: как это можно так задрать голову, что увидеть происходящее за спиной и ниже и сделать 360 градусный проворт головы?!
Модифим – теперь камера не будет превышать указанный нами угол вверх и вниз:
Код:

Function update_user()

        V#=0.5
        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

End Function

ABS-модуль
EntityPitch# ( entity[,global] ) – угол вращения в плоскости Y0Z, т.е. вокруг оси X
SGN – функция-знак
Если что не понятно - ставим курсор на функцию и кликаем 2 раза F1. ;)
Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

;создание игрока
Function create_user(x#=0,y#=1,z#=0)
        user=CreateSphere()
        camera=CreateCamera(user)
        PositionEntity camera,0,1,-0.5
       
        PositionEntity user,x#,y#,z#
       
End Function

;обновление игрока
Function update_user()

        V#=0.5
        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

End Function

;создание игрового мира
Function create_world()
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10
        EntityTexture terrain,tertex
        FreeTexture tertex
        create_user()
End Function
;функции закончились :)
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        RenderWorld()
        Flip
Wend
;----------------------------
End

:glag:

impersonalis 04.09.2005 06:08

"честная" физика :)
Так-с приступим к добавлению гравитации. Объявим где-нибудь в глобале переменную ускорения

свободного падения
Код:

Const G#=1
Обратите внимание на идентификатор const (константа) и тип переменной (#-с плавающей
точкой).
Теперь надо реализовать контроль за отрывом от поверхности...
Это можно сделать 2 способами через просчёт столкновений (Collisions) и через "пики"

(Pick). С одной стороны, так как Collisions обрабатывается дольше, сложнее в коде... и

самое главное так или иначе автоматически производит response - один из 3-х методов

обработки столкновения:
1: stop
2: slide1 - full sliding collision
3: slide2 - prevent entities from sliding down slopes
С другой стороны обрабатывать это будет проще... впал в раздумья... короче делаем через

collision.
Добавим в глобал константы типов
Код:

Const USERT=1;for user
Const TERRT=2;for terrain

Переходим в Create_user() и делаем следующее.
Код:

EntityType user,USERT
Переходим в Create_world() и делаем следующее.
Код:

EntityType terrain,TERRT
и после вызова create_user()
Код:

Collisions USERT,TERRT,2,3
в главном цикле допишем
Код:

UpdateWorld()
и наконец в update_user() нацарапем
Код:

;физика
        TranslateEntity user,0,-G#,0

/сразу предупреждаю физиков: это не окончательный вараинт кода/
И мы смело двигаем пользователя вниз ф-цией TranslateEntity.
TranslateEntity отличается от MoveEntity безразличем к пространственной ориентации самой

модели. Пример: если объект перевернуть "на голову", то команда move вниз приведёт к

движению этого объекта вверх, Translate же будет двигать объект вниз.
Перейдём в create_user() и подправим кое-что:
Код:

Function create_user(x#=0,y#=10,z#=0)
Код:

PositionEntity camera,0,2,-0.5
Можно запускать! В начале текста я немного занизил высоту камеры относительно "ног", что

сейчас и исправил. Теперь, между прочим, стала заметна сфера. Вернёмся к ф-ции создания:
Код:

        user=CreateSphere()
        EntityAlpha user,0

Этим мы задали нулевую "альфу" для сферы - т.е. сделали её полностью прозрачной.

Термины "полностью прозрачный" и "невидимый" здесь не одно и то же. Если первый сводится к
изменению альфы (и объект просто не видим для камер), то второй реализуется командой
HideEntity (и объект убирается из процедуры рендера движка). Но об этом как-нибудь в другой раз.

Как очевидно, код:
Код:

        TranslateEntity user,0,-G#,0
не совсем то, что нам нужно. Хотя вблизи земли разницы никакой нет (допустимые

приближения). Вообще, здесь гравитация (вернее её подобие) нужна не столько для симуляции реалистичной физики, сколько для "прижимания" игрока к земле.
Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

Const G#=1

Const USERT=1;for user
Const TERRT=2;for terrain

;создание игрока
Function create_user(x#=0,y#=10,z#=0)
        user=CreateSphere()
        EntityAlpha user,0
        camera=CreateCamera(user)
        PositionEntity camera,0,2,-0.5
       
        PositionEntity user,x#,y#,z#
        EntityType user,USERT

End Function

;обновление игрока
Function update_user()

        V#=0.5
        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
;физика
        TranslateEntity user,0,-G#,0
End Function

;создание игрового мира
Function create_world()
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10
        EntityTexture terrain,tertex
        FreeTexture tertex
        EntityType terrain,TERRT
        create_user()
        Collisions USERT,TERRT,2,3
End Function
;функции закончились
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        UpdateWorld()
        RenderWorld()
        Flip
Wend
;----------------------------
End


impersonalis 04.09.2005 06:09

B) Конечно, ничто кроме фантазии программиста не ограничивает возможности языка - так что, никто не мешает Вам, немного поразмыслив, реализовать настоящую модель гравитации.
B) Обратите внимание: я стараюсь заменять конструкции типа x/2 на x*0.5 - это связано со спецификой обработки этих мат.операций процессором (спросите у SubZer0)

impersonalis 04.09.2005 06:16

Прицел
Тихо - не ржите: :rolleyes:



Тыкс загрузим картинку прицела в глобале
Код:

Global pricel=LoadImage("pricel.bmp")
назначаем начало координат картинки в её центре
Код:

MidHandle pricel
А это
Код:

DrawImage pricel,MouseX(),MouseY()
нужно вписть в главном цикле... вот не скажу где
Если вы использовали мой прицел, то увидите, что отображается он несовсем как хотелось бы.
Установим Mask-цвет - белый:
Код:

Global pricel=LoadImage("pricel.bmp")
MidHandle pricel
MaskImage pricel,255,255,255

:)

impersonalis 04.09.2005 06:21

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

CameraRange camera,0.1,100
установит минимальный и максимальный радиусы рендеринга
Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

Const G#=1

Const USERT=1;for user
Const TERRT=2;for terrain

Global pricel=LoadImage("pricel.bmp")


;создание игрока
Function create_user(x#=0,y#=10,z#=0)

        MidHandle pricel
        MaskImage pricel,255,255,255
       
        user=CreateSphere()
        k#=3
        ScaleEntity user,k#,k#,k#
        EntityRadius user,k#
        camera=CreateCamera(user)
        CameraRange camera,0.1,100
        PositionEntity user,x#,y#,z#
        EntityType user,USERT

End Function

;обновление игрока
Function update_user()

        V#=0.5
        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
;физика
        TranslateEntity user,0,-G#,0
End Function

;создание игрового мира
Function create_world()
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10
        EntityTexture terrain,tertex
        FreeTexture tertex
        EntityType terrain,TERRT
        create_user()
        Collisions USERT,TERRT,2,3
End Function
;функции закончились
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        UpdateWorld()
        RenderWorld()
        DrawImage pricel,MouseX(),MouseY()
        Flip
Wend
;----------------------------
End


impersonalis 04.09.2005 06:26

Выстрелы

Код:

Global shot_sprite=LoadSprite("sprite.bmp")
это будет спрайт выстрела.
В создании мира добавим
Код:

HideEntity shot_sprite
скрыв таким образом загруженный спрайт.
Создадим функцию порождения выстрела
Код:

Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#)
 
End Function

6 аргументов описывают вектор выстрела: з координаты и углы вращения по осям
Создадим тип выстрела:
Код:

Type shot
        Field entity
End Type

Далее...
Код:

Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#)
        s.shot=New shot
        s\entity=CopyEntity(shot_sprite)
        PositionEntity s\entity,x#,y#,z#,1
        RotateEntity s\entity,pitch#,yaw#,roll#,1
        Return True
End Function

В первой строке при помощи опереатора NEW создаём новый элемент типа shot.
Теперь через S мы можем обратиться к любому полю данного элемента типа shot.
s\entity - обращаемся к полю entity вновь созданного элемента. Сохраняем туда handle копии спрайта. Далее позиционируем вновь созданный (скопированный) спрайт и разворачиваем его. Последний аргумент 1 (true) означает, что действия будут выполнятся относительно мировых координат и осей.
Код:

Function update_shot()
        vs#=1
        For a.shot=Each shot
 MoveEntity a\entity,0,0,vs#
        Next
End Function

For a.shot=Each shot - на первый взгляд, заковыпистая конструкция. Такой цикл будет помещать по очереди в переменную а хендлы всех элементов указанного типа (в данном случае shot). Таким образом данный цикл продвинет все спрайты выстрелов на vs# вдоль оси Z.
Добавляем update_shot() в главный цикл.
Добавим строку
Код:

If MouseHit(1) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0)
В ф-цию обновления игрока.
Уже можно посмотреть в действии:


impersonalis 04.09.2005 06:28

Собственно, текстура спрайта (спрайт):


impersonalis 04.09.2005 06:29

вот так будут улетать вдаль спрайты. Слово далеко относится к неопределённой логике. Ограничим расстояние полёта.
Код:

        Field dist#
добавим ещё одно поле в тип. И обработаем его:
Код:

Function update_shot()
        vs#=1
        max_dist#=1000
        For a.shot=Each shot
 MoveEntity a\entity,0,0,vs#
 a\dist#=a\dist#+vs#
 If a\dist#>max_dist#
        FreeEntity a\entity
        Delete a
 EndIf
        Next
End Function

будем накапливать всоответствующем поле пройденное "снарядом" расстояние, и если оно больше максимального, то: очистим память, занимаемую спрайтом, затем удалим этот элемент типа. В том, что всё работает как надо Вы можите убедиться поэкспериментировав со значением max_dist# и сделав кучу выстрелов (после исчезновения выстрелов фпс возвращается в стабильное состояние).
Код:

Const SHOTT=3;for shot
в глобал.
Код:

Collisions SHOTT,TERRT,2,1
в создание мира
Код:

EntityType s\entity,SHOTT
в создании выстрела.
Если запустить программу теперь, то можно наблюдать забавный эффект - поверхность стала уловителем выстрелов:



Обновим условие в цикле обработки выстрелов
Код:

If a\dist#>max_dist#
        FreeEntity a\entity
        Delete a
 ElseIf EntityCollided(a\entity,TERRT) < > 0
        FreeEntity a\entity
        Delete a
 EndIf

Теперь при столкновении текущего спрайта с объектом, у которого коллизионный тип (а не "обычный" тип - как shot) TERRT, происходит удаление выстрела.

Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

Const G#=1

Const USERT=1;for user
Const TERRT=2;for terrain
Const SHOTT=3;for shot

Global pricel=LoadImage("pricel.bmp")
Global shot_sprite=LoadSprite("sprite.bmp")

Type shot
        Field entity
        Field dist#
End Type

;создание игрока
Function create_user(x#=0,y#=10,z#=0)

        MidHandle pricel
        MaskImage pricel,255,255,255
       
        user=CreateSphere()
        k#=3
        ScaleEntity user,k#,k#,k#
        EntityRadius user,k#
        camera=CreateCamera(user)
        CameraRange camera,0.1,100
        PositionEntity user,x#,y#,z#
        EntityType user,USERT

End Function

;обновление игрока
Function update_user()

        V#=0.5
        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)
;физика
        TranslateEntity user,0,-G#,0
End Function

Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#)
        s.shot=New shot
        s\entity=CopyEntity(shot_sprite)
        EntityType s\entity,SHOTT
        PositionEntity s\entity,x#,y#,z#,1
        RotateEntity s\entity,pitch#,yaw#,roll#,1
        Return True
End Function

Function update_shot()
        vs#=1
        max_dist#=1000
        For a.shot=Each shot
 MoveEntity a\entity,0,0,vs#
 a\dist#=a\dist#+vs#
 If a\dist#>max_dist#
        FreeEntity a\entity
        Delete a
 ElseIf EntityCollided(a\entity,TERRT) < > 0
        FreeEntity a\entity
        Delete a
 EndIf
        Next
End Function

;создание игрового мира
Function create_world()
        HideEntity shot_sprite
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10
        EntityTexture terrain,tertex
        FreeTexture tertex
        EntityType terrain,TERRT
        create_user()
        Collisions USERT,TERRT,2,3
        Collisions SHOTT,TERRT,2,1
End Function
;функции закончились
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        update_shot()
        UpdateWorld()
        RenderWorld()
        DrawImage pricel,MouseX(),MouseY()
        Flip
Wend
;----------------------------
End


impersonalis 04.09.2005 06:30

Кодим-кодим
B) На ряду с описанными в хелпе функциями, для работы с типами, есть и недокументированные команды Object и Handle, которые облегчают программирование доступа к конкретному элементу типа. Об этом в другой раз. ;)

impersonalis 04.09.2005 06:32

коварные боты

Итак, оружие есть - надо и поохотиться )
Создадим тип для ботов
Код:

Type bot
        Field entity
End Type

Далее как обычно:
Код:

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

Для простоты визуализация бота будет сведена отображению на экране сферы.
Код:

Function update_bot()
        v#=0.3
        For a.bot=Each bot
 PointEntity a\entity,user
 MoveEntity a\entity,0,0,v#
        Next
End Function

Тут всё ясно как день. За исключением, быть может, команды PointEntity entity,target[,roll#] . Так как основы создания искусственного
интеллекта на базе нейроподобных сетей здесь рассматривать не имеет смысла. А ур-ий, по которым бот, так или иначе, будет за вами двигаться,
можно придумать достаточно много, то ограничимся результатом работы самой простой (в исполнении Блитца) команды. После её выполнения объект
entity будет развёрнут "лицом" (т.е. в сторону положительного направления Z-оси) к объекту target. Это допустимо, так как в нашем случае
действие происходит на плоскости.
Не забываем про главный цикл программы ;)
Чуть не забыл (куда это добавлять - должно быть уже очевидно, если нет - в конце я прилагаю исходный код всей программы):
Код:

Const BOTT=4;for bot
Код:

EntityType b\entity,BOTT
Код:

        Collisions BOTT,TERRT,2,3
        Collisions BOTT,USERT,2,3
        Collisions BOTT,BOTT,2,3
       
        Collisions USERT,BOTT,2,3
       
        Collisions SHOTT,BOTT,2,1

а в функции создания мира
Код:

create_bot(10,2,50)
Можно запустить и посмотреть. Как видно, на некотором расстоянии от игрока, бот остановится и начнёт совершать непонятные движения - это он
наткнулся на прозрачную сферу, которой окружён игрок.
Перепишем ф-цию обновления выстрелов:
Код:

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
        For q.bot=Each bot
  If q\entity=bot_h
          FreeEntity bot_h
          Delete q
          Exit
  EndIf
        Next
 EndIf
        Next
End Function

Фууу... меня очень угнетает цикл с перебором всех ботов в поисках записи о подбитом, но объяснять как это можно сделать проще, а главное
быстрее здесь не буду (есть небольшой трюк с использованием недокументированных команд и именами объектов). И чтобы превратить игру в
бесконечно долгую, перепишем условие уничтожения бота:
Код:

        FreeEntity bot_h
        Delete q
        create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100))
        Exit

Теперь за каждого убитого бота около игрока (в радиусе 100) будет "респиться" новый бот.
Причём сколько было ботов при загрузке - такое их кол-во и будет поддерживаться в игре.
Перепишем функцию создания мира:
Код:

        c_bot=Input("input Amount")
        For i=1 To c_bot
 create_bot(Rnd(-100,100),2,Rnd(-100,100))
        Next

При загрузке будет запрос на ввод кол-ва ботов.
Попробуйте указать 20 и ничего не делайте - вскоре боты заблокируют вас полностью.
Понаблюдайте за обработкой коллизий.

impersonalis 04.09.2005 06:33

B) Даже если Вы работаете с типами впервые, рассмотренного здесь вполне достаточно, чтобы реализовать "жизни" для ботов и зарезервировать поля для хранения промежуточных результатов работы ИИ.
:rolleyes:

impersonalis 04.09.2005 06:39

Для придания объёма (это надо было ещё вначале сделать) я добавил в create_world() создание и ротацию света:
Код:

        light=CreateLight()
        RotateEntity light,90,0,0

:ok:

Код:

;инициализация графики
Graphics3D 800,600,32
SetBuffer BackBuffer()

Global user
Global camera

Const G#=1

Const USERT=1;for user
Const TERRT=2;for terrain
Const SHOTT=3;for shot
Const BOTT=4;for bot

Global pricel=LoadImage("pricel.bmp")
Global shot_sprite=LoadSprite("sprite.bmp")

Type shot
        Field entity
        Field dist#
End Type

Type bot
        Field entity
End Type

;создание игрока
Function create_user(x#=0,y#=10,z#=0)

        MidHandle pricel
        MaskImage pricel,255,255,255
       
        user=CreateSphere()
        k#=3
        ScaleEntity user,k#,k#,k#
        EntityRadius user,k#
        camera=CreateCamera(user)
        CameraRange camera,0.1,100
        PositionEntity user,x#,y#,z#
        EntityType user,USERT

End Function

;обновление игрока
Function update_user()

        V#=0.5
        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)
;физика
        TranslateEntity user,0,-G#,0
End Function

Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#)
        s.shot=New shot
        s\entity=CopyEntity(shot_sprite)
        EntityType s\entity,SHOTT
        PositionEntity s\entity,x#,y#,z#,1
        RotateEntity s\entity,pitch#,yaw#,roll#,1
        Return True
End Function

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
        For q.bot=Each bot
  If q\entity=bot_h
          FreeEntity bot_h
          Delete q
          create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100))
          Exit
  EndIf
        Next
 EndIf
        Next
End Function

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

Function update_bot()
        v#=0.3
        For a.bot=Each bot
 PointEntity a\entity,user
 MoveEntity a\entity,0,0,v#
        Next
End Function

;создание игрового мира
Function create_world()
        light=CreateLight()
        RotateEntity light,90,0,0
        HideEntity shot_sprite
        terrain=CreatePlane()
        tertex=LoadTexture("terrain1.jpg")
        ScaleTexture tertex,10,10
        EntityTexture terrain,tertex
        FreeTexture tertex
        EntityType terrain,TERRT
        create_user()
       
        c_bot=Input("input Amount")
        For i=1 To c_bot
 create_bot(Rnd(-100,100),2,Rnd(-100,100))
        Next
       
        Collisions USERT,TERRT,2,3
        Collisions SHOTT,TERRT,2,1
       
        Collisions BOTT,TERRT,2,3
        Collisions BOTT,USERT,2,3
        Collisions BOTT,BOTT,2,3
       
        Collisions USERT,BOTT,2,3
       
        Collisions SHOTT,BOTT,2,1
End Function
;функции закончились
;--------------------------------
create_world()

;MAIN LOOP
While Not KeyHit(1)=1
        update_user()
        update_shot()
        update_bot()
        UpdateWorld()
        RenderWorld()
        DrawImage pricel,MouseX(),MouseY()
        Flip
Wend
;----------------------------
End

:glag:


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

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