Руководство по созданию 3D-игр.
Для более детальной и полной информации - рекомендую обратиться к
первоисточникам. А это компания "Медиахаус" и "Руководство пользователя",
которое вы получаете в коробке с лицензионным DarkBASIC-ом.
III. ИГРА
13.Столкновения с окружающими предметами
Цель урока: показать, как оживить 3D-экран путем добавления окружающих предметов.
В этом уроке мы добавим несколько колонн к нашему ландшафту. Мы также применим способ математического столкновения для расчета столкновений с этими колоннами.
rem
Создаем окружающие объекты
load
image "cottag02.bmp",300
t=300
For
x = 1 to 9
For
z = 1 to 9
Make object cube t,100
Scale object t,100,600,100
y = get ground height(1,x*1000,z*1000)
position object t,x*1000,y+275,z*1000
texture object t,300
scale object texture t,1,-6
inc t
next
z
next
x
Эта часть кода вводится до основного цикла, но после того, как создана матрица. Загружаем изображение, чтобы наложить текстуру на колонны. Переменной t присваиваем значение 300. Это номер первого объекта-колонны. Чтобы создать и расположить каждый из этих объектов, мы используем вложенный цикл. Нежелательно, чтобы колонны помещались на краях матрицы, поэтому вместо того, чтобы выполнять цикл от 0 до 10, выполняем его от 1 до 9. Цикл по оси "x" используется для размещения объектов на матрице. Цикл по оси "z" -для размещения объектов сверху и снизу матрицы. Внутри цикла "z" мы создаем объект-куб с помощью переменной t. Задаем значение масштабирования куба 600 по оси Y, чтобы получить высокую колонну. Затем получаем значение высоты ячейки матрицы в новых координатах X и Z. Заметьте, что для получения координат X и Z мы умножаем переменные цикла на 1000, благодаря чему мы сможем поместить колонны на матрице через равные интервалы. Затем мы помещаем объект в 3D-сцене. К величине Y добавляем значение 275. Это почти половина высоты объекта. Эта операция приподнимает его по оси Y, так как видимое основание объекта находится в его центре. Мы не поднимаем объект на все 300 единиц только потому, что хотим, чтобы он "врастал" в землю. Затем накладываем декоративную текстуру на объект. Текстура также масштабируется, чтобы полностью покрыть объект. Для этого используем отрицательное значение 6, а не положительное. Это обращение необходимо, чтобы изображение накладываемой текстуры было обычным, а не перевернутым. С помощью этой уловки можно переворачивать текстуру. Затем мы увеличиваем значение переменной "t", чтобы начать создание следующего объекта.
Rem
Проверка столкновения с декоративными объектами
Function
DecoCollide(X#,Y#,Z#)
for
u = 1 to 9
for v = 1 to 9
if X#>u*1000-60
if X#<u*1000+60
if Z#>v*1000-60
if Z#<v*1000+60
if Y# < Get ground height(1,u*1000,v*1000)+575
Collide=1
Exitfunction Collide
endif
endif
endif
endif
endif
next v
next
u
Collide=0
Endfunction
Collide
Функции являются важной частью программирования. Их можно использовать для создания многих полезных и пригодных для повторного применения частей кода. Функция определяется после основного цикла, при этом используются ключевые слова "Function" и "EndFunction". После ключевого слова "Function" помещается имя создаваемой функции, а непосредственно за ним ставятся открывающая и закрывающая скобки. Внутри скобок помещаются переменные, с которыми работает код, находящийся в теле функции. Функция заканчивается ключевым словом "EndFunction". Чтобы функция возвращала какое-либо значение, необходимо поместить переменную, содержащую это значение, после ключевого слова "EndFunction". В середине функции находится команда "ExitFunction" с последующей переменной "Collide". Чтобы прервать выполнение функции, не дожидаясь выполнения остального кода, достаточно ввести команду "ExitFunction" для выхода из функции и возврата результата.
Function
DecoCollide(X#,Y#,Z#)
Обратите внимание на переменные "X#", "Y#" и "Z#" внутри функции. Это совсем не те переменные, которые используются для указания координат игрока. Хотя имена переменных совпадают, все переменные, объявленные в функции, называются локальными и используются только внутри ее. Любые переменные, определенные вне функции, недоступны внутри ее. Все переменные, создаваемые внутри функции, уничтожаются при выходе из нее. Функция может использовать только значения переменных, указанных в скобках при определении функции. Посмотрите на команду "Object Angle Y(10)" - это функция, встроенная в DarkBASIC. Ее параметр 10, а возвращаемое значение - угол по оси Y для объекта с номером 10. Если какой-либо код вы применяете в программе многократно, то, возможно, его следует вводить в программу как функцию. В этом уроке мы используем функцию "DecoCollide()" для проверки столкновения игрока, монстра и ракеты со всеми декоративными колоннами путем ввода в функцию координат X, Y и Z объекта.
for u = 1 to 9
for v = 1 to 9
if X#>u*1000-60
if X#<u*1000+60
if Z#>v*1000-60
if Z#<v*1000+60
if Y# < Get ground height(1,u*1000,v*1000)+575
Collide=1
Exitfunction Collide
endif
endif
endif
endif
endif
next v
next
u
Collide=0
Два приведенных выше вложенных цикла похожи на те, что мы использовали при создании колонн. Колонны имеют в основании размеры 100х100 единиц, высоту 600 единиц, и заглублены в землю на 25 единиц. Так как колонны расположены на матрице через каждые 1000 единиц, мы используем циклы для вычисления столкновения с каждой колонной. Для проверки значений координат X# и Z# каждой колонны используются вложенные выражения сравнения "If". Мы добавляем и вычитаем по 60 единиц из переменных цикла, умноженных на 1000. На самом деле мы проверяем на 10 единиц больше, чем реальные габариты колонн, благодаря чему игрок не застревает между колонны. Первая колонна проверяется в координатах со значением 1000, если игрок находится в координатах (1059, 50, 10059), это дальний угол колонны. Проверяются следующие параметры. Значение координаты по оси X больше 940? Если да, то продолжаем последовательно выполнять вложенные сравнения. Если значение координаты по оси X меньше 1060, значение координаты игрока по оси Z меньше 1060, значение координаты игрока по оси Y меньше, чем высота колонны плюс 575 единиц, то в таком случае мы имеем столкновение. Значение переменной "Collide" устанавливается равным 1. Другие колонны проверять не требуется - искомый ответ найден. Для выхода из функции и возврата значения переменной Collide используется команда ExitFunction. Теперь мы знаем, что игрок сталкивается с колонной, поэтому можем остановить его дальнейшее движение в направлении колонны. Если столкновение не обнаружено, значение Collide устанавливается равным 0 и функция заканчивается при помощи команды "EndFunction".
rem
Обнаружение столкновений
if
DecoCollide(X#,Y#,Z#) = 1
X#=oldX#
Y#=oldY#
Z#=oldZ#
Endif
Эта часть кода помещается в основном цикле, в части обработки ввода данных. Значения позиции игрока передаются в функцию, и в том случае, если она возвращает значение 1, координаты игрока возвращаются к тем, что были у него до начала движения.
if DecoCollide(bX#,bY#,bZ#) = 1 then BulletLife = 0
if DecoCollide(MbX#,MbY#,MbZ#) = 1 then MonsterBulletLife = 0
Эти две строки кода помещаются в подпрограммах "ShootBullet" и "MonsterShootBullet" для проверки попадания ракета в одну из колонн. Если обнаружено столкновение, ракета прекращает свое существование.
mX#=rnd(10000)
mZ#=rnd(10000)
mY#= get ground height(1,mX#,mZ#)
If DecoCollide(mX#,mY#,mZ#) = 1
mX#=X#
mY#=Y#
mZ#=Z#
endif
X#=rnd(10000)
Z#=rnd(10000)
Y#= get ground height(1,X#,Z#)
If DecoCollide(X#,Y#,Z#) = 1
X#=mX#
Y#=mY#
Z#=mZ#
endif
Эти две части кода помещаются в подпрограммах "PlacePlayer" и "PlaceMonster". Функции "DecoCollide" проверяют, не находятся ли новые координаты игрока и монстра внутри колонн. Если функция возвращает значение 1, то новые координаты игрока и монстра устанавливаются идентичными друг другу, что приводит к возобновлению цикла по созданию новых случайных координат игрока и монстра до тех пор, пока не будут созданы координаты, находящиеся за пределами колонны.
14. Монстр выходит на охоту
Цель урока: показать, как устроить охоту монстра на игрока.
В этом уроке мы добавим монстру искусственный интеллект (ИИ), позволяющий ему вести охоту на игрока, лучше стрелять и определять, не прячется ли игрок за преградой или рельефом местности.
Rem
Загружаем мишень
Load
object "idle.x",3
Append
Object "Walk.x",3,21
Эти строки кода вводятся до начала основного цикла. Команда "Append Object" загружает данные анимации из другого файла и добавляет их к существующему объекту, не загружая новый. Объекты в каждом из файлов, которые вы собираетесь "склеить", должны быть идентичными, в противном случае команда будет выполнена неправильно. Команда использует три параметра. Первый параметр - имя присоединяемого файла. Второй параметр - номер объекта, к которому вы хотите присоединить данные анимации. Третий параметр - номер кадра, в который требуется добавить анимацию. Если номер кадра больше, чем общее число кадров в первой последовательности анимации, команда будет выполнена неправильно.
if DecoCollide(MbX#,MbY#,MbZ#) = 1
MonsterBulletLife = 0
BulletAvoidDeco = BulletAvoidDeco + 2
Endif
if MbY# < Get Ground height(1,MbX#,MbZ#)
MonsterBulletLife=0
ShootUp=3
endif
Переменные "ShootUp" и "BulletAvoidDeco" добавлены в подпрограмму "MonsterShootBullet". Переменной "ShootUp" присваивается значение 3, если ракета монстра попала в "землю". Эта переменная будет использоваться для изменения угла, под которым стреляет монстр, чтобы ракета огибала рельеф местности. Переменная "BulletAvoidDeco" увеличивается каждый раз на 2, когда ракета попадает в одну из колонн. Эта переменная изменяет угол стрельбы монстра, чтобы ракета огибала преграды. Хотя монстр в этом случае стреляет не прямо в игрока, ракета имеет возможность самонаведения после того, как она была выпущена.
Rem Простой ИИ для самонаводящейся ракеты монстра
MonsterAI:
Point object 3,X#,Y#,Z#
If AvoidDeco >0
mA# = Object Angle Y(3)
Dec AvoidDeco
Yrotate object 3,WrapValue(mA#+AvoidDeco*60)
endif
Rem Перемещаем монстра в новое место
Position Object 3,mX#,mY#,mZ#
Rem Проверяем расстояние до игрока
PDist=Sqrt((mX# - X#)^2 + (mY#+25 - Y#)^2 + (mZ# - Z#)^2)
Rem Если игрок в зоне видимости, выпускаем ракету
if PDist<1500
if MonsterBulletLife=0 or MonsterBulletLife < 500-Pdist/10
Point object 3,X#,Y#-25,Z#
If BulletAvoidDeco > 0
CornerAim = Rnd(1)
mA# = object angle Y(3)
if CornerAim = 0 then Yrotate Object 3,WrapValue(mA#+BulletAvoidDeco*10)
if CornerAim = 1 then Yrotate Object 3,WrapValue(mA#+BulletAvoidDeco*-16)
Dec BulletAvoidDeco
Endif
If ShootUp > 0
mA# = object angle X(3)
XRotate Object 3,WrapValue(mA#+ShootUp*-8)
Dec ShootUp
Endif
Position object 102,mX#,mY#+25,mZ#
Set object to object orientation 102,3
MonsterBulletLife =500
show object 102
Loop sound 102
Rem Воспроизводим цикл анимации объекта
Loop Object 3,0,20
Endif
Endif
if PDist>1000
Rem Сохраняем старое положение
OldmX# = mX#
OldmZ# = mZ#
OldmY# = mY#
Rem Воспроизводим анимацию ходьбы
Loop Object 3,21,46
Rem Переместить монстра
Move Object 3,7
Rem Получаем новую позицию
mX# = Object Position X(3)
mZ# = Object Position Z(3)
mY# = Get Ground Height(1,mX#,mZ#)
Rem Проверка столкновения с колоннами
If DecoCollide(mX#,mY#,mZ#) = 1 and AvoidDeco = 0
mX# = OldmX#
mZ# = OldmZ#
mY# = OldmY#
AvoidDeco = 3
Endif
Endif
Return
В подпрограмме "MonsterAI" мы добавили код, необходимый для того, чтобы монстр обходил преграды, а также код, меняющий угол стрельбы монстра, в случае, если две переменные, описанные выше, имеют значение отличное от 0.
If AvoidDeco >0
mA# = Object Angle Y(3)
Dec AvoidDeco
Yrotate object 3,WrapValue(mA#+AvoidDeco*60)
endif
Если значение переменной "AvoidDeco" больше 0, ее значение уменьшается на 1, а также изменяются углы поворота монстра путем добавления величины "AvoidDeco", умноженной на 60, к текущим углам поворота монстра. Когда монстр натыкается на колонну, переменная "AvoidDeco" принимает значение 3. При каждом проходе цикла монстр перемещается сначала на 180, затем на 120, а потом на 60 градусов. Это заставляет монстра полукругом отойти назад от того места, где он "уткнулся" в преграду. Эта часть кода показывает, как монстр перемещается между преградами.
If BulletAvoidDeco > 0
CornerAim = Rnd(1)
mA# = object angle Y(3)
if CornerAim = 0 then Yrotate Object 3,WrapValue(mA#+BulletAvoidDeco*10)
if CornerAim = 1 then Yrotate Object 3,WrapValue(mA#+BulletAvoidDeco*-10)
Dec BulletAvoidDeco
Endif
Логика проверки переменной "BulletAvoidDeco" аналогична той, что описана выше, только величина изменения углов полета ракеты не такая большая. Когда ракета первый раз попадает в колонну, переменная "BulletAvoidDeco" принимает значение 2. Если ракета снова попадает в колонну, к этому значению прибавляется 2. Это делается до тех пор, пока ракета не сможет обогнуть преграду. Ракета выстреливается вправо или влево случайным образом, что увеличивает шансы того, что она пролетит мимо преграды.
if PDist>1000
Rem Сохраняем старое положение
OldmX# = mX#
OldmZ# = mZ#
OldmY# = mY#
Rem Воспроизводим анимацию движения
Loop Object 3,21,46
Rem Перемещаем монстра
Move Object 3,7
Rem Получаем новое положение
mX# = Object Position X(3)
mZ# = Object Position Z(3)
mY# = Get Ground Height(1,mX#,mZ#)
Rem Проверка столкновения с преградой
If DecoCollide(mX#,mY#,mZ#) = 1 and AvoidDeco = 0
mX# = OldmX#
mZ# = OldmZ#
mY# = OldmY#
AvoidDeco = 3
Endif
Endif
Эта часть кода управляет перемещением монстра. Сначала мы сохраняем предыдущие координаты монстра, прежде чем начать перемещать его. Затем воспроизводим цикл анимации ходьбы при помощи команды "Loop Object". В данном случае у этой команды на два параметра больше, чем в предыдущем. Два параметра после номера объекта - это диапазон воспроизводимых кадров. Это те кадры, которые мы добавили к объекту ранее. Анимация воспроизводится в цикле, пока мы перемещаем объект. Потом мы сохраняем новые координаты монстра и проверяем, не сталкивается ли он с препятствиями. Если столкновение происходит, мы не меняем значение координат монстра и устанавливаем значение "AvoidDeco" равным 3, заставляя монстра полукругом обойти объект.
If ShootUp > 0
mA# = object angle X(3)
XRotate Object 3,WrapValue(mA#+ShootUp*-8)
Dec ShootUp
Endif
Выражение сравнения для переменной "ShootUp" также добавлено к подпрограмме "MonsterAI". Если ракета монстра попадает в "землю", то переменная "ShootUp" принимает значение 3, а угол выстрела ракеты монстра увеличивается на 24 градуса. При каждом проходе цикла угол уменьшается на 8 градусов. Это дает возможность ракете монстра лететь по наилучшей траектории в направлении игрока.
Как видите, мы не проверяем, вышел ли монстр за пределы матрицы. В этом нет необходимости, так как монстр всегда движется навстречу игроку, а игрок не может выйти за пределы матрицы. Эти несколько строк кода должны сделать игру более интересной и наделить монстра определенным характером в процессе его охоты на игрока.
В конце концов у вас должно получиться
следующее.
Выпуск закончил 31 октября 2002 года в 11:45 мск