forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Xors3D (http://forum.boolean.name/forumdisplay.php?f=126)
-   -   Делаем квест на Xors3D (http://forum.boolean.name/showthread.php?t=12628)

Hurrit 07.06.2010 19:12

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

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

Всё что я указал сверху, кроме последнего (его можно заменить), необходимо для работы. Обязательно скачайте и установите. Продолжение, я буду стараться выкладывать как можно быстрее, однако если ко мне не придет вдохновление, то придеться немного потерпеть. Вопросы задавать в теме "Вопросы по "Делаем квест на Xors3D" ". В статьях, я постараюсь приводить советы известных геймдевелоперов и всякие интересные факты, однако, тут уж как получиться. Ну, пожалуй на этой ноте мы прервемся. Ждите продолжения!

Hurrit 07.06.2010 19:59

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

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

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

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

Так, небольшой сюжетик есть, можно приступать к програмной части. Ждите, следующей части!

Hurrit 07.06.2010 21:59

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

PHP код:

Include "xors3d.bb"

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



While 
Not xKeyHit(1)

    
xUpdateWorld
    xRenderWorld
    xFlip
Wend
End 

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

PHP код:

Function InitPlayer()
    
player=xCreateSphere()
        
xEntityAlpha player,0
    camera
=xCreateCamera(player)
End Function 

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

PHP код:

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() следующим образом:

PHP код:

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

Так, запускаем игру и видим что нас уносит вниз, это действует физика Xors3D. Команда xEntityAddSphereShape entity, mass#, radius# добавляет физическое тело круглой формы. В неё передается три параметра:
entity - объект, к которому надо добавить физическое тело
mass# - масса физического тела
radius# - радиус физического тела

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

PHP код:

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 сам доделывает нужную работу.

Ну, похоже на сегодня всё! Ждите продолжения! И удачи Вам!

Hurrit 08.06.2010 19:25

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

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

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

PHP код:

Function InitScene()
    
map=xLoadAnimMesh("map.b3d")
End Function 

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

PHP код:

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():

PHP код:

Function InitScene()
    
map=xLoadAnimMesh("map.b3d")
        
SortMapObjects(map)
End Function 

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

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

PHP код:

Const TypePlayer=1TypeMap=

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

PHP код:

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. Так, теперь запускаем игру, и видим, что игрок не падает сквозь стены. Ждите продолжения!

Hurrit 09.06.2010 23:08

Ответ: Делаем квест на Xors3D
 
Вложений: 1
Итак, продолжим. Теперь, когда "фундамент" игры готов, давайте добавим больше деталей для нашего сюжета. По сюжету, игрок должен выбраться из дома, а теперь как он это будет делать. Во-первых, дом сделаем двухкомнатным. Игрок появляется в первой комнате, в которой будет находится тумбочка с двумя выдвижными полочками. В первом полочке будет лежать ключ от двери, ведущей во вторую комнату. Во второй комнате, на стене будет небольшой двухдверный шкафчик, в котором будет лежат батарейка. На стене, во второй комнате, будет небольшой механизм, в который можно будет вставлять батарейку. Как только батарейка будет вставлена, в первой комнате, у нас будет открываться скрытый сейф в стене, в которой будет лежать ключ от выхода из дома. При выходе, будет появляться надпись "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, в которых будут хранится все данные об объектах:

PHP код:

Dim obj.properties(999)

Type properties
    Field entity
obj_type
    Field id_parent
id_objectid_child1id_child2
End Type 

Примечание: Учитывая советы форумчан и некоторые перемены в карте, мне пришлось изменить функцию UpdatePlayer():

PHP код:

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 


Hurrit 10.06.2010 21:56

Ответ: Делаем квест на Xors3D
 
Вложений: 1
А вот и продолжение. Игра уже умеет "читать" объекты на карте, пора бы научить её взаимодействовать с ними. Для начала, надо проанимировать все необходимые объекты (двери, полочки). Я это уже сделал, Вы же, как обычно, можете взять готовую карту в аттаче. Так, ну раз анимация готова, то давайте, для начала изменим функцию 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":

PHP код:

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():

PHP код:

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.: Приношу глубочайшие извинения за то, что продолжение не выходило так долго!

Hurrit 12.06.2010 20:29

Ответ: Делаем квест на Xors3D
 
Вложений: 1
Продолжим. У нас появился инвентарь, давайте же научимся брать предметы. Как мы помним, предметы которые можно брать имеют тип номер 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.

Hurrit 13.06.2010 22:30

Ответ: Делаем квест на 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.: Спасибо Всем, за потраченное время, на прочтение данной статьи!

Hurrit 28.07.2010 14:44

Ответ: Делаем квест на Xors3D
 
Вложений: 1
Внимание: В данной версии используется новая ревизия 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

Также, нам необходимо добавить следующие строчки:

PHP код:

Global dialogfirst_opennpc_textstart_id

Dim render
.npc(100)

Type npc
    Field id
%, next_id%
    
Field npc_string$
End Type 

Также, давайте увеличим шрифт, чтобы лучше читалось:

PHP код:

xSetFont xLoadFont("Arial Cyr",24,True

Примечание: Вставьте эту строчку где-то после globals.

Вот и всё. Вы можете улучшить алгоритм, добавить больше возможностей, всё по Вашему усмотрению. Удачи!

Skaner 03.02.2012 14:44

Ответ: Делаем квест на Xors3D
 
Юх! Супер!


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

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot