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() Код:
;инициализация графики |
Часовой пояс GMT +4, время: 19:41. |
vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot