Использование текстовых файлов для создания простого уровня – неплохая идея, но для более сложных вещей применение текста ограничено. Например, нам может понадобиться больше объектов, и у этих объектов должны быть разные значения свойств – разные типы стен, отличающиеся текстурой, размером, поворотом и т.д.
Вместо текстовых файлов мы могли бы использовать что-то более продвинутое, такое как XML-файлы; XML-файл позволит предоставить дополнительную информацию об объекте, который мы хотим создать в нашей игре. Мы можем использовать такие файлы для визуализации данных, хранящихся в формате XML, включая данные о погоде, финансовых операциях, научных измерениях или новостной информации. Многие из этих XML-файлов находятся в свободном доступе и могут быть доступны и обработаны. Так, например, мы можем создать 3D-среду, которая имитирует научные данные, такие как температура, или потоки в океанах, и так далее; и поскольку эти файлы обычно обновляются на регулярной основе, мы можем создать 3D-приложение, которое имитирует реальные явления жизни.
Теперь, прежде чем перейти к таким сложным задачам, мы создадим простое приложение, которое считывает данные из XML-файла и использует его для настройки игры; затем мы создадим файл для добавления объектов, которые будут включены в уровни, вместе с настройками их свойств.
Что такое XML-файл?
XML означает расширяемый язык разметки (eXtensible Markup Language). Первоначально он был разработан для хранения данных таким образом, чтобы их могли читать как люди, так и компьютеры. Как вы увидите в следующих примерах кода, эти файлы используют расширение .xml и имеют общую структуру, что делает их легко читаемыми.
Давайте рассмотрим XML-файл, который мы могли бы создать для описания сцены; он может выглядеть следующим образом.
Например, правильна следующая вложенность (первый открытый тег является последним закрытым тегом):
Красота этого формата файлов заключается в том, что вы можете создавать свои собственные XML-файлы, используя структуру по вашему выбору, чтобы наилучшим образом отражать и обслуживать требования вашей игры или приложения; вы можете хранить информацию о каждой сцене, о NPC (например, какие пути они могут использовать) или оружии. Так что вы можете сохранить практически любой тип информации с этими файлами, используя простой для чтения формат.
Теперь, когда формат XML более понятен, давайте посмотрим, как мы можем прочитать XML-файл, чтобы создать простую сцену.
Что нужно сделать:
Сначала давайте посмотрим на XML-файл, который мы будем использовать:
Как вы можете видеть в приведенном коде, файл включает в себя следующее:
Итак, на этом этапе у нас есть XML-файл, и мы хотим получить к нему доступ, прочитать его содержимое и создать сцену соответственно;
Во-первых, мы скопируем этот файл в наш проект:
Скопируйте файл под названием scene.xml в папку Resources в Unity (например, перетаскиванием). У нас там уже лежит файлик maze.txt.
Затем мы должны написать код, читающий этот файл.
Откройте файл GenerateMaze в редакторе кода.
Закомментируйте ранее написанный код в функции Start или перенесите его в отдельный метод, например:
Вы можете также переписать код генерации из текстового файла в отдельную функцию:
Создание этих методов сделает код аккуратнее, и его будет легче использовать повторно.
Итак, к настоящему моменту функция Start должна быть пустой, и мы можем добавить в нее код, связанный с чтением XML-файла.
Пожалуйста, добавьте следующий код в начале сценария GenerateMaze:
Следующие строки добавьте в метод Start:
Далее мы прочитаем XML-файл:
Добавьте следующий код:
Мы просматриваем каждый элемент (или узел) уровня типа level, используя ключевое слово foreach. Другими словами, когда мы читаем элемент типа level, он будет называться level.
Ключевое слово foreach можно использовать для просмотра каждого элемента в группе.
Затем для этого уровня мы читаем значения атрибута, называемого number; если это 1, то мы переходим к остальной части кода; то есть мы читаем только содержимое первого уровня (или сцены); конечно, код можно изменить, чтобы прочитать содержимое сцены 2, если хотите. Это просто способ проиллюстрировать, как можно сосредоточиться на содержании, которое будет создано для определенного уровня; в нашем случае это будет первый уровень.
Для каждого узла объекта (или элемента), найденного на этом уровне, мы создадим экземпляр GameObject.
Каждый из этих объектов будет называться gameObject в остальной части кода.
Как только мы получим доступ к каждому gameObject, определенному для уровня 1, мы сможем прочитать его атрибут и создать соответствующий GameObject в сцене следующим образом:
Добавьте следующий код после предыдущего кода.
В этом коде для каждого соответствующего узла или элемента:
Для навигации по нашему XML-документу мы использовали синтаксис XPath; хотя нам больше XPath не понадобится, полезно будет узнать, что этот синтаксис включает набор команд, которые облегчают навигацию по сложным XML-документам.
Добавьте следующий код в сценарий, чтобы условные операторы и функция Start были завершены/закрыты должным образом.
Теперь давайте посмотрим, как можно преобразовать переменную location (то есть типа string) в вектор. Для этого мы будем использовать простой процесс, который включает в себя следующее:
Добавьте следующий код непосредственно перед концом класса:
Мы объявляем массив строк.
Затем мы используем этот массив для хранения различных частей (т. е. координат) строки, которую мы только что разделили.
Команда Split разбивает строку s (которая была передана в качестве параметра).
Поскольку s представляет собой строку в форме “x, y, z”, мы указываем, что сепаратором является запятая; иными словами, любая часть этой строки (т. е. s), которая ограничена запятой, будет использована для создания новой строковой переменной. При разбиении строки s, мы получаем три переменные типа char; они, в свою очередь, преобразуются в строки (т. е., сохраняются в newString), а затем значения типа float (при использовании float.Parse).
Затем создается и возвращается новый вектор, основанный на этих трех float.
Теперь вы можете сохранить свой скрипт.
Прежде чем воспроизвести сцену, нужно сделать следующее:
Запустите сцену. Сейчас вы видите новый объект типа wall с именем wall1 в позиции (10,2,10). Размеры его те же, что и у префаба wall (10,2,10).
Изменим файл scene.xml, добавив еще несколько объектов, и затем сохраним его:
Запустим сцену и выделим третий объект:
Список созданных объектов:
Параметры третьего объекта (который выделен)
Содержание
Часть 3. Создание окружения из файла изображения
Часть 5-1. Создание виртуальной Солнечной системы на основе XML-файла. Скрипт движения планет
Вместо текстовых файлов мы могли бы использовать что-то более продвинутое, такое как XML-файлы; XML-файл позволит предоставить дополнительную информацию об объекте, который мы хотим создать в нашей игре. Мы можем использовать такие файлы для визуализации данных, хранящихся в формате XML, включая данные о погоде, финансовых операциях, научных измерениях или новостной информации. Многие из этих XML-файлов находятся в свободном доступе и могут быть доступны и обработаны. Так, например, мы можем создать 3D-среду, которая имитирует научные данные, такие как температура, или потоки в океанах, и так далее; и поскольку эти файлы обычно обновляются на регулярной основе, мы можем создать 3D-приложение, которое имитирует реальные явления жизни.
Теперь, прежде чем перейти к таким сложным задачам, мы создадим простое приложение, которое считывает данные из XML-файла и использует его для настройки игры; затем мы создадим файл для добавления объектов, которые будут включены в уровни, вместе с настройками их свойств.
Что такое XML-файл?
XML означает расширяемый язык разметки (eXtensible Markup Language). Первоначально он был разработан для хранения данных таким образом, чтобы их могли читать как люди, так и компьютеры. Как вы увидите в следующих примерах кода, эти файлы используют расширение .xml и имеют общую структуру, что делает их легко читаемыми.
Давайте рассмотрим XML-файл, который мы могли бы создать для описания сцены; он может выглядеть следующим образом.
<?xml version="1.0" encoding="UTF-8"?>
<game>
<level number ="1">
<object name = "wall1" color = "red" location = "0,0,0" rotation = "0,0,0" scale ="1,2,1">
</object>
</level>
<level number ="2">
</level>
</game>
- Таким образом, XML-файл включает последовательность вложенных элементов, разделенных их тегами. Например, в предыдущем коде элементы игры начинаются с <game> и заканчиваются на < /game>. Каждый элемент также включает атрибуты. В предыдущем примере мы имеем уровни, вложенные в каждый игровой элемент; для каждого из этих уровней определяется номер. Аналогично, для каждого объекта в пределах уровня также определяются такие атрибуты, как цвет, позиция, вращение и масштаб.
- Первая строка XML-файла является необязательной, но добавить ее считается хорошим тоном. В ней написано, что используется версия 1.0 XML и что используемая кодировка - UTF8 (т. е. это кодировка по умолчанию для XML).
- При создании XML-документов необходимо соблюдать и другие правила:
- Каждый элемент разделен и включает в себя открывающий и закрывающий тег. Как вы можете видеть, существует тег <game> вместе с тегом < /game>. Обратная косая черта ” / " обозначает закрывающий тег для элемента.
- Эти теги чувствительны к регистру; таким образом, используя открывающий тег <game>, нельзя писать < /GAme>.
- Все элементы должны быть вложены правильно.
Например, правильна следующая вложенность (первый открытый тег является последним закрытым тегом):
<game><object></object></game>Следующий код неверен:
<game><object></game></object>
- Элементы могут иметь атрибуты, и они должны быть определены с помощью кавычек.
Красота этого формата файлов заключается в том, что вы можете создавать свои собственные XML-файлы, используя структуру по вашему выбору, чтобы наилучшим образом отражать и обслуживать требования вашей игры или приложения; вы можете хранить информацию о каждой сцене, о NPC (например, какие пути они могут использовать) или оружии. Так что вы можете сохранить практически любой тип информации с этими файлами, используя простой для чтения формат.
Теперь, когда формат XML более понятен, давайте посмотрим, как мы можем прочитать XML-файл, чтобы создать простую сцену.
Что нужно сделать:
- Загрузить XML-файл.
- Открыть этот документ.
- Пройти по каждому узлу или элементу уровня.
- Для каждого из этих узлов создать соответствующие игровые объекты, определенные для этой сцены.
Сначала давайте посмотрим на XML-файл, который мы будем использовать:
<?xml version="1.0" encoding="UTF-8"?>
<game>
<level number ="1">
<object name = "wall1" color = "red" location = "10,2,10" rotation = "0,0,0" scale = "1,2,1">
</object>
</level>
<level number ="2">
<object name = "wall2" color = "red" location = "20,0,10" rotation = "0,0,0" scale = "1,2,1">
</object>
</level>
</game>
Как вы можете видеть в приведенном коде, файл включает в себя следующее:
- Первая строка с информацией о версии XML, используемой вместе с типом кодировки.
- Затем мы определяем корневой узел под названием game (т. е. узел, содержащий все остальное в XML-файле).
- Для этого игрового элемента (или узла) у нас есть два прямых дочерних элемента, называемых level.
- Для каждого элемента уровня (или узла) мы определили атрибут под названием number; мы также добавили объект на каждый уровень, каждый с определенными атрибутами, называемыми name, color, location, rotation и scale. Идея здесь состоит в том, чтобы определить положение объектов в каждой сцене, а также их размеры и внешний вид.
- Для каждого открытого тега мы также создаем закрывающий тег (например, <game> и < /game>, <level> и < /level> или <object> и < /object>).
Итак, на этом этапе у нас есть XML-файл, и мы хотим получить к нему доступ, прочитать его содержимое и создать сцену соответственно;
Во-первых, мы скопируем этот файл в наш проект:
Скопируйте файл под названием scene.xml в папку Resources в Unity (например, перетаскиванием). У нас там уже лежит файлик maze.txt.
Затем мы должны написать код, читающий этот файл.
Откройте файл GenerateMaze в редакторе кода.
Закомментируйте ранее написанный код в функции Start или перенесите его в отдельный метод, например:
void GenerateFromArray()
{
int i, j;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
GameObject t;
if (worldMap[i, j] == 1) t = (GameObject)(Instantiate(wall, new Vector3(50 -
i * 10, 1.5f, 50 - j * 10), Quaternion.identity));
}
}
}
Вы можете также переписать код генерации из текстового файла в отдельную функцию:
void GenerateFromText()
{
TextAsset t1 = (TextAsset)Resources.Load("maze", typeof(TextAsset));
string s = t1.text;
int i;
s = s.Replace("\n", "");
s = s.Replace("\r", "");
for (i = 0; i < s.Length; i++)
{
if (s[i] == '1')
{
int column, row;
column = i % 10;
row = i / 10;
GameObject t;
t = (GameObject)(Instantiate(wall, new Vector3(50 - row * 10, 1.5f, 50 -
column * 10), Quaternion.identity));
}
}
}
Создание этих методов сделает код аккуратнее, и его будет легче использовать повторно.
Итак, к настоящему моменту функция Start должна быть пустой, и мы можем добавить в нее код, связанный с чтением XML-файла.
Пожалуйста, добавьте следующий код в начале сценария GenerateMaze:
using System.Xml;Это позволит нам реализовать функции XML.
Следующие строки добавьте в метод Start:
TextAsset textAsset = (TextAsset)Resources.Load("scene");
XmlDocument doc = new XmlDocument();
doc.LoadXml(textAsset.text);
- Мы создаем объект типа TextAsset, и он будет содержать содержимое файла scene.xml, который хранится в папке Resources.
- Затем мы создаем XML-документ с именем doc.
- Мы передаем текст со сцены.xml-файл для объекта doc.
Далее мы прочитаем XML-файл:
Добавьте следующий код:
foreach (XmlNode level in doc.SelectNodes("game/level"))
{
if (level.Attributes.GetNamedItem("number").Value == "1")
{
foreach (XmlNode gameObject in level.SelectNodes(".//object"))
{
}
Мы просматриваем каждый элемент (или узел) уровня типа level, используя ключевое слово foreach. Другими словами, когда мы читаем элемент типа level, он будет называться level.
Ключевое слово foreach можно использовать для просмотра каждого элемента в группе.
Затем для этого уровня мы читаем значения атрибута, называемого number; если это 1, то мы переходим к остальной части кода; то есть мы читаем только содержимое первого уровня (или сцены); конечно, код можно изменить, чтобы прочитать содержимое сцены 2, если хотите. Это просто способ проиллюстрировать, как можно сосредоточиться на содержании, которое будет создано для определенного уровня; в нашем случае это будет первый уровень.
Для каждого узла объекта (или элемента), найденного на этом уровне, мы создадим экземпляр GameObject.
Каждый из этих объектов будет называться gameObject в остальной части кода.
Как только мы получим доступ к каждому gameObject, определенному для уровня 1, мы сможем прочитать его атрибут и создать соответствующий GameObject в сцене следующим образом:
Добавьте следующий код после предыдущего кода.
string name, location;
name = gameObject.Attributes.GetNamedItem("name").Value;
location = gameObject.Attributes.GetNamedItem("location").Value;
Vector3 v = ConvertStringToVector(location);
GameObject g = (GameObject)Instantiate(wall, v, Quaternion.identity);
g.name = name;
В этом коде для каждого соответствующего узла или элемента:
- Мы определяем две строковые переменные имя и расположение.
- Затем мы получаем атрибут name и location объектов в XML-файле с помощью метода getNamedItem.
- Затем мы вызываем функцию, которую нам еще предстоит определить – ConvertStringToVector. Она преобразует строку для местоположения в значение Vector3, чтобы ее можно было использовать для установки позиции нового объекта, который будет создан. В настоящее время позиция представляет собой строку, содержащую координаты x, y и z, разделенные запятой.
- Затем мы создаем экземпляр нового объекта с помощью префаба wall в позиции, определенной ранее, и назначаем ему имя.
Для навигации по нашему XML-документу мы использовали синтаксис XPath; хотя нам больше XPath не понадобится, полезно будет узнать, что этот синтаксис включает набор команд, которые облегчают навигацию по сложным XML-документам.
Добавьте следующий код в сценарий, чтобы условные операторы и функция Start были завершены/закрыты должным образом.
} } }Вот так должны выглядеть функция Start после наших действий:
void Start()
{
TextAsset textAsset = (TextAsset)Resources.Load("scene");
XmlDocument doc = new XmlDocument();
doc.LoadXml(textAsset.text);
foreach (XmlNode level in doc.SelectNodes("game/level"))
{
if (level.Attributes.GetNamedItem("number").Value == "1")
{
foreach (XmlNode gameObject in level.SelectNodes(".//object"))
{
string name, location;
name = gameObject.Attributes.GetNamedItem("name").Value;
location = gameObject.Attributes.GetNamedItem("location").Value;
Vector3 v = ConvertStringToVector(location);
GameObject g = (GameObject)Instantiate(wall, v, Quaternion.identity);
g.name = name;
}
}
}
}
Теперь давайте посмотрим, как можно преобразовать переменную location (то есть типа string) в вектор. Для этого мы будем использовать простой процесс, который включает в себя следующее:
- Разделение строки, содержащей позицию. Разделитель – запятая.
- В результате создаются три разные строки, каждая из которых соответствует соответственно координатам x, y и z.
- Затем мы можем преобразовать каждую из этих строк в число и использовать эти три результирующих числа для создания нового вектора.
Добавьте следующий код непосредственно перед концом класса:
Vector3 ConvertStringToVector(string s)
{
string[] newString;
newString = s.Split(new char[] { ',' });
float x, y, z;
x = float.Parse(newString[0]);
y = float.Parse(newString[1]);
z = float.Parse(newString[2]);
return new Vector3(x, y, z);
}
Мы объявляем массив строк.
Затем мы используем этот массив для хранения различных частей (т. е. координат) строки, которую мы только что разделили.
Команда Split разбивает строку s (которая была передана в качестве параметра).
Поскольку s представляет собой строку в форме “x, y, z”, мы указываем, что сепаратором является запятая; иными словами, любая часть этой строки (т. е. s), которая ограничена запятой, будет использована для создания новой строковой переменной. При разбиении строки s, мы получаем три переменные типа char; они, в свою очередь, преобразуются в строки (т. е., сохраняются в newString), а затем значения типа float (при использовании float.Parse).
Затем создается и возвращается новый вектор, основанный на этих трех float.
Теперь вы можете сохранить свой скрипт.
Прежде чем воспроизвести сцену, нужно сделать следующее:
- Выберите объект generateMaze в иерархии.
- С помощью инспектора отключите скрипт GenerateFromImage и активируйте скрипт GenerateMaze.
| Рис. 16. Активация и деактивация скриптов |
Запустите сцену. Сейчас вы видите новый объект типа wall с именем wall1 в позиции (10,2,10). Размеры его те же, что и у префаба wall (10,2,10).
Изменим файл scene.xml, добавив еще несколько объектов, и затем сохраним его:
<?xml version="1.0" encoding="UTF-8"?>
<game>
<level number ="1">
<object name = "wall1" color = "red" location = "10,2,10" rotation = "0,0,0" scale = "1,2,1">
</object>
<object name = "wall2" color = "red" location = "20,2,20" rotation = "0,0,0" scale = "1,2,1">
</object>
<object name = "wall3" color = "red" location = "30,2,30" rotation = "0,0,0" scale = "1,2,1">
</object>
</level>
<level number ="2">
<object name = "wall2" color = "red" location = "20,0,10" rotation = "0,0,0" scale = "1,2,1">
</object>
</level>
</game>
Запустим сцену и выделим третий объект:
Список созданных объектов:
Параметры третьего объекта (который выделен)
Содержание
Часть 3. Создание окружения из файла изображения
Часть 5-1. Создание виртуальной Солнечной системы на основе XML-файла. Скрипт движения планет
