Курсы
Регистрация
Методика
Оглавление
Для учащихся

Статьи
Инструменты
Контакты |
Урок
4. Добавим
интерактивности
4.1.
Увеличим
лабиринт..
Думается, что в
пределах одной комнаты любому игроку очень скоро станет немного тесновато,
поэтому пришло время подумать, как расширить границы нашего лабиринта.
Наиболее проницательные из Вас уже наверно догадались, что можно просто поменять
файл уровня, который, как Вы помните называется у нас level1.map.
Так и поступим:
11111111111111111111
1
1
1 1
1
1 11 11 11
1 1 1
1
1
1 1
11 111 111 1
1 1 1
1
1 1 1
1
11 11111111111111 11
1 1 1
1
1
1
1 1 1
1
11 111111111111 1111
1 11
1
1 1
1
11 1111111111 1
1
1
1
1
11111111111111111111 |
Заметьте, мы ранее зарезервировали дополнительное место
в массивах
Dim aMap(20,20)
и
Dim
aCube(20,20))
и теперь создали уровень 20 х 20 кубов точно используя размерность этих
массивов. Замените содержимое файла level1.map,
тем что приведено выше, сохраните файл и запустите программу. Результат, как
говорится, говорит сам за себя - теперь у нас уже не одна комната, а гораздо
больше. Этот пример, хоть и прост, но показывает следующее, как не меняя
программного кода, загружать различные уровни, всего лишь изменяя содержимое
файла уровня. Естественно, в дальнейшем все это будет усложнено, так как
единицами нам никогда не описать структуру уровней современных компьютерных игр.
Но еще раз напомню, что мы лишь в начале пути и движемся от простого к сложному.
А пока можете эксперимента ради менять уровень на свое
усмотрение. Для этого редактируйте наш файл level1.map
и затем запускайте нашу игру, чтоб побродить по только что созданному Вами
лабиринту.
Программа находится тут (room2.bb)
Если вам надоели однообразные текстуры стен нового лабиринта,
давайте внесем некоторые изменения и добавим еще две текстуры, разнообразив
некоторые коридоры. Заменим некоторые единицы на двойки и тройки в нашем
подопытном файле level1.map
11111111111111111111
1
1
3 1
1
1 11 11 11
3 1 1
1
1
1
3
22 222 222 1
1 1 1
3
1 1 1
1
22 22222222222222 22
1 1 1
1
3
1
1 1 1
1
11 111111111111 1111
1
22
2
1
2
2
22 2222222222
2
1
2
1
2
22223222222223222222 |
Поместим ниже приведенные текстуры в нашу папку с проектом для урока 4 и внесем
некоторые изменения в нашу программу. Прежде всего нам нужно обеспечить загрузку
дополнительных текстур в программу, а второе реализовать наложение данных
текстур на необходимые кубы.
Итак, загружаем дополнительные текстуры в том месте, где делали это для первой
нашей текстуры. Помеченная серым строка уже есть у нас в программе, стало быть
нам нужно добавить две новые.
Убедитесь, что вы сохранили текстуры в папку с проектом под именами
tex2.jpg и
tex3.jpg
tex =
LoadTexture ("tex1.jpg")
tex2 =
LoadTexture
("tex2.jpg")
tex3 = LoadTexture
("tex3.jpg")
|
Изменим наш код, ответственный за создание кубов и их текстурирование, как это
показано ниже:
For
j=1 To
mapSize
For
i=1 To
mapSize
If aMap(j,i) <> 0
aCube(j,i)=CreateCube()
Select
aMap(j,i)
Case
1
EntityTexture
aCube(j,i), tex
Case
2
EntityTexture
aCube(j,i), tex2
Case
3
EntityTexture
aCube(j,i), tex3
End
Select
ScaleEntity
aCube(j,i),1.5,1.5,1.5
PositionEntity
aCube(j,i), i*3, 0, j*3
EntityType
aCube(j,i), TypeWall
EndIf
Next
Next |
Для закрепления материала применим оператор выборки Select, который в
нашем случае выполняет проверку значений номеров кубов, находящихся в
массиве
aMap в соответствующих ячейках. Если найдено
значение 1 - то натягивается текстура tex, если
2 - tex2, если 3 - tex3.
Все остальное, за исключением условия if,
которое теперь проверяет ненулевые значения, осталось без изменения.
Внесите все эти изменения в программу и можете присвоить ей
другое имя, например room3.bb Походите по
лабиринту, убедитесь, что в игре появились новые текстуры и сами комнаты стали
более разнообразными.
Программа находится тут (room3.bb)
Думаю не стоит останавливаться на достигнутом и следующим
шагом создадим прозрачный объект стены, за которым будем видеть другую комнату.
Прежде всего, скопируйте следующую текстуру в папку с
проектом и сохраните ее под именем tex4.png.

Формат PNG используется
для того чтоб наиболее правильно работать с прозрачным фоном. В нашем случае
прозрачным считается черный фон. Заменим одну цифру в
структуре нашей комнаты, как это видно на следующем
рисунке (цифра 4):
11111111111111111111
1
1
3 1
1
1 11 11 11
3 1 1
1
1
1 3
22 242 222 1
1 1 1
3
1 1 1
1
22 22222222222222 22
1 1 1
1
3
1
1 1 1
1
11 111111111111 1111
1 22
2
1 2
2
22 2222222222 2
1
2
1
2
22223222222223222222 |
Также внесем некоторые изменения в нашу
программу:
tex4 = LoadTexture
("tex4.png",
4) |
Эта строка загружает текстуру в переменную
tex4
, но при этом
задействован еще и второй опциональный параметр функции
LoadTexture
, который отвечает за маску текстуры. Это
прямое указание программе о том, что используется текстура с цветом
прозрачности.
Теперь натягиваем текстуру на куб, в том месте, где программа встретит четверку
(4) в структуре нашего лабиринта. Чуть чуть меняем наш цикл построения кубов:
For
j=1 To
mapSize
For
i=1 To
mapSize
If aMap(j,i) <> 0
aCube(j,i)=CreateCube()
Select
aMap(j,i)
Case
1
EntityTexture
aCube(j,i), tex
Case
2
EntityTexture
aCube(j,i), tex2
Case
3
EntityTexture
aCube(j,i), tex3
Case
4
; вставлено
EntityTexture
aCube(j,i), tex4
; вставлено
End
Select
ScaleEntity
aCube(j,i),1.5,1.5,1.5
PositionEntity
aCube(j,i), i*3, 0, j*3
EntityType
aCube(j,i), TypeWall
EndIf
Next
Next |
Как видите в нашем цикле появилось еще две
строчки с проверкой на индекс 4, то есть на наш куб с прозрачной текстурой.
Вы можете сохранить программу под именем room4.bb,
при желании можете менять имена карт лабиринта для загрузки, например
level2.map, level3.map...(это на Ваше
усмотрение, безусловно после этого, вы должны учесть это в функции filein
= ReadFile("level1.map") заменив ее аргумент на нужное имя файла, который
вы грузите в игру).
Если мы запустим нашу игру, то в глаза сразу бросается наш прозрачный участок с
заборчиком. Выглядит это примерно так:

Поэкспериментируйте с уровнем, меняйте его
структуру, внося изменения в файл level1.map
или как Вы его назвали в последний раз. После чего мы плавно перейдем к
созданию дверей.
Программа находится тут (room4.bb)
4.2.
Создадим двери.
Какая игра обходится без дверей? Даже в нашем
маленьком лабиринте должны быть двери и не одна, а гораздо больше и поэтому нам
нужно как следует подготовиться для их создания и отображения в игре.
Для начала воспользуемся текстурой для двери с именем
door1.jpg.

