page # 0
Всем привет! По опыту знаю: большинство хочет сразу после просмотра сэмплов сесть и написать FPS. И не просто какой-нибудь: УБЕЙ_ИХ_ВСЕХ_2010, а что-нибудь революционно-инновационное – с крутейшей графикой, умнейшим ИИ, и возможностью сетевой игры на разных протоколах и в разных игровых режимах, с абсолютной нелинейностью сюжета, причём всё это будет требовать смехотворно мало ресурсов. Если Вы именно такой пользователь THEN goto основы. Так как начинать с подобного рода программ – отбивать себе всякое желание программить на Блитце – попишите пока тетрисы, змейки и проч. начало статьи (с иллюстрациями, безвозвратно канувшими в Лету, благодаря очередному переезду): http://blitzetcetera.org/index.php/С...Person_Shooter |
подготовка
Итак… FPS… конечно же 3D! Запускаем компилятор и смело вбиваем Код:
Graphics3D 800,600,32 Код:
While Not KeyHit(1)=1 цикл крутится пока Не ПрерываниеОтКнопки (Escape) команда END в данном случае – чистый формализм: после инструкции WEND у нас в коде ничего нет, таким образом, после выхода из цикла программа и так завершится. Но всё же я настоятельно рекомендую не пренебрегать подобными формальностями! Уже можно давить F5 – и любоваться :) … космосом… без звёзд… Теперь займёмся камерой… даже - лучше сразу игроком ;) Опять же: я настоятельно рекомендую заключать подобные знаковые участки кода в тела функций. Поэтому где-нибудь повыше напишем: Код:
Function create_user() Код:
Graphics3D 800,600,32 Теперь передаваемые в функцию аргументы… Думаю, удобно будет задавать ими место появления игрока. С другой стороны, практически всегда у нас он появляется в 0,h#,0 – в центре карты на небольшом расстоянии от земли, поэтому пишем следующее: Код:
Function create_user(x#=0,y#=1,z#=0) Код:
user=CreateSphere() Запускаем… И снова темнота :sad А как по-вашему: у нас есть описание действия «создать игрока» и пустой цикл. Для начал надо вызвать функцию создания перед главным циклом: Код:
create_user() Код:
RenderWorld() Код:
Flip Код:
Graphics3D 800,600,32 Нам нужна земная твердь. И обязательно текстурка для неё. Ведь компьютер без дополнительных инструкций строит идеальные видео образы – абсолютно ровную поверхность земли (на самом деле мелкие неровности пока тоже ничего бы не дали – нет освещения – которое и позволяет видеть объёмную картину мира , без света всё будет казаться «плосокватым») так что вы просто увидите светлый прямоугольник занимающий часть экрана. Для примера – стандартная Блитц-текстура: Опять же прибегая к упрощениям – земля будет в виде бесконечной ровной поверхности – создадим её плэйном: Код:
terrain=CreatePlane() Код:
tertex=LoadTexture("terrain1.jpg") Код:
EntityTexture terrain,tertex Код:
FreeTexture tertex И ещё немного подумав, объединим вызов ф-ции создания игрока и создание мира в одну функцию: Код:
Graphics3D 800,600,32 ПРОДОЛЖЕНИЕ СЛЕДУЕТ :super: |
кодим-кодим
Для практики: закомментируем команду текстурирования террайна и посмотрим на выполнение: Код:
Graphics3D 800,600,32 Кстати о комментариях: не стоит ими пренебрегать но и объяснять всё подряд не стоит. :o Код:
;инициализация графики http://community.boolean.name/index.php?act=ST&f=4&t=25 Не стоит так же пренебрегать грамотным форматированием кода: Код:
;инициализация графики |
Озираемся и бегаем
Так-с-с : на поверхность посмотрели – пора двигаться дальше. Будем учить игрока двигаться: Код:
;обновление игрока Код:
Global user Игрока мы будем обновлять на каждой итерации главного цикла, поэтому сразу допишем вызов соответствующей функции: Код:
;инициализация графики Код:
V#=0.5 Код:
If KeyDown(203)=1 *Then MoveEntity user,-V#,0,0 Код:
tertex=LoadTexture("terrain1.jpg") Теперь займёмся обзорами местности. Будем вращать камеру вверх-вниз в зависимости от смещения мыши по Y-оси и влево-вправо от смещения по X-оси. Угу: как же ну смотрю я вправо… ещё вправо… опа: курсор упёрся в правый край экрана и всё :sungl . Значит после оценки изменений мы должны вернуть мышь на исходную – самое логичное, в центр экрана. Получить текущий размер экрана в пикселях по X и по Y можно функциями Код:
GraphicsWidth() Код:
MoveMouse GraphicsWidth()*0.5,GraphicsHeight()*0.5 Код:
MouseYSpeed() Код:
TurnEntity camera,MouseYSpeed(),0,0 Код:
Function update_user() Модифим – теперь камера не будет превышать указанный нами угол вверх и вниз: Код:
Function update_user() EntityPitch# ( entity[,global] ) – угол вращения в плоскости Y0Z, т.е. вокруг оси X SGN – функция-знак Если что не понятно - ставим курсор на функцию и кликаем 2 раза F1. ;) Код:
;инициализация графики |
"честная" физика :)
Так-с приступим к добавлению гравитации. Объявим где-нибудь в глобале переменную ускорения свободного падения Код:
Const G#=1 точкой). Теперь надо реализовать контроль за отрывом от поверхности... Это можно сделать 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 Код:
EntityType user,USERT Код:
EntityType terrain,TERRT Код:
Collisions USERT,TERRT,2,3 Код:
UpdateWorld() Код:
;физика И мы смело двигаем пользователя вниз ф-цией TranslateEntity. TranslateEntity отличается от MoveEntity безразличем к пространственной ориентации самой модели. Пример: если объект перевернуть "на голову", то команда move вниз приведёт к движению этого объекта вверх, Translate же будет двигать объект вниз. Перейдём в create_user() и подправим кое-что: Код:
Function create_user(x#=0,y#=10,z#=0) Код:
PositionEntity camera,0,2,-0.5 сейчас и исправил. Теперь, между прочим, стала заметна сфера. Вернёмся к ф-ции создания: Код:
user=CreateSphere() Термины "полностью прозрачный" и "невидимый" здесь не одно и то же. Если первый сводится к изменению альфы (и объект просто не видим для камер), то второй реализуется командой HideEntity (и объект убирается из процедуры рендера движка). Но об этом как-нибудь в другой раз. Как очевидно, код: Код:
TranslateEntity user,0,-G#,0 приближения). Вообще, здесь гравитация (вернее её подобие) нужна не столько для симуляции реалистичной физики, сколько для "прижимания" игрока к земле. Код:
;инициализация графики |
B) Конечно, ничто кроме фантазии программиста не ограничивает возможности языка - так что, никто не мешает Вам, немного поразмыслив, реализовать настоящую модель гравитации.
B) Обратите внимание: я стараюсь заменять конструкции типа x/2 на x*0.5 - это связано со спецификой обработки этих мат.операций процессором (спросите у SubZer0) |
Прицел
Тихо - не ржите: :rolleyes: Тыкс загрузим картинку прицела в глобале Код:
Global pricel=LoadImage("pricel.bmp") Код:
MidHandle pricel Код:
DrawImage pricel,MouseX(),MouseY() Если вы использовали мой прицел, то увидите, что отображается он несовсем как хотелось бы. Установим Mask-цвет - белый: Код:
Global pricel=LoadImage("pricel.bmp") |
Так: немного подправим код для удобства:
внесём прицел в функцию создания перса, м немного морфируем физ. модель игрока. Теперь уже понятно почему и когда мы крутим камеру и основание. И теперь для удобства просчёта коллизий камеру поместим в центр прозрачной сферы. И ещё Код:
CameraRange camera,0.1,100 Код:
;инициализация графики |
Выстрелы
Код:
Global shot_sprite=LoadSprite("sprite.bmp") В создании мира добавим Код:
HideEntity shot_sprite Создадим функцию порождения выстрела Код:
Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#) Создадим тип выстрела: Код:
Type shot Код:
Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#) Теперь через S мы можем обратиться к любому полю данного элемента типа shot. s\entity - обращаемся к полю entity вновь созданного элемента. Сохраняем туда handle копии спрайта. Далее позиционируем вновь созданный (скопированный) спрайт и разворачиваем его. Последний аргумент 1 (true) означает, что действия будут выполнятся относительно мировых координат и осей. Код:
Function update_shot() Добавляем update_shot() в главный цикл. Добавим строку Код:
If MouseHit(1) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0) Уже можно посмотреть в действии: |
Собственно, текстура спрайта (спрайт):
|
вот так будут улетать вдаль спрайты. Слово далеко относится к неопределённой логике. Ограничим расстояние полёта.
Код:
Field dist# Код:
Function update_shot() Код:
Const SHOTT=3;for shot Код:
Collisions SHOTT,TERRT,2,1 Код:
EntityType s\entity,SHOTT Если запустить программу теперь, то можно наблюдать забавный эффект - поверхность стала уловителем выстрелов: Обновим условие в цикле обработки выстрелов Код:
If a\dist#>max_dist# Код:
;инициализация графики |
Кодим-кодим
B) На ряду с описанными в хелпе функциями, для работы с типами, есть и недокументированные команды Object и Handle, которые облегчают программирование доступа к конкретному элементу типа. Об этом в другой раз. ;) |
коварные боты
Итак, оружие есть - надо и поохотиться ) Создадим тип для ботов Код:
Type bot Код:
Function create_bot(x#,y#,z#) Код:
Function update_bot() интеллекта на базе нейроподобных сетей здесь рассматривать не имеет смысла. А ур-ий, по которым бот, так или иначе, будет за вами двигаться, можно придумать достаточно много, то ограничимся результатом работы самой простой (в исполнении Блитца) команды. После её выполнения объект entity будет развёрнут "лицом" (т.е. в сторону положительного направления Z-оси) к объекту target. Это допустимо, так как в нашем случае действие происходит на плоскости. Не забываем про главный цикл программы ;) Чуть не забыл (куда это добавлять - должно быть уже очевидно, если нет - в конце я прилагаю исходный код всей программы): Код:
Const BOTT=4;for bot Код:
EntityType b\entity,BOTT Код:
Collisions BOTT,TERRT,2,3 Код:
create_bot(10,2,50) наткнулся на прозрачную сферу, которой окружён игрок. Перепишем ф-цию обновления выстрелов: Код:
Function update_shot() быстрее здесь не буду (есть небольшой трюк с использованием недокументированных команд и именами объектов). И чтобы превратить игру в бесконечно долгую, перепишем условие уничтожения бота: Код:
FreeEntity bot_h Причём сколько было ботов при загрузке - такое их кол-во и будет поддерживаться в игре. Перепишем функцию создания мира: Код:
c_bot=Input("input Amount") Попробуйте указать 20 и ничего не делайте - вскоре боты заблокируют вас полностью. Понаблюдайте за обработкой коллизий. |
B) Даже если Вы работаете с типами впервые, рассмотренного здесь вполне достаточно, чтобы реализовать "жизни" для ботов и зарезервировать поля для хранения промежуточных результатов работы ИИ.
:rolleyes: |
Для придания объёма (это надо было ещё вначале сделать) я добавил в create_world() создание и ротацию света:
Код:
light=CreateLight() Код:
;инициализация графики |
Баг-фикс
Я всё-таки почти человек, поэтому ничего странного, что забыл дописать ещё вот что: Код:
Function update_bot() |
Чит-мод :)
Попробуйте включить в функцию обработки игрока строку Код:
If MouseDown(2) create_shot(EntityX(user),EntityY(user),EntityZ(user),EntityPitch(camera),EntityYaw(user),0) |
Релиз
В аттаче полностью рабочая версия описанной здесь программы. Со всеми ресурсами :grins http://boolean.name/archive/code/Example%2...0FPS%20v1.0.rar |
Уход от полного перебора
Как и обещал, расскажу, как улучшить цикл обработки выстрелов. В Блитце есть недокументированные команды Handle и Object. Благодаря команде Handle можно получить "указатель" на объект типа (использование термина "указатель" весьма спорно для Блитца). Например: Код:
P.Player=New Player Код:
P.PLAYER=Object.PLAYER(pHANDLE) Модифицируем функцию create_bot Код:
Function create_bot(x#,y#,z#) содержащего данный объект. Теперь модифим кусок функции update_shot() Код:
Function update_shot() Код:
bhandle=EntityName(bot_h) Код:
bc.bot=Object.bot(bhandle) Код:
FreeEntity bot_h производительность. |
Уход от привязки к FPS
Frames Per Second В текущей версии программы события (а именно: обновление ботов, игрока, выстрелов) будут происходить с частотой вызова соответствующих ф-ций. Т.е. (как видно из записи главного цикла) с частотой прорисовки - ФПС. А значит напрямую будут зависеть от производительности системы. Избавиться можно 2мя способами: :?? 1) Производить обновление N раз в секунду - что приведёт к принудительному занижению высоких ФПС и скачкообразной анимации. :?? 2) Рассчитывать переменные скорости, ускорения и проч. в соответствии с дельтой времени. Что сейчас и сделаем. Так как в следующих версиях алгоритма или уже в этой ( при большом кол-ве ботов) время начала обработки первого и последнего ботов вероятно будет отличаться на целое кол-во мс и так же потому, что глобальные переменные делают программу, имхо, менее удобочитаемой и переносимой, а так же плодят логические ошибки (из-за ошибок при вводе имени) - для каждого бота сделаем собственное время последнего обновления. Код:
Type bot Код:
Function update_bot() Переменная v# должна получиться универсальным значением для всех ПК (однако Вы можете откалибровать её по-точнее). Проделаем операцию ухода... для выстрелов: Код:
Type shot Код:
Function update_shot() Теперь сам игрок. Поскольку мультиплеером в игре и не пахнет - игрок реализован не типизированным объектом и он один. Заведём для его обновления глобальную переменную: Код:
Global user_time Код:
Function update_user() Код:
Const G#=0.1 |
Небесная сфера
Вопреки названию, будем создавать не сферу, а SkyBox - коробку, так как для неё проще изготовить текстуру и смотрится, имхо, она красивее. Возьмём стандартную функцию создания из примеров блитца (\Samples\Blitz 3D Samples\AGore\GrassDemo): Код:
Function MakeSkyBox( file$ ) использование ф-ций: в любой момент вы можете использовать свои наработки в других проектах. Переместимся в функцию create_world() Код:
create_user() программы. Уже сейчас можно посмотреть на результат, правда, сомнительный. Из-за недостаточных размеров skybox`а небо ощутимо квадратное: Отскалим небо Код:
ScaleEntity sky,150,150,150 Вероятно оно за максимальным радиусом ренедера. глянем в create_user - именно здесь мы обрезали расстония рендера, правим: Код:
CameraRange camera,0.1,10000 |
Релиз
В аттаче полностью рабочая версия (уже 2ая)описанной здесь программы. Со всеми ресурсами :grins http://boolean.name/archive/code/Example%2...0FPS%20v2.0.rar |
Живучие боты
Бот, умирающий от одного залпа, - это, кончено весело, но не интересно. Добавим ботам шкалу жизни. Итак, переходим в объявление типа и правим: Код:
Type bot Код:
b\Survivability=100 Код:
ElseIf bot_h<>0 Теперь равновесие сместилось в пользу ботов - за это понизим им скорость передвижения: Код:
Function update_bot() |
Жизнь в коробке
Рано или поздно, надо чётко обазначить границы игрового мира. После добавления в игру SkyBox, это становится необходимо сделать. Модифицируем ф-цию создания неба и сохраним её под новым именем (прототип тоже надо оставить в коде): Код:
Function MakeBox() Код:
border=MakeBox() Теперь поставим этой коробке в соответствие тип для колизии Код:
Const BORDERT=5;for box Код:
EntityAlpha border,.5 Код:
Collisions USERT,BORDERT,2,1 Теперь, если идти в одну сторону, в какой-то момент вы просто упрётесь в стену. Ставим стене нулевую альфу и растягиваем её. Код:
border=MakeBox() |
кодим-кодим
Что-то управление больно казуальным вышло. :unsure: Перенастроим движение на W,S,A,D. Функция update_user() Код:
If KeyDown(30)=1 Then MoveEntity user,-V#,0,0 |
Прыжки и реалистичность физики
давно пора было это сделать Для нормальной реализации кинематики нам понадобится: 1)контроль за отрывом от земли и моментом соприкосновения с ней. Код:
EntityPickMode terrain,2 Код:
Global jump_bool Код:
;физика Код:
Text 10,10,"jump_bool="+jump_bool Запустим. И увидим, что программа работает неверно. 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() Код:
EntityRadius user,k# Теперь значение jump_bool характеризует состояние игрока - есть или нет сцепления. 2)время невзаимодействия с землёй. перепишем обновление игрока ещё раз Код:
;физика 3)скорости движения для их динамического изменения. Код:
Global user_vy# Код:
pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0) Код:
TranslateEntity user,0,user_vy#*delta_t,0 после некоторых раздумий подправим физику: Код:
pick_ent=LinePick(EntityX(user,1),EntityY(user,1),EntityZ(user,1),0,-3.6,0) Однако теперь объект не смещается вниз, пока его не сдвинут вперёд-назад или влево-вправо. Я думаю, логика игры не пострадает, если предположить, что человек, прыгая вверх чуть-чуть смещается вперёд. Код:
TranslateEntity user,0,user_vy#*delta_t,user_vy#*0.01,True |
Релиз
В аттаче обновление кода http://boolean.name/archive/code/ES_FPS_3_codeUp.rar Необходимые ресурсы, можно взять из более ранней версии программы http://boolean.name/archive/code/Example%2...0FPS%20v2.0.rar :glag: |
Можно прочитать вышеизложенное и здесь:
http://blitzetc.boolean.name/fps.htm |
:o
Что-то нето.. Во время игры, точнее стрельбы второй клавишей Рантайм еррор сообщает что не существует Ентити bot_h |
Скачал. Все работает.
Только прыжок нужно более реалистичным сделать: чтобы и при движении вверх ускорение было, а не просто мгновенное перемещение по оси Y. |
да? и откуда же ему браться. Тело приобретает ускорение при толчке от земли, а дальше, это ускорение падает до 0 (точка повисает над землёй) и падает ниже 0 (тело с ускорением движется вниз).
|
В данном коде при прыжке игрок мгновенно перемещается в точку над землей, а потом с ускорением падает вниз. Говоря про ускорение при движении вверх, я имел в виду, что игрок не должен сразу перемещаться в точку над землей, он должен подпрыгнуть до нее. Т.е. игрок будет двигаться вверх с определенной скоростью, но так как ускорение направлено вниз, он постепенно остановится и начнет падать вниз.
Например: в файле ES_FPS_3.bb в функции update_user() заменить этот код: Код:
If pick_ent Код:
If if_jumping=0 Код:
Global if_jumping |
Мдя - понизь просто ускорение, движение в компьютерной эмуляции так или иначе состоит из рывков моментального перемещения. У меня игрок перемещается на х потом на ещё часть х и ещё - т.е. плавно до точки зависания. А скачкообразность первого перемещения на х зависит только от кол-ва ФПС и калибровки коэффициентов, при чём так или иначе приращения будут дискретны, а значит характер движения на dt не равномерным.
|
Цитата:
Цитата:
|
А я вообще не вижу здесь перемещения :D
|
Цитата:
Цитата:
|
нестал разбиратся в коде... но пишу что translateentity zzz,0,1,0 переместит обект не резко а плавно ! ;)
|
Блин! по твоему, alcoSHoLiK, строка
If KeyHit(57) And jump_bool=False Then user_vy#=1 каким то таинственным образом двигает объект? Где здесь move,translate,position entity?! |
Я говорил что эта строка двигает объект? Нет.
Я говорил "мгновенно перемещает". Да, я украинец. Возможно, я не знаю русский. Тогда приведу синонимы: "мгновенно перемещает"="телепортирует"="переставляет из одной точки в другую". Т.е. само перемещение происходит фактически за 0 интервал времени. Я утверждал, что строчка If KeyHit(57) And jump_bool=False Then user_vy#=1 не передвигает объект по оси Y, а мгновенно перемещает(телепортирует) его в точку 1 по Y. Но ведь в реальном мире при прыжке человек поднимается вверх, потом опускается. Он не телепортируется в точку над землей, он поднимается по оси Y до нее. В оригинальном коде такого нет. Я привел пример, как это сделать, немного изменив оригинальный код. Это все, о чем говорил. Никаких претензий к автору я не имею, просто показал, как можно сделать прыжок более реалистичным. |
мдя... все опять кричат что я читать не умею
сами вы читать неумеете Цитата:
|
ДЖИМОН!
ТЫ БЫ В КОДЕ СНАЧАЛА РАЗОБРАЛСЯ, А ПОТОМ КОММЕНТАРИИ ОСТАВЛЯЛ! Код:
TranslateEntity user,0,user_vy#*delta_t,user_vy#*0.01,True УЧИСЬ ВНИКАТЬ В СУТЬ СПОРА! |
alcoSHoLiK, я лишь говорю, что перемещение на 1 можно заменить на 0.1 или 0.00001, добившись оного варьированием коэффициентов. И будет тебе плавное смещение. ТО есть , имхо, алгоритм тут не причём. А понятие "плавного движения"- здесь суть абстракция, и используется только на приближения - допустим, в космосиме с размерами порядка 500 скачок на 1 можно считать плавным и т.п.
Короче высказались все, порядочно и пофлудили. |
Вложений: 2
Жаль нет ружья...
Самое время сделать из нашего оружия ружьё (или дробовик - это кому как нравится). Добавим задержку между выстрелами. Как всегда, код будет максимально упрощён и носить чисто ознакомительный характер. Топаем в константы и дописываем: Код:
Const G#=0.001 Код:
Global user_vy# Переходим в update_user(). Переписываем условие выстрела: Код:
If MouseHit(1) And MilliSecs()-last_shot_time>shot_delay Код:
;= |
Автомат в руки
Вот мне тут сообщают в ICQ, что дескать невозможно главгеру оружие вручить из-за моей модели представления... Странно... Попробуем. Смотрим в контакт-лист: ни одного моделлера в сети =/ кхым... Роль оружия будет выполнять цилиндр. Код:
Global user Код:
Function create_user(x#=0,y#=10,z#=0) Но недолго: если стрелять очередью, видно, что огонь ведётся не из ствола, а так сказать, Цитата:
Если бы у меня была нормальная модель, то дельный моделлер просто разместил бы пивот на конце ствола. Сейчас мы сделаем то же самое, но ручками ^_^ Код:
Function create_user(x#=0,y#=10,z#=0) Код:
Local user_gun Завершая тему личной обороны игрока, хотелось бы добавить вот что: Код:
Graphics3D 800,600,32 |
Вложений: 2
Куда глаза глядят
Следующее что хотелось рассмотреть в цикле заметочек. Указание направления дивжения бота при помощи спрайта. Как таковой пример особой ценности не имеет, однако может понадобиться в дальнейшем и может быть модфицирован под нужды "девелопера". Открываем графический редактор (к примеру MS Paint) и творим: нам понадобиться круг, с одним проведённым радиусом. Теперь вопрос: подгружать спрайт по мере необходимости или использовать образец? Для такой простой игры это не критично, однако, стоит приучиться использовать копии объектов. (в идеале должен быть и образец для каждого типа "выстрела"). Приступим: Код:
Global pricel=LoadImage("pricel.bmp") Код:
Function create_world() Код:
NameEntity b\entity,Handle(b) Код:
SpriteViewMode orient_sprite,2 |
Вложений: 2
Квази-интеллект
Немного усложним поведение ботов - чтобы игра не была такой "пресной". Сделаем из бота агр-моба (см LA2). Сценарий следующий: Цитата:
Код:
Type bot Код:
b\point=CreatePivot() Код:
Function update_bot() Рассмотрим подробнее: Код:
If EntityDistance(user,a\entity)<Rdist# Код:
PointEntity a\entity,user Если же игрока рядом нет, то Код:
If EntityDistance(a\entity,a\point)<2 Код:
PositionEntity a\point,EntityX(a\point)+Rnd(-40,40),EntityY(a\point),EntityZ(a\point)+Rnd(-40,40) Код:
PointEntity a\entity,a\point |
Квази-интеллект2
Слегка погонял ботов и понял, что чего-то не хватает. Подправим алгоритм поведения: Цитата:
Код:
Function update_shot() Цитата:
И ещё кое-что: пусть бот становится красным, если он "навёлся" на игрока. Код:
Function update_bot() |
Очень крутая статья все просто и ясно, лучше нигде не втречал, спасибо!
|
Всегда пожалуйста. Будут вопросы - обращайтесь =)
|
Re: Создаём свой FPS (first person shooter)
MANIAK_dobrii первёл данную работу на английский язык:
english-version |
Re: Создаём свой FPS (first person shooter)
Благодаря GoodWin (http://www.boolean.name/showthread.php?p=15571) нарыл баг в коде, который сказывается при использовании уровня с ра3личными высотами. Короче говоря, код создания выстрела должен быть такой:
Код:
Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#) команда позиционирования объекта Код:
PositionEntity s\entity,x#,y#,z#,1 Код:
EntityType s\entity,SHOTT Глюк обидный и вызван только моим недосмотром =( |
Re: Создаём свой FPS (first person shooter)
А как сделать здоровье персонажу и отнятие здоровья при прикосновении бота к игроку?
|
Re: Создаём свой FPS (first person shooter)
Завтра (имеется в виду - т.н. "логическое завтра") отвечу - сегодня спать хочется.
|
Re: Создаём свой FPS (first person shooter)
Цитата:
1) это моя тема 2) выложить просто переправленный аттач моджет каждый - для этого есть рубрики по 2д и 3д. ФАК подразумевает объяснения, тем более что 3)в коде много изменений без которых можно обойтись imper ладно, а так пойдет: назначим еще одну переменную отвечающую за кол-во жизни у игрока Global user, live добавим в функцию создания игрока следующую строку live=100 user=CreateSphere() k#=3 ScaleEntity user,k#,k#,k# теперь необходимо обработать прикосновение бота к игроку изменим функцию update_bot() добавим следующие строки кода: a\time=new_time ;= If EntityCollided (a\entity,USERT); проверякм есть ли колизия между ботои и игроком live=live-1; если есть то уменьшаем кол-во жизни If live<=0; если кол-во жизни игрока 0 или меньше FreeEntity user_h; удаляем игрока и снова его create_user(0,100,0);создаем EndIf ElseIf EntityDistance(user,a\entity)<Rdist# PointEntity a\entity,user Else If EntityDistance(a\entity,a\point)<2 PositionEntity a\point,EntityX(a\point)+Rnd(-40,40),EntityY(a\point),EntityZ(a\point)+Rnd(-40,40) EndIf PointEntity a\entity,a\point EndIf красным новые строки которые необходимо добавить... и конечно-же... идея реализации полность принадлежит Имперсоналису, за что ему огромное спасибо... P.S. извини, что отнял у тебя возможность помочь людям |
Re: Создаём свой FPS (first person shooter)
Смертный герой
Сегодня прикрутим показатель жизни игроку. Как всегда код будет максимально простым - чтобы показать прицип работы. Конечная реализация зависит от вас. В глобал: [highlight=blitzbasic] Global user_H [/highlight] в функцию create_user(x#=0,y#=10,z#=0) [highlight=blitzbasic] user_H=100 [/highlight] в MAIN LOOP: [highlight=blitzbasic] Text 10,40,"H="+user_H [/highlight] Теперь опишем систему нанесения урона пользователю. В функции update_bot() пишем: [highlight=blitzbasic] Function update_bot() Rdist#=15 v#=0.01 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 ;= If EntityDistance(user,a\entity)<Rdist# PointEntity a\entity,user EntityColor a\entity,255,0,0 Else EntityColor a\entity,255,255,255 If EntityDistance(a\entity,a\point)<2 PositionEntity a\point,EntityX(a\point)+Rnd(-40,40),EntityY(a\point),EntityZ(a\point)+Rnd(-40,40) EndIf PointEntity a\entity,a\point EndIf ;===========новое player=EntityCollided(a\entity,USERT) If player<>0 user_H=user_H-1 EndIf ;======конец нового MoveEntity a\entity,0,0,v#*delta_t TranslateEntity user,0,-G#*delta_t,0 Next End Function [/highlight] Если бот столкнулся с объектом, у которого стоит тип коллизии USERT, то убавить игроку жизнь на единицу. Запустите и убедитесь в недостаточности этой конструкции. Жизнь уменьшается при каждом вызове функции, т.е. если у вас 60 ФПС, то за секунду прикосновения к вам бота у вас онимется 60 единиц здоровья. Приступим. [highlight=blitzbasic] Type bot Field entity Field time Field Survivability Field point Field last_damage_time End Type [/highlight] Здесь будем хранить время последнего "удара". Подправим функцию обеновления бота: [highlight=blitzbasic] ;=========== player=EntityCollided(a\entity,USERT) If player<>0 user_H=user_H-1 Else a\ last_damage_time=0 EndIf ;====== [/highlight] и далее в блоке If player<>0 [highlight=blitzbasic] If a\last_damage_time=-1 a\last_damage_time=MilliSecs() EndIf [/highlight] Если мы фиксируем акт соприкосновения первый раз (так сказать "локально" - т.е. в прошлом обновлении коллизии не было, а перед этим соприкосновений могло быть лююбое кол-во), то сохраняем в поле время, прошедшее с начала соприкосновения (а далее - время, с последнего обновления события, влекующего сосприкосновение - т.е. время прошлого "удара"). Вообще говоря, факт коллизии будет зафиксирован при вызове UpdateWorld(), т.е. время, прошедешее с акта коллизии до обработки этого факта в функции обновления бота весьма мало. Но всё же, не "пренебрежимо мало", особенно если частота UpdateWorld и функций обновления бота сильно разняться. Но для данного примера это не критично (в описанном выше случае надо или логику вызвать вместе с UpdateWorld или искать иные пути - зависит от конкретики реализации стабилизации и логики мира) [highlight=blitzbasic] If MilliSecs()-a\last_damage_time>1000 user_H=user_H-1 a\last_damage_time=MilliSecs() EndIf [/highlight] Ну и как завершающий штрих - сделаем силу урона произвольной (из диапазона): [highlight=blitzbasic] Function update_bot() Rdist#=15 v#=0.01 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 ;= If EntityDistance(user,a\entity)<Rdist# PointEntity a\entity,user EntityColor a\entity,255,0,0 Else EntityColor a\entity,255,255,255 If EntityDistance(a\entity,a\point)<2 PositionEntity a\point,EntityX(a\point)+Rnd(-40,40),EntityY(a\point),EntityZ(a\point)+Rnd(-40,40) EndIf PointEntity a\entity,a\point EndIf ;=========== player=EntityCollided(a\entity,USERT) If player<>0 If a\last_damage_time=-1 a\last_damage_time=MilliSecs() EndIf If MilliSecs()-a\last_damage_time>1000 user_H=user_H-Rand(1,7) a\last_damage_time=MilliSecs() EndIf Else a\ last_damage_time=-1 EndIf ;====== MoveEntity a\entity,0,0,v#*delta_t TranslateEntity user,0,-G#*delta_t,0 Next End Function [/highlight] |
Re: Создаём свой FPS (first person shooter)
Вложений: 2
Вот - код и все ресурсы к нему. В коде GoodWin (спс. за старание) я не нашёл временной паузы между нанесением поврежденй игроку - весьма обидное упущение. В моей заметке это есть :@
ATTENTION! Если вы скачивали сегодня уже этот архив, то перекачайте - в коде была допущена некритичная (но всё же!) неточность. |
Re: Создаём свой FPS (first person shooter)
Думаю следующий урок будет "научим Бота стрелять!"
всем это будет интересно и мне тоже.... ждемс.... Цитата:
Цитата:
|
Re: Создаём свой FPS (first person shooter)
Боевая система
Сделаем несколько типов оружия. Модифицируем систему. [highlight=blitzbasic] Type shot Field entity Field dist# Field time Field class_id%;здесь будет хранится инфа о классе снаряда(патрона) End Type [/highlight] Визуализация снаряда зависит от его класса [highlight=blitzbasic] Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#,class%=1) s.shot=New shot s\class_id=class Local shot_sprite_handle=shot_spriteS(s\class_id) s\entity=CopyEntity(shot_sprite_handle) PositionEntity s\entity,x#,y#,z#,1 EntityType s\entity,SHOTT RotateEntity s\entity,pitch#,yaw#,roll#,1 Return True End Function [/highlight] shot_spriteS - это будет наш массив с образцами спрайтов [highlight=blitzbasic] Global pricel=LoadImage("pricel.bmp") Dim shot_spriteS(2);инициализация For i=0 To 1;загрузка shot_spriteS(i)=LoadSprite("sprite"+i+".bmp") Next Global orient_sprite=LoadSprite("orient.bmp") [/highlight] А вот это - нафик [highlight=blitzbasic] Function create_world() light=CreateLight() RotateEntity light,90,0,0 HideEntity shot_sprite; уже не катит :) [/highlight] Скрывать будем сразу после загрузки: [highlight=blitzbasic] For i=0 To 1 shot_spriteS(i)=LoadSprite("sprite"+i+".bmp") HideEntity shot_spriteS(i) Next [/highlight] Так теперь переименуйте файл со спрайтом выстрела в sprite0 и найдите ещё один (отличный от имеющегося) спрайт - назовите его sprite1. Подправим функцию обновления игрока: [highlight=blitzbasic] If MouseHit(2) And MilliSecs()-last_shot_time>shot_delay user_gun=FindChild(GUN,"END") create_shot(EntityX(user_gun,1),EntityY(user_gun,1 ),EntityZ(user_gun,1),EntityPitch(camera),EntityYa w(user),0) last_shot_time=MilliSecs() EndIf If MouseHit(1) user_gun=FindChild(GUN,"END") create_shot(EntityX(user_gun,1),EntityY(user_gun,1 ),EntityZ(user_gun,1),EntityPitch(camera),EntityYa w(user),0,1) EndIf [/highlight] В зависимости от того что нажато (ЛКМ или ПКМ), производится выстрел из 1ого или 2ого оружия. Уже можно псмотреть результаты работы. Далее. И вот тут начинаешь задумываться: у различного оружия - разлиные характеристики (глубокая мысль). Надо как-то оценивать дальность стрельбы, урон, особенности траектории и прочее... Эх - создаём ещё один информационный тип: [highlight=blitzbasic] Type SHOT_TYPE Field sprite Field damage Field max_dist Field v# End Type [/highlight] Самое мерзкое (так делать не надо!) описываем в коде характеристики оружия. В реальных играх это должно хранится в конфигурационных файлах и загружаться специальной функцией. [highlight=blitzbasic] Dim shot_S.SHOT_TYPE(2) For i=0 To 1 shot_S(i)=New SHOT_TYPE Next shot_S(0)\sprite=LoadSprite("sprite0.bmp") HideEntity shot_S(0)\sprite shot_S(0)\damage=95 shot_S(0)\max_dist=100 shot_S(0)\v=0.3 shot_S(1)\sprite=LoadSprite("sprite1.bmp") HideEntity shot_S(1)\sprite shot_S(1)\damage=10 shot_S(1)\max_dist=1000 shot_S(1)\v=0.1 [/highlight] Характеристики зависят от конкретики игры ( можно хоть мат.дисперсию величины повреждения записать). Не забудьте удалить старый код - с массивом под спрайты. [highlight=blitzbasic] Function create_Shot(x#,y#,z#,pitch#,yaw#,roll#,class%=1) s.shot=New shot s\class_id=class s\entity=CopyEntity(shot_S(s\class_id)\sprite) PositionEntity s\entity,x#,y#,z#,1 EntityType s\entity,SHOTT RotateEntity s\entity,pitch#,yaw#,roll#,1 Return True End Function [/highlight] ну и правим обновление снарядов: [highlight=blitzbasic] Function update_shot() 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#=shot_S(a\class_id)\v*delta_t ;= MoveEntity a\entity,0,0,vs# a\dist#=a\dist#+vs# Local copy_a_class_id=a\class_id bot_h=EntityCollided(a\entity,BOTT) If a\dist#>shot_S(a\class_id)\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) bc\Survivability=bc\Survivability-shot_S(copy_a_class_id)\damage ;= PositionEntity bc\point,EntityX(user,1),EntityY(bc\point,1),Entit yZ(user,1) ;= If bc\Survivability<=0 Delete bc FreeEntity bot_h create_bot(EntityX(user)+Rnd(-100,100),2,EntityZ(user)+Rnd(-100,100)) EndIf EndIf Next End Function [/highlight] Запускаем - играем. ГГ... Теперь даже тактика есть: заманить противника на себя из слабого быстроперезаряжаемого оружия а потом, вблизи, замочить из СуперПушки с одного залпа =) |
Re: Создаём свой FPS (first person shooter)
Вложений: 4
Я так смотрю - архив качают :-)
А значит мои коды не лишены смысла - это приятно. В архиве - модифицированный код со всеми ресурсами B) |
Re: Создаём свой FPS (first person shooter)
Для полноценности надо сделать HUD.
|
Re: Создаём свой FPS (first person shooter)
Ух ты, спасибо impersonalis! Полезная статья)
|
Re: Создаём свой FPS (first person shooter)
Инвентарь
Один из самых популярных вопросов. Инвентарь (как и многое другое) - как хочешь так и кодируй. Я предложу один из вариантов. Пишем тип: [highlight=blitzbasic] Type inventar Field s[10] Field current Field last_shot_time End Type [/highlight] создайм "один инвентарь" для игрока: [highlight=blitzbasic] Global user_inv.INVENTAR=New INVENTAR [/highlight] в Function create_user(x#=0,y#=10,z#=0) допишем: [highlight=blitzbasic] For i=0 To 10 user_inv\s[i]=0 Next user_inv\s[0]=1 user_inv\current=0 [/highlight] Global last_shot_time -удаляем из кода. В Function update_user() добавляем универсальности: [highlight=blitzbasic] If MouseHit(1) And MilliSecs()-user_inv\last_shot_time>shot_delay user_gun=FindChild(GUN,"END") create_shot(EntityX(user_gun,1),EntityY(user_gun,1 ),EntityZ(user_gun,1),EntityPitch(camera),EntityYa w(user),0,0) user_inv\last_shot_time=MilliSecs() EndIf [/highlight] Обработчик ПКМ стираем. Неплохо бы сделать для каждого образца оружия своё время перезарядки (ожидания между выстрелами): [highlight=blitzbasic] Type SHOT_TYPE Field sprite Field damage Field max_dist Field v# Field reload_time End Type [/highlight] [highlight=blitzbasic] shot_S(0)\sprite=LoadSprite("sprite0.bmp") HideEntity shot_S(0)\sprite shot_S(0)\damage=95 shot_S(0)\max_dist=100 shot_S(0)\v=0.3 shot_S(0)\reload_time=1500 shot_S(1)\sprite=LoadSprite("sprite1.bmp") HideEntity shot_S(1)\sprite shot_S(1)\damage=10 shot_S(1)\max_dist=1000 shot_S(1)\v=0.1 shot_S(1)\reload_time=0 [/highlight] Const shot_delay=1000 - нам больше не понадобится. [highlight=blitzbasic] If MouseHit(1) And MilliSecs()-user_inv\last_shot_time>shot_S(user_inv\current)\r eload_time [/highlight] Если юзер тыкнул ЛКМ и время с последнего выстрела прошло достаточно для перезарядки текущего оружия, то [highlight=blitzbasic] user_gun=FindChild(GUN,"END") create_shot(EntityX(user_gun,1),EntityY(user_gun,1 ),EntityZ(user_gun,1),EntityPitch(camera),EntityYa w(user),0,user_inv\current) user_inv\last_shot_time=MilliSecs() [/highlight] [highlight=blitzbasic] If MilliSecs()-user_inv\last_shot_time<shot_S(user_inv\current)\r eload_time Text 10,10,"RELOAD..." EndIf [/highlight] Теперь - навигация по инвентарю: [highlight=blitzbasic] Function change_item(i.inventar,move%) p=i\current While True p=p+move If p<0 p=9 If p>9 p=0 If i\s[p]<>0 Exit Wend i\current=p End Function [/highlight] Вот такая вот фунЕция: i.inventar - инвентарь с которым мы будем работать move% -примает значения 1 и -1 -направление движения по инвентарю (след./пред.) В update_user() допишем: [highlight=blitzbasic] change_item(user_inv,MouseZSpeed()) [/highlight] А в Function create_user(x#=0,y#=10,z#=0): [highlight=blitzbasic] user_inv\s[0]=1 user_inv\s[1]=1;добавим [/highlight] Можно полюбоватся (или поужасаться) уже: запускаем, колёскиом выбираем оружие из инвентаря, ЛКМ-ом стреляем. |
Re: Создаём свой FPS (first person shooter)
Вложений: 2
B)
|
Re: Создаём свой FPS (first person shooter)
Цитата:
а это, что за переменная такая... имя такое...или что-то особое? |
Часовой пояс GMT +4, время: 14:12. |
vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot