РУКОВОДСТВО ПО СОЗДАНИЮ 3D-ИГР
I. ОСНОВЫ 
1. Набор команд "sync" 
Цель урока: познакомиться с командами обновления экрана DarkBASIC и уяснить их важность. 
 Давайте начнем урок с программы, которая рисует на экране 3D-сферу. 
  
    Sync On 
    Make Object Sphere 1,100 
    Do 
       Sync 
    Loop 
А теперь объясним, как команды рисуют и выводят на экран сферу. 
   Sync on 
Команда "Sync on" дает указание DarkBASIC обновить изображение на экране, когда будет вызвана команда "Sync". Вам не обойтись без этих команд ни в одной программе на DarkBASIC. 
   Make Object Sphere 1,100 
Как и большинство команд DarkBASIC, "Make Object Sphere" очень легко понять - конечно, если вы знаете английский язык. Она дает указание DarkBASIC создать объект в форме сферы. А каково назначение двух цифр в конце команды? Первая цифра "1" обозначает номер объекта, который нужно ввести в программу. Числа в DarkBASIC выполняют ту же функцию, что и имена. Они сообщают программе, какой именно объект нужно переместить по экрану, изменить или, как в нашем случае, создать. Второе число, "100", - это радиус вашей сферы. 
    Do 
    Loop 
Команды "Do" и "Loop" используются для создания в программе цикла. Любые команды, помещенные вами между "Do" и "Loop", будут выполняться непрерывно, пока, находясь в редакторе, вы не нажмете клавишу "ESC" или "F12". Почти во всех программах, которые вы напишите, будет подобный цикл. Обычно его называют "основным" или "игровым" циклом. В нашем случае мы вставили между "Do" и "Loop" команду "Sync". 
    Sync 
"Sync" означает "синхронизировать" . Когда программа меняет положение объекта, значения нового положения записываются в память, но не влияют на изображение 3D-сцены. Если синхронизацию отключить, экран будет обновляться всякий раз, когда меняются какие-либо значения, а иногда он не будет обновляться вовсе. В результате ваша программа станет выполняться медленно, но может случиться так, что экран останется пустым. Команду "Sync" можно сравнить с занавесом в театре. Когда подходит время поменять что-либо на 3D-сцене, "занавес" опускается, "декорации" меняются, и команда "Sync" снова поднимает "занавес". Она, как правило, вызывается только один раз в конце основного цикла программы, но бывают исключения. Надеемся, что вы уяснили предназначение команды "Sync". Добавим теперь к ней команду "Sync Rate". 
Let's complete our discussion of the sync command set with the addition of the "Sync Rate" command. 
    Sync On 
    Sync Rate 30 
    Make Object Sphere 1,100 
    Do 
     Sync 
    Loop 
Команда "Sync Rate" позволяет установить постоянную частоту обновления экрана. Частота обновления - это то, сколько раз в секунду поднимается и опускается "занавес" перед вашей сценой. Постоянная частота обновления нужна для согласованности работы программы на разных компьютерах. Если на вашем компьютере экран обновляется со скоростью 40 кадров в секунду, то на более производительном компьютере вашего друга он может обновляться со скоростью 120 кадров в секунду, а это слишком быстро. Установив постоянную частоту обновления (в нашем примере - "30"), вы добьетесь того, что экран будет обновляться с одной и той же скоростью на всех компьютерах. Человеческий глаз устроен так, что способен воспринимать скорость обновления экрана до определенных пределов, поэтому если вы установите слишком большую частоту, то будете зря расходовать ресурсы компьютера. А если частота обновления окажется слишком низкой, то движения на экране будут прерывистыми и медленными. Есть одно исключение: частоту обновления можно установить равной "0". Тогда DarkBasic будет обновлять экран с максимально доступной скоростью. 
Итак, мы познакомили вас с набором команд синхронизации. Теперь вы сами сможете установить частоту обновления экрана, понимая, как и когда это нужно делать. Вы также узнали простейшие команды DarkBASIC, которые работают с объектами. Попробуйте изменить или добавить в программу следующие команды. Также попробуйте поменять значения ширины, высоты и глубины и посмотрите, как они влияют на изображения создаваемых вами объектов. В команде "Make Object Triangle" задайте различные значения "x", "y" и "z" и посмотрите, какие удивительные треугольники у вас получатся. Убедитесь, что каждому объекту присвоен уникальный номер. Для этого вместо слов "Номер объекта" поставьте физическое число, соответствующее конкретному объекту. (Это относится ко всем случаям, где рядом с именем команды присутствуют слова "Номер объекта".) 
MAKE OBJECT CUBE Номер объекта, Значение размера 
MAKE OBJECT BOX Номер объекта, Ширина, Высота, Глубина 
MAKE OBJECT CYLINDER Номер объекта, Значение размера 
MAKE OBJECT CONE Номер объекта, Значение размера 
MAKE OBJECT PLAIN Номер объекта, Значение ширины, Значение высоты 
MAKE OBJECT TRIANGLE Номер объекта, X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3 
 2. Ввод данных с клавиатуры и преобразование объектов. 
Цель урока: показать, как можно изменять положение объекта на экране с помощью клавиш. 
Начнем урок с знакомства с командной "Rem" - прекрасным инструментом для создания собственных записей ("комментариев") в программе. Эта команда дает указание DarkBASIC пропускать все, что находится в строке после слова "Rem". Со временем ваши программы будут становиться все сложнее и сложнее, и вы будете тратить все больше времени на их написание, и вскоре начнете забывать, для чего предназначена та или иная переменная, и что именно делает тот или иной блок исходного кода. Именно для таких случаев служит команда "Rem" и вставленные комментарии, которые будут обо всем этом напоминать. Этот инструмент удобен также, если в процессе работы над проектом вы передаете исходный код своей программы коллегам и друзьями или когда одновременно работаете над несколькими разными проектами. 
     Rem Устанавливаем частоту обновления экрана 
    Sync On 
    Sync Rate 30 
    Rem Создаем сферу 
    Make object cube 1,100 
    Rem Основной цикл 
    Do 
       Rem  Сохраним вертикальный угол (по оси Y) объекта в переменной aY# 
       aY# = Object angle Y(1) 
       Rem  Управление вводом данных 
       If Upkey()=1 then Move object 1,2 
       If Leftkey()=1 then aY# = Wrapvalue(aY#+5) 
       If Rightkey()=1 then aY# = Wrapvalue(aY#-5) 
       Rem Вращаем сферу 
       Yrotate object 1,aY# 
       Rem Обновляем экран 
       Sync 
    Loop 
Вы, вероятно, узнали часть кода из предыдущего урока. Не удивляйтесь, если в последующих уроках вам будут встречаться знакомые блоки кода. Если какая-либо команда покажется вам незнакомой и останется в уроке без объяснений, вернитесь к предыдущим урокам, чтобы освежить память, или обратитесь к справочной системе DarkBASIC. 
А сейчас поговорим о переменных. Переменная - это величина, способная принимать разные значения и используемая для их хранения. В DarkBASIC применяется несколько типов переменных, различаемых по расширению. Взгляните на эту строку из нашего примера. 
       aY# = Object angle Y(1) 
