Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Программирование игр для компьютеров > Xors3D

Xors3D Графический движок с поддержкой DirectX9

Ответ
 
Опции темы
Старый 07.06.2010, 19:12   #1
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Сообщение Делаем квест на Xors3D

Привет, дорогие мои булочники! С сегодняшнего дня, Высший Институт Хлебо-Булочного Искусства открыт! На форуме начали появляться весьма пессимистичные темы наподобии: булка скисла, булка уже не торт и т.д. и т.п. Мы надеемся, что ВИХБИ поможет решить нынешний кризис булки, и убрать вялость и унылость на нашем любимом форуме.

Так, раз вступительные слова закончились, то пожалуй приступим к делу. Так, как у меня уже есть опыт создания квеста, то я решил научить Вас тому, как их делать. Конечно, я уверен, что мои методы не самые лучшие, и их можно оптимизировать сотню раз, так что, заранее попрошу извинения у Тех, кого я буду учить, за то, что не могу показать наилучший путь в моих статьях. Итак, для работы нам понадобится:
- Blitz3D. Я пользуюсь обновлением 1.99, чего и Вам советую, дабы избежать будущих проблем.
- Xors3D, revision № 536. Скачать и установить, желательно ту ревизию, которуя я указал, опять же, дабы избежать проблем.
- 3DS Max 9. Вы можете использовать любую удобную для Вас версию, только не забудьте установить B3D Pipeline, без него, у Вас нечего не получится.
- MS Paint =). К сожалению, я слишком ленив, чтобы качать Adobe Photoshop, так что, я буду использовать MS Paint. Вы можете использовать любой удобный для Вас графический редактор.

Всё что я указал сверху, кроме последнего (его можно заменить), необходимо для работы. Обязательно скачайте и установите. Продолжение, я буду стараться выкладывать как можно быстрее, однако если ко мне не придет вдохновление, то придеться немного потерпеть. Вопросы задавать в теме "Вопросы по "Делаем квест на Xors3D" ". В статьях, я постараюсь приводить советы известных геймдевелоперов и всякие интересные факты, однако, тут уж как получиться. Ну, пожалуй на этой ноте мы прервемся. Ждите продолжения!
(Offline)
 
Ответить с цитированием
Эти 18 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
Arles (02.07.2010), Arton (07.06.2010), baton4ik (08.06.2010), BlackOut (08.06.2010), drave (10.06.2010), Dream (08.06.2010), Gector (29.05.2012), Harter (13.11.2010), IGR (07.06.2010), LLI.T.A.L.K.E.R. (07.06.2010), NetBuilding (22.07.2010), Nex (08.06.2010), pax (28.07.2010), Randomize (07.06.2010), strayhnd (10.07.2010), St_AnGer (07.06.2010), viper86 (21.07.2010), ІГРОГРАЙКО (03.02.2012)
Старый 07.06.2010, 19:59   #2
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Ну что ж, продолжим. Если вы устанавили всё, что было указанно в первом посте, то можем приступать. Однако, сначала немного теории.

Что такое квест? Обратимся к книге "Компьютерные игры: как это делается", и вот что там сказано: Квест, игра-приключение, с первых секунд погружает игрока в мир, полный тайн и головоломок. В таких играх обычно присутствует линейный сюжет, согласно которому вы, главный герой, должны достичь некоей высшей цели, общаясь с другими персонажами и используя различные предметы. Иногда здесь могут встретиться и элементы экшен-игры. Прекрасные примеры этого жанра - Grim Fandango (LucasArts), а также Myst (Cyan/Broderbund) и The Last Express (Smoking Car/Broderbund). Надеюсь, Вы поняли, а если нет, то я попытаюсь рассказать всё своими словами: в квестах обычно преследуют какую-то цель, к примеру: найти сокровища, отыскать пропавшего, выйти из здания и т.д. и т.п. Для достижения главной цели, Вам предлагается решить всевозможные задачи: найти нужный ключ, разгадать шифровку, отыскать нужную книгу и т.д. и т.п.

Мы попытаемся написать простейший квест без NPC (non-player character, в простонародье бот), где нужно будет подбирать ключи, открывать двери, находить нужные предметы и т.д. и т.п. Но для начала нам нужен сюжет. Давайте возьмём простейший: Вы застряли в доме, и нужно оттуда выбираться, находя спрятанные ключи.

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

Так, небольшой сюжетик есть, можно приступать к програмной части. Ждите, следующей части!
(Offline)
 
Ответить с цитированием
Эти 11 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
baton4ik (08.06.2010), Igor (07.06.2010), IGR (07.06.2010), Nex (08.06.2010), Nuprahtor (07.06.2010), pax (28.07.2010), Randomize (07.06.2010), St_AnGer (07.06.2010), turBO (07.06.2010), viper86 (21.07.2010), ІГРОГРАЙКО (03.02.2012)
Старый 07.06.2010, 21:59   #3
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Так, давайте создадим новый файл в Blitz3D, назовем его "main.bb", и сохраним его в какую-нибудь папку, к примеру "Quest". Кидаем в эту же папку, файл поставляемый с установочными файлами Xors3D, "xors3d.bb". Теперь, открываем "main.bb" и пишем туда следующее:

Include "xors3d.bb"

xGraphics3D 800,600,32,0,True 
xSetBuffer xBackBuffer
()



While 
Not xKeyHit(1)

    
xUpdateWorld
    xRenderWorld
    xFlip
Wend
End 
Ну, тут всё понятно. Создаем окно, устанавливаем буфер, и запускаем цикл. Теперь, давайте создавать нашего игрока. Квест наш будет от первого лица, так что игрок - это сфера+камера. Давайте, напишем функцию создания игрока:

Function InitPlayer()
    
player=xCreateSphere()
        
xEntityAlpha player,0
    camera
=xCreateCamera(player)
End Function 
Теперь, вставьте InitPlayer() перед главным циклом. Также, не забудьте объявить player и camera глобальными, в дальнейшем нам это понадобится. Так, игрок создаётся, пора научить его передвигаться. Пишем функцию перемещения игрока:

Function UpdatePlayer()
    
mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
    
myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
    
    
xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/Передвигаем мышку в центр окна
    
If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,Проверяем угол поворота камеры по X
    
If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,Проверяем угол поворота камеры по X
    xRotateEntity camera
,xEntityPitch(camera)+myspd,0,Поворачиваем камеру по X
    xRotateEntity player
