Текстовый квест своими руками
…глаза его отвисли, челюсть выкатилась из орбит…
Один хороший текстовый квест
Знаете, какое первое желание студента или школьника, едва овладевшего азами программирования? Нет, не стать Биллом Гейтсом… это приходит позже :-). Написать собственную игру, неважно какую, хоть Тетрис, хоть Quake, ведь главная причина такого желания — приобрести авторитет в собственных глазах. Так вот, прочитав это вступление, угадайте с трех раз, чем мы сегодня будем заниматься? Да, писать собственную игру. Ну, не StarCraft и даже не Unreal, так что в TOP-10 она может и не попасть, но ведь это не есть наша нынешняя цель. Короче, господа хорошие, мы с вами пишем текстовый квест.
Несмотря на общепринятое мнение, что текстовые квесты были первым жанром компьютерных игр, на самом деле все было не так. Первая компьютерная игра — Space War — была написана группой американских студентов в 60-е годы. Это была межзвездная война двух кораблей, между которыми находилась черная дыра. Довольно оригинально на то время, хотя и не очень, учитывая буйное помешательство тогдашнего поколения на научной фантастике.
Если вы считаете, что текстовые квесты — пережиток прошлого, то глубоко заблуждаетесь. Во-первых, они куда интеллектуальнее и интереснее, чем всякие там тетрисы, арканоиды, пасьянсы и Die Hard’ы. Во-вторых, я играл во многие новые текстовые квесты и почти все они мне более или менее понравились. Перед написанием собственной игры советую посетить сайты http://www.text-adventure.narod.ru и http://rusgames.boom.ru и накачать оттуда массу интереснейших вещей.
Предлагаю начать. Итак, сначала о том, что нам потребуется. Нам нужен компилятор языка C++ (рекомендую Borland С++ версии 2-3.1) и азы языка С++ (многого от вас не потребуется, все то, что нужно, можно найти в популярных изданиях). Будем считать, что все это имеется в наличии, и приступим к написанию собственной игры.
Сначала — главная концепция. Игра построена на общении пользователя с машиной — он вводит команду (например, «взять топор»), а компьютер анализирует введенную строку, находит в ней команду («взять») и предмет действия («топор»). Затем на экран выводится сообщение, адекватное введенной команде (например, «я подобрал топор» или «он мне не нужен»). Многие текстовые квесты не запрограммированы на распознавание конкретной фразы, они ищут ее элементы во введенной команде. Иначе говоря, алгоритм выглядит не так:
if(user_command==»взять топор») printf(«Мне не нужен топор»);
Здесь find(char *what_to) — это условная, несуществующая (пока) функция, которая выполняет поиск слова what_to во введенной пользователем строке и возвращает значение 1 при удаче. Преимущество этого алгоритма в том, что пользователь может ввести и «взять топор», и «я думаю, надо бы взять этот топор», и программа в обоих случаях даст положительный результат.
Еще один распространенный алгоритм заключается в анлализе только первых трех-четырех букв, что позволяет отсекать окончания слов.
Сейчас мы опишем искомую функцию find, выполняющую оба алгоритма, и функцию input_(), которая позволяет производить ввод команды (а-ля scanf() или cin>>, только вводимое слово не прерывается на пробеле). Итак:
#include
#include
void input_(void);
int find(char *what_to); /*Прототипы наших функций*/
int x, xy; /*Вспомогательные переменные*/
char user_command[100]; /*Переменная команды, вводимой пользователем*/
void main(void)
<
/*Здесь у нас код самой игры */
>
int find(char what_to[4]) /*Длина слова для поиска — 4 буквы (позволяет отбросить окончания слов)*/
if(user_command[x]==what_to[0]&&user_command[x+1]==what_to[1]&&
user_command[x+2]=what_to[2]&&user_command[x+3]==what_to[3]) return 1;
x++;
>while(x Enter the command:»);
/*Команда вводится в нижней области экрана, оставляя верхнюю для текста. Каждая новая команда стирает предыдущую */
do <
xy=getche();
user_command[x]=xy;
x++;
>while(xy!=13); /*13 — скан-код клавиши Enter*/
x=0;
>
//HAPPY END
Так вот. Если чего-то недопоняли, лучше вернитесь к этому месту сейчас. Лишний раз перечитайте код, чтобы трудности не возникли позже. Теперь, когда функции уже описаны, приступим к написанию собственно программы. Первое, что нужно сделать, это заставка. Не обязательно графическая. Сгодится, например, и такой вариант:
void main(void)
<
printf(«_________________________\n»);
printf(«_______Neverland III_______\n»);
printf(«_____a text-adventure_____\n»);
printf(«___BY PUPKIN VASILY____\n»);
printf(«____All rights reserved_____\n»);
printf(«_________________________\n»);
/*Далее исходники непосредственно игры*/
>
Выглядит, кстати, вполне сносно. Далее — сама игра. Ее создание — процесс очень творческий и интересный (даже лучше, чем битвы в Unreal :)).
Для начала разберемся с типами text-adventure. Вообще-то, классификацией игр этого жанра никто особо не занимался и заниматься не собирается, но каждый, кто играл хотя бы в несколько тестовых квестов, знает, что они бывают лабиринтовыми и линейными. Есть еще и третья категория — линейно-лабиринтовые (например, ТНЕ ADVENTURE OF GREAT HERO VASYA by Elf Dillm). Линейные квесты создаются по принципу: пока не пройдешь эту сцену, на следующую не попадешь. В лабиринтовых пользователю приходится блуждать по коридорам и собирать необходимые предметы. Линейно-лабиринтовые — это те, в которых встречаются как линейные сцены, так и лабиринтовые (впрочем, несложно было догадаться из названия).
Какую бы стезю вы ни выбрали, не увлекайтесь: едва ли кто-нибудь будет мучиться над непроходимым линейным квестом длиной 500 эпизодов. Навряд ли кто-либо хочет блуждать по восьмиэтажным лабиринтам, каждый этаж 100×100 клеток. Но оставим стилистику игры на ваше усмотрение и примемся за ее написание.
В принципе, всех указанных выше алгоритмов вполне достаточно, чтобы написать хороший линейный quest. Оформление и сюжет — дело вашего вкуса.
А вот с лабиринтами у нас все обстоит несколько иначе. Как правило, лабиринт задается таким образом:
char * lab[2][2];
lab[0][0]=»описание локации 0x0
вашего лабиринта»;
lab[1][0]=»описание локации 1×0
вашего лабиринта»;
lab[0][1]=»описание локации 0x1
вашего лабиринта»;
lab[1][1]=»описание локации 1×1
вашего лабиринта»;
Для перемещения по лабиринту можно использовать такой алгоритм:
Пожалуй, это все. Удовольствие собственноручной разработки игры (с возможным использованием кусков кода, напечатанных в этой статье) оставляю вам. Единственный барьер, который может возникнуть у вас на пути — слабое знание С++. Но разве такая благородная цель, как написание собственной игры, не стоит нескольких недель усилий, направленных на изучение программирования? Впрочем, есть и еще одно ограничение — рамки вашего воображения. Но тут уже ничего не сделаешь…
Желаю творческих успехов.
P.S. Если вас не интересуют текстовые квесты, то можно, используя описанные выше функции find() и _input(), написать нечто вроде электронного собеседника — создать солидную базу данных слов и описать реакцию компьютера на их ввод.
Михаил Федотов aka $ky$pe@R
Источник
Как создать игру, ничего не умея. Часть вторая: сцена (уровень), квесты, интерактивные объекты
Предисловие
Привет^ народец, сегодня я продолжу свою попытку создать игру^ используя только информацию из интернета.
Первая часть вот тут — ТЫК.
В этой статье я попробую создать игровую комнату, она же сцена, уровень, локация и так далее, а так же попробуем прописать НПЦ (внутриигровых персонажей) с квестами, и взаимодействие с предметами. Ну что, начнём словоблудие!
Сцена
Для начала откроем наш проект над которым мы работали в прошлой статье, и переименуем нашего персонажа в Player.
Далее^ используя наши познания из прошлого урока^ создаём папку Prefabs, для тех^ кто забыл как это делается, нажимаем на Assets в древе, далее правой кнопкой в папке assets, Create -> Folder.
Далее перетягиваем нашего персонажа Player в папочку Prefabs, что даёт нам возможность его сохранить со всеми настройками и скриптами^ которые мы использовали в прошлой статье. Если выползет диалоговое окно, выбираем Original Prefab, а после в папочке Scene аналогичным способом создаем новую сцену под именем Scene_01 (и снова для самых-самых, правой кнопкой тык, и выбираем Create -> Scene). Это и будет наша рабочая сцена на эту статью, дважды тык на Scene_01 и у нас снова пустая рабочая область.
Смотрим в экран и вот тут начинается главная головная боль, необходимо придумать, как будет выглядеть наш первый уровень, который необходимо построить, а главное продумать его так, чтобы он полностью удовлетворял наши потребности. Обычно для таких целей я беру пачку листов А4 и карандаш, а после несколько дней рисую разные варианты уровня, если что-то мне не нравится после рисовки, я всё равно это оставляю на будущее, поскольку в итоге из всех рисунков и собирается один более-менее играбельный уровень для игры. Но мы сейчас всё делаем по велению интернета, поэтому возьмём за идею стандартную ситуацию.
- Построим закрытые ворота, перед которыми будут стоять стражники, а за воротами будет город, в который нам необходимо попасть.
- Стражники нас не пропустят, пока мы не выполним квест.
- Ворота откроются после выполнения квеста.
Вот такой вот незамысловатый сюжет будет в нашей сцене =)
Строительство
Для начала нам нужна земля, заходим в GameObjects -> 3D Object -> Terrain, это создало нам землю, но она довольно большая для моей сцены, поэтому заходим в Inspector, нажимаем на шестеренку (Terrain Setting) и ищем Mesh Resolution (On Terrain Data) для меня подошли показатели:
- Terrain Width: 70
- Terrain Length: 70
- Terrain Height: 120
Тут я наверное немного поясню ситуацию. Данный Terrain я использовать не буду, у меня есть все готовые префабы для создания играбельной сцены, но для людей, которые собираются САМИ создать свою землю и объекты, прийдётся использовать данную функцию. В ней есть всё необходимое, чтобы сделать из этого квадрата равнины, горы, леса, пустыню. Необходимо просто использовать нужную Вам кисть, и натянуть текстуру. Я же в буду использовать префабы, которые шли в комплекте с персонажем, а Terrain послужит мне границами уровня. (Для тех кто всё же хочет, чтобы я создал игровую область без префабов, пишите в комментариях, тогда будет понятно, нужна ли такая информация).
Используя префабы, я начинаю строительство, выбираю для начала зелень и растягиваю её почти на весь Terrain, далее выбираю ворота, стены, горы, деревья, дорогу, и просто перетаскиваю их в сцену, немного позиционирую, а далее делаю копи-паст и собираю простейший уровень, получилось примерно так.
Все префабы ландшафта я поместил в Terrain, чтобы они не загромождали древо сцены. За 5 минут получилось невесть, что, но если провести за этим делом пару дней, то можно собрать что-то подобное.
Давайте посмотрим как поведёт наш персонаж себя в новом месте проживания. Заходим в папку Prefabs и переносим наш префаб Player в сцену, у меня он появился где-то сбоку, но я простым движением переместил его в начало каменной дороги. После удаляю из сцены Main Camera, потому как к нашему персонажу уже прикреплена камера, и дабы не было конфликтов избавляемся от камеры в сцене. Жму кнопку Play и осматриваю новые земли. Персонаж отлично себя чувствует в новом месте. Исходя из этого мы можем перейти наконец к НПЦ и квестам.
NPC или неигровые персонажи
В качестве НПЦ мы будем использовать такие же префабы рыцарей, что и наш персонаж. Давайте поставим их перед воротами.
И вот двое из ларца одинаковых с лица стоят и охраняют наши ворота. Им я сразу дал имена NPC_01 и NPC_02, давайте подойдём и посмотрим как это выглядит в игре.
Да уж, в игре это выглядит так же ужасно, как и в сцене =) Оживим наших горе-охранников, чтобы они не стояли как пугало в поле. Для этого вернёмся к нашей первой статье, где мы оживляли нашего персонажа, и проделываем туже процедуру с болванчиками у ворот. Для тех, кто не помнит, направляемся в mixamo, находим подходящие нам анимации, добавляем их в проект, создаём аниматор контроллер и прикрепляем его к болванчикам.
Я прикрепил анимацию засыпающего человека к одному болванчику, и строго стоящего второму, это мне нужно для более полного погружения для квеста. Я понимаю, что это совсем необязательно, ведь мы не пытаемся создать ААА проект прямо на коленке как Юбисофт (это лично моё мнение), но всё же и создавать Г* мне не хочется, хоть это и попытка сделать всё при помощи интернета. Наконец пришло время для квеста!
Квест
Тут мне пришлось немного использовать крота, дабы прорыть интернет для правильного скрипта и построения квеста. Его выполнение, тоже должно было работать правильно и не вызывать ошибок, и я снова на коне! Давайте приступим.
Для начала создадим папку с названием Quest, а в ней соответственно создадим три скрипта с названиями: Sword, Quest, NPC (названия произвольны, Вы можете их назвать как Вам будет удобнее, но не забывайте тогда исправить class и обращения к скриптам). Первым же будет скрипт Quest:
using UnityEngine;
using System.Collections;
public class Quest: MonoBehaviour
<
public bool quest; // отображает название квеста на экране;
public string MissionText; // название квеста;
public string ObjectTag; // тэг объекта;
public bool MissionObjects; //отображает собран предмет или нет;
public int Money; // количество денег;
if (quest)
<
GUI.Label(new Rect(20, 80, 300, 30), » » + MissionText); // значение названия квеста будет браться из скрипта NPC;
if (MissionObjects)
< // если предмет собран;
GUI.Label(new Rect(150, 80, 200, 30), «[Выполнено]»); // выводит надпись;
>
>
>
>
Его мы добавляем к нашему персонажу, и изменяем его ТЕГ на Player, тег Вы можете поставить в inspector нашего Player
Далее создаём скрипт NPC
using UnityEngine;
using System.Collections;
public class NPC: MonoBehaviour
public bool quest; //переменная, которая обозначает взят квест или нет;
public bool vis; // переменная, которая будет отображать диалог между персонажами;
public string missionText; // Текст который будет отображать наименование квеста;
public string missionTag; //Тэг объекта, который необходимо принести;
private Quest MP; // подключаем скрипт Quest;
<
MP = GameObject.FindGameObjectWithTag(«Player»).GetComponent (); // определяем что скрипт Quest будет находится на персонаже с тэгом player;
>
<
GameObject MissionTagScanner = GameObject.FindGameObjectWithTag(«Player»); // персонаж у которого берем квест будет взаимодействовать только с тем объектом у которого тэг Player;
if (Input.GetKeyDown(KeyCode.E) & Vector3.Distance(transform.position, MissionTagScanner.transform.position)
Создаём скрипт Sword:
using UnityEngine;
using System.Collections;
public class Sword: MonoBehaviour
<
private Quest MP; // опять подключаем скрипт Quest;
void Start()
<
MP = GameObject.FindGameObjectWithTag(«Player»).GetComponent ();
> // определяем что скрипт Quest будет находится на персонаже с тэгом player;
void OnMouseUp()
<
if (Input.GetMouseButtonUp(0)) // если при нажатии на правую кнопку мыши;
<
if (MP.ObjectTag == gameObject.tag) // и при том что тэг объекта равен тому значению которое написано в ObjectTag;
<
MP.MissionObjects = true; // то переменная принимает значение true, т.е. считается что предмет собран;
Destroy(gameObject); // и удаляем этот объект со сцены;
>
>
>
>
Добавляем скрипт к нашему мечу, и теперь необходимо создать для него тег Sword. В Inspector нажимаем на Tag выбираем Add Tag, жмём на плюсик и прописываем название Sword. Готово, тег Sword готов. Возвращаемся к NPC_01, смотрим Inspector, видим наш NPC(Script) и в строке Mission Tag прописываем sword. Если всё сделано правильно, то у нас появился наш первый квест! Давайте проверим как он работает.Запускаю игру, подхожу к НПЦ и нажимаю клавишу Е (именно она прописана в скрипте как клавиша вызова диалога).
НПЦ дал наш первый квест! Нажимаю ОК и отправляюсь на поиски меча. Как видим, у нас появилась надпись, оповещающая нас, какой именно квест мы сейчас проходим в левой части экрана.
Подойдя к мечу, нажимаю на него, и он пропадает, что говорит о правильной работе скрипта! И также появляется надпись, что мы выполнили квест, а значит нужно возвращаться и отдать меч стражнику.
Вернувшись к стражнику, он задаёт вопрос, я на него отвечаю, и вижу что квест выполнен, YoHOO!
Наш первый выполненный квест в этом мире, теперь мы настоящий герой! Но остался другой вопрос, ведь квест мы выполнили, а охранник обещал нас пропустить. Теперь необходимо сделать анимацию открывающейся решетки, чтобы она открывалась, как только мы выполняем квест. Для этого выбираем нашу решетку и заходим во вкладку Animation (если у Вас её нет, то зайдите в Window -> Animation -> Animation и у Вас появится эта вкладка либо нажмите Ctrl+6). Решетку я назвал Door_01 и теперь во вкладке Animation давайте сделаем анимацию поднятия решетки. Нажимаем Create и я ставлю название DoorOpen. Далее я выбираю кадр 0:55 нажимаю на красную кнопку, а после поднимаю решетку, снова нажимаю на красную кнопку, и вот у нас есть ключи анимации как поднимается решетка.
Создаю еще одну анимацию, но на этот раз решетка находится внизу, и сохраняю её как DoorClose. После захожу в Animator, и подключаю анимацию, как мы это делали уже множество раз. Проверяю и вижу, что анимация работает, решетка непрерывно открывается =).
Ну что же, вперед к финишной прямой сегодняшней статьи =). Первым делом создаю очередной скрипт OpenDoor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OpenDoor: MonoBehaviour
<
private Quest MP; // подключаем скрипт Quest;
<
MP = GameObject.FindGameObjectWithTag(«Player»).GetComponent (); // определяем что скрипт Quest будет находится на персонаже с тэгом player;
>
Это единственный скрипт, что я нашел в интернете, который мне подошел. Я конечно его немного подправил под мои нужны в виде MP.Money. А для того, чтобы эта строчка работала, необходимо подключить скрипт Quest, что мы и сделали в самом начале. В скриптах, что я нашел ранее, есть параметр, который даёт вознаграждение в виде денег за выполнение квеста, поэтому мы добавим одну строчку, которая нам даст 100 монет, на которые и будет отвечать скрипт открывающий решетку.
if (GUI.Button(new Rect((Screen.width — 100) / 2, (Screen.height — 300) / 2 + 250, 100, 40), «Да»)) // то появится кнопка да, при нажатии на которую;
<
quest = false; // переменная квест принимает значение false, т.е. не взят ;
MP.quest = false; // название квеста не будет отображаться на экране ;
MP.MissionText = «»; // убирается название квеста;
MP.ObjectTag = «»; // обнуляется тэг объекта;
MP.MissionObjects = false; // объект считается не подобранным;
MP.Money = MP.Money + 100;
vis = false; // диалоговое окно закрывается;
После всех манипуляций, добавляем наш скрипт OpenDoor на объект решетки, и вперед тестить!
После того как я сдал квест и получил свои 100 монет, решетка открылась =). На этом я думаю на сегодня хватит, продолжение будет скоро!
Заключение
Очередной этап создания игры закончен, абсолютно всё, что я тут проделал было взято из уроков или статей интернета.
Я конечно многое бы сделал по другому, но эксперимент есть эксперимент, и с каждой статьёй мне кажется игру я всё же закончу. Всегда рад Вашим комментариям, увидимся в следующей статье!
Большинство роликов на ютубе были абсолютно бесполезны! Зря потраченное время =).
Источник