Сохраните ее в ваш каталог с проектом. Рекомендуем создать новый файл
программы, например room5.bb естественно на
основе предыдущего примера. Изменения в программе будут весьма значительные. С
каждым разом она будет все более усложняться, поэтому лучше если у Вас останутся
все копии и предыдущих примеров в отдельных файлах. Итак если у Вас уже все
готово, и текстура сохранена в папку с проектом, то начнем производить некоторые
изменения в нашей программе.
Сперва пару слов, что же мы по сути хотим сделать. Так как двери у нас в карте
уровня будут располагаются как бы "вертикально" и "горизонтально", а в игре они
соответственно будут открываться вдоль осей X и
Z, то создадим две константы:
Const
HDOOR = 6, VDOOR = 7 |
Так как для наших дверей мы
назначили номера 6 и 7, то и расположим на карте двери с этими номерами:
11111111111111111111
1
1
3 1
1
1 11 7 11611
3 1 1
1
1 7
1 3
2262426222 7 1
1 1 1
3
1 1 1
1
22622222222222222622
1 1 1
1
3 7 7
1
1 1 1
1
11611111111111161111
1 22
2
1 2
2
2262222222222 2
1
2
1
2
22223222222223222222 |
Как мы видим, "горизонтальные"
двери у нас обозначены цифрами 6, а "вертикальные" цифрами 7. Сохраните эту
карту в файл, к примеру level5.map
и поменяйте аргумент у функции, открывающей этот файл с диска:
filein
= ReadFile("level5.map") |
Также загрузим нашу текстуру для
двери:
tex5 =
LoadTexture
("door1.jpg") |
В главном цикле произойдут
следующие изменения:
For
j=1 To
mapSize
For
i=1 To
mapSize
If aMap(j,i) <> 0
aCube(j,i)=CreateCube()
Select
aMap(j,i)
Case
1
EntityTexture
aCube(j,i), tex
ScaleEntity
aCube(j,i),1.5,1.5,1.5
Case
2
EntityTexture
aCube(j,i), tex2
ScaleEntity
aCube(j,i),1.5,1.5,1.5
Case
3
EntityTexture
aCube(j,i), tex3
ScaleEntity
aCube(j,i),1.5,1.5,1.5
Case
4
EntityTexture
aCube(j,i), tex4
ScaleEntity
aCube(j,i),1.5,1.5,1.5
Case
HDOOR
EntityTexture
aCube(j,i), tex5
ScaleEntity
aCube(j,i),1.5,1.5,0.2
Case
VDOOR
EntityTexture
aCube(j,i), tex5
ScaleEntity
aCube(j,i),0.2,1.5,1.5
End
Select
PositionEntity
aCube(j,i), i*3, 0, j*3
EntityType
aCube(j,i), TypeWall
EndIf
Next
Next |
Что же произошло в наших
вложенных циклах по созданию кубов? Во первых добавились два CASE
для "вертикальной" и "горизонтальной" дверей. Функция
ScaleEntity
теперь определяется для каждого типа
куба, т.е. для стен и для дверей. Это сделано потому, что мы меняем масштаб
сечения двери для того чтоб они выглядели более правдоподобно. То есть
"вертикальные" VDOOR двери сужены по оси X
(
ScaleEntity
aCube(j,i),1.5,1.5,0.2)
, а "горизонтальные" HDOOR
двери сужены по оси Z.
Вносим все изменения и запускаем программу. Результат должен быть примерно
следующим:

Программа находится тут (room5.bb)
Отчетливо видно, что двери утоплены в косяки и отличаются от обычных стен.
Побегав по лабиринту, Вы можете убедиться, что теперь вы отрезаны от остальной
части лабиринта! Двери все плотно закрыты и попасть в другие коридоры теперь
попросту невозможно. Ничего не остается делать, как заставить наши двери
открываться и закрываться. Но для начала определимся с условиями взаимодействия
с дверями.
4.3.
Определение
взаимодействия с дверью.
Мы уже раньше познакомились с методикой определения столкновения объектов.
Воспользуемся ей сейчас для определения коллизий с дверьми нашего лабиринта. Но
перед этим сделаем некоторые заготовки для следующего пункта нашего урока, где
будет рассмотрен принцип открывания и закрывания дверей. Чтоб знать с какой
именно дверью происходит взаимодействие, кроме информации о том, что этот куб на
самом деле является дверью, нужно еще знать, как она должна смещаться. Для
"вертикальных" смещение идет вдоль оси
Z, для "горизонтальных" вдоль оси Х. Кроме того, дверь еще
должна "знать" смещение на которое она открыта в данный момент и время задержки
после того как она откроется. Кроме этого в любой момент времени статус двери
тоже должен быть однозначно определен. Чтоб не создавать кучу переменных для
каждой из дверей, создадим один объект (список) отвечающий за двери, а затем
объявим массив этих объектов. Вот как это будет выглядеть на языке
BlitzBasic (предварительно
сохраните наш проект уже под именем room6.bb):
Const
OPENING = 10, OPENED = 20, CLOSING = 30, CLOSED = 40
Const
TypeDoor = 3
Global ALL_DOORS =
50
Type
doorsinfo
Field
ix%,iz%
Field
oDoor%
Field
status%
Field
pos#
Field
doortype%
Field
ticks#
End
Type
Dim
Doors.doorsinfo( ALL_DOORS )
Global
NumDoors = 0
Collisions
TypePlayer, TypeDoor, 2, 3
|
Эта наша заготовка для
манипулирования дверьми.
Первая строка констант задает
режимы в которых может находиться наша дверь. Она может
находиться в четырех состояниях: открываться (OPENING ),
быть открытой (OPENED ), закрываться (CLOSING
) и быть закрытой (CLOSED ).
Во второй строке определяем тип двери. Мы уже так делали раньше для типа стены.
Глобальная переменная
ALL_DOORS
будет содержать число
дверей, которые будет поддерживать наш лабиринт. На самом деле, мы будет
манипулировать столькими дверьми, сколько их найдет наша программа, считывая
карту уровня из файла.
Далее идет наша структура doorsinfo
в которой мы предусмотрели следующие поля:
Field ix%,iz%
Field oDoor%
Field status%
Field pos#
Field doortype%
Field ticks# |
Текущие
координаты двери (вертикальная координата нас не волнует)
Сам объект двери - наш куб
Статус двери или режим .см. выше
Смещение двери в текущий момент (при закрытии/открытии)
Тип двери ("вертикальная","горизонтальная")
таймер на закрытие двери |
Вслед за нашей структурой идет
объявление массива из 50 элементов (для запаса). Каждый элемент массива и есть
экземпляр нашей структуры. То есть в каждой ячейке массива можно разместить
описание одной из дверей нашего лабиринта. И наконец завершает этот кусочек кода
описаний глобальная переменная
NumDoors,
которая после считывания файла с картой будет хранить реальное количество
дверей, которое у нас есть.
Не забудем также описать коллизию уже известной нам функцией -
Collisions,
которая
определит столкновения игрока с дверьми. Если мы этого не
сделаем, наш персонаж будет попросту проходить сквозь стены.
Снова поменяем наш многострадальный цикл, где создаются стены и двери, теперь он
примет следующий вид:
For
j=1 To
mapSize
For
i=1 To
mapSize
If aMap(j,i) <> 0
aCube(j,i)=CreateCube()
Select
aMap(j,i)
Case
1
EntityTexture
aCube(j,i), tex
ScaleEntity
aCube(j,i),1.5,1.5,1.5
EntityType
aCube(j,i), TypeWall
Case
2
EntityTexture
aCube(j,i), tex2
ScaleEntity
aCube(j,i),1.5,1.5,1.5
EntityType
aCube(j,i), TypeWall
Case
3
EntityTexture
aCube(j,i), tex3
ScaleEntity
aCube(j,i),1.5,1.5,1.5
EntityType
aCube(j,i), TypeWall
Case
4
EntityTexture
aCube(j,i), tex4
ScaleEntity
aCube(j,i),1.5,1.5,1.5
EntityType
aCube(j,i), TypeWall
Case
HDOOR
EntityTexture
aCube(j,i), tex5
ScaleEntity
aCube(j,i),1.5,1.5,0.2
EntityType
aCube(j,i), TypeDoor
NumDoors = NumDoors + 1
Doors(NumDoors) = New doorsinfo
Doors(NumDoors)\oDoor = aCube(j,i)
Doors(NumDoors)\ix = i*3
Doors(NumDoors)\iz = j*3
Doors(NumDoors)\status = CLOSED
Doors(NumDoors)\pos = 0
Doors(NumDoors)\doortype = HDOOR
Case
VDOOR
EntityTexture
aCube(j,i), tex5
ScaleEntity
aCube(j,i),0.2,1.5,1.5
EntityType
aCube(j,i), TypeDoor
NumDoors = NumDoors + 1
Doors(NumDoors) = New doorsinfo
Doors(NumDoors)\oDoor = aCube(j,i)
Doors(NumDoors)\ix = i*3
Doors(NumDoors)\iz = j*3
Doors(NumDoors)\status = CLOSED
Doors(NumDoors)\pos = 0
Doors(NumDoors)\doortype = VDOOR
End
Select
PositionEntity
aCube(j,i), i*3, 0, j*3
EndIf
Next
Next |
Из данного кода видно, как
заполняется массив с описанием объекта дверей. При этом после нахождения каждой
новой двери увеличивается их счетчик, за который несет ответственность
переменная
NumDoors.
Если Вы заметили, изначально всем дверям присваивается статус
CLOSED. то есть все наши двери
после загрузки уровня полагаются закрытыми
(Doors(NumDoors)\status
= CLOSED) и их позиция
смещения приравнивается нулю
(Doors(NumDoors)\pos =
0). Всем кубам
символизирующим наши двери присваивается тип
TypeDoor. Вот
собственно и все. Ничего сложного как Вы видите. Затем в следующем пункте, мы
уже напишем функцию, которая будет закрывать и открывать двери, а сейчас
определимся со столкновениями игрока и двери. Сделаем следующее - когда игрок
наталкивается на дверь, она должна каким-то образом сообщить нам о себе.
Модифицируем наш ГЛАВНЫЙ цикл таким образом, как показано в следующем примере:
While
Not
KeyDown( 1 )
If
KeyDown(200)
MoveEntity
player, 0, 0, 0.2
If
KeyDown(208)
MoveEntity
player, 0, 0, -0.2
If
KeyDown(203)
TurnEntity
player, 0, 2, 0
If
KeyDown(205)
TurnEntity
player, 0, -2, 0
entitywall% =
EntityCollided(Player,TypeWall)
entitydoor% =
EntityCollided(Player,TypeDoor)
UpdateWorld
RenderWorld
Text
12,10,
"Entity Wall: "+Str(entitywall)
Text
12,20,
"Entity Door: "+Str(entitydoor)
Flip
Wend |
Думаю у Вас не должны вызвать затруднений
вышеперечисленные изменения главного цикла. Команда
EntityCollided
вызывается два раза для проверки столкновений объектов
типов: игрок- стена и игрок-дверь. Команда
Text
выводит результат
возвращаемый этой функции для первого и второго типов столкновений на экран в
точки с координатами 12,10 и 12,20 соответственно.
Запускаем наше приложение и видим следующую картину: при столкновении со стенами
у нас выводится идентификатор текущей стены, при столкновении с дверьми -
идентификатор текущей двери. Бывают случаи и двойного столкновения. Результаты
отображаются в левом верхнем углу нашего игрового экрана.
BlitzBasic сам назначает эти
идентификаторы и на об этом беспокоится не стоит. Главное что мы определились -
как находить момент столкновения с дверью. Теперь нужно научиться реагировать на
столкновение и открывать дверь.
4.4.
Открывание
и закрывание двери.
Для выполнения этих действий мы напишем солидную функцию, поэтому рекомендуем
сохранить наш проект под именем
room7.bb и почитать
теоретическую часть нашего подхода к открыванию и закрыванию дверей.
А теория такова:
Когда мы сталкиваемся с дверью, функция коллизий игрока и двери возвращает
значение идентификатора нашего куба с которым произошло столкновение. Мы должны
написать функцию, которая получает этот идентификатор и осуществляет его поиск в
массиве структур описывающих объект двери т.е в массиве
Doors.
Как только нужная дверь найдена ее статусу присваивается
значение - "открывается" (OPENING)
и вторая функция которую мы напишем должна отображать
процесс открывания - смещения двери до некоторой позиции. Когда дверь достигает
позиции, когда она считается открытой, ей присваивается статус
OPENED и тут же активируется таймер,
который должен ждать определенное время до начала закрывания этой двери. Далее
дверь начинает закрываться и ей присваивается статус
CLOSING. Достигнув нулевого смещения,
дверь получает статус
CLOSED т.е. возвращается в свое
исходное состояние.
Функция, которая отвечает за начальную активацию двери приведена ниже:
Function
CollideDoors( door )
For i=1
To NumDoors
If
Str(Doors(i)\oDoor) =
Str(door)
Doors(i)\status =
OPENING
EndIf
Next
End Function |
Наша функция
CollideDoors
получает код объекта с которым мы столкнулись и перебирает все двери в
массиве дверей. Найдя нужную, присваивает ее статусу значение - "открывается" (OPENING).
Это реализация самой функции и мы должны поместить ее в самый конец
программы. Отступите от ключевого слова END
несколько строчек и добавьте код приведенный выше. Сам вызов этой функции
разместите в главном цикле программы, т.е так:
While
Not
KeyDown( 1 )
If
KeyDown(200)
MoveEntity
player, 0, 0, 0.2
If
KeyDown(208)
MoveEntity
player, 0, 0, -0.2
If
KeyDown(203)
TurnEntity
player, 0, 2, 0
If
KeyDown(205)
TurnEntity
player, 0, -2, 0
entitywall% =
EntityCollided(Player,TypeWall)
entitydoor% =
EntityCollided(Player,TypeDoor)
CollideDoors(entitydoor)
; добавленная строка
UpdateWorld
RenderWorld
Text
12,10,
"Entity Wall: "+Str(entitywall)
Text
12,20,
"Entity Door: "+Str(entitydoor)
Flip
Wend |
Теперь, согласно выше расписанного алгоритма поведения дверей, напишем
как можно реализовать на Blitz3D открывание и
закрывание дверей. Кроме этого наша функция должна корректно обновлять состояние
всех дверей, которые были активированы нашим
персонажем и следить за их статусом. Итак, вот эта функция:
Function
UpdateDoors()
If
current + time_delay <
MilliSecs()
For
i=1
To
NumDoors
; Обработка открывания
If
Doors(i)\status = OPENING
Doors(i)\pos = Doors(i)\pos + 0.1
If
Doors(i)\doortype = VDOOR
PositionEntity
Doors(i)\oDoor, Doors(i)\ix, 0, Doors(i)\iz + Doors(i)\pos
EndIf
If
Doors(i)\doortype = HDOOR
PositionEntity Doors(i)\oDoor,
Doors(i)\ix + Doors(i)\pos, 0, Doors(i)\iz
EndIf
If
Doors(i)\pos > 2.8
Doors(i)\pos = 2.8
Doors(i)\status = OPENED
Doors(i)\ticks =
MilliSecs()+3000
EndIf
EndIf
; Обработка состояния открыта
If
Doors(i)\ticks <
MilliSecs()
And
Doors(i)\status = OPENED
Then
If
Doors(i)\doortype = VDOOR
Doors(i)\status =
CLOSING
EndIf
If
Doors(i)\doortype = HDOOR
Doors(i)\status = CLOSING
EndIf
EndIf
; Обработка закрывания
If
Doors(i)\status = CLOSING
Doors(i)\pos = Doors(i)\pos - 0.1
If
Doors(i)\pos < 0
Doors(i)\pos = 0
Doors(i)\status = CLOSED
EndIf
If
Doors(i)\doortype = VDOOR
PositionEntity
Doors(i)\oDoor, Doors(i)\ix, 0, Doors(i)\iz + Doors(i)\pos
EndIf
If
Doors(i)\doortype = HDOOR
PositionEntity
Doors(i)\oDoor, Doors(i)\ix + Doors(i)\pos, 0, Doors(i)\iz
EndIf
EndIf
Next
EndIf
current =
MilliSecs()
End
Function |
Если даже данный код и вызывает у Вас затруднения, постарайтесь
перечитать его несколько раз, вдумываясь в каждую строчку. Вскоре все станет
ясным. В зависимости от типа двери (HDOOR или
VDOOR) функция производит
смещение куба двери вдоль оси Х или Z. В открытом
состоянии дверь пребывает 3 секунды (3000 миллисекунд).
Функция постоянно анализирует состояние двери и
действует согласно ему. Сколько бы мы дверей не активировали, все они будут
корректно обработаны этой функцией.
Добавьте ее в конец проекта т.е. после нашей функции
CollideDoors, которую мы добавили до
этого. Сам вызов функции
UpdateDoors
вы должны поместить в главный цикл программы.
Прямо после строчки с вызовом
CollideDoors поместите вызов
UpdateDoors
, т.е. так:
CollideDoors(entitydoor)
UpdateDoors() |
Теперь все должно работать!

