• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Уроки програмирования на Blitz3d.

  • Автор темы Zato
  • Дата начала
Z

Zato

Вот краткое описание Blitz3d

Софт предназначен для создания 2D и 3D игр для Windows. Работа с инструментом Blitz3D заключается именно в написании программного кода - пусть даже и не на пугающем новичков C++, а на одном из диалектов Basic'а. Многие достаточно известные игры написаны с его помощью (их список можно найти на официальном сайте программы), что, несомненно, поднимает статус этого инструмента в глазах game developer'ов. Для рендеринга движок Blitz3D использует DirectX. Характеристики: Blitz3D компилятор, IDE, отладчик, примеры кода, обучающие материалы и документация. Требования: Windows 95, 98, ME, 2000, XP; видео карта; DirectX7 или выше.

Лично я рекомендую его для людей, не знакомых или плохо знакомых с языками программирования, желающих создать собственное 3d приложение.

Итак, в первую очередь вам необходим скачать сам редактор, в котором у будем писать, а потом компилировать все приложения. Это можно сделать по ссылке

Далее я сразу рекомендую заменить хелп на русский Посмотреть вложение help.rar.

Сперва мы рассмотрим виды объектов в блице:

Код:
Объекты являются одними из главнейших составляющих любой компьютерной игры. Ниже перечисленны основные объекты в Blitz3D и их назначение:

Камеры (Cameras).

Объекты Камеры позволяют Вам видеть то, что происходит в Вашем игровом мире. Если Вы не сделаете хотябы одну камеру, Вы ничего неувидите. Камеры также управляют 3D экраном и обеспечивают различные атмосферные эффекты и туман.

Свет (Lights).

Световые объекты обеспечивают освещение Вашего трехмерного мира. Без любых огней, Вы будете все еще видеть то, что случается в мире благодаря отраженному свету. Отраженный свет - это такой свет, который освещает все одинаково. Однако, так как отраженный свет не имеет никакой позиции или направления, Вы останетесь без эффектов тени, и все будет выглядеть плоским.

3D объекты (Meshes).

3D объекты - это способ добавить физические элементы Вашему миру.3D объекты составлены из треугольников или прямоугольников (полигонов), и каждый треугольник или прямоугольник может быть окрашен или текстурирован. Путь добавления 3D объекта в игровой мир состоит в загрузке его из файла, сделанного в любом 3D редакторе. Вы можете также создавать Ваши собственные 3D объекты.

Спрайты (Sprites).

Спрайты - плоские 2D объекты, наиболее часто используемые для создания систем частиц, подобных взрывам, искрам, дыму, огню и струйкам воды. Спрайты автоматически стоят перед камерой, независимо от угла, под которым Вы рассматриваете их и часто текстуированы сферической текстурой.

Плоскости (Planes).

Объекты плоскости - бесконечно большие, плоские поверхности, которые могут использоваться для ряда эффектов типа земли или воды. Так как плоскости - объекты, они могут также перемещаться и вращаться, что означает, что Вы можете также использовать их для эффектов облаков и неба.

Ландшафты (Terrains).

Объекты ландшафты используются для создания больших ландшафтов. Ландшафты созданы из сетки треугольников, но не смотря на это огромные ландшафты визуализируются очень быстро. Ландшафты несильно требовательны к ресурсам компьютера. При создании 3D игр с открытыми пространствами Вам несомненно понадобятся ландшафты.

Центры (Pivots).

Объект центр фактически не делает ничего. Их основная цель состоит в том, чтобы быть родителем для других объектов. Это может быть полезно по многим причинам. Например, прикрепляя несколько 3D объектов к центру, Вы можете перемещать все 3D объекты сразу, просто перемещая центр, что существенно облегчает работу.

Сейчас я рассмотрю простенький пример из хелпа и поясню смысл каждой строки:

Код:
Graphics3D 640,480,32;инициализация 3D графики и создание окна программы
SetBuffer BackBuffer();установка буфером для рисования BackBuffer()


camera=CreateCamera();создаём камеру, через которую будем глядеть на мир и помещаем её указатель в переменную camera

light=CreateLight();создаём источник света, который нам увидеть куб и помещаем его указатель в переменную light
RotateEntity light,00,0,0;поворачивает источник света для того чтобы не свет от него падал на куб.

; Создание куба 
cube=CreateCube();создаём куб и помещаем его указатель в переменную cube

PositionEntity cube,0,0,5;отодвигаем куб от камеры по оси Z, чтобы его можно было увидеть

While Not KeyDown( 1 );начало цикла, который будет продолжаться до нажатия кнопки Esc
RenderWorld;отрисовка мира;необходима в каждом цикле
Flip;смена буфера;пока не важно
Wend;конец цикла

End;завершение программы.

Как вы наверно уже поняли, эта программа всего лишь выводит на экран белый квадрат. Хотя, на самом деле создаётся куб,но из-за того, что мы видим только одну грань, он нам кажется квадратом.

Чтобы убедиться, что куб действительно объёмный, попробуйте код:


Код:
Graphics3D 1024,768,32,1 ;инициализация 3D графики и создание окна программы
SetBuffer BackBuffer();установка буфером для рисования BackBuffer()

camera=CreateCamera();создаём камеру, через которую будем глядеть на мир и помещаем её указатель в переменную camera

light=CreateLight();создаём источник света, который нам увидеть куб и помещаем его указатель в переменную light
RotateEntity light,0,0,0;поворачивает источник света для того чтобы не свет от него падал на куб.

; Создание куба 
cube=CreateCube();создаём куб и помещаем его указатель в переменную cube

PositionEntity cube,5,0,5;отодвигаем куб от камеры по осям Z, и Х, чтобы его можно было увидеть

While Not KeyDown( 1 );начало цикла, который будет продолжаться до нажатия кнопки Esc

x=x+1;прибавляем к переменной 1
RotateEntity camera,0,x,0;назначаем камере поворот в х градусов, заметьте, что х может быть и больше 360, в таком случае угл поворота будет равен остатку от деления х на 360

RenderWorld;отрисовка мира;необходима в каждом цикле
Flip;смена буфера;пока не важно
Wend;конец цикла

End;завершение программы.
 
G

Gamlet

Интересно, хотя ничего нового не прочитал.
 
Z

Zato

Вопрос в том, надо ли продолжать уроки по B3D? Мне как бы не сложно, но несмотря на все мои попытки, народ сюда идти не хочет...
 
Z

Zato

Вот, решил, что пригодится. НА АВТОРСТВО НЕ ПРЕТЕНДУЮ.

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Учебник по Blitz3D для начинающих. Часть 1</div></div><div class="sp-body"><div class="sp-content">
Введение в создание игр
Итак, у Вас есть прекрасная возможность почувствовать себя богом, который создаёт вселенную. Но сначала давайте разберёмся, из чего же она будет состоять. А состоит она из разных объектов. Большая часть – это так называемые 3D объекты (Mesh). Что это такое? 3D объекты – это просто модели. Цилиндр, куб, шар, Ваша собственная модель – всё это 3D объекты. Они состоят из вершин – это просто точки в пространстве, и треугольников, состоящих из трёх соединённых вершин. Но это можно пока не запоминать – к этому мы подойдём чуть позже. Но, кроме 3D объектов, к объектам относятся: свет (Light), камеры(Camera), и пивоты (Pivot) или центры. Пивоты – это невидимые точки в пространстве, которые очень помогают в процессе работы. Далее, из двухмерного мира к нам пришли спрайты (Sprite). Спрайты - это просто плоские объекты, состоящие из четырёх вершин и двух треугольников, т.е. это просто плоская картинка. Потом плоскость (Plane) – тот же спрайт, только бесконечный. Их обычно используют для создания неба, воды – всего, что должно быть плоским и бесконечным. И, наконец ландшафт (Terrain), но его мы затрагивать не будем. Далее, вселенная - трёхмерная, так что у неё есть 3 оси – X, Y, Z. Ось X – это ось ширины, направленная слева направо. Ось Y – высота, идущая снизу вверх. И ось Z – как, бы длина, направленная вперёд! Эти оси существуют не только у всей вселенной, но и у каждого объекта тоже, но и об этом тоже по порядку. Это была вводная часть в 3D мир, без которого нельзя было обойтись, чтобы знать, что вообще мы собираемся делать. Но, более - менее мы всё поняли, так что идём дальше! Пора уже приступать к программированию – зайдите в Blitz3D, и наберите следующий код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Repeat
Until KeyHit(1)
End

Момент, когда Вы это запустите, можете считать историческим, и отмечать его, как день рождения - Вы только что создали собственный мир! Пусть там ничего не видно – просто он пока пустой! Разберём все команды по порядку: Graphics3D 640,480,16,1 – эта строчка инициализирует Direct3D, ну, и создаёт вселенную. Синтакс такой: Graphics3D ширина, высота, глубина цвета, режим. Ширина, высота – размер создаваемого экрана (в пикселях). Глубина – глубина цвета – 16, 24 или 32 бита на пиксель. Режим: 0 - оконный во время разработки (если возможно), полноэкранный в .exe формате; 1 - всегда полноэкранный
2 - всегда оконный
3 - всегда оконный, с возможностью изменения размеров окна. SetBuffer BackBuffer() - устанавливает буфер на задний буфер. Те, кто знали, те поняли, те же, кто не знали, не поняли вообще. В общем, если хотите, чтобы всё было правильно, пишите эту команду после Graphics3D. Repeat и Until KeyHit(1) - между этими двумя командами, у нас будет происходить сам цикл игры. Перевод таков: Повторять до нажатия клавиши Esc. End – эта команда показывает, что программа завершена. Что, не впечатляет? А зря – вы только что создали окно Direct3D – практически Ваше окно в мир создания компьютерных игр! Создание простейшей 3D программы Вставим следующий кусок кода после инициализации графики: cam=CreateCamera()
cub=CreateCube() В этих строках мы создаём наши первые объекты. cam=CreateCamera() – здесь, мы создаём камеру, (как бы наши «глаза» в виртуальном мире), и помещаем её в переменную cam. cub=CreateCube() – таким же образом создаём 3D-объект – куб, и называем cub. Созданные нами объекты очутятся в начале координат, т.е. в точке с координатами 0, 0, 0. Чтобы камера не была в одной точке с кубом, а смотрела на него со стороны, нам нужно поставить куб в другое место. Для этого можно воспользоваться командой PositionEntity, которую мы расположим после команды создания куба. PositionEntity cub,0,0,5 PositionEntity cub,0,0,5 - ставит объект куб в точку с координатами 0,0,5 – т.е. немного впереди. Синтакс: PositionEntity объект, координата X, координата Y, координата Z. В цикл программы вставим следующие три строчки: UpdateWorld
RenderWorld
Flip Эти команды (точнее последние две) должны присутствовать в Вашей программе, если Вы хотите, чтобы она что-то Вам показывала. UpdateWorld - анимирует все объекты в созданном мире, и проводит проверку на столкновения. RenderWorld - рендерит все объекты. Т.е. создаёт картинку той части мира, которую видит камера(ы). Flip – меняет местами передний и задний буфер. На этом основан способ двойной буферизации, но это знать не обязательно – просто нужно вставлять эту команду, для того, чтобы на экране что-то появлялось. Итак, теперь запустите программу. Что Вы видите? Серый квадрат? Практически да, хотя на самом деле – это куб, просто мы смотрим на него прямо, и видим только одну грань. Хоть какой-то прогресс. Дальше интереснее. Заставим-ка его двигаться! Поставим эту строчку в цикл. Прямо в самое его начало после команды Repeat. TurnEntity cub,.1,.2,.3 TurnEntity cub,.1,.2,.3 – эта функция поворачивает объект cub на 0.1 градус по оси X, 0.2 градуса по оси Y, и 0.3 градуса по оси Z, относительно системы координат объекта. Естественно, здесь можно использовать отрицательные числа и ноль. Например, команда TurnEntity cub,0,.1,0 – будет поворачивать куб просто влево, а TurnEntity cub,0,-.1,0 – вправо. Итак, первая сложность (хотя в общем ничего сложного здесь нет, это наоборот даже удобно). Попробую объяснить: помните, я говорил, что оси X, Y и Z (т.е. система координат) есть не только у самой вселенной, но и у каждого объекта. Сделано это для удобства. Главная – «мировая» система координат – статичная, и нужна нам для того, чтобы узнать где, например, находится объект, а система координат объекта – двигается и поворачивается вместе с самим объектом. Например, у нас есть персонаж, который должен поворачиваться и ходить вперёд, допустим, мы его повернули на какой-то угол. И, если мы захотим сделать так, чтобы он шёл в ту сторону, в которую он направлен через «мировую» систему координат, нам придётся делать вычисления, рассчитывающие то, на какой угол он повёрнут, и насколько должен передвинуться относительно каждой оси, но, к счастью, у него есть собственная система координат, и, где бы он не находился, как бы он не был повёрнут относительно «мировой» системы координат, его ось Z будет указывать направление вперёд - назад (относительно него), Y – вверх-вниз, X – влево-вправо. Иногда нужно наоборот – независимо от того, куда направлен объект, двигать его в какую-то определённую сторону, относительно всего мира. Так, например, действует гравитация – т.е. как бы не был повёрнут объект со своей системой координат, мы просто двигаем его вниз по оси Y по «мировой» системе координат – и он падает. Это было небольшое отступление, чтобы всё было ясно. Сами команды будут позже. Итак, когда Вы запустите эту программу, Вы увидите вращающийся куб. Правда чего-то не хватает? Да! Все его грани одинакового цвета, и мы понимаем, что это куб чисто интуитивно. Что же закрашивать каждую грань разными цветами? Нет до этого мы не доросли, да это и не нужно. То, что нам не хватает – это свет! Именно он должен оживить всю сцену. Да будет свет! lit=CreateLight() Здесь всё также, как и раньше! Эту команду можно поставить в любом месте перед главным циклом, но я обычно создаю свет сразу после создания камеры. Теперь запустите программу. Так намного лучше, неправда ли? Да, кстати, свет – это объект, а это значит, что им тоже можно управлять, как и всеми объектами. Поэтому, чтобы он лучше смотрелся, сразу же после создания повернём его: TurnEntity lit,45,45,0 По-моему так лучше… или у Вас есть другие варианты? А весь код выглядит так:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
lit=CreateLight()
TurnEntity lit, 45,45,0
cub=CreateCube()
PositionEntity cub,0,0,5
Repeat
TurnEntity cub,.1,.2,.3
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, и как Вам? Всего 14 строчек сделали то, для чего в других языках пришлось бы писать несколько страниц! Вот, в общем первая трёхмерная программа. Не бойтесь экспериментировать – попробуйте подставить свои значения углов, или назвать объекты по-другому (тут главное изменить названия переменных во всём коде, а не только в команде создания). А чтобы убедиться, что вы всё хорошо усвоили, попробуйте сделать так, чтобы в цикле поворачивался не только куб, но и свет. В следующем уроке мы научимся управлять объектом сами. Использование клавиатуры для перемещения объекта Можно, конечно, до посинения смотреть на разные там вращающиеся кубы, сферы, конусы, цилиндры, свет, камеры – но когда-нибудь это надоест. Кроме того – игра (а ведь мы именно игры хотим писать а не всякие там трёхмерные абстрактные пейзажи) требует ввода чего- нибудь с клавиатуры. Ну, тогда давайте разбираться с перемещением предмета. Я надеюсь, что предыдущие уроки Вы хорошо освоили, так что начнём сразу с такого шаблона:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
Repeat
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Здесь мы (по порядку) инициализируем графику, создаём камеру, свет, куб и в цикле всё это рендерим и выводим на экран, пока не будет нажата клавиша Esc. Обратите внимание, что здесь мы поменяли позицию камеры (а не куба, как в прошлый раз), и теперь она имеет координаты X=0 Y=5 Z=-10. В итоге, получилось, что мы смотрим на куб сзади, и чуть-чуть сверху. В нашем распоряжении имеется куб. Наша миссия двигать его, в направлении, задаваемом с клавиатуры. Итак, новые команды: KeyDown(сканкод) – (вообще-то не команда, а функция) – проверяет, нажата ли соответствующая клавиша. В скобках нужно указать сканкод клавиши (сканкоды клавиш можно узнать в Help'e Blitz3D, в разделе Command Reference). Мы будем управлять клавишами управления курсора и манипулятором типа мышь. Значит сразу напишу сканкоды (всем запомнить – пригодится): кнопка вверх – 200, вниз – 208, влево – 203, вправо – 205. С мышкой разберёмся позже. MoveEntity объект, перемещение по X, по Y, по Z – перемещает объект относительно своей собственной системы координат. Так, у нас есть команда проверки нажатия клавиш, и есть команда перемещения объекта, осталось только связать их вместе. Как это сделать? Очевидно, просто надо поставить условие (ЕСЛИ НАЖАТА кнопка ВВЕРХ то ПЕРЕДВИНУТЬ ОБЪЕКТ ВПЕРЁД), ну, и так далее. Я надеюсь Вы прочитали учебник по основам Blitz Basic, поэтому команду условия IF я уж объяснять не буду. Вот так это должно выглядеть: If KeyDown(200) MoveEntity cub,0,0,.1 Поставьте это условие в начало цикла, и запустите прогу. Теперь, когда Вы будете нажимать кнопку вверх, куб будет двигаться по направлению оси Z, т. е. вперёд. По аналогии сделаем остальные условия:

If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0

Полностью код выглядит так:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Кубик двигается! Что ещё нужно для счастья? Я думаю достаточно, но Blitz3D предоставляет нам очень много возможностей на этой почве, и в следующих уроках мы постараемся уяснить самые важные. Доработка кода программы Круто, конечно, объект перемещается, но мы попробуем немного модернизировать программу. Две последние строчки проверки нажатия клавиш немного изменим. Теперь, когда мы будем нажимать кнопки ВЛЕВО и ВПРАВО он будет поворачиваться влево и вправо (раньше он перемещался влево и вправо). Итак строчки: If KeyDown(203) MoveEntity cub,-.1,0,0
If KeyDown(205) MoveEntity cub,.1,0,0 Нужно заменить на: If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0 Вот у нас уже что-то вроде автогонок вырисовывается (точнее кубогонок). Так, команду TurnEntity Вы уже знаете, но обратите внимание, что если мы хотим, чтобы наш объект вертелся именно влево или вправо, мы поворачиваем его по оси Y. Чтобы немного было понятнее, где у него передняя часть, а где боковые, я предлагаю его немного трансформировать. ScaleEntity cub,1,.5,2 Эту команду нужно вставить после команды создания куба (смотрите не вставьте её в цикл ). Синтаксис: ScaleEntity объект, трансформация по X, по Y, по Z. То есть в примере мы оставляем такой же размер по X (ширину) (1 значит, что мы оставляем тот же размер), трансформируем размер по Y (высоту) до 0.5 от его размера (то есть просто уменьшаем в 2 раза) – получается как бы сплющиваем его сверху, и в два раза увеличиваем размер по Z (длину) – то есть растягиваем его в длину. Получился, что-то похожее на кирпич. Вот ездит этот кирпич, ездит, да заезжает за экран, так что мы перестаём его видеть. А как бы сделать так, чтобы не терять его из виду? Здесь нам поможет такая замечательная команда PointEntity объект1, объект2 – эта команда просто поворачивает объект1 в сторону объекта2. То есть если мы хотим, чтобы наша камера всегда следила за нашим кубиком, нам просто нужно вставить эту команду в цикл. Вот как это делается: PointEntity cam,cub Эту команду можно вставить в любое место в цикле, но, чтобы у нас было всё по порядку, поставим её после команд проверяющих нажатия клавиш. Вот полный код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
PointEntity cam,cub
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Опять же столько всего, только в 18 строчках! Если запустить программу, то видно, что камера как бы следит за кубиком, хотя это плохо понятно – такое чувство, как будто он ездит сам как хочет. Всё это потому, что у нас нет ничего, с чем мы могли бы сравнивать его положение.

Создание плоскости и операторы текстурирования
Итак, как я уже говорил, нам не с чем сравнивать положение нашего объекта, поэтому он так странно двигается. Для этого урока нам понадобится: код из прошлого урока, и текстура. Что такое текстура? Для тех кто не знает попробую объяснить. Текстура – это картинка в любом формате (самые популярные это .jpg, .bmp, .tga, .pcx, .png и.т.д.), которой мы закрашиваем какой-нибудь объект. Если объект большой, а текстура – нет, то она накладывается как бы повторяющимися квадратами. Вы наверняка видели в каких нибудь трёхмерных играх повторяющуюся траву, скалы – где-то это видно сразу, где-то сильно скрыто. Когда создаётся объект, он создаётся белым, а когда мы его текстурируем – то получается покрываем его картинкой – текстурой. Ну, будем надеяться, что кто этого не знал – примерно понял. В общем возьмите любую картинку из перечисленных форматов, и поместите её в ту же папку, где у Вас сохранён этот код программы. Сделаем мы так: создадим плоскость, которая будет находиться на одном месте, и относительно неё будет хорошо видно, что наш кубик всё таки движется. pln=CreatePlane() Эта команда, также как и остальные команды создаёт объект, на этот раз плоскость (Plane). Что это такое? Ну, плоскость – это плоскость. Она плоская и бесконечная. Да, один момент – плоскость видна только с одной стороны – с другой она невидимая (как и спрайт, кстати). Поставьте эту команду перед циклом, после создания куба. После создания плоскости загрузим текстуру из файла: tex=LoadTexture("Picture.bmp") Эта команда загружает текстуру из файла Picture.bmp (это у меня картинка называлась Picture.bmp, а Вы можете изменить имя файла, кстати, там можно писать полный путь к файлу, например “C:MyGamePicture.bmp”. Так, плоскость есть, текстура есть – осталось только затекстурировать эту плоскость: EntityTexture pln,tex Синтаксис: EntityTexture объект, текстура – эта команда элементарно покрывает заданный объект заданной текстурой. Да, и ещё одно – эта текстура наверняка будет смотреться мелко, поэтому я советую вставить такую команду после загрузки текстуры: ScaleTexture tex,10,10 Она просто расширяет данную текстуру в10 раз по ширине и в 10 раз по высоте. Вот, вроде, и всё готово! Теперь у нас внизу есть плоскость, а куб как бы ездит по ней! Весь код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,-10
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,30,30
EntityTexture pln,tex
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
PointEntity cam,cub
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Теперь попробуйте сами затекстурировать куб. Работа с мышью В этот раз я покажу, как можно управлять объектом с помощью мышки. Итак, берём шаблон:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
cam=CreateCamera()
PositionEntity cam,0,5,0
lit=CreateLight()
cur=CreateSphere(8)
EntityColor cur,255,215,0
PositionEntity cur,0,0,10
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
Repeat UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Здесь мы создаём всё, что нам нужно, расставляем и создаём цикл. Новые команды: CreateSphere(количество сегментов) – создаёт сферу, в скобках указываем количество сегментов – 8 = 224 полигона, 16 = 960 полигонов и 32 = 3968 полигонов. Естественно, чем больше полигонов, тем "круглее" наша сфера, и тем больше памяти он занимает. EntityColor объект, красный, зелёный, синий – эта команда закрашивает данный объект цветом RGB, где указывается сколько должно быть красного, зелёного, синего цвета, значения которых могут быть от 0 до 255. Вот, например, 0,0,0 – чёрный цвет, 255,255,255 – белый, 0,255,0 – самый зелёный. Теперь насчёт мышки. Хотя курсор и не показывается на экране, на самом деле он есть – то есть он двигается, если вы двигаете мышку, остаётся только определить на сколько. Просто поставьте эти команды в начале цикла: mx=MouseXSpeed()
my=MouseYSpeed()
MoveMouse 320,240 Что эта всё значит? Поясняю. MouseXSpeed() – это функция, которая говорит нам об изменении X координаты мышки на экране, с момента последнего вызова этой функции. Вобщем, на сколько пикселей её в последний раз передвинули (по оси X). MouseYSpeed() – тоже самое, но по Y (есть ещё MouseZSpeed() – это передвижение колёсика). MoveMouse x,y – устанавливает курсор мышки в точку 320, 240. Итак, мы знаем, на сколько у нас передвигается мышка с каждым кадром, осталось только передвигать сферу, в зависимости от передвижения мышки (данные о передвижении находятся у нас в переменных mx и my): MoveEntity cur,mx,0,-my Теперь можно запускать программу. Шар передвигается с помощью мыши, правда он какой-то гиперактивный, нужно сбавить ему скорость – просто заменим эту строчку на: MoveEntity cur,mx*.1,0,-my*.1 Так будет намного удобнее. Всё – у нас есть трёхмерный курсор. Но мы на этом не остановимся! Мы пойдём дальше! Сделаем так, чтобы камеру можно было вертеть: If KeyDown(203) TurnEntity cam,0,2,0
If KeyDown(205) TurnEntity cam,0,-2,0 Если Вы теперь запустите программу, то обратите внимание, что когда камера повёрнута нормально (как стоит в начале) – то всё как бы нормально – двигаешь мышку влево, сфера двигается влево, двигаешь вперёд – и сфера двигается вперёд… но стоит нам повернуться на 90 градусов влево, как становится совсем неудобно: передвигаешь мышку влево – курсор уходит вперёд, передвигаешь мышку вперёд – курсор уходит вправо. А если повернуться на 180 градусов – то всё вообще становится наоборот. Почему же происходит такое неправильное движение? Вообще-то движение-то правильное – оно как было, так и осталось – просто мы теперь смотрим на это под другим углом. Что же теперь камеру не вертеть что ли? Конечно нет! Настоящие программеры не сдаются. Значит будем думать вместе, хм, это, э-э-э, а может, хотя нет, да! Есть идея! Смотрите: сфера всегда повёрнута прямо. Когда камера повёрнута прямо, получается так, что их оси совпадают по направлению, а когда камера повёрнута налево, то получается что её ось X совпадает с осью Z сферы. Короче, надо сделать так, чтобы их оси совпадали, говоря человеческим языком – чтобы они были направлены в одну сторону. Но как это сделать? Очень просто:

ex=EntityPitch#(cam)
ey=EntityYaw#(cam)
ez=EntityRoll#(cam)
RotateEntity cur,ex,ey,ez

Или так (результат один и тот же):

RotateEntity cur,EntityPitch#(cam),EntityYaw#(cam),EntityRoll#(cam)
Итак новые командосы. EntityPitch#(объект) – функция, возвращающая наклона данного объекта относительно оси X мировой системы координат. EntityYaw#(объект)-по оси Y, EntityRoll#(объект)-по оси Z. RotateEntity объект, X, Y, Z – вобщем похожа на команду TurnEntity (тем, что она поворачивает объект), вот только делает она это относительно мировой системы координат, а не системы координат объекта. Вот мы и научились работать с мышкой и курсором! Родительская зависимость объектов или привязки В этом уроке мне бы хотелось рассказать о том что такое привязки, и что с ними можно делать. Начнём сразу с примера. Да возьмём один из прошлых – помните, когда мы поворачивали кубик?

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
cam=CreateCamera()
PositionEntity cam,0,5,-10
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
PositionEntity pln,0,-1,0
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

В общем-то ничего не изменилось, мы только поставили плоскость внизу, и создали камеру после создания куба. Хорошо, теперь кое-что изменим. Представим, например, что мы делаем какую-нибудь игру с видом от третьего лица. Тогда нам надо, чтобы камера двигалась вместе с кирпичом – вернее сзади него. Сделайте вот что - замените простую строчку создания камеры на: cam=CreateCamera(cub) Какая-то хитрая строчка вроде ничего не изменилось, кроме того, что мы поставили в скобки переменную созданного нами куба. В этом и весь фокус! Когда мы создаём какой-то объект, и в скобках ничего не указываем, объект создаётся свободным, а если мы что-то укажем – объект становится зависимым от другого объекта, указанного в скобках – в данном случае это куб. Немного расскажу об этой зависимости. Значит так, во-первых свободный, непривязанный ни к чему объект создаётся в точке 0,0,0 относительно мировой системы координат. Объект же, привязанный таким образом к другому объекту - родителю, создаётся в той точке, где находится его родитель. Второе - все команды которые писались раньше относительно мировой системы координат – теперь пишутся относительно системы координат родителя т.е. в данном случае если бы камера не была привязана к кубу, команда PositionEntity cam,0,5,-10 означала бы поставить объект cam в точку с координатами 0,5,-10. А так как она привязана к кубу, камера ставится в точку 0,5,-10 относительно куба (т.е. центр куба для камеры считается точкой 0,0,0). Получается, что камера располагается сзади и немного сверху относительно куба. И куда мы этот куб перед этим бы не поставили, как бы не повернули – всё равна камера бы поставилась именно таким образом. И, наконец, самое главное, все движения и повороты, которые применяются к родителю автоматически применяются к зависимым от него объектам – то есть они как будто бы привязаны к родителю. Например, если мы подвинем родителя, все зависимые от него объекты также подвинутся. Если повернём – все зависимые объекты относительно него повернутся. Но не наоборот! Надеюсь понятно объяснил. Хотелось бы только отметить: если всё же нам нужно будет поместить, передвинуть или повернуть зависимый объект относительно мировой системы координат, то это делается очень просто – в конце добавляется True (что значит, что команда совершается глобально – то есть относительно мировой системы координат.). Например, если нам камеру надо будет поставить в точку 5,5,-20 относительно мировой системы координат, а не относительно мировой системы родителя - мы просто пишем PositionEntity cam,5,5, -20,True. И всё. И связь всё равно от этого не потеряется. Запустите программу, и вы увидите, что камера стоит ровно, и следит за кубом. Чтобы показать, что движение зависимых объектов никак не влияет на родителей, добавим ещё две строчки в цикл:

If KeyDown(30) MoveEntity cam,0,0,.1
If KeyDown(44) MoveEntity cam,0,0,-.1

В них, как Вы видите, условие – если нажата кнопка A – двигать камеру вперёд, а если нажата Z – назад. Куб остаётся на месте. Полный код:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
cub=CreateCube()
ScaleEntity cub,1,.5,2
pln=CreatePlane()
tex=LoadTexture("Picture.bmp")
ScaleTexture tex,10,10
EntityTexture pln,tex
PositionEntity pln,0,-1,0
Repeat
If KeyDown(200) MoveEntity cub,0,0,.1
If KeyDown(208) MoveEntity cub,0,0,-.1
If KeyDown(203) TurnEntity cub,0,1,0
If KeyDown(205) TurnEntity cub,0,-1,0
If KeyDown(30) MoveEntity cam,0,0,.1
If KeyDown(44) MoveEntity cam,0,0,-.1
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, мы раскрыли секрет игр, сделанных от третьего лица! Но использование привязок не ограничивается «следящей» камерой. О том, что ещё можно делать с привязками, я расскажу в следущем уроке. Фишки с привязками А вот что можно сделать, если правильно использовать технологию привязок. Алгоритм галактики:

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim sp(99)
center=CreateCube()
For i=0 To 99
sp(i)=CreateSphere(8,center)
ScaleEntity sp(i),.2,.2,.2
PositionEntity sp(i),Rnd(-20,20),Rnd(-20,20),Rnd(-20,20)
Next
cam=CreateCamera()
PositionEntity cam,0,0,-40
Repeat
TurnEntity center,.0,.5,.5
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Если Вы запустите этот код, то увидите как шарики совершают поступательное движение вокруг куба по кругу. На самом деле всё проще. Эти шары просто находятся в родительской зависимости от куба, и остаются на месте, а поворачивается только куб – ну, а они вместе с ним. Так, допустим, что нам этот алгоритм понравился, и мы захотели всунуть его в нашу игру, но нас немного смущает этот куб в середине. Конечно, можно его как подобает затекстурировать, и сказать, мол, это наш антигравитационный голографический трансхренолятор, или сделать сферу, и сказать, что это чёрная дыра (и такое в нашей практике бывает, когда лень что-то исправлять), но здесь мы поступим подругому. Помните, в самом начале я перечислял какими бывают объекты, и упомянул о Центрах (Pivots). Так вот – центр – это просто точка в пространстве, она невидимая, но у неё есть (как и всех остальных объектов) своя система координат, а значит – своё направление. Центры – это очень полезные объект, когда дело касается всяких там привязок. Уже догадались, что мы собираемся делать? Неправильно, мы собираемся заменить этот куб в середине на центр. А, Вы так и подумали? Ну, тогда, заменим команду сами знаете что на команду создания центра: center=CreatePivot() А для тех, кто все-таки не понял объясняю: мы меняем команду создания куба (center=CreateCube()) на команду создания центра (center=CreatePivot()). Всё – теперь Вы можете наслаждаться видом крутящегося скопления звёзд без всяких там лишних вещей! Но не обязательно делать такую одинарную привязку - можно привязать один объект к другому, который в свою очередь привязан к третьему – тот к четвёртому, и так далее. Для чего это делать? А вот посмотрите пример:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim prv(2)
prv(0)=CreateCube()
prv(1)=CreateCube(prv(0))
PositionEntity prv(1),5,0,0
prv(2)=CreateCube(prv(1))
PositionEntity prv(2),5,0,0
obj=CreateSphere(8,prv(2))
PositionEntity obj,5,0,0
cam=CreateCamera(0)
PositionEntity cam,0,0,-30
Repeat
For i=0 To 2
TurnEntity prv(i),0,0,1
Next
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Как видите, привязки играют очень большую роль в программировании игр, и они очень облегчают жизнь. Несколько дополнений: 1) Помните - синтаксис команды создания сферы таков: CreateSphere (количество сегментов, [родитель]) – очень частой ошибкой является то, что в поле, где указывается количество сегментов, вместо них указывают родителя. 2) Задать привязку можно не только во время создания объекта, но и после этого – командой EntityParent объект, родитель – эта команда привязывает заданный объект к заданному родителю. Вместо родителя можно поставить 0 (ноль) – и это отвяжет заданный объект от родителя, каким бы страшным этот родитель ни был. Ну, и напоследок – то, что можно сделать, изучив технологию привязок:

Graphics3D 640,480,16,1
SetBuffer BackBuffer()
lit=CreateLight()
Dim sp(99)
sp(0)=CreateSphere(8)
EntityAlpha sp(0),0
For i=1 To 99
sp(i)=CreateSphere(8,sp(i-1))
PositionEntity sp(i),1,1,1
EntityColor sp(i),250,215,i*2
EntityAlpha sp(i),(100-i)*.01
Next
cam=CreateCamera(0)
PositionEntity cam,0,30,0
Repeat
For i=0 To 99
TurnEntity sp(i),.1,.2,.3
Next
PointEntity cam,sp(99)
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Ну, Вы поняли, как это работает? Ну, давайте – подумайте! Вы же всё-таки программеры как-никак– даже если Вы задумали всю жизнь программировать один, Вам всё равно придётся читать чужие коды – чтобы понять как работает та или иная фишка. А про команду EntityAlpha – посмотрите в Help'е, потому что, даже если Вы совершенно ничего не знаете в английском – в Help придётся обращаться очень часто, поэтому, лучше учиться сейчас. Инициализация столкновений объектов Вот мы плавно подошли к главной составной части физики Blitz3D (не надо только пугаться – никаких формул типа E=mc^2 вспоминать не нужно – здесь Вы их сами будете придумывать), и одного из его самых главных компонентов – проверки на соприкосновение или столкновение. Я думаю, после того, как мы его разберём, Вы будете готовы к тому, чтобы приступить к самой разработке игр! Ну, ладно, начнём – чего время-то терять?

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Const TypePlayer=1,TypeWall=2
Player=CreateSphere()
EntityType Player,TypePlayer
Wall=CreateCube()
PositionEntity Wall,0,0,10
EntityType Wall,TypeWall
Collisions TypePlayer,TypeWall,2,3
cam=CreateCamera()
PositionEntity cam,0,30,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Repeat
If KeyDown(200) MoveEntity Player,0,0,.1
If KeyDown(208) MoveEntity Player,0,0,-.1
If KeyDown(203) TurnEntity Player,0,2,0
If KeyDown(205) TurnEntity Player,0,-2,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Итак, в этой программке представлен основной принцип проверки на прикосновения. Здесь мы создаём шар и куб, затем, стрелками управляем шаром, и, если Вы захотите заехать внутрь куба, у Вас ничего не получится. Ура! Это и есть та самая инициализация столкновений – она не пропускает одни объекты в другие, если, конечно перед этим указать, какие именно объекты, и куда именно не пускать. А теперь разберём все нововведения. Сначала мы создали две константы – TypePlayer, равной единице и TypeWall, равной двум. Зачем мы это сделали? Так это чтобы не запутаться. Я думаю, потом поймёте в чём именно. Ещё, Вы, наверное, заметили, ещё одну новую команду – EntityType объект, тип объекта – присваивает данному объекту данный тип. Тип (в данном случае) - это просто цифры. Здесь мы вместо цифр использовали константы – чтобы не запутаться, да и чтобы легко можно было прочитать – к какому типу этот объект относится, и с чем соприкасается (а то представьте такую ситуацию, у нас есть (какие-то) цифры для главного героя, стен, врагов, ну, и прочей дряни – всё это мы вначале создания проекта распределили, расставили, и благополучно забыли за ненадобностью, но вдруг в середине проекта Вы вспоминаете, что забыли задать проверку на столкновение между врагом и героем, и начинаете судорожно вспоминать какая же цифра у Вас обозначала врага, просматриваете килобайты кода, ну в общем можно поступить намного удобней, задав сразу константы, таким вот образом Тип_Герой = 1, Тип_Стена = 2, Тип_Пол = 3, Тип_Враг = 13, Тип_Враг_Босс = 113. Следуящая команда: Collision первый тип, второй тип, метод, результат – в общем то, что она делает я только что написал, ну а подробней: Первый тип – объект, который будет проверяться на столкновения.
Второй тип – объект, с которым эти самые столкновения и будут происходить. Метод: 1 – соприкосновение сферы со сферой
2 – соприкосновение сферы с полигонами
3 – соприкосновение сферы с параллелепипедом Результат: 1 – остановка
2 – скольжение – полное скольжение
3 – скольжение – защита объекта от скольжения вниз (может я перевёл не правильно, конечно, ну в общем этот метод создан для работы с ландшафтом). Теперь постараюсь обо всём этом, да поподробнее, начнём с методов. Как видите, объект, который будет проверяться на столкновения (первый объект) должен будет иметь «сферу» столкновения – так как методы столкновения бывают только сферы с чем-либо. Величину этой самой сферы проверки можно задать для каждого объекта отдельно – с помощью команды EntityRadius объект, радиус#. Это накладывает некоторые ограничения (зато сама проверка – очень быстро реализована) и как бы Ваша фигура не выглядела – столкновение будет проверяться именно по какой-либо сфере – т.е. допустим, у Вас есть предмет – спичка – здесь Вам придётся делать либо большую сферу, получая довольно приличное расстояние в середине спички между радиусом сферы соприкосновения и радиусом самой спички, либо сделать сферу поменьше, но сверху и снизу спичка будет вылезать из сферы (как вариант – только верхняя или нижняя часть) – т.е. тут уже эта выпирающая часть может залезть в другой объект… ну, со спичкой я утрирую – но, вот модельки людей, например, тоже не похожи на шары – так что рано или поздно, такой вопрос встанет, и о нём лучше подумать заранее – например, можно сделать более приземистых, коренастых юнитов – как в Quake'e первом, например. Далее – насчёт столкновений. 1-е: это самое столкновение происходит только при движении первого объекта внутрь второго. 2-е: столкновение – вещь односторонняя – т.е. если мы, как в примере, хотим, чтобы герой соприкасался с врагами, а те, в свою очередь – с ним, то нам нужно писать две команды - «Коллизион между Типом_Героем и Типом_Врагом» и «Коллизион между Типом_Врагом и Типом_Героем», а так как первый объект должен иметь именно сферу соприкосновения, то единственный доступный метод здесь – первый! Так что к сфере надо будет привыкнуть, и уже начинать думать, что с ними делать, правда, я слышал о том, что разработчики думают сделать соприкосновение по эллипсоиду, но когда это будет? Так, незадолго до того, как я дописал учебник, вышел апдейт 1.82 (поэтому я тут немного дополняю), где эллипсоидный метод столкновения уже реализован, правда удлинять или сплющивать можно только по Y оси, но и это неплохо. Если я не ошибаюсь, новая команда выглядит так: EntityRadius объект, радиус по X и Z, радиус по Y. Вот, в общем-то и вся основа 3D. Понравилось? Теперь будет намного интереснее. Конечно при условии, что Вы старательно изучали все ранее изложенные главы, на которые потратили лучшие минуты своей жизни и большую часть поняли. Что же пора заняться тем, о чём я обещал – простенькой игрушкой!!!


<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Учебник по Blitz3D для начинающих. Часть 2</div></div><div class="sp-body"><div class="sp-content">
Основы движка
Итак, мы уже достаточно вооружены, чтобы сделать простенькую игру, так что хватит абстрактных примеров, а начнём уже делать игры, а то, что мы ещё не изучили, узнаем в самом процессе, по мере надобности. Что это будет за игра? Сделаем такой вариант игры, которую делали в основах по BlitzBasic`у, только трёхмерный. Там у нас будет шарик, который будет ездить по плоскости, собирать, допустим, жёлтые кубики, и умирать от столкновения с красными, короче, сюжет стандартный. Начнём с того, что создадим шарик игрока и его управление (ну и камеру со светом):

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Global Player=CreateSphere()
cam=CreateCamera()
PositionEntity cam,0,60,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Repeat
If KeyDown(200) MoveEntity Player,0,0,.2
If KeyDown(208) MoveEntity Player,0,0,-.2
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Итак, всё просто – есть шарик, который ездит где-то внизу и поворачивается, вот только одно меня здесь смущает – не понятно, куда этот шарик повёрнут – он везде круглый, не видно, где перед, где, гм, зад. У меня вот такое решение этого вопроса – что если сделать у него такой мини хвост – как у капли или метеора? Т.е. здесь просто бы подошёл конус. Можно, конечно, сделать ещё один объект – как раз конус и просто сделать привязку к шару, но привязки полезны, когда нам нужно обязательно 2 различных объекта, а не один, или, например, если привязка через некоторое время должна пропасть, здесь же нам желателен объект, представляющий из себя цельный 3D-объект. Можно сделать модель в 3D Studio MAX, конечно, а потом её сюда загрузить, вот только зачем забивать лишнее дисковое пространство на модели, которые мы сами можем сделать в самом Blitz3D! Как? Очень просто – присоединив к 3D-объекту шара объект конуса! Вот как мы поступим (после создания шара):

Plac=CreateCone(8)
RotateMesh Plac,-90,0,0
ScaleMesh Plac,1,1,1.2
PositionMesh Plac,0,0,-1.5

Здесь мы создаём конус по имени Plac, вертим его там и по всякому подгоняем. Так как после создания шар будет стоять в точке 0,0,0 – конус будет ровно подогнан к нему – можете запустить и посмотреть. Вот только если Вы будете этот шар двигать, конус останется на месте (естественно). Итак, как я уже говорил, можно этот конус привязать (особой разницы не будет), но всё таки основываясь на программерскую этику, сделаем из них один объект (у нас есть возможность, да и зачем нам 2 объекта вместо одного). Короче, добавляем: AddMesh Plac,Player Эта команда добавляет один 3D-объект к другому, то есть в данном случае мы добавляем 3D-объект конуса к 3D-объекту шара. Но! При этом сам объект конуса не исчезает (то есть мы этот конус как бы добавляем копированием). Короче, теперь у нас два объекта – шар присоединённый к конусу, и сам конус, который нам сейчас не нужен. Поэтому (опять новая команда) мы его уберём: FreeEntity Plac Эта команда убирает заданный объект (в данном случае конус) и (самое главное) очищает от него память. Когда будете делать большие игры, где будет много уровней, при выходе из одного из них, не забывайте очищать ненужные объекты, иначе они будут копиться и занимать память, которая не резиновая, в результате всё будет тормозить после определённого времени игрового процесса. Дальше, не будет же этот шарик просто так ездить по полю. Надо создать ему определенные препятствия. Создадим-ка мы кубики, и разбросаем их по нашему будущему полю:

Dim Walls(29)
For i=0 To 29
Walls(i)=CreateCube()
PositionEntity Walls(i),Rnd(-50,50),0,Rnd(-50,50)

Next Здесь мы, как Вы видите, создаём массив на 30 элементов, и затем расставляем в них кубики. А сами кубики раскидываем в случайном порядке по полю. Так, кубики есть, столкновений нет, вывод: надо вставить сюда наш инициализатор столкновений, который мы так старательно изучали в прошлой главе. Поехали. Сначала мы должны определить две константы для наших объектов – для шарика и для кубиков: Const TypePlayer=1,TypeWalls=2 Вставьте эту строчку после инициализации графики – т.е. в начало. Так типы есть, осталось только указать что шар и кубики к ним принадлежат: EntityType Player, TypePlayer Эту строчку поставьте после создания самого объекта шарика - т.е. после присоединения к нему конуса. EntityType Walls(i), TypeWalls А эту строчку вставьте в конец цикла создания кубиков (после команды PositionEntity Walls(i)…). Так, типы задали, конечно, но сталкиваться они всё равно не будут – пока мы прямо не укажем, что они, мол, сталкиваться должны. Collisions TypePlayer,TypeWalls,2,2 Здесь мы задали, что TypePlayer – который у нас является шаром должен сталкиваться с TypeWalls – т.е. с кубиками, соприкасаясь методом сфера к полигону, полностью скользя по нему. Можно последнюю цифру заменить на 1 – тогда шарик будет как бы «прилипать» к кубикам. Эту строчку поставьте после создания всех кубиков. Хочу сразу заметить (из своего опыта), что команды Collisions нужно ставить одной из последних – т.е. сначала нужно инициализировать все предметы, затем их все расставить по местам, и только потом обозначать столкновения между ними, можно, конечно сразу задать константы, и все столкновения между этими константами, но потом, когда Вы будете создавать объекты, помните, что создаются-то они все в одной точке – 0, 0, 0 и получается, что находятся друг в друге, и затем, когда мы будем их расставлять, могут появиться баги – т.е. мы скажем объекту, типа, «поставься в точку 0, 10, 100», а он окажется в точке 0,8,70 (или типа того). Либо в таком случае нужно делать так: создавать объекты, и тут же их переставлять в новые места, но, по-моему первая идея была лучше! Так, теперь можно со спокойной душой всё это дело запустить, да нет, не в производство - рановато пока. Уже что-то вырисовывается! По традиции – полный код всего этого безобразия:

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Const TypePlayer=1,TypeWalls=2
Global Player=CreateSphere()
Plac=CreateCone(8)
RotateMesh Plac,-90,0,0
ScaleMesh Plac,1,1,1.2
PositionMesh Plac,0,0,-1.5
AddMesh Plac,Player
EntityType Player, TypePlayer
FreeEntity Plac
Dim Walls(29)
For i=0 To 29
Walls(i)=CreateCube()
PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40)
EntityType Walls(i), TypeWalls
Next
cam=CreateCamera()
PositionEntity cam,0,60,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Collisions TypePlayer,TypeWalls,2,2
Repeat
If KeyDown(200) MoveEntity Player,0,0,.2
If KeyDown(208) MoveEntity Player,0,0,-.2
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
UpdateWorld
RenderWorld
Flip
Until KeyHit(1)
End

Доработка движка Я не спорю, ездить шариком по плоскости, лавируя между кубиков, в созданной нами программе конечно очень захватывающе, но боюсь другие люди (не Вы – да, да существуют ещё и другие!) скажут что это, конечно, круто, но Quake им нравится больше, правильно, они ничего не понимают в искусстве! Вот только таких людей подавляющее большинство, да и сами игры обычно создаются для других людей (за редким исключением), а этим варварам нужно что? Ну не нравится им просто спокойно ездить по полю. Хорошо, мы им сейчас такое устроим! Помните, в змейке использовали такие квадратики, которые надо было собирать, используем этот примитивный метод. Для начала добавим для него отдельную константу – вот так Const TypePlayer=1,TypeWalls=2,TypeTarget=3 Дальше создадим сам объект, на этот раз у нас будет цилиндр, и укажем его тип: Target=CreateCylinder()
EntityType Target,TypeTarget Ну, и добавим обработку столкновений: Collisions TypePlayer,TypeTarget,2,2 Ок, вроде бы основу инициализировали. Дальше сделаем так, чтобы цилиндр перемещался в другое место при столкновении с шаром. Как это сделать? Естественно – условием внутри главного цикла (Вы хоть помните такой?): If EntityCollided (Player,TypeTarget) PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40) Новая команда? Точно! Вы наверное уже поняли что это? Да – это и есть проверка на соприкосновение: Пишется так EntityCollided (Объект, тип с которым он соприкасается) – итак, эта команда возвращает нам True (Истина), если заданный в скобках объект соприкасается с заданным типом. Т.е. в данном случае – шар соприкасается с типом цилиндра (TypeTarget). Перед использованием этой команды нужно обязательно указать этот тип столкновения командой Collisions, иначе она работать не будет! Так, теперь уже ездить можно не бесцельно, а гоняясь за цилиндром. Но, опять же – слишком миролюбиво, нам нужен Game Over. Как его реализовать? Очень просто – когда шар будет сталкиваться с кубиками – тогда и будет конец игры. Следующее условие: If EntityCollided (Player,TypeWalls) End Здесь мы пишем, что когда шар сталкивается с типом кубиков, игра заканчивается, она просто выходит, грубо, конечно, но мы всё это потом исправим, я Вам обещаю! Правда исправим!!! Да я Вас вообще когда-нибудь обманывал? ...... Когда??? Пойдём дальше, игрок начинает подумывать о том, что ему хочется выйти тогда, когда игра даёт ему шанс расслабиться – то есть проиграв или просто затормозив где-нибудь, у игрока начинает происходить процесс анализа происходящего. К чему это я? А, да – внимание игрока должно быть сосредоточено на игре – т.е. шарик должен всё время быть в движении, что бы у центрального процессора человека под названием «мозг» даже не появлялось мысли о кнопке Esc, но и двигаться быстро он тоже не должен – если человек будет проигрывать сразу после нажатия кнопки Старт – тоже будет не очень хорошо. Теперь нам нужна постоянная скорость, и, естественно, стрелки управления – вперёд-назад теперь нам будут не нужны (если игрок будет передвигаться с постоянной скоростью), поэтому строчки: If KeyDown(200) MoveEntity Player,0,0,.2
If KeyDown(208) MoveEntity Player,0,0,-.2 Нужно заменить на: MoveEntity Player,0,0,.2 Так, уже что-то, во что можно поиграть, во всяком случае уже интереснее, но поиграв какое-то время (минут десять) можно натренироваться так, что пальцы уже сами будут нажимать на стрелки, и обходить препятствия используя уже один спинной мозг, и опять же центральный процессор головного мозга будет посылать сигналы о том, что, типа, не пора бы выйти? Поэтому требуется усложнять игру. Как это сделать? О, в голову приходят всякие извращённые мысли о том, что можно сделать с игроком, но, думаю, лучше всего просто так увеличивать скорость после каждого собранного цилиндра, заодно ещё будем записывать очки игрока! Для этого нам потребуется указать новые переменные (эту строчку нужно вставить где-нибуть в начале, допустим, после констант): Global Speed#=.1,Score=0 Здесь у нас Speed# (не забудьте, что когда Вы собираетесь использовать дробные числа, Вы должны использовать # после названия переменных, а то у Вас ничего не получится) – скорость, и Score – очки. Теперь, скорость должна увеличиваться после столкновения с цилиндром, поэтому простое условие:

If EntityCollided (Player,TypeTarget) PositionEntity Target,Rnd(-40,40),0,Rnd(-40.40)
If EntityCollided (Player,TypeTarget)
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
Score=Score+1
Speed=Speed+.01
EndIf

Т.е. здесь мы при условии, что шар сталкивается с типом цилиндра, выполняются три действия – цилиндр перемещается в новое место, переменная Score увеличивается на 1, и переменная Speed увеличивается на 0.1, вот только скорость-то от этого не меняется, поэтому строчку перемещения самого игрока в цикле: MoveEntity Player,0,0,.2 Заменим на MoveEntity Player,0,0,Speed# Что-то изменилось? Видите – теперь игрок передвигается не просто со скоростью 0.2, а со значением переменной Speed#, которое изменяется. Ну, вот так намного интереснее – теперь это уже может, хоть и с натягом, но называться игрой! В это даже самому можно поиграть, чем я сейчас и займусь, после того, как покажу, как писать на экране количество очков игрока. Итак новая команда: Text 320,10,"Score : "+Score,True,True Синтакс: Text x, y, текст, центрирование по х, центрирование по у – эта команда схожа с командой Print, только предназначена для графического режима. Эта команда 2-х мерная, т.е. рисуется поверх экрана, запомните, 2-х мерные команды типа рисования линий, картинок, текста нужно ставить между командами RenderWorld и Flip, иначе Вы их не увидите. Ну всё кажется самая глючная из Ваших игр готова, да, глючная, а всё же игра!!! Но все глюки мы постараемся убрать в следующей части. Вот весь код:

EntityType Player, TypePlayer
FreeEntity Plac
Dim Walls(29)
For i=0 To 29
Walls(i)=CreateCube()
PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40)
EntityType Walls(i), TypeWalls
Next
Target=CreateCylinder()
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
EntityType Target,TypeTarget
cam=CreateCamera()
PositionEntity cam,0,60,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Collisions TypePlayer,TypeWalls,2,2
Collisions TypePlayer,TypeTarget,2,2
Repeat
MoveEntity Player,0,0,Speed#
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
If EntityCollided (Player,TypeTarget)
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
Score=Score+1
Speed=Speed+.01
EndIf
If EntityCollided (Player,TypeWalls) End
UpdateWorld
RenderWorld
Text 320,10,"Score : "+Score,True,True
Flip
Until KeyHit(1)
End

Устранение багов Надоело играть, слишком сырая получилась, хотелось бы её переделать. Вот и давайте её доработаем. Первым, что бросается в глаза – во-первых, (во всяком случае на более медленных машинах это заметно) это то, что когда мы прикасаемся к цилиндру, он меняет своё положение, причём 2 раза, во-вторых очки тоже прибавляются на 2, хотя мы белым по синему написали Score=Score+1” то бишь на 1, а не на 2, ну, и кроме того в общем скорость тоже увеличивается в 2 раза быстрее чем надо. Когда я это заметил (а заметил я это сразу), моё подсознание сразу выдало мне способ решения этого бага (интуиция - хорошая вещь), затем через некоторое время сознание дало подробное объяснение, почему это происходит, ну, а память, посчитала эту информацию ненужной и не запомнила. Короче не помню я почему, но решается это всего одной единственной строкой: UpdateWorld Что Вы уже эту команду знаете? И она уже у нас стоит? Правильно, а Вы добавьте ещё одну. Да не рядом с предыдущей. Добавьте её в условие соприкосновения шара и типа цилиндра после установки цилиндра на новое место. Кому здесь не понятно? Объясняю подробнее, кто не хочет осмысливать мою предыдущую фразу: поставьте эту команду после строчки PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40) Как где она? В программе! Почему так происходит? Вспоминать не охото, попробуйте догадаться сами, помню только, что это связано с обработкой столкновений и, кажется после первого UpdateWorld как бы столкновение остаётся, хотя цилиндр перемещается, поэтому надо ставить ещё один, тьфу ты блин, не помню, в общем и всё тут. (программист называется). Второй баг. Вы наверное уже заметили, что иногда, когда Вы запускаете игру, она сразу выходит? Вот здесь всё наоборот: объяснение простое, а попариться придётся дольше! Так вот, если Вы ещё не поняли, в таких случаях получается так, что кубик создаётся слишком близко к шарику, так что шарик уже «влезает» в него при своём создании, дольше срабатывает условие, и игра выходит, так что нам нужно будет сделать так, чтобы кубики не создавались слишком близко к центру. Сделать это можно в начале. Алгоритм таков: когда кубики случайным образом расставляются по местам, нужно проверять их положение, и, если они будут находиться на расстоянии меньше чем 3 от центра, задавать их положение заново, т.е. циклом с условием. Так вот просто строчку в цикле создания кубиков: PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40) Нужно заменить на: Repeat
PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40)
Until Abs(EntityX(Walls(i)))>4 Or Abs(EntityZ(Walls(i)))>4 Так, новые слова: Abs(число) – это не команда, а функция. Она возвращает нам модуль числа стоящего в скобках (т.е. если в скобках было –4, то она возвращает 4, если 5 – то число так и остаётся положительным и равным 5). Эти строчки расшифровываются так. Сначала в случайное место ставится кубик, затем идёт проверка, если координата X или координата Z меньше 4 по модулю (т.е. в диапазоне от –4 до 4), то цикл повторяется (так пока кубик не встанет в нужное место), короче, получается такой квадрат размером 4*4 вокруг центра, в который кубики уже никогда не попадут. Третий баг. Особенно некрасиво смотрится, когда цилиндр оказывается внутри какого-либо кубика, и его невозможно достать сейчас мы с ней справимся. Опять же исправлять это надо в тот момент, когда цилиндр куда-то ставится случайным образом. Алгоритм будет примерно такой же как в прошлый раз – т.е. в цикле с условием, только на этот раз предметом проверки будет пересечение цилиндра с кубиками. Так, строчку в главном цикле:

PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40) Нам нужно заменить, Repeat
inter=False
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
For i=0 To 29
If MeshesIntersect(Target, Walls(i)) inter=True
Next

Until inter=False Объясняю как это происходит. Для начала новая команда – MeshesIntersect(объект1,объект2) возвращает True (Истина), если объект1 пересекается с объектом2, так как здесь применяется метод проверки полигон к полигону, она довольно медленная(зато точная), но для нашей игры как раз. Дальше – как идёт цикл. Сначала мы задаём переменной inter значение False, затем ставим в случайное место цилиндр, и затем идёт цикл из 29 проверок, и если цилиндр пересекается хотя бы с одним из кубиков, значение переменной inter становится равным True (Истина). Ну и дальше проверяем – если inter так и осталась False (Ложь) идём дальше, если нет – возвращаемся и проводим все операции заново. Ну, вот вроде все самые бросающиеся в глаза баги убрали – теперь это довольно играбельный движок. А вот всё что у нас есть на данный момент:

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Const TypePlayer=1,TypeWalls=2,TypeTarget=3
Global Speed#=.1,Score=0
Global Player=CreateSphere()
Plac=CreateCone(8)
RotateMesh Plac,-90,0,0
ScaleMesh Plac,1,1,1.2
PositionMesh Plac,0,0,-1.5
AddMesh Plac,Player
EntityType Player, TypePlayer
FreeEntity Plac
Dim Walls(29)
For i=0 To 29
Walls(i)=CreateCube()
Repeat
PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40)
Until Abs(EntityX(Walls(i)))>10 Or Abs(EntityZ(Walls(i)))>10
EntityType Walls(i), TypeWalls
Next
Target=CreateCylinder()
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
EntityType Target,TypeTarget
cam=CreateCamera()
PositionEntity cam,0,60,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Collisions TypePlayer,TypeWalls,2,2
Collisions TypePlayer,TypeTarget,2,2
Repeat
MoveEntity Player,0,0,Speed#
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
If EntityCollided (Player,TypeTarget)
Repeat
inter=False
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
For i=0 To 29
If MeshesIntersect(Target, Walls(i)) inter=True
Next
Until inter=False
UpdateWorld
Score=Score+1
Speed=Speed+.01
EndIf
If EntityCollided (Player,TypeWalls) End
UpdateWorld
RenderWorld
Color 255,215,0
Text 320,10,"Score : "+Score,True,True
Flip
Until KeyHit(1)
End

Внешний вид
Итак, движок разработан, что ещё делать программисту? Улучшать внешний вид! Всё таки игрок будет смотреть и оценивать игру «извне» - т.е. по тому, как она выглядит, и играется, а не по тому, каким образом там реализована проверка на столкновения. Поэтому нельзя недооценивать некоторые мелочи, которые могут приукрасить Вашу игру, что можно сделать сначала? Ну, во-первых, я предлагаю всё разукрасить! (а то чёрно-белое всё как-то примитивно смотрится) Делать-то для этого много не надо, а всё равно смотреться будет круче! Так, сначала разукрасим игрока зелёным (поставьте эту строчку после создания игрока): EntityColor Player,0,255,0 Затем кубики красным (эту строчку нужно вставить в цикл создания кубиков): EntityColor Walls(i),255,0,0 И, наконец, закрасим цилиндр жёлтым: EntityColor Target,255,215,0 Во, теперь куда красочнее всё смотрится! Красный, жёлтый, зелёный! Если хотите, конечно, можете подставить свои цвета. Как выбрать себе цвет? Самый лёгкий способ – зайдите в PaintBrush, затем в меню Палитра > Изменить Палитру, и нажмите на кнопочку «Определить цвет», там такой спектр вылезет, и снизу цифры. Те что слева – как раз те, которые нужны! Во-вторых. Есть такие мелочи, которые очень сильно улучшают внешний вид игры, это какой-нибудь вид, дополнительный выбор или ещё что-нибудь, что не очень сложно реализовать, но при этом сильно изменяет игру в лучшую сторону. Так вот, сейчас предлагаю внести два изменения, во-первых камеру при её создании поставить чуть пониже, строчку: PositionEntity cam,0,60,0 Заменить на: PositionEntity cam,0,40,0 И вставить такую ма-а-аленькую строчку кода в главный цикл игры, которая правда, покажет, что игра всё-таки трёхмерная: PointEntity cam,Player Догадались, что она будет делать? Она будет следить за нашим шариком, находясь в одной точке над серединой! Это немного ухудшит вид, зато смотреться сама игра будет в два раза круче вот так какая-то строчка может сильно повлиять на целую игру! Вот, вроде как и оформили чуть-чуть.. ладно я тут ещё хотел кое-что добавить – стены, которые будут ограничивать область самой игры (а то раньше забыл добавить, а сейчас как бы и место как раз есть). Чтобы особо не париться, сделаем их того же типа, что и кубики, более того, предлагаю сделать их из того же массива, что и кубики. Но, для этого нам надо добавить ещё 4 элемента массива, поэтому: Dim Walls(29) Заменим на: Dim Walls(33) Дальше создадим 4 кубика, которые у нас скоро станут стенами

For i=30 To 33
Walls(i)=CreateCube()
EntityColor Walls(i),100,20,0
EntityType Walls(i),TypeWalls
Next

Здесь мы создаём ещё 4 кубика, меняем цвет и задаём тип столкновения, ну, понятно. Теперь расставим их по местам, где они будут стоять:

PositionEntity Walls(30),-50,0,0
PositionEntity Walls(31),50,0,0
PositionEntity Walls(32),0,0,-50
PositionEntity Walls(33),0,0,50

Но, кубики-то так кубиками и останутся, поэтому мы должны их расширить. Можно использовать команду ScaleEntity, но здесь я предлагаю использовать новую команду – FitMesh! Синтаксис: FitMesh объект, X, Y, Z, ширина, высота, длина – эта команда так трансформирует объект, и перемещает его вершины, что он занимает ровно отведённый куб, задаваемый этими параметрами - очень полезная команда! Позволяет Вам задать именно такой размер для модели, какой Вы хотите, независимо от того, какой размер модели был до этого! Так, вот для примера, возьмём Walls(30) – который находится слева от нашего игрока (в начале, во всяком случае), что нам надо с ним сделать? Пусть в ширине будет 2, в высоту он будет от –1 до 2 (т.е. выше всех остальных наших объектов), и в длину – на всё поле + ещё немножко (т.к. кубики у нас расставляются от –40 до 40 – т.е. на дистанции 80, сделаем размер поля равным 100…) – т.е. от –50 до 50. Значит мы пишем: FitMesh –1,-1,-50,2,3,100 – не забудьте, что последние три цифры – не координаты, а размеры! А для Walls(33) – который находится спереди, нам надо сделать ширину от –50 до 50, высоту от –1 до 2, а длину от –1 до 1. И для неё мы пишем: FitMesh –50,-1,-1,100,3,2. Так по аналогии все команды:

FitMesh Walls(30),-1,-1,-50,2,3,100
FitMesh Walls(31),-1,-1,-50,2,3,100
FitMesh Walls(32),-50,-1,-1,100,3,2
FitMesh Walls(33),-50,-1,-1,100,3,2

Вот, вроде внешний вид и приведён в норму, на выставку она может не попрёт, но для первой игры очень даже неплохо! Зато я знаю, что Ваша вторая (ну не вторая, так третья!) – точно будет намного красочнее и интереснее! (Главное не забудьте прислать мне демку или полную версию Вашей игры! И через некоторое время Ваше чудо будет красоваться в разделе "Игры"! Буду ждать!). Весь код:

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Const TypePlayer=1,TypeWalls=2,TypeTarget=3
Global Speed#=.1,Score=0
Global Player=CreateSphere()
Plac=CreateCone(8)
RotateMesh Plac,-90,0,0
ScaleMesh Plac,1,1,1.2
PositionMesh Plac,0,0,-1.5
AddMesh Plac,Player
EntityType Player, TypePlayer
EntityColor Player,0,255,0
FreeEntity Plac
Dim Walls(33)
For i=0 To 29
Walls(i)=CreateCube()
Repeat
PositionEntity Walls(i),Rnd(-40,40),0,Rnd(-40,40)
Until Abs(EntityX(Walls(i)))>10 Or Abs(EntityZ(Walls(i)))>10
EntityType Walls(i), TypeWalls
EntityColor Walls(i),255,0,0
Next
For i=30 To 33
Walls(i)=CreateCube()
EntityColor Walls(i),100,20,0
EntityType Walls(i),TypeWalls
Next
PositionEntity Walls(30),-50,0,0
FitMesh Walls(30),-1,-1,-50,2,3,100
PositionEntity Walls(31),50,0,0
FitMesh Walls(31),-1,-1,-50,2,3,100
PositionEntity Walls(32),0,0,-50
FitMesh Walls(32),-50,-1,-1,100,3,2
PositionEntity Walls(33),0,0,50
FitMesh Walls(33),-50,-1,-1,100,3,2
Target=CreateCylinder()
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
EntityColor Target,255,215,0
EntityType Target,TypeTarget
cam=CreateCamera()
PositionEntity cam,0,40,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Collisions TypePlayer,TypeWalls,2,2
Collisions TypePlayer,TypeTarget,2,2
Repeat
MoveEntity Player,0,0,Speed#
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
PointEntity cam,Player
If EntityCollided (Player,TypeTarget)
Repeat
inter=False
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
For i=0 To 29
If MeshesIntersect(Target, Walls(i)) inter=True
Next
Until inter=False
UpdateWorld
Score=Score+1
Speed=Speed+.01
EndIf
If EntityCollided (Player,TypeWalls) End
UpdateWorld
RenderWorld
Color 255,215,0
Text 320,10,"Score : "+Score,True,True
Flip
Until KeyHit(1)
End

От массива к спискам В этой части не произойдёт никаких изменений во внешнем виде игры, не изменится геймплей и даже не увеличится частота кадров, но без этой части нам не подойти к следущей. Сейчас мы будем оптимизировать движок, делать его более понятным и унифицированным! Для начала, мы переведём наши кубики из массива в список. Итак, грядут большие изменения в программе. Для начала создадим новый тип - Walls: Type Walls
Field model
End Type Эту строчку поместите до констант. Здесь мы создаём новый тип с одним полем – model. В этом поле у нас будет содержаться модели кубика. Далее, можно стереть строчку Dim Walls(33). Так как нам теперь не надо будет задавать, какое именно количество элементов мы будем использовать. Дальше, весь процесс создания кубиков нам нужно переделать:

For i=0 To 29
w.Walls = New Walls
wmodel=CreateCube()
Repeat
PositionEntity wmodel,Rnd(-40,40),0,Rnd(-40,40)
Until Abs(EntityX(wmodel))>10 Or Abs(EntityZ(wmodel))>10
EntityType wmodel, TypeWalls
EntityColor wmodel,255,0,0
Next

Здесь мы создаём новый элемент типа Walls, и создаём новый куб в его поле model, ну остальное всё осталось также. Теперь мы свободно можем поставить любую другую конечную цифру вместо 29 (раньше нам бы понадобилось менять конечное количество элементов когда мы описывали массив). Дальше также меняем создание стен:

w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,-50,0,0
FitMesh wmodel,-1,-1,-50,2,3,100
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,50,0,0
FitMesh wmodel,-1,-1,-50,2,3,100
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,0,0,-50
FitMesh wmodel,-50,-1,-1,100,3,2
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,0,0,50
FitMesh wmodel,-50,-1,-1,100,3,2

К сожалению у нас не получится так же как раньше сначала создать сразу 4 модельки, а потом их расставлять и трансформировать (вернее вряд ли получится это сделать написав меньшее количество строк и не усложняя), поэтому создадим четыре стены и расставим их по отдельности (чего нам места жалко, что ли?). И дальше поменяем цикл проверки пересечения модельки цилиндра и кубиков в главном цикле (это когда мы ставим цилиндр в новое место – помните?) – поменяем строчки:

For i=1 To 29
If MeshesIntersect(Target, Walls(i)) inter=True
Next На: For w.Walls = Each Walls
If MeshesIntersect(Target, wmodel) inter=True
Next

Здесь даже проще получается – т.е. независимо от того, насколько большой у нас список, мы просто «пролистываем» его от начала до конца. Всё – переход на списки прошёл успешно! Можете запустить игру – видите, никаких изменений! Возможно кому-то покажется, что это лишь усложнение, но это только видимость. Списки намного удобнее чем массивы, если научиться с ними работать. Не нужно указывать, какое именно количество элементов Вы собираетесь использовать, а значит, игра стала более гибкой – т.е. сейчас мы можем задать любое число во время создания кубиков. Допустим, если бы мы захотели изменить цифру, если бы мы использовали массив, то нам надо было бы поменять цифру: 1) Когда мы указываем какое кол-во элементов у нас будет в массиве 2) Когда мы создаём стены (потому что стены мы создавали последними элементами) 3) В главном цикле – когда мы делали проверку на пересечение цилиндра и кубиков – опять же кол-во кубиков изменилось, значит и количество проверок должно измениться. Три замены. Конечно, это немного, но ведь и игрушка у нас маленькая. Представьте, что было бы в большом проекте!

SeedRnd MilliSecs()
Graphics3D 640,480,16,1
SetBuffer BackBuffer()
Type Walls
Field model
End Type
Const TypePlayer=1,TypeWalls=2,TypeTarget=3
Global Speed#=.1,Score=0
Global Player=CreateSphere()
Plac=CreateCone(8)
RotateMesh Plac,-90,0,0
ScaleMesh Plac,1,1,1.2
PositionMesh Plac,0,0,-1.5
AddMesh Plac,Player
EntityType Player, TypePlayer
EntityColor Player,0,255,0
FreeEntity Plac
For i=0 To 29
w.Walls = New Walls
wmodel=CreateCube()
Repeat
PositionEntity wmodel,Rnd(-40,40),0,Rnd(-40,40)
Until Abs(EntityX(wmodel))>10 Or Abs(EntityZ(wmodel))>10
EntityType wmodel, TypeWalls
EntityColor wmodel,255,0,0
Next
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,-50,0,0
FitMesh wmodel,-1,-1,-50,2,3,100
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,50,0,0
FitMesh wmodel,-1,-1,-50,2,3,100
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,0,0,-50
FitMesh wmodel,-50,-1,-1,100,3,2
w.Walls = New Walls
wmodel=CreateCube()
EntityColor wmodel,100,20,0
EntityType wmodel,TypeWalls
PositionEntity wmodel,0,0,50
FitMesh wmodel,-50,-1,-1,100,3,2
Target=CreateCylinder()
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
EntityColor Target,255,215,0
EntityType Target,TypeTarget
cam=CreateCamera()
PositionEntity cam,0,40,0
TurnEntity cam,90,0,0
lit=CreateLight()
TurnEntity lit,70,70,0
Collisions TypePlayer,TypeWalls,2,2
Collisions TypePlayer,TypeTarget,2,2
Repeat
MoveEntity Player,0,0,Speed#
If KeyDown(203) TurnEntity Player,0,3,0
If KeyDown(205) TurnEntity Player,0,-3,0
PointEntity cam,Player
If EntityCollided (Player,TypeTarget)
Repeat
inter=False
PositionEntity Target,Rnd(-40,40),0,Rnd(-40,40)
For w.Walls = Each Walls
If MeshesIntersect(Target, wmodel) inter=True
Next
Until inter=False
UpdateWorld
Score=Score+1
Speed=Speed+.01
EndIf
If EntityCollided (Player,TypeWalls) End
UpdateWorld
RenderWorld
Color 255,215,0
Text 320,10,"Score : "+Score,True,True
Flip
Until KeyHit(1)
End
 
X

xKlonx

Zato
Спасибо!!!Скачал,буду пробовать
 
R

Rus59Wolf

Blitz вещь прикольная но своеобразная. Я бы сказал что это обучалка. И на мой взгляд зря начали с 3d, там есть вполне интересные команды для работы в плоскости. Собственно ограничение по директам в 7 версию и выше означает что язык работает с командами именно 7 версии и ниже а там уж простите по нынешним меркам сильно не разгуляешься.

ИМХО для 3d вещей лучше подойдет darkBasic а на блитце куда лучше писать казуалки и игры с "плоской" графикой. Например герои третьи (в плане графики ессно) пишутся на ура. Проблема только с прозрачностью вернее с ее програмным изменением. А ручной просчет уж больно жесток.

Ну и на последок. Чтото серьезное на блитце написать достаточно сложно. Здесь очень простой код, очень легкий стиль его написания, очень удобная среда. НО чтобы написать даже не Quake а Doom, нужно очень сильно заморочится. А вот чтобы обучать программированию это весьма хорошая вещь. Ибо тот же (нежно любимый мной) Borland Pascal все же и выглядит не очень и строг через чур, а тут привлекательный интерфейс и основа языка Basic.



Собсвенно мои примеры можно посмотреть по адресу выше. Там есть поиск пути. есть маленький скрол шутер (точнее самая основа но астероиды сбивать уже можно).
 
Z

Zato

Хм, я с тобой не согласен, во-первых упомянутый тобой DB значительно слабее в плане 3D, намного медленнее и вообще примитивен со своим 8 (или 7, я уже не помню) дерексом, в блитзе уже есть удобные решения для шрейдеров и других интересностей. А для 2D тогда лучше PureBasic или Blitz2D, они и адоптированы для этого больше, да и быстрее в 2-10 раз.

А по поводу чего-то серьёзного, ты не прав. Можно получить вполне неплохую игру уровня портала или HL2, не заморачиваясь особенно с движком.
 
A

ALPHA

заинтересовался вот этим ЯП, но человек я новый в этой среде, знаю практически нечего собственно не знаю :) но есть большое желание начать создавать свою игру, пускай даже и не супер внешнего вида, но все же с чего то нужно начинать!
а отсюда мой первый вопрос, какой набор ПО мне необходим, и если несложно какие есть на сегодняшний день их последние версии и где их можно достать?

П.С. надеюсь на поддержку уже опытных людей заранее спасибо за уделенное время:(
 
Z

Zato

Скачай для начала Blitz3D, это самое главное, дальше ставишь его и можешь начинать прогать.
Если понадобится
 
A

ALPHA

сяб за адреса буду разбираться если че еще отпишу




уже скачал и установил правда с отображением версии проблемы, вроде бы патчи поставил что в комплекте шли, в инструкции написано что патчит до версии 1.99 да и патч сам называется так, а показывает в хелпе что прога 1.98 версии неурядица может лучше с оф сайта пробную версию она там наверняка самая последняя или это того не стоит вообщем?




кстати ли существует версия 2.00 все таки в ней обещана поддержка директикса9 что значительно добавить визуальной привлекательности игрового мира?

если есть то где? кроме офа?



вот уже понемногу читаю учебный курс и там много посылаются на встроенную хелп справку, но она на английском, есть ли возможность русификации? и если да то где взять? в тех силках что били дани найти не удалось? и гугл всякую гадость с вирусами бросает ? может кто то знает где в100% есть, киньте силочку пожалуйста проверенную, заранее спасибо
 
Z

Zato

По поводу хелпа, прочти шапку.

2.0 я не нашёл, он вроде ещё даж не вышел. На офф сайте таже 1.99.

Девятый дерекс и так поддерживается при помощи сторонних библиотек, покопай, не помню, как они называются. Тока сам по себе он картинку лучше не сделает, пока шрейдеры не подключишь, разницы не заметишь...
 
A

ALPHA

ой и впрямь насчет хелпа тупанул :) сори, сейчас как ты подсказал нашел хотя и не сразу , почему то кнопку скачать только самый краюшек видно все остальной за край монитора вылазить, сразу не заметил просто ее:) кстати и теста половина ту даже уходит:) а сам форум как раз ровно в рамки моего монитора влазить :)
а вообщем оглавление читал потому и заинтересовался:)

П.С. сяб за подсказки ;)

у меня возникли проблемы с функцией Val которая должна строки в числа переводить при попытке скомпилировать проект появляется окошко в котором пишется что она не найдена как быть?

вот пример программки


Graphics 640,780

mn1#=5.6
mn2$=""

mn2=Str(mn1)

Print mn2

mn3$=mn2
mn4#=""

mn4=val(mn3)

Print mn4

WaitKey()

в справочнике тоже эту функцию найти не удалось??:(

еще одна штука символы кириллицей в строчных переметных отображаются как просто вертикальные штрихи

например так

ms$="это текст"

но вместо такой картинки он показывает следующее...


ms$="||| |||||"

как можно исправить этот баг? при компиляции в окне приложения текст отображается корректно, но при наборе в самом окне компилятора
и при запуске из сохраненного файла тоже штрихи:(

пытался вернуть родной английский хелп но в нем справки о функции Val тоже нету:( есть ли такая функция вбще ?? и почему она в учебном курсе присутствует тогда???

вот вырезка из курса по этой теме...


Строковые функции
Эта группа функций, часть представителей которой всегда нужна под руками довольно часто.
Функция Str - предназначена для конвертирования числового значения в строку. Наиболее часто она нужна для дальнейшей манипуляции такими преобразованными строками с помощью других строковых функций. Обратное преобразование в число осуществляется функцией Val

Graphics 640, 480

myNum1# = 6.4
myNum2$ = ""

myNum2 = str( myNum1 ) ; преобразование в строку

WaitKey()




как бить в чем я ошибся вить функция Str в хелпе присутствует а Val нет???

уважаемые посетители сайта после длительного поиска я нашел в чем тут закавыка оказывается в этом языке нет такой функции вместо не используется иной способ преобразования переменных из числовой в строчную просто в повторном указании переменой ставится знак% вместо прежнего # очевидно тот кто писал статью курса допустил ошибку вить в остальных ЯП основанных на бейсике такая функция есть вот что нашел на эту тему :)...



Str variable/value

Параметры
Код
Copy to buffer
variable/value = числовая переменная


Описание
Используйте эту команду, чтобы преобразовать числовое значение в строковое значению для использования с командами обработки строк. Blitz запросто печатает числовые значения так же, как и строчные, но если Вы захотите использовать такие функции, как LEFT$ или подобные, Вы должны будете преобразовать вашу числовую переменную в строчную. Заметьте: в процессе преобразования все 6 десятичных цифрт будут представлены в виде числа с плавающей запятой .

Если Вы пожелаете выполнить обратное действие - преобразовать строку в число, то не найдете в Blitz-е никакой команды, эквивалентной команде Val в другтх версиях Бейсика. Вместо этого просто присвойте строковую переменную числовой переменной, и Blitz сам преобразует её.

Пример
Код
Copy to buffer
; Пример STR

num#=2.5
mynum$=str num#

Print mynum$




Добавлено: кстати проблемы с превращением кириллицы в штрихи решить так и не удалось:( кто может подсказать какую гайку ему подкрутить?:)
 
R

Rus59Wolf

Однако да. ИЗВИНИТЕ, Я ПРОТУПИЛ!!!

Дело в том что я работал в блитце очень давно, и версия моего дистра - 1.64. Очень радует что появились новые версии. Видимо многое сильно доработано, однако попрежнему считаю что это инструмент в первую очередь для новичков (простой и нетребовательный синтаксис, удобный интерфейс). И еще это отличная обучалка. Именно потому что здесь все очень просто.

Если вы считаете что можно создавать достаточно серьезные продукты, то вам виднее. Это бесспорно!
В свое время изучал эту среду вообще по файлу помощи и более менее понял именно 2д секцию, поэтому она мне так и понравилась. Собственно там нет никакой сложности - надо разобраться с загрузкой изображений, их анимацией, выводом на экран, функцией определения пересечения пары изображений и таймером... И вполне можно заниматся логикой программы, то есть, скажем, танчики пишутся вообще на раз (если без иск ина)

По тексту не встречался с такой проблемой. Были комманды Print, Write, Text - все они отображали русские символы вполне коректно, НО если загрузить шрифт LoadFont и использовать его SetFont то вместо кирилических символов появлялись "кракозябры" - явно не совпадали кодировочные таблицы, причем в чем должен был быть текст чтобы он отображался правильно я не помню.
 
P

pojurgy

кстати ли существует версия 2.00 все таки в ней обещана поддержка директикса9 что значительно добавить визуальной привлекательности игрового мира?

где то читал что для работы с DX9 есть Xors3D он идёт типа как дополнение к блицу
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!