,0,xEntityYaw(player)-mxspd,Поворачиваем камеру по Y
    
    
If xKeyDown(17xMoveEntity player,0,0,0.1
    
If xKeyDown(31xMoveEntity player,0,0,-0.1
    
If xKeyDown(30xMoveEntity player,-0.1,0,0
    
If xKeyDown(32xMoveEntity player,0.1,0,0
    
    Select True
        
Case xKeyDown(29)
            
xPositionEntity camera,0,0,0
            
        
Case Not xKeyDown(29)
            
xPositionEntity camera,0,2,0
    End Select
End 
Function 
Теперь, наш игрок умеет ходить, вертеть головой и сидеть. Ну, отлично! Давайте, ещё добавим немного физики! Изменяем функцию InitPlayer() следующим образом:

Function InitPlayer()
    
player=xCreateSphere()
        
xEntityAlpha player,0
        xEntityAddSphereShape player
,10,1
    camera
=xCreateCamera(player)
End Function 
Так, запускаем игру и видим что нас уносит вниз, это действует физика Xors3D. Команда xEntityAddSphereShape entity, mass#, radius# добавляет физическое тело круглой формы. В неё передается три параметра:
entity - объект, к которому надо добавить физическое тело
mass# - масса физического тела
radius# - радиус физического тела

Давайте, добавим временную платформу, чтобы наш игрок не уходил далеко вниз:

Function InitScene()
    
platform=xCreateCube()
        
xScaleEntity platform,10,1,10
        xPositionEntity platform
,0,-3,0
        xEntityAddBoxShape platform
,0
End 
Function 
После, добавьте строчку InitScene() после InitPlayer() перед циклом. Запускаем, и видим как наш игрок мягко падает на куб. Теперь расскажу про команду xEntityAddBoxShape entity, mass#, width#, height#, depth#, которая добавляет физическое тело параллепидного типа:
entity - это объект, к которому добавляется физическое тело
mass# - масса физического тела (в дальнейшем, я буду использовать сокрашение ф.т.)
width# - ширина ф.т.
height# - высота ф.т.
depth# - глубина нашего ф.т.

Примечание: Если значение mass# сделать равным нулю, то объект становиться статичным. Также, если объект стандартный примитив, то не обязательно передавать какие-либо параметры, помимо entity и mass#, Xors3D сам доделывает нужную работу.

Ну, похоже на сегодня всё! Ждите продолжения! И удачи Вам!
Вложения
Тип файла: rar Quest.rar (6.1 Кб, 958 просмотров)
(Offline)
 
Ответить с цитированием
Эти 8 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
baton4ik (08.06.2010), Harter (11.07.2010), Nex (14.06.2010), Nuprahtor (07.06.2010), pax (28.07.2010), Randomize (08.06.2010), St_AnGer (08.06.2010), viper86 (21.07.2010)
Старый 08.06.2010, 19:25   #4
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Давайте продолжим нашу работу. Итак, последний раз мы остановились на управлении. Наш игрок умеет передвигаться, сидеть, вертеться и взаимодействует с предметами. Теперь, начнем работу над уровнем. По сюжету, наш игрок застрял в доме, так что, сделаем дом. При помощи 3ds max'a, я сделал небольшую комнату, при помощи модификатора boolean из compound objects. В редакторе я дал имя этой комнате "map", затем, в комнате я разместил небольшой кубик и дал ему имя "player".

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

Теперь, экспортируем всё это дело при помощи B3D Pipeline в папку с проектом и называем "map.b3d". Ну, и пожалуй, изменим функцию создания карты следующим образом:

Function InitScene()
    
map=xLoadAnimMesh("map.b3d")
End Function 
Не забудьте добавить map в глобалы, в дальнейшем, нам это понадобится. Запускаем, и видим как наш игрок уходит в тьму. Теперь, мы с Вами приступим к самому вкусному, функции сортировки объектов в карте:

Function SortMapObjects(entity)
    
name$=xEntityName(entity)
    
    If 
Instr(name,"player"Then
        xPositionEntity player
,xEntityX(entity),xEntityY(entity),xEntityZ(entity)
        
xHideEntity entity
    
EndIf
    
    For 
i=0 To xCountChildren(entity)-1
        SortMapObjects
(xGetChild(entity,i))
    
Next
End 
Function 
Также, мы немного изменим функцию InitScene():

Function InitScene()
    
map=xLoadAnimMesh("map.b3d")
        
SortMapObjects(map)
End Function 
Примечание: Друзья, в своих кодах, я использую очень много отступов, дабы было легче в них разбираться, чего и Вам советую. С плохим стилем писания кода, с Вами трудно будет иметь общий проект. А хороший стиль кодирования, только повысит Вас в глазах Ваших одногруппников.

Здесь, мы используем рекурсию, и проверяем имена всех объектов в нашей карте, и если в имени содержится слово "player" (вспоминаем наш куб), то мы позиционируем нашего игрока в точке, где находится объект, и прячем сам объект. Однако, наш игрок продолжает падать. Для предотварщения этого, добавим коллизий в нашу игру. Для этого, мы создадим константы:

Const TypePlayer=1TypeMap=
И изменияем функции InitPlayer(), InitScene() и SortMapObjects(), как приведенно ниже:

Function InitPlayer()
    
player=xCreateSphere()
        
xEntityAlpha player,0
        xEntityAddSphereShape player
,10,1
        xEntityType player
,TypePlayer
    camera
=xCreateCamera(player)
End Function

Function 
InitScene()
    
light=xCreateLight()

    
map=xLoadAnimMesh("map.b3d")
        
SortMapObjects(map)
        
    
xCollisions TypePlayer,TypeMap,2,2
End 
Function

Function 
SortMapObjects(entity)
    
name$=xEntityName(entity)
    
    If 
Instr(name,"player"Then
        xPositionEntity player
,xEntityX(entity),xEntityY(entity),xEntityZ(entity)
        
xHideEntity entity
    
EndIf
    
    If 
Instr(name,"map"Then
        xEntityType entity
,TypeMap
    
EndIf
    
    For 
i=0 To xCountChildren(entity)-1
        SortMapObjects
(xGetChild(entity,i))
    
Next
End 
Function 
Примечание: В функции InitScene(), я добавил строчку создания света, чтобы легче было отличать стены.

Так, и что же мы сделали: в функции InitPlayer(), мы присвоили тип TypePlayer объекту player; в функции InitScene(), мы включили коллизию для объектов типа TypePlayer и TypeMap; и наконец-то, в функции SortMapObjects(), при нахождении объекта с именем "map", мы присвоили ему тип TypeMap. Так, теперь запускаем игру, и видим, что игрок не падает сквозь стены. Ждите продолжения!
Вложения
Тип файла: rar Quest.rar (19.0 Кб, 936 просмотров)

Последний раз редактировалось Hurrit, 09.06.2010 в 23:09.
(Offline)
 
Ответить с цитированием
Эти 8 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
baton4ik (08.06.2010), Harter (11.07.2010), Mhyhr (08.06.2010), Nex (14.06.2010), pax (28.07.2010), Randomize (08.06.2010), St_AnGer (08.06.2010), viper86 (21.07.2010)
Старый 09.06.2010, 23:08   #5
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Итак, продолжим. Теперь, когда "фундамент" игры готов, давайте добавим больше деталей для нашего сюжета. По сюжету, игрок должен выбраться из дома, а теперь как он это будет делать. Во-первых, дом сделаем двухкомнатным. Игрок появляется в первой комнате, в которой будет находится тумбочка с двумя выдвижными полочками. В первом полочке будет лежать ключ от двери, ведущей во вторую комнату. Во второй комнате, на стене будет небольшой двухдверный шкафчик, в котором будет лежат батарейка. На стене, во второй комнате, будет небольшой механизм, в который можно будет вставлять батарейку. Как только батарейка будет вставлена, в первой комнате, у нас будет открываться скрытый сейф в стене, в которой будет лежать ключ от выхода из дома. При выходе, будет появляться надпись "Game Over". Вот и весь сюжетик и квесты. Теперь, приступим к теории, как мы будем воплощать всё это в игру.

Когда я писал свой первый квест, то я много думал о том, как бы избавиться от необходимости прописывать все квесты в коде, и меня осенила прекрасная (на мой взгляд) идея: ведь можно всё прописать в 3ds max, а игру просто научить правильно "читать" карту. Вот как мы поступим: в 3ds max'е, мы в имена будем записывать свойства объекта, а потом уже через код, присваивать эти самые свойства. Вот из чего будут состоять имена:
1 - Первые три буквы будут "obj", чтобы мы знали, что с этим объектом можно работать.
2 - Затем будет идти цифра, которая будет говорить, с каким типом объекта мы имеем дело. Всего таких будет типов будет 5.
3 - Здесь будет хранится ID объекта, для взаимодействия с другими объектами. ID будет состоять из трех цифр, начиная с 001.


Остальные свойства зависят от типа объекта. Давайте, их расмотрим:
Тип 1 - Это будут предметы, к которым ничего не надо применять и которые нельзя брать. В нашем случае, к этим объектам относятся выдвижные полочки и дверцы шкафчика. Дополнительных свойств у этого типа объектов нету.
Тип 2 - Данный тип будет у предметов, которые можно подбирать. В нашем случае - это ключи и батарейка. Дополнительное свойство у этих объектов один: ID родителя. Это нужно для того, чтобы объект двигался вместе с родителем, к примеру выдвигался вместе с полочкой тумбочки.
Тип 3 - Объекты к которым надо применять подобранные предметы. К примеру двери, которые открываются при помощи ключей. Дополнитьное свойство у этих объектов, это ID предмета, который надо к ним применить.
Тип 4 - К этому типу объектов будут относиться те, от которых зависят ещё одни предметы, и к которым надо применять вещи. К примеру, механизм в который надо вставить батарейку, после чего откроется сейф. Дополнительные свойства у этого объекта будут ID предмета, который надо к ним применить, и после него будет идти ID предмета, который надо активировать. Последним свойством будет ID предмета, который надо показать. В нашем случае ID батарейки, которая будет заранее вставлена в механизм и спрятана.
Тип 5 - Предпоследний тип объектов будет относиться к тем предметам, которые зависят от объектов 4 типа. В нашем случае это сейф. Дополнительных свойств нету.
Тип 6 - Вот и добрались мы до последнего типа. Объекты, которые надо показать, в случае активации объектов 4 типа, это тип 6. Дополнительных свойств нету.


Звучит сложно, давайте попытаюсь обяснить на примере: есть у нас на карте ключ и дверь, которая откроется после того, как на ней будет использован ключ. Мы даем имя ключу "obj2001000", где:
obj - обозначение того, что мы работаем с используемым объектом.
2 - тип объекта.
001 - ID объекта.
000 - ID родителя (в данном случае, у объекта нету родителя).

А двери мы даем имя "obj3002001", где:
obj - обозначение того, что мы работаем с используемым объектом.
3 - тип объекта.
002 - ID объекта.
001 - ID объекта, который необходим для открытия двери.


Вот так будет действовать наш алгоритм. Пора, кстати, и начать писать его. Карту я уже подготовил, Вы можете скачать его в аттаче.

Примечание: Запомните, перед экспортом, желательно всегда применять ResetXForm на вкладке Utilities.

Теперь, когда всё готово, давайте изменим функцию SortMapObjects():

Function SortMapObjects(entity)
	name$=xEntityName(entity)
	
	If Instr(name,"player") Then
		xPositionEntity player,xEntityX#(entity),xEntityY#(entity),xEntityZ#(entity)
		xResetEntity player
		xHideEntity entity
	EndIf
	
	If Instr(name,"map") Then
		xEntityType entity,TypeMap
	EndIf
	
	If Instr(name,"obj") Then
		object_type=Int(Mid(name,4,1))
		object_id=Int(Mid(name,5,3))
				obj(object_id)=New properties
				obj(object_id)\entity=entity
		Select object_type ; Здесь, мы получаем тип объекта
			Case 2
				obj(object_id)\id_parent=Int(Mid(name,8,3))
			Case 3
				obj(object_id)\id_object=Int(Mid(name,8,3))	
			Case 4
				obj(object_id)\id_object=Int(Mid(name,8,3))	
				obj(object_id)\id_child1=Int(Mid(name,11,3))	
				obj(object_id)\id_child2=Int(Mid(name,14,3))	
		End Select
	EndIf
	
	For i=0 To xCountChildren(entity)-1
		SortMapObjects(xGetChild(entity,i))
	Next
End Function
И не забудьте добавить массив obj и тип properties, в которых будут хранится все данные об объектах:

Dim obj.properties(999)

Type properties
    Field entity
obj_type
    Field id_parent
id_objectid_child1id_child2
End Type 
Примечание: Учитывая советы форумчан и некоторые перемены в карте, мне пришлось изменить функцию UpdatePlayer():

Function UpdatePlayer()
    
mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
    
myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
    
    
xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/Передвигаем мышку в центр окна
    
If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,Проверяем угол поворота камеры по X
    
If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,Проверяем угол поворота камеры по X
    xRotateEntity camera
,xEntityPitch(camera)+myspd,0,Поворачиваем камеру по X
    xRotateEntity player
,0,xEntityYaw(player)-mxspd,Поворачиваем камеру по Y
    
    
If xKeyDown(KEY_WxMoveEntity player,0,0,0.5
    
If xKeyDown(KEY_SxMoveEntity player,0,0,-0.5
    
If xKeyDown(KEY_AxMoveEntity player,-0.5,0,0
    
If xKeyDown(KEY_DxMoveEntity player,0.5,0,0
    
    Select xKeyDown
(KEY_LCONTROL)
        Case 
True
            xPositionEntity camera
,0,5,0
            
        
Case False
            xPositionEntity camera
,0,15,0
    End Select
End 
Function 
Вложения
Тип файла: rar Quest.rar (159.7 Кб, 901 просмотров)
(Offline)
 
Ответить с цитированием
Эти 11 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
Артем Валерьевич (08.07.2010), baton4ik (10.06.2010), drave (10.06.2010), Dream (10.06.2010), Gector (29.05.2012), Harter (11.07.2010), Nex (14.06.2010), pax (28.07.2010), Randomize (10.06.2010), St_AnGer (10.06.2010), viper86 (21.07.2010)
Старый 10.06.2010, 21:56   #6
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

А вот и продолжение. Игра уже умеет "читать" объекты на карте, пора бы научить её взаимодействовать с ними. Для начала, надо проанимировать все необходимые объекты (двери, полочки). Я это уже сделал, Вы же, как обычно, можете взять готовую карту в аттаче. Так, ну раз анимация готова, то давайте, для начала изменим функцию SortMapObjects():

Function SortMapObjects(entity)
	name$=xEntityName(entity)
	xEntityPickMode entity,2 ; Устанавливаем PickMode равным 2, чтобы мы могли "ловить" его камерой
	
	If Instr(name,"player") Then
		xPositionEntity player,xEntityX#(entity),xEntityY#(entity),xEntityZ#(entity)
		xResetEntity player
		xHideEntity entity
	EndIf
	
	If Instr(name,"map") Then
		xEntityType entity,TypeMap
	EndIf
	
	If Instr(name,"obj") Then
		object_type=Int(Mid(name,4,1))
		object_id=Int(Mid(name,5,3))
				obj(object_id)=New properties
				obj(object_id)\entity=entity
					xExtractAnimSeq(obj(object_id)\entity,0,30) ; Получаем анимацию открывания
					xExtractAnimSeq(obj(object_id)\entity,30,60) ; Получаем анимацию закрывания
					xEntityType obj(object_id)\entity,TypeMap ; Устанавливаем тип для коллизий
					xNameEntity obj(object_id)\entity,name ; Даем имя объекту, для дальнейшей идентификации
		Select object_type ; Здесь, мы получаем тип объекта
			Case 2
				obj(object_id)\id_parent=Int(Mid(name,8,3))
			Case 3
				obj(object_id)\id_object=Int(Mid(name,8,3))	
			Case 4
				obj(object_id)\id_object=Int(Mid(name,8,3))	
				obj(object_id)\id_child1=Int(Mid(name,11,3))	
				obj(object_id)\id_child2=Int(Mid(name,14,3))	
		End Select
	EndIf
	
	For i=0 To xCountChildren(entity)-1
		SortMapObjects(xGetChild(entity,i))
	Next
End Function
Теперь, мы можем приступать к добавлению взаимодействия с объектом, для этого мы изменим функцию UpdatePlayer():

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
					Select obj(id)\time ; Узнаем, какая была последняя анимация
						Case 0 ; Если закрытия, то проигрываем открывание
							xAnimate obj(id)\entity,3,1,1
							obj(id)\time=1 ; Меняем состояние
						Case 1
							xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
							obj(id)\time=0 ; Меняем состояние
					End Select					
				EndIf
		EndIf
	EndIf

	
End Function
Примечание: Добавьте поле time в тип properties.

Жмыкаем F5, подходим к двери, нажимаем Е, и вуаля, она открывается. Если нажать ещё раз, то она закрывается. Так, теперь нам понадобится инвентарь, чтобы брать объекты, и использовать их.

У нас в коде будет 3 функции для работы с инвентарем: InventDraw(), InventUpdate(), InventAddItem(). Нам понадобится тип inventory и массивы inv.inventory(100), img(100), и картинки "key.png", "batareyka.png", "back.png":

Global back=xLoadImage("back.png")

Dim inv.inventory(100)
Dim img(100)
    
img(1)=xLoadImage("key.png")
    
img(2)=xLoadImage("batareyka.png")

Type inventory
    Field image
id%
End Type 
А вот и функции для работы с инвентарем:

Function InventoryDraw()
	For i=1 To 10
		For j=1 To 10
			xDrawBlock back,j*30,i*30 ; Рисуем бэкграунд
		Next
	Next

	k=1
	For i=1 To 10
		For j=1 To 10
			If inv(k)\image<>0 xDrawBlock inv(k)\image,j*30,i*30 ; Если ячейка не пуста, то отрисовываем её		
			k=k+1
		Next
	Next
End Function

Function InventoryAddItem(image,id)
	For i=1 To 100
		If inv(i)\image=0 Then 
			inv(i)\image=img(image) ; Здесь, мы добавляем в инвентарь рисунок с номером равному image
				inv(i)\id=id ; И присваиваем ему ID равное значению ID
			Exit
		EndIf
	Next
End Function

Function InventoryUpdate()
	If (xMouseX()>=30) And (xMouseX()=<330) And (xMouseY()>=30) And (xMouseY()=<330) Then ; Проверяем, находится ли наш курсор наш курсор над ячейками
		If xMouseHit(1) Then
			num=((xMouseY()/30)*10)-(10-(xMouseX()/30)) ; Этой функцией, мы вычисляем кликнутую ячейку
				selected_obj=inv(num)\image
				selected_id=inv(num)\id
		EndIf
	EndIf
End Function
Примечание: Добавьте в глобалы три переменные: selected_obj, selected_id, inv_draw. Также, картинки вы можете взять в аттаче.

Так, теперь нам надо модифицировать функцию InitScene():

Function InitScene()
    
light=xCreateLight()
    
    
map=xLoadAnimMesh("map.b3d")
        
SortMapObjects(map)
        
    For 
i=1 To 100
        inv
(i)=New inventory Создаем новую ячейку
    Next
        
    xCollisions TypePlayer
,TypeMap,2,2
End 
Function 
И конечно же, функция UpdatePlayer():

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
					Select obj(id)\time ; Узнаем, какая была последняя анимация
						Case 0 ; Если закрытия, то проигрываем открывание
							xAnimate obj(id)\entity,3,1,1
							obj(id)\time=1 ; Меняем состояние
						Case 1
							xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
							obj(id)\time=0 ; Меняем состояние
					End Select					
				EndIf
		EndIf
	EndIf
	
End Function
Примечание: Перенесите вызов функции UpdatePlayer() между xUpdateWorld и xFlip, чтобы наш инвентарь мог отрисовываться.

P.S.: Приношу глубочайшие извинения за то, что продолжение не выходило так долго!
Вложения
Тип файла: rar Quest.rar (162.1 Кб, 909 просмотров)

Последний раз редактировалось Hurrit, 12.06.2010 в 20:09.
(Offline)
 
Ответить с цитированием
Эти 8 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
Android (12.06.2010), baton4ik (12.06.2010), Harter (11.07.2010), IGR (10.06.2010), Nex (14.06.2010), pax (28.07.2010), St_AnGer (10.06.2010), viper86 (21.07.2010)
Старый 12.06.2010, 20:29   #7
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Продолжим. У нас появился инвентарь, давайте же научимся брать предметы. Как мы помним, предметы которые можно брать имеют тип номер 2, и чтобы научить игру отличать эти предметы от других, нам надо модифицировать функцию SortMapObjects(), InventoryAddItem() и UpdatePlayer(), также, нам надо будет изменить массив img(100):

Dim img(100)
	img(3)=xLoadImage("key.png")
	img(7)=xLoadImage("batareyka.png")
	img(11)=xLoadImage("key.png")

Function SortMapObjects(entity)
	name$=xEntityName(entity)
	xEntityPickMode entity,2 ; Устанавливаем PickMode равным 2, чтобы мы могли "ловить" его камерой
	
	If Instr(name,"player") Then
		xPositionEntity player,xEntityX#(entity),xEntityY#(entity),xEntityZ#(entity)
		xResetEntity player
		xHideEntity entity
	EndIf
	
	If Instr(name,"map") Then
		xEntityType entity,TypeMap
	EndIf
	
	If Instr(name,"obj") Then
		object_type=Int(Mid(name,4,1))
		object_id=Int(Mid(name,5,3))
				obj(object_id)=New properties
				obj(object_id)\entity=entity
				obj(object_id)\obj_type=object_type ; Здесь, мы записываем тип объекта в поле obj_type
					xExtractAnimSeq(obj(object_id)\entity,0,30) ; Получаем анимацию открывания
					xExtractAnimSeq(obj(object_id)\entity,30,60) ; Получаем анимацию закрывания
					xEntityType obj(object_id)\entity,TypeMap ; Устанавливаем тип для коллизий
					xNameEntity obj(object_id)\entity,name ; Даем имя объекту, для дальнейшей идентификации
		Select object_type ; Здесь, мы получаем тип объекта
			Case 2
				obj(object_id)\id_parent=Int(Mid(name,8,3))
			Case 3
				obj(object_id)\id_object=Int(Mid(name,8,3))	
			Case 4
				obj(object_id)\id_object=Int(Mid(name,8,3))	
				obj(object_id)\id_child1=Int(Mid(name,11,3))	
				obj(object_id)\id_child2=Int(Mid(name,14,3))	
		End Select
	EndIf
	
	For i=0 To xCountChildren(entity)-1
		SortMapObjects(xGetChild(entity,i))
	Next
End Function

Function InventoryAddItem(image)
	For i=1 To 100
		If inv(i)\id=0 Then 
			inv(i)\image=img(image) ; Здесь, мы добавляем в инвентарь рисунок с номером равному image
				inv(i)\id=image ; И присваиваем ему ID равное значению image
			Exit
		EndIf
	Next
End Function

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				Select obj(id)\obj_type ; Узнаем, какой тип объекта мы пикнули
				
					Case 1 ; Если тип 1, то
						If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
							Select obj(id)\time ; Узнаем, какая была последняя анимация
								Case 0 ; Если закрытия, то проигрываем открывание
									xAnimate obj(id)\entity,3,1,1
									obj(id)\time=1 ; Меняем состояние
								Case 1
									xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
									obj(id)\time=0 ; Меняем состояние
							End Select					
						EndIf
						
					Case 2
						InventoryAddItem(id) ; Добавляем в инвентарь картинку с ID объекта
						xHideEntity obj(id)\entity ; Скрываем объект
						
				End Select
		EndIf
	EndIf
	
End Function
Жмем F5, открываем полочку, и берем ключик, затем жмем правую кнопку мыши, и видим ключ в инвентаре. Также, Вы можете заметить что дверь, к которой нужен ключ не открывается, без него не открывается. Однако, есть две проблемы: 1 - ключ не выкатывается вместе с полочкой, 2 - дверь не откроется и с ключом. Давайте, для начала исправим первую ошибку:

Function InitScene()
	light=xCreateLight()
	
	map=xLoadAnimMesh("map.b3d")
		SortMapObjects(map)
	
	For i=1 To 999
		If obj(i)<>Null Then ; Если наш объект существует, то
			Select obj(i)\obj_type ; Получаем его тип
				Case 2 ; И если он равен двум, то 
					If obj(i)\id_parent<>0 Then ; Проверяем, существует ли у него родитель
						xEntityParent obj(i)\entity, obj(obj(i)\id_parent)\entity; Если да, то присваиваем его ему
					EndIf
			End Select
		EndIf
	Next
	
	For i=1 To 100
		inv(i)=New inventory ; Создаем новую ячейку
	Next
		
	xCollisions TypePlayer,TypeMap,2,2
End Function
Теперь, ключ выкатывается вместе с полкой. Давайте, решать проблему номер два:

Function InventoryUpdate()
	If (xMouseX()>=30) And (xMouseX()=<330) And (xMouseY()>=30) And (xMouseY()=<330) Then ; Проверяем, находится ли наш курсор наш курсор над ячейками
		If xMouseHit(1) Then
			num=((xMouseY()/30)*10)-(10-(xMouseX()/30)) ; Этой функцией, мы вычисляем кликнутую ячейку
				selected_obj=inv(num)\image ; Присваиваем картинку выбранного объекта
				selected_id=inv(num)\id ; И также ID
				selected_num=num
		EndIf
	EndIf
End Function

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	If selected_obj<>0 xDrawImage selected_obj,600,100 ; Если у нас имеется выбранный предмет, то отрисовываем его
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				Select obj(id)\obj_type ; Узнаем, какой тип объекта мы пикнули
				
					Case 1 ; Если тип 1, то
						If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
							Select obj(id)\time ; Узнаем, какая была последняя анимация
								Case 0 ; Если закрытия, то проигрываем открывание
									xAnimate obj(id)\entity,3,1,1
									obj(id)\time=1 ; Меняем состояние
								Case 1
									xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
									obj(id)\time=0 ; Меняем состояние
							End Select					
						EndIf
						
					Case 2
						InventoryAddItem(id) ; Добавляем в инвентарь картинку с ID объекта
						xHideEntity obj(id)\entity ; Скрываем объект
						
					Case 3
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(id)\entity,3,1,1 ; Если да, то проигрываем анимацию
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
				End Select
		EndIf
	EndIf
	
End Function
Так, теперь всё должно работать просто великолепно. Продолжим. Давайте "учиться" вставлять батарейки, для этого, нам надо изменить функцию UpdatePlayer():

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	If selected_obj<>0 xDrawImage selected_obj,600,100 ; Если у нас имеется выбранный предмет, то отрисовываем его
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				Select obj(id)\obj_type ; Узнаем, какой тип объекта мы пикнули
				
					Case 1 ; Если тип 1, то
						If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
							Select obj(id)\time ; Узнаем, какая была последняя анимация
								Case 0 ; Если закрытия, то проигрываем открывание
									xAnimate obj(id)\entity,3,1,1
									obj(id)\time=1 ; Меняем состояние
								Case 1
									xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
									obj(id)\time=0 ; Меняем состояние
							End Select					
						EndIf
						
					Case 2
						InventoryAddItem(id) ; Добавляем в инвентарь картинку с ID объекта
						xHideEntity obj(id)\entity ; Скрываем объект
						
					Case 3
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(id)\entity,3,1,1 ; Если да, то проигрываем анимацию
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
					Case 4
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(obj(id)\id_child1)\entity,3,1,1  ; Если да, то проигрываем анимацию объекта номер 1
							xShowEntity obj(obj(id)\id_child2)\entity ; И показываем объект номер 2
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
				End Select
		EndIf
	EndIf
	
End Function
Если Вы, нажмете F5 и немного поиграете, то поймете, что всё работает отлично, однако, есть одно "но": батарейка, заранее вставленная в механизм не была изначально спрятана, но это можно изменить, изменив функцию InitScene():

Function InitScene()
	light=xCreateLight()
	
	map=xLoadAnimMesh("map.b3d")
		SortMapObjects(map)
	
	For i=1 To 999
		If obj(i)<>Null Then ; Если наш объект существует, то
			Select obj(i)\obj_type ; Получаем его тип
			
				Case 2 ; И если он равен двум, то 
					If obj(i)\id_parent<>0 Then ; Проверяем, существует ли у него родитель
						xEntityParent obj(i)\entity, obj(obj(i)\id_parent)\entity; Если да, то присваиваем его ему
					EndIf
				
				Case 4
					xHideEntity obj(obj(i)\id_child2)\entity	 ; Прячем объект с ID объекта номер 2
			End Select
		EndIf
	Next
	
	For i=1 To 100
		inv(i)=New inventory ; Создаем новую ячейку
	Next
		
	xCollisions TypePlayer,TypeMap,2,2
End Function
Ну и последнее что осталось сделать, это показать надпись "Game Over", для этого мы изменим функцию UpdatePlayer():

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	If selected_obj<>0 xDrawImage selected_obj,600,100 ; Если у нас имеется выбранный предмет, то отрисовываем его
	If win=1 Then ; Если win равен 1, то 
		xText 400,300,"Game Over",True ; Показывать надпись "Game Over"
	EndIf
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				Select obj(id)\obj_type ; Узнаем, какой тип объекта мы пикнули
				
					Case 1 ; Если тип 1, то
						If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
							Select obj(id)\time ; Узнаем, какая была последняя анимация
								Case 0 ; Если закрытия, то проигрываем открывание
									xAnimate obj(id)\entity,3,1,1
									obj(id)\time=1 ; Меняем состояние
								Case 1
									xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
									obj(id)\time=0 ; Меняем состояние
							End Select					
						EndIf
						
					Case 2
						InventoryAddItem(id) ; Добавляем в инвентарь картинку с ID объекта
						xHideEntity obj(id)\entity ; Скрываем объект
						
					Case 3
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(id)\entity,3,1,1 ; Если да, то проигрываем анимацию
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							
							If id=12 Then ; Если ID равен ID последней двери, то
								win=1 ; Присвоить переменной win значение 1
							EndIf
							
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
						
					Case 4
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(obj(id)\id_child1)\entity,3,1,1  ; Если да, то проигрываем анимацию объекта номер 1
							xShowEntity obj(obj(id)\id_child2)\entity ; И показываем объект номер 2
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
				End Select
		EndIf
	EndIf
	
End Function
Примечание: Добавьте переменную win в Globals.
Вложения
Тип файла: rar Quest.rar (162.8 Кб, 884 просмотров)

Последний раз редактировалось Hurrit, 13.06.2010 в 22:23.
(Offline)
 
Ответить с цитированием
Эти 7 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
baton4ik (13.06.2010), Harter (11.07.2010), Nex (14.06.2010), pax (28.07.2010), Randomize (11.10.2011), strayhnd (10.07.2010), viper86 (21.07.2010)
Старый 13.06.2010, 22:30   #8
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

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

Боб Бейтс (Bob Bates), Legend Entertainment

Боб Бейтс, глава студии Legend Entertainment, начал свою карьеру в 1986 году с создания игр для компании Infocom, навсегда вошедшей в историю индустрии со своими текстовыми квестами. С тех пор его графические игры, в том числе Eric The Unready и недавно вышедшая John Saul's Blackstone Chronicles, не раз удостаивались различных наград.

В своем очерке Боб Бейтс делится идеями и секретами создания игр-приключений.

Не давайте игроку скучать

Самое ужасное преступление, какое только может совершить создатель игры, - наскучить игроку. Поясним это положение.

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

Если в вашей игре появляется сейф с кодовым замком, то, как только игроку удалось с ним справиться, добавьте в его арсенал команду «Открыть сейф». Не заставляйте его набирать код снова и снова всякий раз, когда ему понадобится воспользоваться сейфом.

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

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

в). Сделайте так, чтобы игрок мог свободно перемещаться в окружающем пространстве. Если созданный вами мир занимает большую площадь, позвольте игроку одной командой преодолевать расстояния из конца в конец. Не утомляйте его бесконечными путешествиями туда и обратно.

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

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

Не владеете словом - не беритесь за перо

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

Как оценить собственную литературную профпригодность? Я придерживаюсь следующего правила. Если вы никогда не слышали о книге «Elements of Style» Странка и Уайта (издательство Allyn & Bacon), если вам не приходилось изучать литературные приемы и задумываться о различиях между хорошим и плохим текстом, предоставьте сочинительство кому-нибудь другому.

Почувствуйте игрока

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

Если же у вас отсутствует эта способность, боюсь, вы не сможете стать хорошим разработчиком ни в одном из существующих жанров.

«Я советую разработчикам сначала прокрутить все в голове. Закрыть глаза и представить всю игру - еще до написания программы», - говорит Боб Бейтс.


Рон Гилберт (Ron Gilbert), Humongous Entertainment

«На совести» этого господина - множество популярнейших игр производства компании Humongous Entertainment (вспомните хотя бы игре Maniac Mansion), легендарный сериал Monkey Island и несколько детских квестов. Ниже приводятся соображения Рона касательно самого главного аспекта творческого процесса - вдохновения. А именно:

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

При этом не следует забывать об аудитории. Если вы создаете игру только для собственного удовольствия и ваши предпочтения вдув вразрез с требованиями рынка, она не будет иметь успеха. В то же время замысел должен зародиться не в голове, а в сердце. Если вы полагаетесь только на исследования рынка, у игры не будет «души».

Какие примеры может привести Рон Гилберт, основываясь на своих работах? Как ему пришла в голову идея Monkey Island. По его словам, изучение спроса на игровом рынке, конечно, проводилось (как выяснилось, «Обезьяний остров» была первой игрой на «пиратскую» тематик» но главную роль сыграли его собственные пристрастия.

После Maniac Mansion, действие которой происходило в современном мире, мне захотелось придумать игру-сказку, и тут я вспомнил о своих любимых пиратских историях... На создание Monkey Island ушло около двух лет. Я написал пять сценариев, прежде чем остановился на одном, самом удачном. Зато Monkey Island II отняла меньше года.


Чэд Фримен (Chad Freeman), Dreamforge Intertainment

Чэд Фримен работал в качестве программиста над такими широко известными играми, как Anvil of Dawn и Warwind, а в последнее время являлся ведущим программистом игры Sanitarium (в русской версии «Шизариум». - Примеч. ред.), изданной компанией ASC Games. В главе 8 вы встретитесь с его советами по программированию, но у Чэда есть что рассказать и об общих принципах создания компьютерных игр.

Важность сюжета

Для начала замечу, что для хорошей игры самое главное - это сюжет. Конечно, существует множество неплохих игр и даже целые жанры, в которых нет и намека на сюжетную линию, и все же самые лучшие игры - те, где присутствует интересная фабула. Quake, бесспорно, является классическим шутером от первого лица, но игру Half-Life, относящуюся по сути к тому же жанру, увлекательная сюжетная линия ставит на порядок выше.

Поддерживайте «обратную связь»

Работая над игрой, обязательно учитывайте пожелания других игроков. Создавая Sanitarium, мы попросили каждого сотрудника нашей компании пройти игру от начала до конца. Заметив, что коллег не особенно впечатлила одна из частей игры, мы убрали ее и приступили к доработке проекта. Однако не спешите менять и удалять отдельные фрагменты игры только потому, что кому-то (или даже всем) так хочется. Если вы уверены, что ваша идея сработает, оставляйте ее без изменений. И не важно, окажетесь вы правы или нет, - кто не рискует, тот не пьет шампанского!

Изучите свои возможности

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


Ли Шелдон (Lee Sheldon), SouthPeak Interactive

Писатель и разработчик компьютерных игр Ли Шелдон участвовал в создании ряда широко известных квестов, включая Dark Side of the Moon, Temujin и «орденоносный» The Riddle of Master Lu. Мы попросили господина Шелдона рассказать, что в первую очередь следует помнить создателю игры, и по возможности подкрепить свои слова примерами.

Зачем вы это делаете?

Прежде всего спросите себя, ради чего вы хотите создать игру. Это самый главный вопрос, на который необходимо знать ответ. Вам нужны деньги? Или вы решили заняться этим от скуки? А может, все дело в том, что творчество - ваша стихия? Чтобы ответить на этот вопрос, надо заглянуть в свое сердце и познать себя. Это первый шаг, и без него не обойтись, если вы хотите создать что-либо действительно стоящее. Сделать этот шаг не так просто, как кажется. Часто бывает, что этому не придают особенного значение.

Выступая в защиту этого незаслуженно обойденного вопроса, Ли Шелдон приводит следующий пример.

Игру Dark Side of the Moon я получил «в наследство» от другого разработчика. Должен признаться, что работать над игрой, задуманной не тобой, гораздо сложнее. Сюжет Dark Side основывался на научной фантастике, действие происходило в открытом космосе. Таких игр к тому времени было более чем достаточно. Итак, я спросил себя: «Зачем тебе эта игра? Что тебя привлекает в ее сюжете?» И в голову мне пришла одна идея. Задумка была простая и не особенно оригинальная, но что-то в ней было... Вот эта мысль: человеку необходима семья. Семья воспитывает нас и дает нам силы противостоять жизненным невзгодам. Но традиционные представления о семье помогают нам далеко не всегда. Я создал персонаж, который по ходу игры теряет своих кровных родственников (в результате смерти или предательства), но находит новую семью в совершенно неожиданном месте. Увы, ни один рецензент так им отметил это обстоятельство. Я их не виню. Сюжеты в компьютерных играх редко претендуют на звание литературного шедевра. Но именно тема семьи вдохновила меня на создание этой космической игры.

Для кого эта игра?

Делая СВОЮ игру, не забывайте об игроках, для которых вы работаете. Даже если вы заняты, скажем, «стрелялкой», которую терпеть не можете. Соблюдайте требования жанра и учитывайте вкусы вашей аудитории. Не пытайтесь навязать правила стратеги ролевым играм. Прививайте игрокам любовь к прекрасному, если вам это по силам, но не игнорируйте их пристрастия. Покупателю нет никакого дела до того, что вы вложили в игру сердце и душу. Ему важно только одно: насколько интересна эта игра. Ему лично.

Свою карьеру в индустрии я начинал с детских игр. Квест Once Upon a Forest предназначался для совсем маленьких игроков. Я видел, что головоломки игры и ее прохождение не отличаются особой сложностью и не требуют дополнительных подсказок. Но я на собственном опыте знал, как обидно бывает застрять на какой-нибудь заумной задачке. Так в игре появилась система помощи. Если малыш не знает, что делать, или ему лень ломать голову над загадкой, он может нажать на свечку («доэлектрический» аналог лампочки) в поле интерфейса, и загадка решится сама собой. Итак, мне пришлось представить себя на месте маленького игрока, как бы почувствовать себя в его крохотной «шкурке»!

Испытайте себя

Ни в коем случае не беритесь за создание игры с намерением сделать ее «как игру N, только лучше». Конечно, большинство элементов, присущих выбранному игровому жанру, будут использоваться и здесь, и пренебрегать ими не стоит, но постарайтесь найти к ним новые подходы. Поднимайте планку везде, где только можно: в техническом воплощении, сюжете, персонажах и т.д. Вам потребуется вся ваша фантазия, чтобы выделиться на общем фоне.

Вот как я бросил вызов самому себе в игре Dark Side of the Moon: в порядке эксперимента я создал систему модульной сюжетной линии, чтобы проверить, можно ли ввести в игру сложную и запутанную историю. С одной стороны, повествование должно было разворачиваться по многим направлениям, а с другой - находиться под моим постоянным контролем. Среди многочисленных достоинств квестов нелинейность обычно не значится. Если игроку и дают возможность управлять сюжетом, то очень ненадолго. Даже если история, положенная в основу игры, имеет развернутую структуру, события в ней следуют друг за другом в строго определенном порядке.

Итак, я решил сочинить для игры длинную, подробную и при этом нелинейную фабулу. Никто не просил меня об этом. Было невероятно трудно отслеживать все возможные изменения: я должен был убедиться, что игрок никогда не попадает в заведомо ошибочное положение, выпадающее из общей последовательности. Поскольку точно предсказать, как будет развиваться игра для каждого конкретного пользователя, оказалось практически неразрешимой задачей, игроку приходится постоянно менять диски. Это было новое слово в создании квестов, хоть и не революционное: мне просто хотелось узнать, возможно ли это сделать в принципе. Теперь, когда я убедился, что это возможно, а рецензенты и игроки заметили и оценили это нововведение, я решил, что в следующей игре смогу еще больше расширить набор вариантов сюжетного развития.

Наконец, заключительные слова Ли Шелдона посвящены стремлению к совершенству.

Я начинал на телевидении. Когда мне приходилось беседовать с коллегами о создании телепрограмм, я часто слышал в ответ, что поскольку телевидение само по себе «сущая чепуха», то работа для него не требует больших усилий. Проще говоря, они смотрели на СМИ свысока. И зря. Никогда не следует опускаться до халтуры, даже если вы видите, как «по ящику» бесконечно раскручивают чужие поделки не самого лучшего качества. Старайтесь выкладываться в любом деле полностью. Используйте все свое умение и талант и не занижайте планку заранее. Всегда найдутся «доброжелатели», которые с радостью сделают это за вас. Необходимость согласовывать свои идеи с другими людьми и материальная сторона дела в конечном итоге спустят вас с небес на землю. Поэтому старайтесь начинать с высочайшей горной вершины, иначе в итоге от вашей задумки не останется даже мало-мальски заметного холмика.


Роберта Вильяме (Roberta Williams), Sierra

Это имя еще не раз встретится вам на протяжении всей книги, здесь же Роберта Вильяме, создатель множества великолепных компьютерных игр, поделится своими соображениями по поводу начальной идеи новой игры и приведет в качестве примера свои последние проекты. Как ей кажется, преуспевающий разработчик компьютерных игр обязан хорошо чувствовать рынок. Роберта объясняет это так: «Да, очень важно, чтобы вам самому захотелось создать игру, чтобы вам нравилось над ней работать, но нужно также знать свою аудиторию и четко представлять, какую нишу на компьютерном рынке вы можете занять».

На примере своей последней игры King's Quest: Mask of Eternity госпожа Вильяме рассказывает, что заставило ее внести изменения в традиционную структуру игры.

У меня создалось впечатление, что пользователям надоело продираться сквозь бесконечные головоломки, не получая быстрого вознаграждения за свои труды. Я поняла, что если Mask of Eternity будет сделана в том же ключе, что и предыдущие части King's Quest, она вряд ли будет отвечать нынешним потребностям. Поэтому сделала игру трехмерной и добавила в нее в качестве изюминки экшен-элементы. И снова рынок и его законы! Mask of Eternity сохранила хорошее отношение давних поклонников этой серии, а вот что касается новой аудитории... привлечь ее оказалось сложнее, но мне так хотелось внести свежую струю в традиционный квестовый жанр...

Говоря о «нишах» в индустрии, Роберта проливает свет на подробности создания своего давнего хита Phantasmagoria.

Я обнаружила, что в литературе и кино есть популярнейший жанр - ужасы, у нас же до недавнего времени эта тема вообще не затрагивалась. Таким образом, Phantasmagoria стала полноценным интерактивным фильмом ужасов, в котором задействованы настоящие актеры.

P.S.: Спасибо Всем, за потраченное время, на прочтение данной статьи!
(Offline)
 
Ответить с цитированием
Эти 15 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
Артем Валерьевич (08.07.2010), Android (16.06.2010), Arton (14.06.2010), baton4ik (13.06.2010), drave (14.06.2010), Harter (11.07.2010), IGR (13.06.2010), Lowlet (03.02.2012), Morganolla (14.06.2010), NetBuilding (22.07.2010), Nex (14.06.2010), Nuprahtor (13.06.2010), pax (28.07.2010), Skaner (03.02.2012), viper86 (21.07.2010)
Старый 28.07.2010, 14:44   #9
Hurrit
Мастер
 
Аватар для Hurrit
 
Регистрация: 27.01.2008
Адрес: Россия, СКФО, ЧР, Грозный
Сообщений: 1,144
Написано 578 полезных сообщений
(для 2,207 пользователей)
Ответ: Делаем квест на Xors3D

Внимание: В данной версии используется новая ревизия Xors3D. Так что, не забудьте обновиться.

Сегодня, я попытаюсь помочь Вам в создании NPC (кто не знает, это такой бот). Сразу говорю: сложных систему с выдачей (или приемом) предметов или квестов не ждите, будет простенькая система общения. Как Вы помните, имя нашего NPC, как и имя любого другого взаимодействующего с игроком объекта, будет означать его свойства:

Первые три буквы будут obj, для обозначения того, что это рабочий объект.
Затем, будет идти номер типа объекта, это будет 7 (кстати, это новый тип объекта).
И, последние три цифры будут означать ID объекта.

Затем, использую ID объекта, мы получим файл с текстом для NPC. К примеру, для NPC с ID 013, текст будет носить имя "13.txt". Структура текста будет такой:

Сначала будет идти первая фраза NPC c ID 001. После, с этими же ID будут выходить варианты ответа для игрока. У каждого варианта ответа будет свой ID для ответа. Если ID равен 100, то диалог обрывается. Звучит сложно, но давайте посмотрим, как это выглядит на практике:

001 Чего надо?
001 Привет! 002
001 Ничего. 100
002 Привет. Так, чего надо?
002 Как открыть дверь? 003
002 Спасибо, ничего. 100
003 Найди ключ!
003 Спасибо за совет! 100


Смотрите, если игрок ответит "Привет!", то высветиться фраза "Привет. Так, чего надо?", а если выберит "Ничего.", то диалог закроется, и в таком духе. Попробуем написать код:

Function UpdatePlayer()
	mxspd#=xMouseXSpeed()*0.05 ; Получаем координаты передвижения мышки по Х
	myspd#=xMouseYSpeed()*0.05 ; Получаем координаты передвижения мышки по Y
	
	If inv_draw=0 xMoveMouse xGraphicsWidth()/2,xGraphicsHeight()/2 ; Передвигаем мышку в центр окна
	If xEntityPitch(camera)+myspd<-89 xRotateEntity camera,-89,0,0 ; Проверяем угол поворота камеры по X
	If xEntityPitch(camera)+myspd>89 xRotateEntity camera,89,0,0 ; Проверяем угол поворота камеры по X
	xRotateEntity camera,xEntityPitch(camera)+myspd,0,0 ; Поворачиваем камеру по X
	xRotateEntity player,0,xEntityYaw(player)-mxspd,0 ; Поворачиваем камеру по Y
	
	If xKeyDown(KEY_W) xMoveEntity player,0,0,0.5
	If xKeyDown(KEY_S) xMoveEntity player,0,0,-0.5
	If xKeyDown(KEY_A) xMoveEntity player,-0.5,0,0
	If xKeyDown(KEY_D) xMoveEntity player,0.5,0,0
	If xMouseHit(2) inv_draw=1-inv_draw ; Если нажата правая кнопка мыши, то меняем значение inv_draw на обратное
	
	If inv_draw=1 Then ; Если inv_draw=1, то отрисовываем и обновляем наш инвентарь
		InventoryDraw()
		InventoryUpdate()
	EndIf
	
	If dialog<>0 Then ; Если мы пикнули какой-нибудь NPC, то начинается диалог с NPC, ID у которого равен dialog
		If first_open=1 Then ; Если, это первое (Прим.: не самое первое, а первое после того, как был пикнут NPC) открытие диалога, то
			npc_text=ReadFile(dialog+".txt") ; Открываем текст для NPC с его ID
			first_open=0 ; Меняем переменную, дабы наш файл больше не открывался

			i=1 ; Это нужно для цикла
	
			While Not Eof(npc_text) ; Пока наш текст не закончиться
				string_npc$=ReadLine(npc_text) ; Считываем строчку
	
				render(i)=New npc ; Создаем новое поле в render()
					render(i)\id=Int(Left(string_npc,3)) ; Выдаем ему ему ID
					
					If Int(Right(string_npc,3))<>0 Then ; Если сбоку есть ID (Прим.: это надо для того, чтобы определять где ответы)
						render(i)\next_id=Int(Right(string_npc,3)) ; Выдаем для ответа ID следующей фразы NPC, в случае, если выберут этот ответ
						render(i)\npc_string=Mid(string_npc,5,Len(string_npc)-8) ; Ну и конечно же, даем ему его текст
					Else
						render(i)\npc_string=Mid(string_npc,5,Len(string_npc)) ; В против случае, просто выдаем текст для фразы NPC
					EndIf	
				i=i+1 ; Это необходимо для того, чтобы не перезаписалась уже записанная строка render()
			Wend		
			CloseFile(npc_text) ; После обработки текста, закрываем файл
		EndIf		
		
		For i=1 To 100 ; Пробегаемся по всему render()
			 If render(i)<>Null Then ; Если поле render(i) заполнено
				If render(i)\id=start_id Then ; И его ID совпадает с ID первой фразы NPC
					xText 100,20*i,render(i)\npc_string ; Тогда, пишем фразу
					
					If RectsOverlap(xMouseX(),xMouseY(),1,1,150,20*i,100,24) Then ; Если мышка находится над строкой
						If xMouseDown(1) And (render(i)\next_id<>0) start_id=render(i)\next_id ; И левая клавиша мышки зажата, а также, ID следующей фразы не равна 0 (необходимо для определения ответа), тогда следующая фраза NPC равна render(i)\next_id
						If start_id=100 Then ; Если start_id=100
							For render(i)=Each npc ; Тогда, зачищаем весь render()
								Delete render(i)
							Next
							dialog=0 ; И закрываем диалог
						EndIf
					EndIf
				EndIf
			EndIf
		Next
	EndIf
	
	If selected_obj<>0 xDrawImage selected_obj,600,100 ; Если у нас имеется выбранный предмет, то отрисовываем его
	If win=1 Then ; Если win равен 1, то 
		xText 400,300,"Game Over",True ; Показывать надпись "Game Over"
	EndIf
	
	Select xKeyDown(KEY_LCONTROL)
		Case True
			xPositionEntity camera,0,5,0
			
		Case False
			xPositionEntity camera,0,15,0
	End Select
	
	If xKeyHit(KEY_E) Then
		xCameraPick(camera,xMouseX(),xMouseY()) ; Делаем пик камерой
		picked=xPickedEntity() ; Даем picked'у значение "пойманного" нами объекта
		picked_name$=xEntityName(picked) ; Получаем имя picked'a
	EndIf
	
	If picked<>0 Then ; Проверяем, равен ли picked 0
		If Instr(picked_name,"obj") Then ; Проверяем, есть ли в имени picked'a "obj"
			id=Mid(picked_name,5,3) ; Получаем ID объекта
				Select obj(id)\obj_type ; Узнаем, какой тип объекта мы пикнули
				
					Case 1 ; Если тип 1, то
						If xAnimating(obj(id)\entity)=0 Then ; Проверяем, играет ли сейчас какая-нибудь анимация
							Select obj(id)\time ; Узнаем, какая была последняя анимация
								Case 0 ; Если закрытия, то проигрываем открывание
									xAnimate obj(id)\entity,3,1,1
									obj(id)\time=1 ; Меняем состояние
								Case 1
									xAnimate obj(id)\entity,3,1,2 ; Если открытия, то проигрываем закрывание
									obj(id)\time=0 ; Меняем состояние
							End Select					
						EndIf
						
					Case 2
						InventoryAddItem(id) ; Добавляем в инвентарь картинку с ID объекта
						xHideEntity obj(id)\entity ; Скрываем объект
						
					Case 3
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(id)\entity,3,1,1 ; Если да, то проигрываем анимацию
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							
							If id=12 Then ; Если ID равен ID последней двери, то
								win=1 ; Присвоить переменной win значение 1
							EndIf
							
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf
						
						
					Case 4
						If selected_id=obj(id)\id_object Then ; Проверяем, сходится ли ID объекта выбранного из инвентаря, с ID объектом, который надо применять для данного предмета
							xAnimate obj(obj(id)\id_child1)\entity,3,1,1  ; Если да, то проигрываем анимацию объекта номер 1
							xShowEntity obj(obj(id)\id_child2)\entity ; И показываем объект номер 2
							xFreeImage inv(selected_num)\image ; Удаляем предмет из инвентаря
							selected_id=0 ; Обнуляем ID выбранного предмета
							inv(selected_num)\id=0
							selected_num=0
						EndIf

					Case 7
						dialog=id ; Даем dialog ID пикнутого NPC
						first_open=1 ; Это описанно выше
						start_id=1 ; Даем start_id ID первой фразы NPC
						
				End Select
		EndIf
	EndIf
	
End Function
Также, нам необходимо добавить следующие строчки:

Global dialogfirst_opennpc_textstart_id

Dim render
.npc(100)

Type npc
    Field id
%, next_id%
    
Field npc_string$
End Type 
Также, давайте увеличим шрифт, чтобы лучше читалось:

xSetFont xLoadFont("Arial Cyr",24,True
Примечание: Вставьте эту строчку где-то после globals.

Вот и всё. Вы можете улучшить алгоритм, добавить больше возможностей, всё по Вашему усмотрению. Удачи!
Вложения
Тип файла: rar Quest.rar (170.1 Кб, 1062 просмотров)
(Offline)
 
Ответить с цитированием
Эти 9 пользователя(ей) сказали Спасибо Hurrit за это полезное сообщение:
Артем Валерьевич (01.08.2010), baton4ik (01.08.2010), Coover (10.02.2012), drave (28.07.2010), Gector (29.05.2012), pax (28.07.2010), Randomize (11.10.2011), Skaner (03.02.2012), St_AnGer (28.07.2010)
Старый 03.02.2012, 14:44   #10
Skaner
ПроЭктировщик
 
Аватар для Skaner
 
Регистрация: 30.01.2012
Сообщений: 162
Написано 40 полезных сообщений
(для 86 пользователей)
Ответ: Делаем квест на Xors3D

Юх! Супер!
__________________
Blitz3D - СИЛА!
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

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

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 16:51.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com