Программа находится тут (room6.bb)
Походите по лабиринту - пооткрываете двери,
последите как они закрываются через некоторое время. Если у Вас возникли
трудности в запуски программы и нет времени выискивать ошибки Вашего набора, то
полный код программы, вы можете скачать тут
Еще раз проверьте что все текстуры сохранены в
нужной папке, где находится и файл программы.
4.5.
Рычаги
открывающие двери. Взаимоувязка объектов.
Давайте на закуску создадим маленькую
кнопочку, которая будет открывать одну из дверей. Чуть позже, когда мы научимся
создавать модели, мы заменим кнопку на модель рычага, а сейчас ограничимся
кубической кнопкой.
Итак, наша цель научится использовать мышь для активации
объектов в игре. Представьте себе, что ваш герой находится в лабиринте с твердым
намереньем проникнуть в какую-то потайную комнату, но она открывается только
после активации специального рычага. Мы чуть упростим задачу. Кнопка будет
находиться прямо возле двери и дверь все равно будет открываться и без нее, но
пример покажет Вам как взаимодействовать с разнообразными объектами в игровой
сцене и добиваться отклика от других объектов. По сути это взаимоувязка
объектов.
Еще раз оговоримся, что реализация эадуманного будут
чрезвычайно проста и не претендует на рациональность алгоритма, главное условие
простота!
Для начала создадим объект нашей кнопки. Как было ранее замечено,
это обычный куб. Мы даже не будем его текстурировать, чтоб его белый цвет хорошо
был заметен в нашем лабиринте и мы не промахнулись по нему мышкой :)
Создаем куб - кнопку:
; Lever
lever=CreateCube()
ScaleEntity
lever, 0.1, 0.1, 0.1
PositionEntity
lever, 12, 0.2, 19.5
EntityPickMode
lever, 2
NameEntity
lever,
"LEVER1" |
В данном описании участвуют две
неизвестные Вами команды:
EntityPickMode -
устанавливает режим взаимодействия объекта с мышью и
получает два параметра:
entity
- идентификатор (handle)
объекта
pick_geometry - тип геометрии используемой для
выбора мышью:
0: не выбирается мышью
1: сфера
2: полигон
3: куб (короб)
В нашем примере мы использовали полигон, так как это рекомендовано
разработчиками Blitz3D для взаимодействия с объектом
типа mesh. В дальнейшем мы будем применять это и к
моделям.
NameEntity -
устанавливает имя для объекта.
Таки образом мы указали программе, что будем взаимодействовать с объектом куб
(нашей кнопкой) при помощи мыши по полигональной геометрии и назначили этой
кнопке имя
LEVER1.
Теперь заставим открываться какую-то из дверей в нашем
лабиринте. Чтоб далеко не ходить, это будет дверь с индексом 4 в массиве дверей.
Вставьте следующий код в главный цикл нашей программы:
If MouseHit(1) =
True
ent =
CameraPick(cam,MouseX(),MouseY())
If
ent
If
EntityDistance(ent,
player) < 4.5
name$ =
EntityName(ent)
If
name =
"LEVER1"
Doors(4)\status = OPENING
EndIf
EndIf
EndIf
EndIf |
Для того чтоб определить был ли выбран какой-то
объект в сцене мышью, нужно снова обратиться к встроенной библиотеке функций
Blitz3D.
Функция
MouseHit(1)
как раз и возвращает истину если была нажата левая
кнопка мыши. Далее функция
CameraPick(cam,MouseX(),MouseX())
выбирает объект, в координатах, возвращаемых функциями
MouseX
и MouseX.
Заметим, что это не трехмерные координаты, а координаты
плоскости экрана (viewport координаты).
CameraPick возвращает нам
идентификатор объекта, который оказался под нашей мышью в момент щелчка. Далее
этот идентификатор проверяется на ненулевое значение (If
ent) потому что с таким же успехом мы могли щелкнуть на
потолке, ролу и стенах, а может быть вообще в таком месте, где нет никакого
объекта. В дальнейшем работа с таким несуществующим объектом привела бы к ошибке
выполнения программы. Еще одна проверка осуществляется у нас функцией
EntityDistance. Суть сводится к тому,
чтоб блокировать нажатие на нашу кнопку с расстояния превышающего некоторую
фиксированную длину (в нашем случае это - 4.5) По крайней мене
наш персонаж еще не дорос до того чтоб пользоваться магией телекинеза и
взаимодействовать с объектами на большом удалении.
Далее функция
EntityName
возвращает
нам имя объекта на котором мы щелкнули мышью. Помнится мы присваивали
имя только нашей кнопке и потому для остальных объектов будет возвращена
пустая строка. После проверки, что имя объекта действительно
"LEVER1"
то есть если мы действительно щелкнули на нашу кнопку,
четвертой двери в массиве
Doors(4) присваивается статус
OPENING. Вы конечно догадались,
что наша собственная функция UpdateDoors тут же
начнет ее открывать, что мы и увидим на экране.

В последствии мы научимся открывать двери с помощью ключей и блокировать их от
обычного открывания.
Программа находится тут (room7.bb)
Что будет далее:
В следующем уроке мы научимся создавать модели игровых объектов
и помещать их в игровую сцену
|
|