Главная » Статьи » Blitz3D » Для новичков

Учебник по Blitz3D для начинающих. Часть 2
Основы движка
Итак, мы уже достаточно вооружены, чтобы сделать простенькую игру, так что хватит абстрактных примеров, а начнём уже делать игры, а то, что мы ещё не изучили, узнаем в самом процессе, по мере надобности. Что это будет за игра? Сделаем такой вариант игры, которую делали в основах по 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

Категория: Для новичков | Добавил: Samarik (15.05.2007)
Просмотров: 1727 | Комментарии: 6 | Рейтинг: 4.0/2
Всего комментариев: 6
6  
<< http://blitzetc.blitzmax.ru/index.php/Основы_трехмерной_графики_в_среде_Blitz3D,_учебник >>
вот приличненький учебничегг

5  
Интересно, те другие по твоиму мнению должны сами все искать и собирать, а ты потом воспользуешься их трудом??

4  
да

3  
Казалось бы, чего проще - зайти в Google и найти все, что надо. Поймите, что никто не захочет просто так слать всем желающим книжки и советы. Это - время, а у некоторых его не так уж много. Попробуйте найти инфу сами, составьте свой сборник и выложите в сеть - люди скажут вам огромное спасибо. Научитесь давать, а не только брать. Извините, обидеть никого не хочу.

2  
Здрасти, я бы очень хотыл бы заниматься программой Blitz Basic. Честно говоря я уже овлодел навыками пакетом 3DS Max. Теперерь уж очень хочеться овлатеть и этой программой. Если у вас есть книги или хотябы ссылки с уроками, не могли бы вы скинуть мне их на мой майл. Спасибо.

1  
[color=blue]

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Корзина
Ваша корзина пуста
Наш опрос
Что в гоночных играх самое важное?
Всего ответов: 262
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0