В переменной "aY#" мы сохранили значение угла "Y" объекта "1". Обратите внимание на символ "#" в конце выражения "aY#". Он сообщает DarkBASIC, что нам нужно сохранить в переменной значение с плавающей точкой, то есть, число с десятичным знаком, например, 3.5, -5.5 и 0.00001. Такая переменная (она называется вещественной) позволяет хранить дробные числа, ее можно использовать для хранения координат или значения угла. Другие типы переменных в DarkBASIC - это символьные переменные, переменные для хранения массивов данных и переменные целого типа. Для обозначения символьных переменных используется символ "$", например, "A$ = "Здравствуй, мир" ". Данные, которые вы хотите сохранить в символьных переменных, должны заключаться в кавычки. В переменных для хранения массивов данных хранятся наборы данных одного и того же типа. Мы рассмотрим массивы в последующих уроках. Переменные целого типа хранят целые числа, как положительные, так и отрицательные, например, -5, 5, 2112, -10000. Для обозначения таких переменных расширение не используется. Как назвать переменные, решайте сами, но помните, что лучше давать им имя, близкое по смыслу к тому, что эти переменные хранят. Это окажется полезным при отладке программы и обсуждении вашего исходного кода с друзьями. Мы назвали переменную "aY#", чтобы ее имя напоминало нам о том, что в ней хранится значение угла "Y" объекта "1". Обратите внимание, что мы не используем полное название "AngleY#" (Угол Y#). По мере роста исходного кода вам придется много раз набирать на клавиатуре имена переменных. Сокращая имена, вы делаете свой код более легким для чтения и облегчаете себе работу. 
        Object angle Y(1) 
Команда "Object Angle Y" возвращает значение угла объекта, если он поворачивается по оси Y. Ось Y, по которой вращается объект, - это вертикальная линия, проходящая снизу вверх. Можно сравнить ее с земной осью, вокруг которой вращается наша планета. Ось X - это горизонтальная линия, проходящая слева направо. По горизонтальной оси вращаются колеса автомобиля. Ось Z - это направление от вас вдаль, вращение по этой оси можно сравнить с вращением автомобильного руля. В команде "Object Angle Y" в скобках указывается номер объекта, данные о вращении которого (по оси Y) вы хотите узнать. Помните, что команду нужно писать точно так, как она указана в справке. В этой команде пробела между "Y" и первой скобкой нет. Если команду написать неправильно, DarkBASIC не узнает ее и примет за ошибку. 
       If Upkey()=1 then Move object 1,2 
Выражение "If then" - очень мощная команда DarkBASIC, выполняющая функцию сравнения. Переменная, значение которой нужно проверить, помещается после "If". Если переменная имеет значение "Истина", то выполняются команды, стоящие после "Then". Если оно имеет значение "Ложь", то команды после "Then" не выполняются. В этом примере после "If" стоит команда "Upkey()". Она проверяет, нажата ли клавиша-стрелка вверх на клавиатуре и возвращает "1", если клавиша была нажата, и "0", если не была. Если клавиша нажата, выполняется команда "Move Object", перемещающая объект в том направлении, куда он "повернут лицом". Вслед за именем команды стоят числа, называемые параметрами. Первое число после команды "Move Object" - это номер перемещаемого объекта. Второе число обозначает количество единиц пространства сцены, на которые необходимо переместить объект каждый раз, когда выполняется эта команда. 
       If Leftkey()=1 then aY# = Wrapvalue(aY#+5) 
       If Rightkey()=1 then aY# = Wrapvalue(aY#-5) 
Эти два выражения "If Then" проверяют, нажаты правая или левая клавиша-стрелка. Результаты нажатия этих двух клавиш разные. Используя эти выражения, мы также сохраняем значение в переменной "aY#". Команда "Wrapvalue" позволяет нам сэкономить целый блок программного кода. Дело в том, что команды вращения в DarkBASIC принимают значения только от 0 до 360. Этот диапазон чисел используется для вычисления угла поворота, а команда "Wrapvalue" отслеживает, чтобы значение не выходило за границы этого диапазона. Значение 365 будет преобразовано в 5. Внутри скобок команды "Wrapvalue" мы поместили нашу переменную угла по оси Y и оператор сложения или вычитания. Если нажата левая клавиша, к значению "aY#" прибавляется 5, а затем новое значение сохраняется в "aY#". То же самое происходит, если нажата правая клавиша, только в этом случае значение переменной "aY#" уменьшается на 5. 
       Yrotate object 1,aY# 
Команда "Yrotate object" поворачивает объект по оси Y на указанное число градусов. Первое число после команды обозначает номер объекта, второе - угол, на который нужно повернуть этот объект. Обратите внимание, что во втором параметре вместо числа использована переменная. В параметрах большинства команд DarkBASIC числа можно заменять переменными. 
Затем мы используем команду "Sync", чтобы открыть занавес вашей сцены и вывести на экран новое положение объектов. 
Попробуйте изменить значение, прибавляемое или вычитаемое из переменной в команде "Wrapvalue", чтобы ускорить или замедлить вращение. 
Попробуйте также поменять значение второго параметра в команде "Move Object", чтобы увеличить количество пространственных единиц, на которые перемещается объект. Можно использовать значения с плавающей точкой, например, 0.5 или 1.5. Если число меньше единицы, то перед десятичным знаком лучше всегда ставить "0". 
Попробуйте поменять команду "YRotate Object" на команду "ZRotate Object" или "XRotate Object", чтобы посмотреть, как будут поворачиваться объекты. 
РУКОВОДСТВО ПО СОЗДАНИЮ 3D-ИГР
I. ОСНОВЫ 
3. Перемещение камеры 
Цель урока: показать, как нужно изменять положение и угол поворота камеры с помощью ввода с клавиатуры. 
     Rem Устанавливаем синхронизацию 
      Sync On 
      Sync Rate 30 
    Rem Создаем 5 кубиков и расставляем их в случайном порядке 
      For x = 1 to 5 
        Make object cube x,100 
        Position object x,Rnd(2000),0,Rnd(2000) 
      Next x 
    Rem Основной цикл 
      Do 
      Rem  Сохраняем угол поворота камеры 
        caY#= Camera angle Y() 
      Rem  Управление камерой с клавиатуры 
        If Upkey()=1 then Move camera 10 
        If Leftkey()=1 then Yrotate Camera Wrapvalue(caY#-5) 
        If Rightkey()=1 then Yrotate Camera Wrapvalue(caY#+5) 
      Rem Обновляем экран 
        Sync 
      Loop 
Для начала познакомимся с циклом "For Next". 
      For x = 1 to 5 
        Make object cube x,100 
        Position object x,Rnd(2000),0,Rnd(2000) 
      Next x 
Цикл "For Next" применяется для выполнения одной или нескольких команд заданное число раз. Чтобы указать, сколько раз этот цикл должен выполняться, мы используем переменную "x". В выражении "For Next" для этого всегда нужно ставить слово "To". В нашем случае мы выполняем цикл 5 раз - от 1 до 5. Необходимые команды помещаются в тело цикла. Оператор "Next" с переменной "x", которая подсчитывает число проходов по циклу, обозначает его завершение и увеличивает значение "x" на единицу. Внутри цикла мы создаем объекты в форме куба, присваивая им номер с помощью той же переменной "x". Затем команда "Position Object" помещает на сцене объект в соответствии со значениями 3D-координат. Первое число в команде "Position Object" - это номер помещаемого в сцену объекта. Мы заменили его переменной "x". Три значения после номера объекта обозначают координаты X, Y и Z. Вместо чисел мы поставили команду "Rnd", которая создает случайное число от 0 до значения, находящегося в скобках. Каждый раз при вызове команды "Position Object" новый объект помещается в случайных координатах X и Z с координатой Y, равной 0. 
      If Upkey()=1 then Move camera 10 
Следующая наша команда - "Move Camera". Она похожа на команду "Move Object" за исключением того, что не нужно указывать номер объекта. Число после команды обозначает количество единиц, на которое требуется переместить камеру при каждом проходе основного цикла, если нажата клавиша-стрелка вверх. 
      If Leftkey()=1 then Yrotate Camera Wrapvalue(caY#-5) 
      If Rightkey()=1 then Yrotate Camera Wrapvalue(caY#+5) 
Команда "YRotate Camera" выполняет ту же функцию, что и "YRotate Object", только она поворачивает по оси Y не объект, а камеру. 
Таким образом, вы получили представление, как нужно перемещать камеру и изменять ее положение. Чтобы лучше понять все команды, попробуйте изменить текст исходной программы. 
Измените значения в команде "Rnd", чтобы посмотреть, как меняется положение объектов относительно друг друга. 
Измените значения в командах "Move Camera" и "Yrotate Camera", чтобы посмотреть, как можно замедлить или ускорить перемещение и поворот камеры. 
РУКОВОДСТВО ПО СОЗДАНИЮ 3D-ИГР
I. ОСНОВЫ 
4. Вид от третьего лица 
Цель урока: показать, как нужно вводить вид от третьего лица. 
      Rem Устанавливаем синхронизацию 
      Sync On 
      Sync Rate 30 
      Rem Создаем 5 кубиков и расставляем их в случайном порядке 
      For x = 1 to 5 
          Make object cube x,100 
          Position object x,Rnd(2000),0,Rnd(2000) 
      Next x 
      Rem Создаем сферу 
      Make object sphere 10,50 
      Rem Основной цикл 
      Do 
         Rem  Сохраняем вертикальный угол объекта в переменной aY# 
         aY# = Object angle Y(10) 
         Rem  Управление камерой с клавиатуры 
         If Upkey()=1 then Move object 10,10 
         If Leftkey()=1 then Yrotate object 10,Wrapvalue(aY#-5) 
         If Rightkey()=1 then Yrotate object 10,Wrapvalue(aY#+5) 
         Rem Устанавливаем положение игрока и сохраняем его в переменных X# и Z# 
         X# = Object position x(10) 
         Z# = Object position z(10) 
         Rem Получаем новое положение камеры и сохраняем его в переменных cZ# и cX# 
         cZ# = Newzvalue(Z#,aY#-180,100) 
         cX# = Newxvalue(X#,aY#-180,100) 
         Rem Устанавливаем положение камеры 
         Position Camera cX#,100,cZ# 
         Rem Направляем камеру на объект "Игрок" 
         Point camera X#,50,Z# 
         Rem Обновляем экран 
         Sync 
      Loop 
В этом уроке мы познакомимся с тем, как применить в игре вид от третьего лица. Вид от третьего лица можно определить, как вид из камеры, которая находится позади игрока и чуть выше него. Большую часть кода этого урока, мы уже описывали в предыдущих уроках. Давайте сосредоточимся на новых строках кода и новых командах. 
         X# = Object position x(10) 
         Z# = Object position z(10) 
Команды "Object Position X" и "Object Position Z" похожи на команду "Object Angle" за исключением того, что они возвращают не угол поворота объекта, а положение объекта в относительных координатах. В скобках ставится номер объекта, координаты которого нужно получить. Значения координат сохраняем в переменных X# и Z#. 
         cZ# = Newzvalue(Z#,aY#-180,100) 
         cX# = Newxvalue(X#,aY#-180,100) 
Команды "NewZValue" и "NewXValue" помогают вам избежать сложных математических функций, таких как синус и косинус. Они вычисляют новое положение объекта на основании старого. Мы используем эти команды, чтобы вычислить точку, находящуюся в 100 единицах пространственных координат позади объекта "Игрок". Первый параметр - это относительная координата, в которой расположен игрок в данный момент. Помните, что мы сохранили положение этого объекта в переменных X# и Z#. Второй параметр - угол оси новых координат. Мы взяли значение вертикального угла объекта, сохранили значение в переменной Y# и вычли из него 180 градусов. Этот угол направлен точно за объект. Третий параметр обозначает точку, удаленную от объекта на указанное количество единиц пространственных координат. Мы задали значение 100. Другими словами, мы сделали следующее: нашли точку, находящуюся позади объекта "Игрок" и удаленную от него на 100 единиц, и сохранили ее координаты в переменных cZ# и cX#, обозначающих положение нашей камеры. 
         Position camera cX#,100,cZ# 
Команда "Position Camera" работает так же, как и команда "Position Object", за исключением того, что не нужно указывать номер объекта - только значения координат. Для координат X и Z мы используем вновь созданные значения, а для координаты Y - значение 100. Поскольку вертикальная координата Y объекта-игрока равна 0, а его радиус 50, значение 100 координаты помещает камеру над объектом. 
         Point camera X#,50,Z# 
Наша камера направлена поверх объекта-игрока. С помощью команды "Point Camera" мы направим камеру на объект. Параметры этой команды - это координаты X, Y и Z той точки, на которую нужно направить камеру. Мы используем переменные X# и Z#, хранящие значения положения объекта по осям X и Z. В качестве значения по оси Y будем использовать 50. Теперь камера направлена в центр нашего объекта-игрока. 
  
Попробуйте изменить количество повторений цикла, чтобы создать больше кубиков. Не забудьте поменять номер объекта-игрока, чтобы это число было больше, чем число кубиков, иначе программа выведет сообщение об ошибке "Объект уже существует". 
В командах "NewXValue" и "NewYValue" попробуйте поменять значение 180 на другое, меньше 360, чтобы посмотреть, как оно влияет на положение камеры. Измените также последний параметр в этих командах, чтобы переместить камеру ближе или дальше к объекту. 
Попробуйте изменить значение 50 в команде "Point Camera", чтобы изменить высоту камеры. 
5. Обнаружение столкновений 
Цель урока: объяснить, как обнаруживать столкновения. 
      Rem Устанавливаем синхронизацию 
      Sync On 
      Sync Rate 30 
      Rem Создаем кубики в случайном порядке 
      For x = 1 to 5 
          Make object cube x,100 
          Position object x,Rnd(2000),0,Rnd(2000) 
          Set object collision to boxes x 
      Next x 
      Rem Создаем сферу 
      Make object sphere 10,50 
      Position object 10,-100,0,-100 
      Set object collision to spheres 10 
      Rem Основной цикл 
      Do 
         Rem  Сохраняем значение вертикального угла в aY# 
         aY# = Object angle Y(10) 
         Rem  Управление камерой с клавиатуры 
         If Upkey()=1 then Move object 10,10 
         If Leftkey()=1 then Yrotate object 10,Wrapvalue(aY#-5) 
         If Rightkey()=1 then Yrotate object 10,Wrapvalue(aY#+5) 
         Rem Обнаруживаем столкновение 
         If Object collision(10,0)>0 then position object 10,X#,0,Z# 
         Rem Сохраняем положение игрока в X# и Z# 
         X# = Object position x(10) 
         Z# = Object position z(10) 
         Rem Получаем новое положение камеры и сохраняем его в cZ# и cX# 
         cZ# = Newzvalue(Z#,aY#-180,100) 
         cX# = Newxvalue(X#,aY#-180,100) 
         Rem Устанавливаем камеру 
         Position Camera cX#,75,cZ# 
                    Rem Направляем камеру на объект 
         Point camera X#,25,Z# 
         Rem Обновляем экран 
         Sync 
      Loop 
В этом уроке мы покажем, как использовать команды DarkBASIC для обнаружения столкновений. Мы добавили их в исходный код предыдущего урока. 
      Set object collision to boxes x 
В цикл, где создаются и размещаются кубические объекты, мы вставили команду "Set Object Collision To Boxes". Она устанавливает тип обнаружения столкновений с объектами в форме куба, то есть соответствующий форме наших объектов. Чтобы передать команде "Set Object Collision To Boxes" номера объектов, мы используем переменную цикла "х". 
      Set object collision to spheres 10 
Эта команда тоже устанавливает тип обнаружения столкновений с объектами, но не в форме куба, а в форме сферы, такой, как у нашего объекта-игрока. Выбор типа столкновений зависит от вас. Старайтесь только, чтобы он лучше всего подходил для игры, которую вы создаете. 
      If Object collision(10,0)>0 then position object 10,X#,0,Z# 
Эта строка кода определяет столкновение объектов. Если наш объект-игрок сталкивается с каким-либо другим объектом, он перемещается в место, где находился перед столкновением. Эти координаты хранятся в переменных X# и Z#. Обратите внимание, что мы определяем столкновение после перемещения объекта и перед тем, как сохраняем новые значения его координат. Этим мы обеспечиваем возвращение объекта-игрока на старое место, где он не может столкнуться с каким-нибудь другим объектом. Команда "Object Collision" используется для проверки столкновения. Первый параметр в скобках - это номер вашего объекта-игрока, для которого определяется столкновение. Второе значение - номер объекта, столкновение с которым вашего объекта-игрока нужно проверить. Здесь мы применяем значение 0, чтобы DarkBASIC обнаруживал столкновение с любым объектом. В этом случае команда "Object Collision" возвращает номер объекта, с которым столкнулся объект под номером 10, и значение 0, если столкновения не произошло. Если эта команда возвратит значение больше 0, то столкновение обнаружено, и мы помещаем объект-игрок в то место, где он находился перед столкновением. 
Еще один тип столкновений - полигонный. Он используется при обнаружении столкновений с крупными объектами, такими как здания, построенными вами в 3D-сцене. Теперь, после знакомства с новыми командами, вы можете применять обнаружение столкновений в своих программах. Чтобы научиться свободно пользоваться ими, попробуйте внести в них следующие изменения. 
Попробуйте поменять значение второго параметра в команде "Object Collision" на 3. Перемещаясь по 3D-сцене и сталкиваясь с кубическими объектами, попытайтесь найти объект под номером 3. 
Измените тип столкновений, чтобы обнаруживать столкновения объекта-игрока с объектами не в форме сферы, а в форме куба, и посмотрите, как это отражается на столкновениях с объектами в форме куба. 
 6. Текстурирование объектов 
Цель урока: научиться накладывать текстуры на объекты. 
    Rem Устанавливаем синхронизацию 
      Sync On 
      Sync Rate 30 
    Rem Загружаем текстуры 
      Load image "cottag02.bmp",1 
      Load image "barry.bmp",2 
  
    Rem Создаем кубики в случайном порядке 
      For x = 1 to 5 
          Make object cube x,100 
          Position object x,Rnd(2000),0,Rnd(2000) 
          Set object collision to boxes x 
          Texture object x,1 
      Next x 
    Rem Создаем сферу 
      Make object sphere 10,50 
      Position object 10,-100,0,-100 
      Set object collision to spheres 10 
      Texture object 10,2 
    Rem Основной цикл 
      Do 
       Rem  Сохраняем вертикальный угол в aY# 
         aY# = Object angle Y(10) 
       Rem  Управление камерой с клавиатуры 
         If Upkey()=1 then Move object 10,10 
         If Leftkey()=1 
             Yrotate object 10,Wrapvalue(aY#-5) 
         Endif 
         If Rightkey()=1 
            Yrotate object 10,Wrapvalue(aY#+5) 
         Endif 
       Rem Обнаруживаем столкновение 
         If Object collision(10,0)>0 
            Position object 10,X#,0,Z# 
         Endif 
       Rem Сохраняем положение объекта-игрока в X# и Z# 
         X# = Object position x(10) 
         Z# = Object position z(10) 
       Rem Получаем новое положение камеры и сохраняем его в cZ# and cX#м 
         cZ# = Newzvalue(Z#,aY#-180,100) 
         cX# = Newxvalue(X#,aY#-180,100) 
       Rem Устанавливаем камеру 
         Position Camera cX#,75,cZ# 
       Rem Направляем камеру на объект "Игрок" 
         Point camera X#,25,Z# 
       Rem Прокручиваем текстуру на кубических объектах 
         Scroll object texture 1,0.005,0 
         Scroll object texture 2,0,0.005 
       Rem Масштабируем текстуру на кубических объектах 
         Scale object texture 3,0.998,0.998 
         Scale object texture 4,1.001,1.001 
       Rem Обновляем экран 
         Sync 
      Loop 
В этом уроке мы научимся текстурировать объекты. В DarkBASIC текстуры загружаются из файлов растровых изображений с расширением ".bmp". Советуем создавать текстуры с размерами кратными степени 2, так как некоторые графические карты не могут обрабатывать текстуры, размеры которых выражены нечетными числами и не кратны степени 2. Это означает, что размеры ваших изображений должны быть 256x256, 32x128, 256x16 или любых других размеров кратными степени 2. Можно загружать текстуры и большего размера, чем 256x256, но в этом случае убедитесь, что ваша графическая карта может обрабатывать такие большие текстуры. 
        Load image "cottag02.bmp",1 
        Load image "barry.bmp",2 
Команда "Load Image" дает DarkBASIC указание загрузить изображение из файла растрового изображения. Первый параметр команды - это имя загружаемого файла растрового изображения. Имя файла нужно заключить в кавычки, иначе произойдет ошибка. В этом параметре можно использовать имена каталогов, например, "C:\Darkbasic\Images\mybitmap.bmp". Если вы храните изображения в подкаталоге проекта, в параметре необходимо указать его имя. Мы советуем создавать отдельный подкаталог, если в вашей программе много мультимедийных файлов. Второй параметр - это уникальный номер (или идентификатор) изображения, который используется так же, как при создании объекта. Нельзя присваивать один и тот же номер двум разным изображениям. 
        Texture object x,1 
        Texture object 10,2 
Эти две строки стоят перед основным циклом. Первая строка находится в теле цикла "For Next" и служит для создания кубических объектов. Вторая внесена после того, как мы создали объект "Игрок". Команда "Texture Object" предназначена для наложения изображения на объект. Первый параметр - номер объекта, на который нужно наложить текстуру. Второй параметр - номер накладываемого на объект изображения. 
        Scroll object texture 1,0.005,0 
        Scroll object texture 2,0,0.005 
Команда "Scroll Object Texture" перемещает (прокручивает) наложенную на объект или полигон текстуру вверх и вниз или вправо и влево. Первый параметр - номер объекта, чья текстура будет прокручиваться. Следующие два параметра - это U и V координаты. Вы можете представить их как координаты X и Y двумерной текстуры. Если присвоить этим параметрам значения, отличные от 0, текстура будет прокручиваться при каждом вызове этой команды. Можно использовать как положительные, так и отрицательные значения, в зависимости от того, в каком направлении вы хотите прокрутить текстуру на объекте. В нашем примере используется очень маленькое значение с плавающей точкой. Значения прокрутки U и V можно представить как процент от размера текстуры. Если бы мы установили значение, равное "0.5", что означает 50 процентов, текстура на кубе передвинулась бы ровно наполовину, а это - слишком быстро. 
        Scale object texture 3,0.998,0.998 
        Scale object texture 4,1.001,1.001 
Команда "Scale Object Texture" похожа на предыдущую, только эта команда увеличивает или уменьшает масштаб текстуры на объекте. Первый параметр - это номер объекта, текстура которого изменяет масштаб (масштабируется). Следующие два параметра являются U и V координатами текстуры. Изменение этих значений на любую величину, кроме 0, увеличит или уменьшит размер текстуры. Когда эти значения равны 0, никакого изменения размеров не произойдет. Если присвоить параметрам значение меньше 1, масштаб текстуры уменьшится, и наоборот, если присвоить им значение больше 1, масштаб текстуры увеличится. 
Теперь вы умеете загружать изображение из файла и накладывать его на объект, а также масштабировать и прокручивать текстуру объекта. Попробуйте внести следующие изменения, чтобы посмотреть, как они повлияют на текстурированные кубики. 
Измените значения U и V в команде "Scroll Object Texture", чтобы посмотреть, как это повлияет на скорость прокрутки текстур объектов. 
Измените значения U и V в команде " Scale Object Texture", чтобы посмотреть, как это отразится на масштабе текстур. 
II. МАТРИЦА 
1. Создание матрицы 
Цель урока: ознакомление с командами, создающими матрицу. 
    Rem Устанавливаем синхронизацию 
    Sync On 
    Sync Rate 30 
    Rem Создаем матрицу 
    Make Matrix 1,2000,2000,50,50 
    Rem mY - переменная для хранения высоты матрицы 
    mY=100 
    Rem Помещаем камеру над матрицей 
    Position Camera 0,1000,0 
    Rem Основной цикл 
    Do 
         Rem  Управление камерой с клавиатуры 
         If Upkey()=1 then Move camera 10 
         If Downkey()=1 then Move camera -10 
         Rem Ввод для заполнения матрицы случайными значениями 
         If Spacekey()=1 then randomize matrix 1,mY 
         If Leftkey()=1 then mY=mY+1 
         If Rightkey()=1 then mY=mY-1 
         If mY < 1 then mY =1 
         Rem Направляем камеру в центр матрицы 
         Point Camera 1000,0,1000 
         Rem Обновляем экран 
         Sync 
    Loop 
  
  
Этот урок познакомит вас с командами, создающими матрицу. Матрица часто используется для создания объекта местности. Вы можете легко создавать горы, пологие холмы, пещеры и океаны при помощи нескольких строк кода. Помните, если вы встретите незнакомую команду без объяснений в данном руководстве, просмотрите предыдущие руководства или обратитесь за помощью к справочной системе DarkBASIC. Приведенная выше простая программа создает матрицу и позволяет вам масштабировать ее в изометрический проекции с использованием клавиш "стрелка вверх" и "стрелка вниз". Клавиши-стрелки и пробел позволяют вам управлять высотой и видом матрицы. 
    Make Matrix 1,2000,2000,50,50 
Первая новая команда, которую мы обсудим - это команда Make Matrix. Она создает объект "Матрица" в вашей 3D-сцене. Что означают параметры этой команды? Первое число после команды - это номер создаваемой матрицы. Этот номер, как и в командах управления объектами и изображениями, является уникальным идентификатором. Он позволяет вам создавать и манипулировать многочисленными матрицами в 3D-сцене. Последующие два числа - это размеры матрицы, которую вы хотите создать, в пространственных координатах сцены. Они представляют значения координат X и Y. В большинстве случаев эти два значения будут одинаковыми, но мы уверены, что вы сможете найти применение для матриц различных размеров. Следующие два числа - это количество сегментов матрицы. В этом примере матрица размером 2000х2000 разделена в каждом направлении на 50 сегментов (размер ячейки получается 40x40). 
      If Spacekey()=1 then Randomize Matrix 1,mY 
Эта строка кода знакомит нас с командой Randomize Matrix. При нажатии на пробел осуществляется вызов этой команды, которая задает случайное значение высоты каждой из вершин матрицы в диапазоне между 0 и числом, содержащимся в переменной mY. Первое число после команды -- это номер изменяемой матрицы. Второе число - это диапазон случайного значения для изменения высоты матрицы. В программе вы можете изменить значение переменной mY, нажимая клавиши "стрелка вверх" и "стрелка вниз". Нажатие на клавишу "пробел" позволяет изменить матрицу, что дает вам возможность создавать крутые пики гор или пологие холмы. 
Попытайтесь изменять значения параметров в команде Make Matrix и посмотреть, каким образом они влияют на величину и размеры матрицы. 
 2. Текстурирование матрицы 
Цель урока: научиться накладывать текстуры на матрицу. 
Мы начнем с программы, которая создает рельефную местность и текстурирует каждую ячейку матрицы случайным образом. 
Sync On 
Sync Rate 30 
  
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,2,2 
rem Заполняем матрицу случайными значениями высоты 
randomize matrix 1,250 
rem Накладываем текстуру случайным образом на каждый тайл матрицы 
For x = 0 to 19 
 For z = 0 to 19 
  t= rnd(3)+1 
  Set Matrix Tile 1,x,z,t 
 Next z 
Next x 
Rem Вносим изменения в матрицу 
update matrix 1 
Rem Основной цикл 
Do 
 Rem  Сохраняем угол объекта 
 CameraAngleY# = Camera angle Y() 
  
 Rem  Управление перемещением камеры 
 If Upkey()=1 then Move camera 10 
 If Leftkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#-5) 
 If Rightkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#+5) 
    If Spacekey()=1 
        Fill matrix 1,Rnd(50),Rnd(3)+1 
          randomize matrix 1,250 
    endif 
X# = Camera position X() 
   Z# = Camera position Z() 
 Position Camera X#,250,Z# 
  
 Rem Обновляем экран 
 Sync 
Loop 
 Начнем с подготовки текстуры для матрицы. 
    Prepare matrix texture 1,1,2,2 
The "Команда "Prepare Matrix Texture" применяется для разбиения на ячейки текстуры, накладываемой на матрицу. Первый параметр после команды - это номер матрицы, для которой мы собираемся подготовить текстуру. Второй параметр - это номер изображения, используемого для текстурирования матрицы. Третий и четвертый параметры - это число создаваемых на каждой оси ячеек. Здесь мы использовали в качестве первого параметра значение 2. Это число делений по оси X. Значение 2 сообщает DarkBASIC, что текстуру надо разделить пополам на две части. В последнем параметре мы также использовали значение 2. Это, в свою очередь, число делений по оси Y. Таким образом, мы разделили изображение на четыре секции, или четыре отдельные текстуры. Изображение, используемое для наложения текстуры на матрицу, не может быть больше, чем 256х256 пикселов, однако вы можете разбить его максимум на 256х256 отдельных тайлов. В большинстве случаев значение двух последних параметров команды будут одинаковы. Если значения параметров будут разными, то текстуры будут непропорционально растянуты при наложении на квадратные ячейки матрицы. Тайлы нумеруются, начиная с левого верхнего угла, слева направо и сверху вниз. Нумерация начинается с 1. 
 For x = 0 to 19 
 For z = 0 to 19 
  t= rnd(3)+1 
  Set Matrix Tile 1,x,z,t 
 Next z 
Next x 
Эти вложенные циклы проходят по каждой ячейке матрицы, и случайным образом назначают для каждой из них ранее подготовленный тайл. Чтобы назначить отдельной ячейке матрицы определенный тайл, мы используем команду "Set Matrix Tile". Первый параметр после команды - номер матрицы, которую мы желаем изменить. Второй и третий параметры - это номера строки и колонки, в которых располагается ячейка матрицы, для которой мы хотим изменить тайл. Последний параметр - это номер тайла изображения, размещаемого в определенной ячейке матрицы. Для выбора случайного номера тайла в интервале от 0 до 3 мы применили команду "Rnd". Заметьте, что мы добавили 1 к этому числу, так как тайла с номером "0" не существует. Таким образом, после добавления 1, производится выбор случайного номера тайла в интервале от 1 до 4. 
    If Spacekey()=1 
        Fill matrix 1,Rnd(50),Rnd(3)+1 
        randomize matrix 1,250 
    endif 
Эта часть кода изменяет высоту матрицы и случайным образом изменяет тайлы для каждой ячейки матрицы при каждом нажатии клавиши "пробел". Команда "Fill Matrix" устанавливает определенную высоту и определенный номер тайла изображения для всех тайлов матрицы. Эта команда наилучшим образом подходит для установки начальных значений для всех ячеек матрицы. Первый параметр после команды - номер матрицы, которую мы хотим изменить. Второй параметр - это высота, устанавливаемая нами для всех вершин матрицы. Это полезно при добавлении еще одной матрицы (например, матрицы "вода"), или создании слоев матриц, наложенных друг на друга. Для выбора случайного значения высоты в интервале от 0 до 50 мы использовали команду "Rnd". Эта величина соответствует координатам по оси Y в созданном вами 3D-пространстве. Наконец, последний параметр - это номер тайла подготовленной матричной текстуры, который мы хотим использовать. Для случайного выбора тайла мы применили команду "Rnd". 
На этом мы завершаем главу об основах текстурирования матрицы. 
Попробуйте изменить значения последних двух параметров в команде "Prepare Matrix Texture", чтобы посмотреть, как изменятся изображения в тайлах. Затем измените значения параметров каждой команды "Rnd" в соответствии с измененным числом тайлов, подготовленных вами для матрицы. 
3. Перемещение по матрице 
Цель урока: объяснить, как установить камеру в определенную позицию на матрице. 
Sync On 
Sync Rate 30 
  
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,1,1 
Fill matrix 1,0,1 
rem Присваиваем координатам вершин матрицы случайные значения 
randomize matrix 1,125 
  
Rem Основной цикл 
Do 
set cursor 0,0 
print X# 
print Y# 
print Z# 
 Rem  Сохраняем угол объекта 
 CameraAngleY# = Camera angle Y() 
 Rem  Управление перемещением камеры 
 If Upkey()=1 
  XTest# = Newxvalue(X#,CameraAngleY#,20) 
  ZTest# = Newzvalue(Z#,CameraAngleY#,20) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   Move camera 10 
  Endif 
 Endif 
 If Leftkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#-5) 
 If Rightkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#+5) 
 X# = Camera position X() 
   Z# = Camera position Z() 
 Y# = Get ground height(1,X#,Z#) 
 Position Camera X#,Y#+35,Z# 
 Rem Обновляем экран 
 Sync 
Loop 
Эта программа демонстрирует, как поместить камеру таким образом, чтобы она следовала рельефу местности на матрице. 
set cursor 0,0 
print X# 
print Y# 
print Z# 
Мы начнем этот урок с объяснения двух полезных текстовых команд "Set Cursor" и "Print". Первая из них помещает курсор в точку на экране с координатами X и Y (параметры команды), с которой мы собираемся начать вывод текста, а вторая выводит текст на экран. Здесь мы используем переменные X#, Y# и Z#. На экран будут выведены значения каждой из них. Если вы хотите вывести на экран текстовую строку, вы должны заключить ее в кавычки. 
   If Upkey()=1 
  XTest# = Newxvalue(X#,CameraAngleY#,20) 
  ZTest# = Newzvalue(Z#,CameraAngleY#,20) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   Move camera 10 
  Endif 
 Endif 
Эта часть кода ограничивает перемещение камеры для того, чтобы предотвратить выход игрока за пределы матрицы. Переменные Xtest# и Ytest# хранят координаты позиции, в которой мы собираемся установить камеру. Имейте в виду, что значение "20" на самом деле является расстоянием вдвое большим, чем то, на которое мы желаем переместить камеру. Такой прием не подпускает игрока к абсолютной границе матрицы. Выражение сравнения "If" проверяет, не вышли ли новые значения координат за границы матрицы. Матрица занимает площадь в координатах от 0 до 10000 в каждом направлении. Мы используем союз "and" для того, чтобы осуществить более одного сравнения в выражении "If". Если игрок не вышел за пределы указанных границ, то камера перемещается в новое место. 
  X# = Camera position X() 
  Z# = Camera position Z() 
 Y# = Get ground height(1,X#,Z#) 
 Position Camera X#,Y#+35,Z# 
После того, как камера переместилась, мы сохраняем значения ее координат в переменных X# и Z#. Затем мы получаем координату высоты матрицы в том месте, где расположена камера, и сохраняем это значение в переменной Y#, используя команду "Get Ground Height". В скобках после команды первый параметр - это номер матрицы, для которой мы определяем значение высоты. Следующие два параметра - координаты X и Z, в этой точке мы желаем узнать высоту матрицы. После определения высоты мы устанавливаем камеру на 35 пунктов выше матрицы. Это позволяет перемещаться по матрице на 35 пунктов выше ее рельефа. 
4. Вид от третьего лица и перемещение на матрице 
Цель урока: научиться перемещать объект на матрице, используя вид от третьего лица. 
Sync On 
Sync Rate 30 
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,1,1 
Fill matrix 1,0,1 
Rem Создаем объект 
Load image "barry.bmp",2 
Make object sphere 10,25 
Texture object 10,2 
position object 10,100,0,100 
rem Устанавливаем случайные значения для вершин матрицы 
randomize matrix 1,125 
Rem Основной цикл 
Do 
set cursor 0,0 
print screen fps() 
 Rem  Сохраняем угол объекта 
 AngleY# = object angle Y(10) 
 Rem  Управление перемещением камеры 
 If Upkey()=1 
  XTest# = Newxvalue(X#,AngleY#,20) 
  ZTest# = Newzvalue(Z#,AngleY#,20) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   Move object 10,10 
  Endif 
 Endif 
 If Leftkey()=1 then Yrotate object 10,Wrapvalue(AngleY#-5) 
 If Rightkey()=1 then Yrotate object 10,Wrapvalue(AngleY#+5) 
 X# = Object position x(10) 
 Z# = Object position z(10) 
 Y# = Get Ground Height(1,X#,Z#) 
 Position object 10,X#,Y#+12.5,Z# 
 CameraZ# = Newzvalue(Z#,AngleY#-180,100) 
 CameraX# = Newxvalue(X#,AngleY#-180,100) 
 CameraY# = Get Ground Height(1,CameraX#,CameraZ#) 
 Position camera CameraX#,CameraY#+50,CameraZ# 
 Point camera X#,Y#+25,Z# 
 Rem Обновляем экран 
 Sync 
Loop 
Эта программа позволяет пользователю перемещать текстурированную сферу по матрице при помощи клавиатуры. Мы начнем этот урок с описания простой служебной команды. 
 print screen fps() 
"Screen Fps()" является полезной командой, с помощью которой можно узнать, с какой скоростью происходит обновление экрана. Попробуйте выставить значение синхронизации 0 и запустите программу. Вы увидите число на экране, показывающее, сколько раз в секунду происходит обновление экрана. Если частота обновления на вашей видеокарте привязана к частоте обновления монитора, вы не сможете узнать максимальную скорость обновления, которую способна выдавать ваша видеокарта. Некоторые видеокарты позволяют отключать синхронизацию ("VSync"), при этом экран обновляется с максимально возможной скоростью. 
 X# = Object position x(10) 
 Z# = Object position z(10) 
 Y# = Get Ground Height(1,X#,Z#) 
 Position object 10,X#,Y#+12.5,Z# 
 CameraZ# = Newzvalue(Z#,AngleY#-180,100) 
 CameraX# = Newxvalue(X#,AngleY#-180,100) 
 CameraY# = Get Ground Height(1,CameraX#,CameraZ#) 
 Position camera CameraX#,CameraY#+50,CameraZ# 
 Point camera X#,Y#+25,Z# 
Как и в предыдущем уроке, для нахождения высоты матрицы в определенном месте мы используем команду "Get Ground Height". Здесь мы используем эту команду дважды: один раз для того, чтобы определить высоту в месте, где располагается сфера, и второй раз для того, чтобы определить высоту в том месте, где располагается камера. Вы заметите, что оставшаяся часть кода такая же, как и в предыдущем уроке. Единственное, что мы здесь изменили - это добавили значение Y#. 
Попробуйте использовать выражение "Rem", чтобы закомментировать следующую строку кода: 
CameraY# = Get Ground Height(1,CameraX#,CameraZ#) 
А в строке: 
Position camera CameraX#,CameraY#+50,CameraZ# 
замените переменную CameraY# на Y# и посмотрите, как это отражается на поведении камеры. 
5. Туман и задник 
Цель урока: объяснить применение основных команд для создания тумана и задника. 
Команды тумана в основном применяются для создания тумана в 3D-сцене. Немного изменив параметры команды, можно создавать такие эффекты, как дым, подводное окружение, сумерки и т.д. Другое очень важное применение команды тумана состоит в том, чтобы использовать ее для создания эффекта "скрывающейся геометрии". В насыщенной 3D-среде с большим количеством полигонов можно использовать туман, чтобы скрыть геометрию сцены за пределами максимального расстояния видимости камеры. Этот метод может значительно увеличить скорость выполнения программ. 
Sync On 
Sync Rate 30 
Backdrop on 
Set camera range 1,5000 
Fog on 
Fog distance 4000 
Fog color RGB(128,128,128) 
Color Backdrop RGB(128,128,128) 
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,1,1 
Fill matrix 1,0,1 
Rem Создаем простейший объект 
Load image "barry.bmp",2 
Make object sphere 10,25 
Texture object 10,2 
position object 10,100,0,100 
rem Присваиваем случайные значения координатам вершин матрицы 
randomize matrix 1,125 
Rem Основной цикл 
Do 
 Rem  Сохраняем угол объекта 
 AngleY# = object angle Y(10) 
 Rem  Управление перемещением камеры 
 If Upkey()=1 
  XTest# = Newxvalue(X#,AngleY#,20) 
  ZTest# = Newzvalue(Z#,AngleY#,20) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   Move object 10,10 
  Endif 
 Endif 
 If Leftkey()=1 then Yrotate object 10,Wrapvalue(AngleY#-5) 
 If Rightkey()=1 then Yrotate object 10,Wrapvalue(AngleY#+5) 
 X# = Object position x(10) 
 Z# = Object position z(10) 
 Y# = Get Ground Height(1,X#,Z#) 
 Position object 10,X#,Y#+12.5,Z# 
 CameraZ# = Newzvalue(Z#,AngleY#-180,100) 
 CameraX# = Newxvalue(X#,AngleY#-180,100) 
 Position camera CameraX#,Y#+50,CameraZ# 
 Point camera X#,Y#+25,Z# 
 Rem Обновляем экран 
 Sync 
Loop 
Начнем объяснение с нескольких установочных команд. 
 Backdrop on 
Команда "Backdrop on" включает задник. Если он выключен, вы увидите эффект зеркального помещения, выражающийся в том, что можно перемещаться за пределами матрицы, но объект-игрок не сможет подбирать или удалять находившиеся там прежде предметы. Задник необходимо использовать в том случае, если вы знаете, что игроку будет виден задний план. 
  Set camera range 1,5000 
Команда "Set Camera Range" устанавливает дальность видимого пространства для камеры. Первый параметр определяет, на каком расстоянии от камеры DarkBASIC должен начать рендеринг 3D-сцены. Второй параметр определяет расстояние от камеры, на котором прекращается рендеринг сцены. При выборе значения последнего параметра необходимо принять во внимание то, насколько далеко будет "видеть" игрок, и какое число полигонов будет одновременно отображаться. Попробуйте изменить значение первого параметра на 1000, а второго - на 100000 или 1000 и посмотреть, как это повлияет на рендеринг сцены. 
   Fog on 
Команда "Fog On" указывает DarkBASIC, что на определенном расстоянии в сцене будет использоваться туман. 
   Fog distance 4000 
Команда "Fog Distance" устанавливает расстояние, с которого должен начинаться наиболее плотный туман. Советуем установить этот параметр равным или чуть меньшим, чем последний параметр в команде "Set Camera Range", в противном случае вам придется наблюдать неприятный эффект обрезанных полигонов. 
   Fog color RGB(128,128,128) 
Команда "Fog Color" устанавливает цвет тумана. Параметр после команды - цвет тумана. Здесь для установки цвета мы используем команду "RGB". 
   Color Backdrop RGB(128,128,128) 
Команда "Color Backdrop" устанавливает цвет задника. Параметр после команды - цвет задника. Так же, как и в предыдущей команде, для установки цвета задника мы используем команду "RGB". Чтобы туман выглядел более реалистичным, советуем окрасить задник в тот же цвет, что и туман. В этом случае туман сольется с задником. Для создания других эффектов можно установить различные значения этих цветов. Попробуйте задать расстояние для тумана равным 500, установить значение цвета тумана равным "0,0,0", а цвета задника равным "0,0,32". Это создаст эффект вырисовывающегося ландшафта на фоне почти черного неба. 
 III. ИГРА 
1. Загрузка объектов 
Цель урока: научиться загружать объекты в 3D-сцену и строить фундамент игровой программы. 
Существует множество различных приложений, способных создавать и сохранять 3D-модели. В DarkBASIC можно использовать как формат "X" DirectX, так и формат "3DS" 3DStudio. В отличие от стандартных примитивов DarkBASIC, модели этих форматов помогут вам создавать более реалистичные и богатые графикой сцены. 
Sync On 
Sync Rate 30 
Hide Mouse 
autocam off 
  
Backdrop on 
Set camera range 1,5000 
Fog on 
Fog distance 4000 
Fog color RGB(128,128,128) 
Color Backdrop RGB(128,128,128) 
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,1,1 
Fill matrix 1,0,1 
rem Присваиваем координатам вершин матрицы случайные значения 
randomize matrix 1,125 
rem Загружаем объект 
Load object "idle.x",2 
position object 2,5000,Get Ground Height(1,5000,5500),5500 
Rem Основной цикл 
Do 
set cursor 0,0 
print X# 
print Y# 
print Z# 
 Rem  Сохраняем угол объекта 
 CameraAngleY# = Camera angle Y() 
 Rem  Управление перемещением камеры 
 If Upkey()=1 
  XTest# = Newxvalue(X#,CameraAngleY#,20) 
  ZTest# = Newzvalue(Z#,CameraAngleY#,20) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   Move camera 10 
  Endif 
 Endif 
 If Leftkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#-5) 
 If Rightkey()=1 then Yrotate Camera Wrapvalue(CameraAngleY#+5) 
 X# = Camera position X() 
   Z# = Camera position Z() 
 Y# = Get ground height(1,X#,Z#) 
 Position Camera X#,Y#+35,Z# 
 Rem Обновляем экран 
 Sync 
Loop 
 Прежде всего, введем несколько полезных команд. 
  Hide Mouse 
Команда "Hide mouse" убирает с экрана курсор мыши. В большинстве случаев в 3D-сценах курсор мыши на экране вам не понадобится. 
  autocam off 
Команда "Autocam Off" выключает режим автоматической камеры. Когда этот режим включен, то при создании каждого новый объект в DarkBASIC, камера перемещается для того, чтобы показать этот объект. Если загружается большое число объектов, камера будет показывать каждый из них по мере загрузки. Выключение этого режима также играет свою роль при загрузке матрицы. Когда режим автоматической камеры выключен, камера устанавливается в центр созданной вами матрицы. Если режим включен, то камера будет установлена в позицию с координатами (0,0,0) в 3D-сцене. 
  Load object "idle.x",2 
"Load Object" - основная команда для загрузки объекта в DarkBASIC. Первый параметр этой команды - имя файла объекта, загружаемого в 3D-сцену. Чтобы команда распознала имя файла, оно должно быть заключено в кавычки. Второй параметр команды - это номер, назначаемый данному объекту. Эта команда очень похожа на команды "Make Object". Все команды для работы с объектами, описанные в предыдущих уроках, будут работать с загруженными объектами точно так же, как они работают с объектами, созданными в DarkBASIC. 
  Loop object 2 
Команда "Loop Object" осуществляет циклическую анимацию загруженного в DarkBASIC объекта. Если загруженный объект не содержит данных об анимации, использование этой команды может вызвать ошибку. Создавая анимированный объект в специальной программе, обратите внимание на плавность анимации в цикле, в противном случае будет наблюдаться дергающееся движение при переходе модели от последнего к первому кадру анимации. 
2. Управление при помощи мыши 
Цель урока: объяснить, как использовать команды мыши для перемещения камеры. 
Во многих играх используется такое свойство настройки, как "mouse look", позволяющая поворачивать камеру перемещением мыши. Это более эффективный метод управления камерой, чем управление с клавиатуры. 
Sync On 
Sync Rate 30 
hide mouse 
Backdrop on 
Set camera range 1,5000 
Fog on 
Fog distance 4000 
Fog color RGB(128,128,128) 
Color Backdrop RGB(128,128,128) 
Rem Создаем матрицу 
Make matrix 1,10000,10000,20,20 
Rem Текстурируем матрицу 
Load image "grass09.bmp",1 
Prepare matrix texture 1,1,1,1 
Fill matrix 1,0,1 
rem Присваиваем координатам вершин матрицы случайные значения 
randomize matrix 1,125 
X#=5000 
Z#=5000 
Rem Основной цикл 
Do 
 OldCamAngleY# = CameraAngleY# 
 OldCamAngleX# = CameraAngleX# 
 CameraAngleY# = WrapValue(CameraAngleY#+MousemoveX()*0.2) 
 CameraAngleX# = WrapValue(CameraAngleX#+MousemoveY()*0.2) 
 Rem  Управление перемещением камеры 
 If Upkey()=1 
  XTest# = Newxvalue(X#,CameraAngleY#,10) 
  ZTest# = Newzvalue(Z#,CameraAngleY#,10) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   X#=XTest# 
   Z#=ZTest# 
  Endif 
 Endif 
 If Downkey()=1 
  XTest# = Newxvalue(X#,Wrapvalue(CameraAngleY#-180),10) 
  ZTest# = Newzvalue(Z#,Wrapvalue(CameraAngleY#-180),10) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   X#=XTest# 
   Z#=ZTest# 
  Endif 
 Endif 
  
 Yrotate camera CurveAngle(CameraAngleY#,OldCamAngleY#,24) 
 Xrotate camera CurveAngle(CameraAngleX#,OldCamAngleX#,24) 
  
 Y# = Get ground height(1,X#,Z#) 
 Position Camera X#,Y#+50,Z# 
 Rem Обновляем экран 
 Sync 
Loop 
Вы уже заметили, что мы используем тот же код, что и в предыдущем уроке. В последующих уроках мы продолжим "надстраивать" этот код, чтобы в итоге создать завершенную игру. 
 OldCamAngleY# = CameraAngleY#> 
 OldCamAngleX# = CameraAngleX# 
 CameraAngleY# = WrapValue(CameraAngleY#+MousemoveX()*0.2) 
 CameraAngleX# = WrapValue(CameraAngleX#+MousemoveY()*0.2) 
Здесь в основном цикле мы сохраняем значения углов поворота камеры по осям Y и X в переменных "OldCamAngle" и после этого присваиваем новые значения переменным CameraAngle. Мы используем команду "WrapValue", чтобы значения углов находились в диапазоне от 0 до 360 градусов. Чтобы изменить значение переменных "CameraAngle", мы прибавляем к ним значения, возвращаемые командами "MouseMove", умноженные на поправочный коэффициент 0,2. Команды "MouseMoveX" и MouseMoveY()" возвращают относительное значение пространственных координат, на которое переместилась мышь с момента последнего считывания данных. Если быстро перемещать мышь вверх, вниз, вправо и влево, то возвращаемое значение будет намного больше, чем то, которое бы было при медленном перемещении мыши. Возвращаемое значение также зависит от размера экрана, используемого программой. Перемещение мыши со средней скоростью обычно возвращает значения от 8 до 16. Если эти значения прибавлять непосредственно к значениям углов поворота камеры, то скорость ее поворота будет слишком высокой. Нормализация этой величины путем умножения возвращаемого значения перемещения мыши на поправочный коэффициент 0,2 приводит к более приемлемым результатам. 
 Yrotate camera CurveAngle(CameraAngleY#,OldCamAngleY#,24) 
 Xrotate camera CurveAngle(CameraAngleX#,OldCamAngleX#,24) 
  
 Y# = Get ground height(1,X#,Z#) 
 Position Camera X#,Y#+50,Z# 
После обработки всех значений, введенных с клавиатуры и мыши, мы поворачиваем камеру при помощи команд поворота камеры, используя команду "CurveAngle" для плавного перехода от старого угла положения камеры к новому. Первый параметр этой команды - новое значение угла, на который мы желаем повернуть камеру. Второй параметр - текущий угол поворота камеры. Третий параметр - скорость, с которой мы желаем перейти от старого к новому положению камеры. Команда "CurveAngle" изменяет это значение по кривой, в результате чего значение, используемое для вращения, находится в промежутке между старым и новым значением. 
Заметьте интересный эффект поворота камеры, получаемый путем замены следующих двух строки кода 
 OldCamAngleY# = CameraAngleY# 
 OldCamAngleX# = CameraAngleX# 
на 
 OldCamAngleY# = Camera Angle Y() 
 OldCamAngleX# = Camera Angle X() 
с использованием команд "Camera Angle". 
 . Боковое движение (стрейфинг) 
Цель урока: Показать способ реализации бокового движения (стрейфинга). 
Боковое движение (стрейфинг) - это отличный способ ловко уходить от ракет или в движении просматривать боковые пространства. Мы продемонстрируем, как реализовать этот режим с использованием клавиш "стрелка вправо" и "стрелка влево". Здесь мы приводим только ту часть кода, которая претерпела изменения по сравнению с предыдущим уроком. По мере добавления новых возможностей в нашей игре, код становится слишком большим, чтобы приводить его целиком. 
  
  
 If Leftkey()=1 
  XTest# = Newxvalue(X#,Wrapvalue(CameraAngleY#-90),10) 
  ZTest# = Newzvalue(Z#,Wrapvalue(CameraAngleY#-90),10) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   X#=XTest# 
   Z#=ZTest# 
  Endif 
 Endif 
 If Rightkey()=1 
  XTest# = Newxvalue(X#,Wrapvalue(CameraAngleY#+90),10) 
  ZTest# = Newzvalue(Z#,Wrapvalue(CameraAngleY#+90),10) 
  If XTest#>0 and XTest#<10000 and ZTest#>0 and ZTest#<10000 
   X#=XTest# 
   Z#=ZTest# 
  Endif 
 Endif 
Боковое движение реализуется с помощью команд "NewXValue" и "NewYValue", предназначенных для определения значений X# и Z# следующей позиции. Для определения координат перемещения влево, мы сначала вычитаем 90 градусов из текущего значения угла поворота камеры "CameraAngleY#", чтобы обозначить направление движения. Чтобы значение угла не превышало 360 градусов, мы применяем команду "WrapValue". Используя значение этого угла в командах "NewValue", мы определяем координаты точки, находящейся в 10 единицах пространственных координат левее. Новые координаты сохраняются в переменных "XTest" и "ZTest". Затем производится проверка этих переменных, чтобы убедиться, что новые значения находятся в пределах матрицы. Если проверка оканчивается успешно, то новые значения сохраняются в переменных "X#" и "Y#" и используются для перемещения камеры. Как вы помните из урока I:2, ось Y проходит от верхней к нижней части экрана, поэтому любой поворот вокруг оси Y будет параллельным 
4. Стрельба 
Цель урока: показать, как реализовать стрельбу в режиме вида от первого лица. 
В этом уроке объясняется, как создать оружие и стрелять из него в режиме вида от первого лица. Как и в предыдущем уроке, мы добавляем новые строки к существующему коду. Мы обсудим только изменения в коде, связанные с реализацией оружия и стрельбы. 
rem Создаем пушку 
Make object cylinder 1,2 
XRotate Object 1,90 
Fix object pivot 1 
Scale object 1,100,100,500 
position object 1,0,-7,15 
Lock object on 1 
Эта часть кода, помещенная перед основным циклом, создает цилиндрический объект и поворачивает его под прямым углом по отношению к установленному по умолчанию виду из камеры. Чтобы сделать такую ориентацию объекта ориентацией по умолчанию, мы используем команду "Fix Object Pivot". Это означает, что углы поворота повернутого объекта устанавливаются равными 0 без смены ориентации объекта. Эта команда часто используется для загрузки моделей с нефиксированной ориентацией. Если загружается модель персонажа, а он лежит лицом вниз, то с помощью этой команды можно повернуть модель так, что персонаж будет стоять. Точка вращения объекта фиксируется для того, чтобы сделать текущее положение объекта положением по умолчанию. После выравнивания объекта по отношению к камере, мы используем команду "Scale Object" для масштабирования объекта в направлении оси Z. Таким образом, создается объект - вытянутый ствол пушки. Первый параметр этой команды - номер объекта для масштабирования. Следующие три параметра команды - направления X, Y и Z, в которых мы хотим масштабировать объект. Мы устанавливаем значение масштабирования по осям X и Y равным 100, что не приводит к изменению масштаба объекта в этих направлениях, так как эта величина представляет собой процентное отношение (100%). Масштабирование по оси Z мы указываем равным 500. Это означает, что размер объекта по оси Z будет увеличен в пять раз. Затем мы позиционируем объект. По умолчанию камера установлена в позицию с координатами (0,0,0) и углами поворота (0,0,0). Мы размещаем цилиндр на 7 единиц пространственных координат ниже камеры и на 15 единиц впереди камеры. Таким образом, создается сходство со стволом пушки. 
Rem Создаем снаряд 
Make Object Sphere 2,2 
Hide Object 2 
Этот код создает небольшой сферический объект, применяемый в игре в качестве снаряда. Чтобы скрыть объект, мы используем команду "Hide Object". Причина, по которой мы это делаем, заключается в том, что снаряд не должен быть виден до тех пор, пока мы не выстрелим из пушки. 
  if Mouseclick()=1 and BulletLife=0 
    Position object 2,X#,Y#+43,Z# 
    Set object to camera orientation 2 
    BulletLife =25 
    show object 2 
 Endif 
В этой части кода описывается ввод данных при нажатии кнопки мыши. Затем задаются координаты снаряда при выстреле из пушки. Чтобы определить, была ли нажата кнопка мыши, мы используем команду "MouseClick()". Эта команда возвращает номер нажатой кнопки мыши: 1 - для левой кнопки, 2 - для правой, и 0 - если ни одна из кнопок не была нажата. Сравнение "if" также проверяет, не был ли снаряд уже выпущен, сравнивая значение переменной "BulletLife" с 0. Если обе проверки проходят успешно, то объект "снаряд" помещается на семь единиц ниже камеры. После этого он позиционируется по отношению к камере при помощи команды "Set Object To Camera Orientation". Эта команда имеет только один параметр - номер позиционируемого объекта. Так как снаряд только что создан, мы устанавливаем значение переменной "BulletLife" равным 25. Чтобы программа произвела рендеринг объекта-снаряда, используем команду "Show Object". 
  If BulletLife > 0 
     Dec BulletLife 
     Move object 2,20 
     if BulletLife = 0 then Hide object 2 
  Endif 
Эта часть кода отвечает за перемещение снаряда. Если значение переменной "BulletLife" больше нуля, то выполняется команда "Dec", уменьшающая это значение на единицу. Одновременно снаряд перемещается в направлении выстрела на 20 единиц. Затем снова проверяем значение переменной "BulletLife". Если оно равно 0, то "жизнь" снаряда закончилась и мы снова "прячем" этот объект. 
Теперь вы можете добавить стрельбу в вашу игру. 
 5. Обычный звук и 3D-звук 
Цель урока: показать, как загрузить и позиционировать источники 3D-звука. 
В предыдущем уроке мы показали, как можно создать снаряд и стрелять. А сейчас добавим звуковые эффекты, чтобы выстрел в игре был более натуральным. Поскольку мы будем достаточно часто использовать переменные "CameraAngle", мы сократили их до аббревиатуры "cA" для удобства чтения кода. 
 Load sound "crickets.wav",1 
 Loop sound 1 
Прежде чем мы рассмотрим 3D-звук, нам не помешает добавить в игру какой-нибудь звук окружающей среды. Команда "Load Sound" загружает звук в программу, но не воспроизводит его. Первый параметр этой команды - имя загружаемого звукового файла. Как и в любой другой команде, где требуется указывать имя файла, имя файла должно быть заключено в кавычки. Второй параметр команды - присвоенный этому звуку номер. DarkBASIC использует этот номер точно так же, как и при загрузке объектов или изображений. Номер является уникальным идентификатором для этого звука. Команда "Loop Sound" использует один параметр, номер воспроизводимого звука. Эта команда постоянно воспроизводит указанный звуковой файл. Понадобилось всего две команды, чтобы озвучить игру стрекотанием сверчков! 
 Load 3Dsound "fireball2.wav",2 
Команда "Load 3DSound" загружает звук выстреливаемого снаряда. Первый параметр этой команды - имя загружаемого звукового файла, а второй параметр - присвоенный этому звуку номер. Загружая трехмерный звук, мы сообщаем DarkBASIC, что желаем поместить источник звука в определенной точке 3D-сцены. 
 Rem Поворачиваем камеру 
 if cAX#>270 
  if cAX#-270 > 90 then cAX#=270 
 else 
  if cAX# > 90 and cAX#-270<270 then cAX#=90 
 endif 
 YRotate camera CurveAngle(cAY#,oldcAY#,24) 
 XRotate camera CurveAngle(cAX#,oldcAX#,24) 
Эта часть кода добавлена в игровую программу для того, чтобы игрок случайно не перевернул камеру. Углы камеры также должны содержать корректную информацию для правильного позиционирования слушателя 3D-звука. 
 Rem Позиционируем слушателя 
 Position Listener X#,Y#+50,Z# 
 Rotate Listener 0,cameraAngleY#,0 
Команда "Position Listener" используется для позиционирования источника звука. Три параметра после команды обозначают координаты X, Y и Z, где вы хотите поместить слушателя. Мы будем использовать ту же позицию, в которой расположена камера. Команда "Rotate Listener" служит для поворота слушателя. Ее параметры - это углы X, Y и Z. Чтобы направление слушателя совпадало с направлением камеры, мы оставим значения углов X и Z равными 0, и будем учитывать только вращение вокруг оси Y. Для определения значения Y используем переменную "CameraAngleY#". 
 Rem Выстреливаем снаряд 
 if Mouseclick()=1 and BulletLife=0 
  Position object 2,X#,Y#+43,Z# 
  Set object to camera orientation 2 
  BulletLife =25 
  show object 2 
  Loop sound 2 
 Endif 
В код, где мы проверяем, была ли нажата кнопка мыши, мы добавили команду "Loop sound", чтобы начал воспроизводиться звук выстреливаемого снаряда. Этот звук будет длиться до тех пор, пока не будет остановлен кодом, который проверяет время "жизни" снаряда. 
If BulletLife > 0 
  Dec BulletLife 
  Move object 2,20 
  bX#=Object position X(2) 
  bY#=Object position Y(2) 
  bZ#=Object position Z(2) 
  Position sound 2,bX#,bY#,bZ# 
  if BulletLife = 0 
   Hide object 2 
   stop sound 2 
  endif 
 Endif 
В код стрельбы мы добавили две команды для обработки позиции и окончания звука. Команда "Position Sound" используется для размещения звука летящего снаряда в указанных 3D-координатах. Первый параметр этой команды - номер позиционируемого звука. Следующие три параметра определяют позицию, в которую помещен источник звука. Мы получили значения координат X, Y и Z снаряда и сохранили их в переменных "'bX#", "bY#" и "bZ#". Это и есть значения, используемые нами для позиционирования источника звука снаряда. Как только программа определит, что величина "BulletLife" достигла значения 0, применяется команда "Stop Sound" для прекращения воспроизведения звука. 
6. Математические столкновения 
Цель урока: показать, как реализовать простые математические столкновения. 
Сейчас мы объясним, как использовать математические столкновения для проверки столкновения снаряда и цели. Математическое столкновение вычисляется быстрее обычного столкновения, так как последнее должно проверить большее число объектов в программе. Применив простую функцию математического столкновения, мы можем узнать, насколько близко снаряд находится от цели. Если расстояние между ними достаточно мало, мы можем с достаточной уверенностью сказать о том, что они столкнулись. 
  
If BulletLife > 0 
  Dec BulletLife 
  Move object 2,20 
  bX#=Object position X(2) 
  bY#=Object position Y(2) 
  bZ#=Object position Z(2) 
  Position sound 2,bX#,bY#,bZ# 
  set cursor 10,10 
   if Sqrt((mX# - bX#)^2 + (mY#+25 - bY#)^2 + (mZ# - bZ#)^2) <20 
   text 240,220 ,"Цель поражена" 
    BulletLife = 0 
  endif 
  if BulletLife = 0 
   Hide object 2 
   stop sound 2 
  endif 
 Endif 
Следующие строки добавлены в ту часть кода, в которой проверяется время "жизни" снаряда. 
  if Sqrt((mX# - bX#)^2 + (mY#+25 - bY#)^2 + (mZ# - bZ#)^2) <20 
   text 240,220 ,"Цель поражена" 
    BulletLife = 0 
  endif 
Это сравнение использует математическую функцию "Sqrt" для расчета расстояния между двумя объектами. Если расстояние меньше 20, то на экран выводится сообщение о том, что игрок поразил цель, и объект-снаряд прекращает свое существование путем установки времени "жизни" снаряда равным 0. Функция математического столкновения вычитает из значения координаты X цели, которое хранится в переменной mX#, значение координаты X снаряда. Затем это число умножается само на себя при помощи экспоненциальной операции " ^ ". Аналогичные вычисления осуществляются в отношении координат Y и Z. Заметьте, что мы добавили 25 к координате Y цели, так как это будет примерно соответствовать координате середины стоящей цели. Полученные значения складываются, и вычисляется квадратный корень полученной суммы. Окончательное значение можно рассматривать как расстояние между двумя объектами. Советуем запомнить эту строку кода на случай, если вам когда-либо потребуется определять расстояние между двумя точками в 3D-сцене. 
Если вы не знакомы с квадратными корнями и экспонентами, вам придется обратиться к справочнику по математике, чтобы в деталях уяснить, как вычисляется расстояние. Знание того, чем являются переменные и значение, рассчитываемое по формуле, должно помочь вам в дальнейшем использовать эту часть кода в ваших собственных программах. 
7. Эффект шлейфа (particle system) 
Цель урока: создание простого эффекта шлейфа. 
В этом уроке мы объясним, как сделать хвост из огня и дыма с использованием простого эффекта шлейфа. Шлейф определяется как совокупность объемных частиц, движущихся в соответствии с неким набором правил. Правила могут определять положение, скорость, направление движения, цвет, вращение, форму, размер, прозрачность и многие другие атрибуты, используемые для создания таких эффектов, как дым, огонь, водопад или рост листьев на дереве. Применение шлейфа помогает создавать многочисленные реалистичные эффекты. Для наших объемных частиц мы будем использовать плоский объект с текстурой огня и дыма. Настраиваемые свойства - это положение, размер, прозрачность и угол поворота. 
rem Загружаем частицы (шлейф) 
Load Image "fire.bmp",2 
For x  = 0 to 10 
 Make object plain x+10,5,5 
 Texture object x+10,2 
 Set object x+10,1,0,0 
 Ghost object on x+10 
Next x 
rem Включаем счетчик частиц 
Pn=10 
Эту часть кода следует поместить в начале программы, где мы загружаем все наши объекты и звуки. Прежде всего, загружаем текстуру для частиц и создаем десять плоских объектов. Команда "Set Object" используется для установки особых свойств объекта. Семь настраиваемых свойств объекта - это каркас, прозрачность, выбраковка, фильтр, источник света, туман и окружение. Нам потребуются только три из них: каркас, прозрачность и выбраковка. Первый параметр после команды - номер используемого объекта. Второй параметр - атрибут каркаса. Устанавливаем его равным 1, чтобы объект отображался заполненным полигонами, а не в виде каркаса. Это установки по умолчанию для большинства 3D-объектов. Третий параметр - атрибут прозрачности. Мы устанавливаем его равным 0. В данном контексте прозрачность означает, что черный цвет на растровых изображениях не будет показан при текстурировании объекта. Четвертый параметр - атрибут выбраковки. Этот параметр устанавливается равным 0, если в 3D-сцене требуется рендеринг обеих, видимой и обратной, сторон полигона. Обычно требуется рендеринг только одной стороны полигона. В большинстве случаев нет необходимости прорисовывать обе стороны, но поскольку мы используем плоский объект, не имеющий объема, то должны все-таки сделать это, чтобы наблюдатель мог видеть полигон отовсюду. Еще одна новая команда "Ghost Object" применяется для того, чтобы сделать объект прозрачным. При этом он появится подобно привидению. С помощью этой команды можно включать и выключать режим прозрачности. Команда использует один параметр - номер объекта, который надо сделать прозрачным. Мы также инициализируем переменную "Pn", и она становится равной номеру первой созданной нами частицы. Переменная "Pn" будет использоваться для хранения номера следующей обрабатываемой частицы. 
  If BulletLife > 0 
  Dec BulletLife 
  Move object 2,10 
  bX#=Object position X(2) 
  bY#=Object position Y(2) 
  bZ#=Object position Z(2) 
  Rem Шлейф частиц 
  inc Pn 
  if Pn=20 then Pn=10 
  Scale object Pn,100,100,100 
  Position object Pn,bX#,bY#,bZ# 
  point object Pn,X#,Y#,Z# 
  Zrotate object Pn,rnd(180) 
  for x = 1 to 10 
   scale object int((Wrapvalue((Pn-9+x)*36))/36)+10,100+x*25,100+x*25,100+x*25 
  set cursor 10,10 
  print int((Wrapvalue((Pn+x)*36))/36)+10 
  next x 
  Position sound 2,bX#,bY#,bZ# 
  set cursor 10,10 
  if Sqrt((mX# - bX#)^2 + (mY#+25 - bY#)^2 + (mZ# - bZ#)^2) <20 
   print "Цель поражена" 
   BulletLife = 0 
  endif 
  if BulletLife = 0 
   Hide object 2 
   stop sound 2 
  endif 
 Endif 
Здесь мы добавили простой шлейф в часть игрового кода, вычисляющую время "жизни" снаряда. 
 inc Pn 
  if Pn=210 then Pn=10 
  Scale object Pn,100,100,100 
  Position object Pn,bX#,bY#,bZ# 
  point object Pn,X#,Y#,Z# 
  Zrotate object Pn,rnd(180) 
  for x = 1 to 10 
   scale object int((Wrapvalue((Pn-9+x)*36))/36)+10,100+x*25,100+x*25,100+x*25 
  next x 
Когда снаряд начинает свою "жизнь", мы увеличиваем значение переменной "Pn", чтобы задействовать следующий объект системы частиц в множестве. Затем проверяем, не превысило ли значение переменной "Pn" числа созданных нами частиц, и если превысило, устанавливаем "Pn" равным 10. Возвращаем масштаб частицы в первоначальное значение и помещаем ее туда, где находится снаряд. Затем направляем частицу на камеру и поворачиваем ее на угол со случайным значением. Затем в цикле For … Next с помощью математической формулы масштабируем каждую частицу, чтобы казалось, что дым рассеивается. Попутно решаем задачу, какая частица находится в начале хвоста дыма, а какая - в его конце. Для этого воспользуемся уже знакомой командой "WrapValue". Обычно она применяется для того, чтобы значения углов не превышали 360 градусов. Так как мы используем 10 объектов, то вычитаем 9 из значения "Pn". Величина "Pn" всегда будет меняться от 10 до 19, что соответствует 10 объектам. Затем мы добавляем значение переменной "х" и умножаем все на 36. Если значение "Pn" равно 5, и мы находимся на шестой итерации цикла, тогда значение параметра "WrapValue" будет равно 11, помноженному на 36, или 396. Эта величина явно превышает 360, поэтому функция "WrapValue" делает значение переменной равным 36. Затем мы делим 36 на 36 и получаем значение 1. Оно является номером следующей после десятого объекта частицы, которая будет первой.  
По существу, этот алгоритм последовательно, циклически перебирает все значения от 1 до 10, причем неважно, с какого значения мы начали. Поиск необычных решений трудных задач - неотъемлемая часть программирования. Можно по-разному решить задачу, и наиболее простой способ часто оказывается наилучшим. Этот пример также показывает вам, что вы не обязаны использовать команды DarkBASIC каким-то одним определенным способом. Нетрадиционное мышление приводит ко многим необычным решениям. Каждый объект масштабируется в соответствии с его позицией в хвосте дыма, представленной значением переменной цикла. Это дает эффект рассеивающегося дыма. 
III. ИГРА 
8. Gosub 
Цель урока: показать действие команды Gosub. 
В этом уроке мы продемонстрируем действие команды "Gosub". Мы будем использовать "Gosub" для того, чтобы упорядочить и упростить программу, когда начнем добавлять к ней большое количество функций, а также чтобы сделать более понятным то, что происходит в ее основном цикле. 
   If BulletLife > 0 then Gosub ShootBullet 
Мы заменили несколько строк кода для проверки времени "жизни" снаряда всего одной строкой. Команда "Gosub" применяется для того, чтобы выполнить код, расположенный в другом месте программы, а затем вернуться туда, откуда был произведен вызов команды "Gosub". Здесь мы вызываем подпрограмму "ShootBullet". 
  ShootBullet: 
  Dec BulletLife 
  Move object 2,10 
  bX#=Object position X(2) 
  bY#=Object position Y(2) 
  bZ#=Object position Z(2) 
  inc Pn 
  if Pn=21 then Pn=10 
  Scale object Pn,100,100,100 
  Position object Pn,bX#,bY#,bZ# 
  point object Pn,X#,Y#,Z# 
  Zrotate object Pn,rnd(180) 
  for x = 1 to 10 
   scale object int((Wrapvalue((Pn-9+x)*36))/36)+10,100+x*25,100+x*25,100+x*25 
  set cursor 10,10 
  next x 
  if bY# < Get Ground height(1,bX#,bZ#) then BulletLife=0 
  Position sound 2,bX#,bY#,bZ# 
  set cursor 10,10 
  if Sqrt((mX# - bX#)^2 + (mY#+25 - bY#)^2 + (mZ# - bZ#)^2) <20 
   print "Цель поражена" 
   BulletLife = 0 
  endif 
  if BulletLife = 0 
   Hide object 2 
   stop sound 2 
   for x=10 to 20 
    hide object x 
   next x 
   Explode = 20 
  endif 
Return 
 Чтобы объявить подпрограмму, ей нужно задать уникальное имя и расположить его в начале подпрограммы. После имени подпрограммы нужно поставить двоеточие, иначе имя не будет распознано, что вызовет ошибку. В том месте, где заканчивается код подпрограммы, следует поместить команду "Return", чтобы исполнение кода было продолжено с того места, откуда была вызвана подпрограмма. Этот метод значительно уменьшает беспорядок в основном цикле, позволяя легче понять, что в нем происходит. Четко разработав структуру подпрограмм, вы избавите себя от многих часов головной боли по поводу упорядочивания структуры вашего 
9. Эффект взрыва 
Цель урока: создание простого эффекта взрыва. 
В этом уроке мы покажем, как создать простой эффект взрыва, когда снаряд попадает в цель, в землю или просто заканчивается время его "жизни". 
rem Создаем эффект взрыва 
Make Object Sphere 30,20 
texture object 30,2 
ghost object on 30 
Hide Object 30 
Make Object Sphere 31,20 
texture object 31,2 
ghost object on 31 
Hide Object 31 
Load 3Dsound "Explode.wav",3 
Эти строки кода надо поместить в начало программы, где мы загружаем объекты, звуки и изображения. Здесь мы создаем объекты, используемые для эффекта взрыва. Заметьте, что мы применяем ту же текстуру, что и для создания огня в шлейфе. Мы также загрузили звуковой файл, который будем использовать в качестве источника звука взрыва. 
 if Mouseclick()=1 and Explode =0 
  if BulletLife<50 
   Position object 2,X#,Y#-7,Z# 
   Set object to camera orientation 2 
   BulletLife =120 
   show object 2 
   Loop sound 2 
  Endif 
 Endif 
Как видите, мы добавили в программу переменную "Explode". Проверяем, не произошел ли взрыв. Если взрыв происходит в данный момент, то не будем выстреливать еще один снаряд, потому что это может резко обрезать звук взрыва, уменьшая действие эффекта. Мы также установили интервал, через который снаряд может быть снова выпущен. Новый снаряд можно будет выстрелить, если при проверке окажется, что время "жизни" снаряда меньше 50. Это сделано для того, чтобы убедиться, что снаряд находится достаточно далеко, и отсутствие эффекта взрыва не будет замечено игроком. 
   if bY# < Get Ground height(1,bX#,bZ#) then BulletLife=0 
Эта строка кода находится в подпрограмме "ShootBullet". Она проверяет, не попал ли снаряд в землю, и устанавливает значение переменную "BulletLife" равное 0 при положительном результате. 
  if BulletLife = 0 
   Hide object 2 
   stop sound 2 
   for x=10 to 20 
    hide object x 
   next x 
   Explode = 20 
  endif 
Эта часть кода также находится в подпрограмме "ShootBullet". Код выполняется после того, как время "жизни" снаряда установлено равным 0, либо по причине столкновения с чем-либо, либо по причине окончания "жизни" снаряда. При этом убираются все объекты шлейфа, равно как и сам объект "снаряд". Переменной взрыва присвоено значение 20 для начала отсчета взрыва. 
If Explode > 0 then Gosub ExplodeRocket 
Эта строка кода располагается в нашем основном цикле. Если счетчик взрыва больше 0, то для выполнения кода взрыва вызывается подпрограмма "ExplodeRocket". 
Rem Взрыв снаряда 
ExplodeRocket: 
 Position object 30,bX#,bY#,bZ# 
 Show object 30 
 Position object 31,bX#,bY#,bZ# 
 Show object 31 
 EScale=20*(30-Explode) 
 Scale object 30,EScale,EScale,EScale 
 Yrotate object 30,WrapValue(Explode*37) 
 Scale object 31,EScale/1.5,EScale/1.5,EScale/1.5 
 Yrotate object 31,WrapValue(359-Explode*37) 
 Dec Explode 
 If Explode = 0 then hide object 30: Hide object 31 
 If Explode=18 
  position sound 3,bX#,bY#,bZ# 
  play sound 3 
 endif 
 If Explode < 15 then position sound 3,X#,Y#,Z# 
Return 
Подпрограмма "ExplodeRocket" производит взрыв. В место попадания снаряда мы помещаем две сферы, созданные для эффекта взрыва. Затем мы рассчитываем коэффициент масштабирования взрыва и сохраняем его в переменной "EScale". Затем масштабируем первую сферу и вращаем ее со скоростью, уменьшающейся по мере того, как сфера становится больше. То же самое мы делаем со второй сферой, только ее мы масштабируем с меньшей скоростью, чем первую сферу, а вращаем с большей скоростью, основанной на значении "Explode". После этого мы применяем команду "Dec" для уменьшения значения "Explode" на единицу. Потом проверяем, равно ли значение этой переменной 0. Если оно равно 0, обе сферы взрыва убираются. Заметьте, что мы использовали двоеточие, чтобы выполнить две команды "HideObject" в одной строке. Мы могли бы применить сравнение "If … EndIf", но двоеточие в качестве разделителя делает наш код короче и легче читаемым. Следующие строки кода создают звуковой эффект, когда вы сначала видите, как что-то происходит вдали, а затем слышите взрыв. Далее код задает ожидание, пока значение счетчика не достигнет 18, и только после этого начинает воспроизводить звук. Программа помещает источник звука в конечной точке полета снаряда. Когда счетчик достигает 15, источник звука перемещается в координаты камеры, что создает эффект отдаленного взрыва. За счет того, что используется команда "Play Sound", а не "Loop Sound", звук воспроизводится только один раз. 
10. Монстр отстреливается 
Цель урока: показать, как создать игровую ситуацию, когда монстр начинает отстреливаться. 
В этом уроке мы создадим управляемую ракету монстра, а также определим метод, с помощью которого монстр сможет определить местонахождение игрока. 
rem Создаем снаряд монстра 
Make Object Sphere 102,2 
texture object 102,2 
Hide Object 102 
Эта часть кода, создающая сферу для ракеты монстра, помещается в программе до основного цикла. Заметьте, что в качестве текстуры объекта мы используем текстуру под номером 2. Эта та же самая текстура, которую мы применяли для ракеты игрока и других эффектов. Повторное применение текстур сокращает объем памяти, используемой вашей программой. 
rem Создаем эффект взрыва для монстра 
Make Object Sphere 130,20 
texture object 130,2 
ghost object on 130 
Hide Object 130 
Make Object Sphere 131,20 
texture object 131,2 
ghost object on 131 
Hide Object 131 
Этот код также вставляется в программу до основного цикла. Он создает объекты взрыва для ракеты монстра. 
rem Загружаем шлейф ракеты монстра 
Load Image "fire.bmp",2 
For x  =100 to 110 
 Make object plain x+10,5,5 
 Texture object x+10,2 
 Set object x+10,1,0,0 
 Ghost object on x+10 
 Hide object x+10 
Next x 
MPn=110 
Как вы заметили, эта часть кода взята из эффекта шлейфа снаряда игрока. Сам код остался прежним, изменились только номера объектов. Счетчик частиц шлейфа ракеты монстра "MPn" установлен в значение 110, что соответствует номеру первого объекта в совокупности частиц этого шлейфа. 
Load 3Dsound "fireball2.wav",102 
Load 3Dsound "Explode.wav",103 
Этот код загружает звуковые эффекты для ракеты монстра. Заметьте, что мы не задействовали повторно звуковые эффекты, использованные ранее для снаряда игрока. Мы загрузили новые звуки, потому что звуковые эффекты для снарядов игрока и монстра могут воспроизводиться одновременно. 
 If MonsterBulletLife > 0 then Gosub MonsterShootBullet 
 If MonsterExplode > 0 
  Gosub MonsterExplodeRocket 
 else 
   Gosub MonsterAI 
 endif 
Этот код помещается в нашем основном цикле. Здесь производится проверка времени "жизни" ракеты монстра, продолжительности ее взрыва и выполняются соответствующие подпрограммы, как и в отношении снаряда игрока. Добавилась лишь подпрограмма "MonsterAI". Мы ведь не хотим, чтобы монстр пытался стрелять в то время, когда взрывается предыдущий снаряд. Чтобы убедиться, что этого не происходит, мы используем сравнение "If Then Else". 
Rem Простой AI для управляемой ракеты монстра 
MonsterAI: 
   Point object 3,X#,Y#,Z# 
   PDist=Sqrt((mX# - X#)^2 + (mY#+25 - Y#)^2 + (mZ# - Z#)^2) 
   if PDist<1500 
    if MonsterBulletLife=0 or MonsterBulletLife < 500-PDist/10 
     Point object 3,X#,Y#-25,Z# 
     Position object 102,mX#,mY#+25,mZ# 
     Set object to object orientation 102,3 
     MonsterBulletLife =500 
     show object 102 
     Loop sound 102 
    Endif 
   endif 
Return 
Когда вызывается подпрограмма"MonsterAI", объект "монстр" направлен прямо на игрока. Рассчитывается расстояние между игроком и монстром. Если оно меньше 1500, то продолжается выполнение кода. Если игрок находится вне зоны видимости, ничего не происходит и подпрограмма далее не выполняется, а продолжает выполняться код из основного цикла. Если игрок находится в пределах досягаемости, программа проверяет, не закончилось ли время "жизни" ракеты и не попала ли она в игрока. Способ, с помощью которого мы проверяем, попала ли ракета в цель, заключается в вычислении значения "500-PDist/10". Ракета монстра пролетает 12 единиц координат в единицу своей "жизни". Время ее жизни составляет 500, таким образом, она может преодолеть расстояние в 6000 единиц. Если мы разделим расстояние, или величину переменной "PDist", на 12, то получим число единиц "жизни", которое ракета монстра затратила на подлет к игроку. Если расстояние между монстром и игроком составляет 1200 единиц, то необходимое на подлет к игроку число единиц "жизни" ракеты будет равно 100. Если мы вычтем эту величину из общего числа единиц "жизни" ракеты, то получим 400. Если это значение больше, чем значение переменной "MonsterBulletLife", тогда мы знаем, что ракета не попала в игрока. Мы делим расстояние на 12, а не на 10, что позволяет снаряду пролететь немного дальше, прежде чем можно будет выстрелить еще раз. Это делает игру более реалистичной, позволяя ракете пролететь мимо игрока. Если значение переменной "MonsterBulletLife" позволяет выстрелить еще раз, ракета монстра нацеливается прямо на координаты игрока. Ракете монстра задаются начальные и конечные координаты, устанавливается время ее "жизни" равное 500, объект "ракета" становится видимым и начинает воспроизводиться ее звук. 
Rem Выстрел ракеты монстра 
MonsterShootBullet: 
  Dec MonsterBulletLife 
  Move object 102,12 
  MbX#=Object position X(102) 
  MbY#=Object position Y(102) 
  MbZ#=Object position Z(102) 
  inc MPn 
  if MPn=121 then MPn=110 
  Scale object MPn,100,100,100 
  Position object MPn,MbX#,MbY#,MbZ# 
  Position sound 102,MbX#,MbY#,MbZ# 
  point object MPn,X#,Y#,Z# 
  Zrotate object MPn,rnd(180) 
  Show object MPn 
  for x = 1 to 10 
   scale object int((Wrapvalue((MPn-9+x)*36))/36)+110,100+x*25,100+x*25,100+x*25 
  next x 
  if MbY# < Get Ground height(1,MbX#,MbZ#) then MonsterBulletLife=0 
  Pdist=Sqrt((X# - MbX#)^2 + (Y#+25 - MbY#)^2 + (Z# - MbZ#)^2) 
  if  Pdist<50 
   GoSub PlacePlayer 
   MonsterBulletLife = 0 
  endif 
  Rem Управляемая ракета 
  if Pdist <500 and Pdist>250 then Point object 102,X#,Y#,Z# 
  if Pdist < 100 then point object 102,X#,Y#,Z# 
  if MonsterBulletLife = 0 
   Hide object 102 
   stop sound 102 
   for x=110 to 120 
    hide object x 
   next x 
   MonsterExplode = 20 
  endif 
Return 
Подпрограмма "MonsterShootBullet" очень похожа на подпрограмму "ShootBullet", однако мы добавили несколько строк для того, чтобы сделать ракету монстра управляемой. Это создает игровое напряжение и бросает вызов игроку, пытающемуся уклониться от ракеты монстра. 
  Rem Управляемая ракета самонаведения 
  if Pdist <500 and Pdist>250 then Point object 102,X#,Y#,Z# 
  if Pdist < 100 then point object 102,X#,Y#,Z# 
Это код для управляемой ракеты самонаведения. Если ракета находится на расстоянии от 500 до 250 единиц от игрока, она самонаводится по текущим координатам игрока. Находясь на расстоянии 100 единиц от игрока, ракета снова получает текущие координаты цели. Это дает игроку немного времени на попытку уклониться от ракеты. 
Rem Взрыв ракеты монстра 
MonsterExplodeRocket: 
 Position object 130,MbX#,MbY#,MbZ# 
 Show object 130 
 Position object 131,MbX#,MbY#,MbZ# 
 Show object 131 
 EScale=20*(30-MonsterExplode) 
 Scale object 130,EScale,EScale,EScale 
 Yrotate object 130,WrapValue(MonsterExplode*37) 
 Scale object 131,EScale/1.5,EScale/1.5,EScale/1.5 
 Yrotate object 131,WrapValue(359-MonsterExplode*37) 
 Dec MonsterExplode 
 If MonsterExplode = 0 then hide object 130: Hide object 131 
 If MonsterExplode=18 
  position sound 103,X#,Y#,Z# 
  play sound 103 
 endif 
 If MonsterExplode < 15 then position sound 103,X#,Y#,Z# 
Return 
Код подпрограммы "MonsterExplodeRocket" такой же, как и в подпрограмме "ExplodeRocket". Единственное, что отличает эти две подпрограммы - это переменные координат ракеты монстра и номера объектов и звуков. 
11. Как вести счет 
Цель урока: показать, как ведется счет игры. 
В этом уроке мы покажем, как отслеживать счет игры для игрока и монстра, а также как перемещать игрока и монстра в новое положение, когда их поражает снаряд или ракета. 
Gosub PlaceMonster 
Gosub PlacePlayer 
Мы поместили две команды "Gosub" до основного цикла. Соответствующие подпрограммы помещают игрока и монстра на случайные позиции матрицы. 
PlaceMonster: 
   mX#=X# 
   mZ#=Z# 
   mY#=Y# 
   While Sqrt((X# - MX#)^2 + (Y#+25 - MY#)^2 + (Z# - MZ#)^2)<1600 
     mX#=rnd(10000) 
     mZ#=rnd(10000) 
     mY#= get ground height(1,mX#,mZ#) 
   EndWhile 
   Position object 3,mX#,mY#,mZ# 
Return 
Подпрограмма "PlaceMonster", предназначенная для установки случайных координат монстра на матрице, первоначально устанавливает для монстра те же координаты, что и координаты игрока и затем проверяет, чтобы монстр и игрок находились на достаточном расстоянии друг от друга. Первоначальная установка одинаковых координат является необходимым условием для генерации случайной позиции. В тексте этой подпрограммы мы используем цикл "While", чтобы обеспечить определенное условие: цикл повторяется до тех пор, пока расстояние между игроком и монстром не станет больше 1600. Для вычисления расстояния между игроком и монстром мы используем формулу вычисления расстояний из урока по математическому столкновению. Если расстояние меньше 1600, снова выполняется код поиска позиции. Новая случайная позиция рассчитывается и сохраняется в переменных положения монстра. Когда расстояние между игроком и монстром становится больше 1600, цикл завершается и монстр помещается в новом месте. 
PlacePlayer: 
   While Sqrt((X# - mX#)^2 + (Y#+25 - mY#)^2 + (Z# - mZ#)^2)<1000 
    X#=rnd(10000) 
    Z#=rnd(10000) 
    Y#= get ground height(1,X#,Z#) 
    EndWhile 
   Position camera X#,Y#,Z# 
  
Return 
Подпрограмма "PlacePlayer" аналогична подпрограмме "PlaceMonster". Различие только в том, что мы помещаем на матрицу игрока, а не монстра. Мы также направляем камеру в сторону монстра, чтобы его было легче найти. 
 set cursor 550,20 
 print "MScore: ",MonsterScore 
 set cursor 550,40 
 print "PScore: ",PlayerScore 
Этот код помещается внутри основного цикла. Используя команду "Set cursor", помещаем текст в правой части экрана. В позиции курсора выводим слова "MScore" и "PScore", чтобы показать игровой счет монстра и игрока. Затем выводим значения переменных "MonsterScore" и "PlayerScore". 
 if Sqrt((mX# - bX#)^2 + (mY#+25 - bY#)^2 + (mZ# - bZ#)^2) <20 
   Gosub PlaceMonster 
   inc PlayerScore 
   MonsterBulletLife = 1 
   BulletLife=0 
  endif 
Эта часть кода из подпрограммы "ShootBullet", которая проверяет, был ли монстр поражен снарядом игрока. Мы добавили в сравнение "If" две строки кода. Если монстр был поражен, вызывается подпрограмма "PlaceMonster", чтобы поместить его в новом месте. Затем мы увеличиваем счет игры в пользу игрока, изменяя переменную "PlayerScore" при помощи команды "Inc". 
Pdist=Sqrt((X# - MbX#)^2 + (Y#+25 - MbY#)^2 + (Z# - MbZ#)^2) 
  if  Pdist<50 
   GoSub PlacePlayer 
   MonsterBulletLife = 0 
   inc MonsterScore 
  endif 
Эта часть кода из подпрограммы "MonsterShootBullet", которая, как и предыдущая, проверяет, был ли поражен игрок ракетой монстра. В случае поражения, вызывается подпрограмма "PlacePlayer", помещающая игрока в новом месте. После этого мы увеличиваем счет игры в пользу монстра при помощи команды "Inc" и возвращаемся в подпрограмму "MonsterShootBullet". 
12. HUD 
Цель урока: создание "вида сверху" и простого экрана радара. 
В этом уроке мы покажем, как создать "вид сверху", также известный как HUD (heads up display). Чтобы упростить задачу поиска монстра, мы создадим экран радара на нашем виде сверху и прозрачный задний план для отображения счета игры. 
rem Создаем HUD 
Make object Plain 200,1,1 
position object 200,-2.7,1.9,4 
Lock object on 200 
ghost object on 200 
Эта часть кода помещается до основного цикла. Мы создаем плоский объект и привязываем его к положению камеры. Эта плоскость будет использоваться в качестве экрана радара. 
Make object Plain 201,1,1 
position object 201,2.7,1.9,4 
Lock object on 201 
ghost object on 201 
Эта часть кода также вводится до основного цикла. Плоский объект будет использоваться в качестве заднего плана для экрана отображения счета игры. 
rem Загружаем и создаем растровые изображения hud 
Load Bitmap "Radar.bmp",2 
Create Bitmap 1,50,50 
В этой части кода мы загружаем изображение радара и сохраняем его как изображение под номером 2. Растровое изображение отличается от текстуры. На нем можно рисовать и оно может быть изменено в самой программе. Особенно важным является растровое изображение под номером 0. Это изображение становится вашим экраном, на котором вы видите 3D-сцену. При вызове команды "Sync" именно это изображение перерисовывается и отображается. Мы загружаем изображение "Radar.bmp" в изображение под номером 2 и создаем новое пустое изображение, присваивая ему номер 1. Имейте в виду, что в DarkBASIC можно загрузить или создать не более 32 изображений. Команда "Load Bitmap" аналогична команде "Load Image". Первый параметр - имя загружаемого растрового изображения. Второй параметр - присвоенный ему номер - уникальный идентификатор изображения. Команда "Create Bitmap" создает в памяти пустое изображение указанного размера. Первый параметр - номер, присваиваемый изображению. Второй и третий параметры - это ширина и высота создаваемого растрового изображения. Параметры ширины и высоты представляют собой размеры изображения в пикселах. 
 Rem Создаем радар 
 Copy Bitmap 2,1 
 set current bitmap 1 
 ink rgb(0,0,255),rgb(0,0,0) 
 PRX#=X#/200 
 PRZ#=50-(Z#/200) 
 Circle PRX#,PRZ#,1 
 ink rgb(255,0,0),rgb(0,0,0) 
 MRX#=mX#/200 
 MRZ#=50-(mZ#/200) 
 Circle MRX#,MRZ#,1 
 Get image 200,0,0,50,50 
 set current bitmap 0 
 texture object 200,200 
 ink rgb(255,128,128),rgb(0,0,0) 
Этот код помещается ближе к концу основного цикла. Так создается, прорисовывается и накладывается на плоскость экран радара. 
 Copy Bitmap 2,1 
Команда "Copy Bitmap" копирует одно растровое изображение из памяти в другое, также находящееся в памяти. Первый параметр этой команды - номер копируемого изображения. Второй параметр - номер изображения, в которое вы хотите скопировать первое. Здесь мы копируем изображение, загруженное для заднего плана экрана радара в изображение, которое мы будем использовать в качестве экрана радара. 
 set current bitmap 1 
С помощью команды "Set Current Bitmap", вы указываете программе, что определенное изображение будет изображением для рисования по умолчанию. Чтобы вновь увидеть 3D-сцену, вы должны установить номер текущего изображения снова равным 0. Текущим мы делаем изображение радара. Оно содержит скопированное изображение, которое мы загрузили для заднего плана радара. 
 ink rgb(0,0,255),rgb(0,0,0) 
Команда "Ink" применяется для установки значения цвета переднего и заднего плана для любой команды рисования или текста. Первый параметр команды "Ink" - это цвет, используемый для рисования на переднем плане. Чтобы указать голубой цвет, мы применяем команду "RGB". Второй параметр - цвет заднего плана, или фона. При помощи команды "RGB" мы устанавливаем для него черный цвет. 
 PRX#=X#/200 
 PRZ#=50-(Z#/200) 
 Circle PRX#,PRZ#,1 
С помощью этих трех строк кода рисуется голубая окружность на изображении радара, представляющая игрока. Переменные "PRX#" и "PRZ#" используются для хранения координат игрока. Так как наша матрица - квадрат со стороной 100000 единиц, а растровое изображение радара - квадрат со стороной 50 пикселов, то мы делим 100000 на 50 и получаем 200. 200 - это переменная масштабирования. Мы делим значение координаты X игрока на 200, чтобы получить примерное значение координаты X на экране радара, и сохраняем эту величину в переменной "PRX#". Те же вычисления повторяем и для координаты по оси Z. Так как отсчет координат растрового изображения начинается в левом верхнем углу, а координат матрицы - в левом нижнем, мы берем масштабированное значение Z и вычитаем его из 50 для того, чтобы получить обращенное значение для экрана радара. Если этого не сделать, то экран радара будет отображать обращенные координаты. 
 Circle PRX#,PRZ#,1 
Мы используем координаты X и Z игрока на радаре, чтобы с помощью команды "Circle" нарисовать окружность по этим координатам на изображении радара. Первые два параметра этой команды - это координаты X и Y центра окружности на изображении. Третий параметр - радиус окружности. Мы установили этот параметр равным 1, чтобы нарисовать очень маленький кружок. 
 ink rgb(255,0,0),rgb(0,0,0) 
 MRX#=mX#/200 
 MRZ#=50-(mZ#/200) 
 Circle MRX#,MRZ#,1 
Эти строки кода аналогичны приведенным выше, только мы устанавливаем цвет красным и используем переменные X и Y монстра для рисования окружности, которая символизирует монстра на радаре. 
 Get image 200,0,0,50,50 
Команда "Get Image" копирует изображение или его часть для использования в качестве текстуры. Первый параметр этой команды - это номер создаваемого изображения. Второй и третий параметры - координаты X и Y левого верхнего угла прямоугольной области, которую мы желаем скопировать. Четвертый и пятый параметры - координаты правого нижнего угла этой прямоугольной области. Так как наше изображение имеет размеры 50х50 пикселов, мы копируем изображение целиком. Наше основное изображение - это по-прежнему изображение под номером 1, откуда и копируется изображение для текстуры. 
 set current bitmap 0 
 texture object 200,200 
 ink rgb(255,128,128),rgb(0,0,0) 
Последняя часть кода радара снова устанавливает в качестве текущего изображение под номером 0, в котором и происходит прорисовка нашей 3D-сцены. Затем мы накладываем на плоский объект текстуру из только что созданного изображения. Оно содержит картинку радара с нарисованными на ней двумя кружками, соответствующими игроку и монстру. Меняем цвет из красного на матовый красный. Этот цвет будет использоваться до обновления изображения на экране радара. Отметим, что значения счета в игре выводятся тем же цветом. 
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#v*1000-60 
      if Z#u*1000-60 
    if X#v*1000-60 
      if Z#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 градусов. Это дает возможность ракете монстра лететь по наилучшей траектории в направлении игрока. 
Как видите, мы не проверяем, вышел ли монстр за пределы матрицы. В этом нет необходимости, так как монстр всегда движется навстречу игроку, а игрок не может выйти за пределы матрицы. Эти несколько строк кода должны сделать игру более интересной и наделить монстра определенным характером в процессе его охоты на игрока. 

На главную страницу.

Hosted by uCoz