Назначение этого документа
Этот технический документ описывает язык UnrealScript. Это не учебник и не он содержит подробных примеров полезного кода UnrealScript. Примеры сценариев UnrealScript вы найдете в исходном коде движка, содержащем десятки тысяч строк рабочего кода UnrealScript, который решает такие задачи, как ИИ, движение, инвентарь и триггеры. Для начала изучите реальзацию сценариев "Actor", "Object", "Controller", "Pawn" и "Weapon".
Этот документ предполагает, что читатель хорошо знает язык C/C++ или Java, знаком с объектно-ориентированным программированием, играл в Unreal и пользовался редактором UnrealEd.
Программистам, новичкам в ООП, я настоятельно рекомендую посетить Amazon.com или книжный магазин и купить книгу по программированию на Java. Java очень похож на UnrealScript, а его изучение позволит понять язык UnrealScript проще и быстрее.
Причины разработки языка UnrealScript
UnrealScript был создан, чтобы обеспечить команду разработчиков Unreal и сторонних разработчиков мощным встроенным языком программирования, охватывающим все задачи и нюансы программирования игр.
Преимущественно язык UnrealScript разработан:
Для поддержки основных концепций времени, состояний, свойств и сетей, которые не охватываются традиционными языками программирования. Это значительно упрощает код UnrealScript. Основными сложностями при программировании на C/C++ являются разработка ИИ и программирование игровой логики, заключающиеся в работе с событиями, занимающими строго отведенное количество игрового времени, и с событиями, зависящими от аспектов состояний объектов. При программировании на C/C++ это приводит к созданию запутанного кода, который трудно писать, понимать, поддерживать и отлаживать. Язык UnrealScript включает в себя встроенную поддержку времени, состояний и репликации сети, что значительно упрощает программирование игр.
Для обеспечения простоты объектно-ориентированного программирования в стиле Java, и выявления ошибок в процессе компиляции. Подобно тому, как Java приносит чистую программную платформу для веб-программистов, UnrealScript представляет из себя чистый, простой и надежный язык для программирования 3D-игр. Основные концепции программирования языка UnrealScript, вытекающие из Java:
отсутствие указателей и автоматическая уборка мусора;
невозможность множественного наследования классов;
строгая проверка типов при компиляции;
безопасное выполнение на стороне клиента "sandbox" (песочница);
знакомый внешний вид кода, сходный с C/C++ и Java.
Для обеспечения высокоуровневого программирования игровых объектов и взаимодействий, а не манипуляции с битами и пикселями. Для обеспечения простоты и в целях повышения мощности языка UnrealScript мы пожертвовали скоростью исполнения. В конце концов, низкоуровневые задачи, для которых критична скорость исполнения, и другие важные участки кода Unreal, для которых высокая производительность намного важнее, чем простота реализации, написаны на языке C/C++. UnrealScript работает на уровне объектов и взаимодействий, а не на уровне битов и пикселей.
С начала разработки UnrealScript и до его текущей реализации, было изучено и отброшено несколько различных крупных парадигм программирования. Во-первых, мы исследовали возможность использования в качестве основы для языка сценариев Unreal платформы Sun и Microsoft Java VM для Windows. Оказалось, что преимущества Java, по сравнению с применением C/C++, в контексте Unreal добавляют разочаровывающие ограничения, связанные с отсутствием необходимых возможностей языка (например, переопределения операторов), а скорость выполнения программ оказалась довольно медленной из-за накладных расходов VM по переключению между задачами и из-за неэффективности сборщика мусора Java в случае сложной структуры наследования объектов. Во-вторых, мы рассматривали возможность реализации языка UnrealScript на базе Visual Basic, который хорошо работал, но был менее дружественным для программистов, привыкших к C/C++. Окончательное решение заключалось в реализации языка UnrealScript, сочетающего возможности языков C++ и Java, и включающего категории, отражающие объекты, специфичные для разработки игр. Это оказалось правильным решением, значительно упростившим многие аспекты кода Unreal.
Новое в Unreal Engine 3
Для тех, кто уже знаком с UnrealScript, мы представляем краткий обзор основных изменений в UnrealScript, дополняющих версию языка сценариев движка Unreal Engine 2.
Репликация - формулировки репликации в UE3 изменились:
Блок репликации теперь используется только для переменных
Репликация функций теперь определяется с помощью спецификаторов функций ( Server, Client, Reliable )
Стек состояний - Теперь вы можете помещать состояния в стек и извлекать их обратно
Препроцессор UnrealScript - поддержка макроопределений и условной компиляции
Отладочные функции - были добавлены новые функции, связанные с отладкой
Свойства по умолчанию - обработка блока defaultproperties была незначительно изменена и улучшена
Свойства по умолчанию для структур - теперь структуры также могут иметь свойства по умолчанию
Больше нельзя установить значения по умолчанию для файлов конфигурации или локализованных переменных
Defaultproperties во время исполнения теперь доступны только для чтения, выражения class'MyClass'.default.variable = 1 теперь не допускаются
Динамические массивы - для динамических массивов теперь доступна функция find(), осуществляющая поиск элемента по индексу
Итераторы динамических массивов - Для динамических теперь доступен оператор foreach.
Делегаты, как аргументы функций - UE3 теперь позволяет передавать делегаты в качестве аргументов функций
Интерфейсы - Добавлена поддержка интерфейсов
Доступ к константам из других классов: class'SomeClass'.const.SOMECONST
Поддержка множества таймеров
Значения по умолчанию для аргументов функций - Для необязательных аргументов функции теперь могут быть указаны значения по умолчанию.
Поддержка подсказок - окна редакторов свойств при наведении указателя мыши на сфойство теперь отображают подсказки, если это свойство имеет комментарии вида /** текст всплывающей подсказки */ над его объявлением в UnrealScript.
Поддержка метаданных - Расширение внутриигровой функциональности и функциональности редактора путем связывания свойств с различными типами метаданных.
Примерная структура программы
Этот пример иллюстрирует типичный простой класс UnrealScript и подчеркивает синтаксис и особенности языка UnrealScript. Обратите внимание, что этот пример может отличаться от его реализации в исходном коде Unreal, так как этот документ не синхронизирован с исходным кодом движка.
//=====================================================================
// TriggerLight.
// A lightsource which can be triggered on or off.
//=====================================================================
class TriggerLight extends Light;
//---------------------------------------------------------------------
// Variables.
var() float ChangeTime; // Time light takes to change from on to off.
var() bool bInitiallyOn; // Whether it's initially on.
var() bool bDelayFullOn; // Delay then go full-on.
var ELightType InitialType; // Initial type of light.
var float InitialBrightness; // Initial brightness.
var float Alpha, Direction;
var actor Trigger;
//---------------------------------------------------------------------
// Engine functions.
// Called at start of gameplay.
function BeginPlay()
{
// Remember initial light type and set new one.
Disable( 'Tick' );
InitialType = LightType;
InitialBrightness = LightBrightness;
if( bInitiallyOn )
{
Alpha = 1.0;
Direction = 1.0;
}
else
{
LightType = LT_None;
Alpha = 0.0;
Direction = -1.0;
}
}
// Called whenever time passes.
function Tick( float DeltaTime )
{
LightType = InitialType;
Alpha += Direction * DeltaTime / ChangeTime;
if( Alpha > 1.0 )
{
Alpha = 1.0;
Disable( 'Tick' );
if( Trigger != None )
Trigger.ResetTrigger();
}
else if( Alpha < 0.0 )
{
Alpha = 0.0;
Disable( 'Tick' );
LightType = LT_None;
if( Trigger != None )
Trigger.ResetTrigger();
}
if( !bDelayFullOn )
LightBrightness = Alpha * InitialBrightness;
else if( (Direction>0 && Alpha!=1) || Alpha==0 )
LightBrightness = 0;
else
LightBrightness = InitialBrightness;
}
//---------------------------------------------------------------------
// Public states.
// Trigger turns the light on.
state() TriggerTurnsOn
{
function Trigger( actor Other, pawn EventInstigator )
{
Trigger = None;
Direction = 1.0;
Enable( 'Tick' );
}
}
// Trigger turns the light off.
state() TriggerTurnsOff
{
function Trigger( actor Other, pawn EventInstigator )
{
Trigger = None;
Direction = -1.0;
Enable( 'Tick' );
}
}
// Trigger toggles the light.
state() TriggerToggle
{
function Trigger( actor Other, pawn EventInstigator )
{
log("Toggle");
Trigger = Other;
Direction *= -1;
Enable( 'Tick' );
}
}
// Trigger controls the light.
state() TriggerControl
{
function Trigger( actor Other, pawn EventInstigator )
{
Trigger = Other;
if( bInitiallyOn ) Direction = -1.0;
else Direction = 1.0;
Enable( 'Tick' );
}
function UnTrigger( actor Other, pawn EventInstigator )
{
Trigger = Other;
if( bInitiallyOn ) Direction = 1.0;
else Direction = -1.0;
Enable( 'Tick' );
}
}
Ключевые элементы этого сценария, на которые вам следует обратить внимание:
Объявление класса. Каждый класс "расширяет" (основан на) один из базовых классов, а каждый класс включается в "пакет", набор объектов, распространяемых вместе. Все функции и переменные, принадлежащие классу, доступны только через актора, принадлежащего этому классу. Это не общесистемные глобальные функции и переменные.
Объявления переменных. UnrealScript поддерживает очень разнообразный набор типов переменных, включая большинство базовых типов C/Java, ссылки на объекты, структуры и массивы. Кроме того, переменные могут быть сделаны как редактируемые свойства, к которые могут получить доступ в UnrealEd дизайнеры, не прибегая к программированию. Эти свойства синтаксически обозначаются с помощью var(), вместо var.
Функции. Функции могут принимать список параметров и, при необходимости, возвращать значение. Функции могут включать локальные переменные. Некоторые функции вызываются движком Unreal напрямую (например, BeginPlay), а некоторые функции, вызываются из кода других сценариев (например, Trigger).
Код. Поддерживаются все стандартные ключевые слова C и Java, включая for, while, break, switch, if и прочие. Фигурные скобки и точка с запятой в UnrealScript применяются также, как и в C, C++ и Java.
Актор и ссылки на объекты. Здесь вы видите несколько случаев, когда функция вызывается внутри другого объекта с использованием ссылки на объект.
Этот сценарий определяет несколько "состояний", являющихся группами функций, переменных и кода, которые выполняются только тогда, когда актор в находится в определенном состоянии.
Отметим, что все имена переменных, функций и имена объектов в языке UnrealScript не чувствительны к регистру. В UnrealScript имена Demon, demON и demon - это одно и то же.
Виртуальная машина Unreal
Виртуальная машина Unreal состоит из нескольких компонентов: сервер, клиент, движок визуализации и код поддержки движка.
Сервер Unreal контролирует весь геймплей и взаимодействия между персонажами и акторами. В одиночной игре, как клиент Unreal и сервер Unreal работают на одной машине, в интернет-игре, выделенный сервер работает на одной машине, а все игроки подключаются к этой машине как клиенты.
Все игры происходят внутри "уровня" - автономной среды, включающей геометрию и акторов. Хотя UnrealServer может одновременно работать и более, чем с одиним уровнем, все уровни работают независимо и защищены друг от друга: акторы не могут перемещаться между уровнями, а акторы одного уровня не могут связываться с акторами других уровней.
Каждый актор на карте может быть под контролем игрока (в сетевой игре игроков может быть много) или управляться сценарием. В случае, когда актор контролируется сценарием, сценарий полностью определяет поведение и взаимодействует актора с другими акторами.
Посмотрев на все эти бегающие акторы, выполняющиеся сценарии и событиями, происходящие в игровом мире, вы, вероятно, задаетесь вопросом, как как это все реализовано в UnrealScript. Ответ на этот вопрос ниже:
Для управления временем Unreal делит каждую секунду геймплея на кванты времени, тики ("Ticks"). Тик - это наименьшая единица времени, за которую обновляются все акторы на уровне. Тик обычно занимает от 1/100 до 1/10 доли секунды. Время тика ограничивается только мощностью процессора; чем быстрее машина, тем ниже продолжительность тика.
В UnrealScript время выполнения некоторых команд занимает ноль тиков (то есть их выполнение происходит без затрат игрового времени), а выполнение других команд занимает много тиков. Функции, выполнение которых занимает игровое время, называюься "латентными функциями" ("latent functions"). Латентными функциями, например, являются Sleep, FinishAnim и MoveTo. Латентные функции в UnrealScript могут вызываться только из "кода состояний" ("state code"), а не из кода функций (включая функции, определенные в состоянии).
Пока актор выполняет латентную функцию, он не может выполнить код состояния до завершения выполнения кода этой латентной функции. Однако при этом функции текущего актора могут вызывать другие акторы или VM. В итоге все функции UnrealScript все же могут вызываться в любой момент, даже во время выполнения латентных функций.
В традиционных терминах программирования, UnrealScript действует так, как будто каждый актор на уровне имеет свой собственный исполняемый "поток". Внутренне, Unreal не использует потоки Windows, потому что это было бы крайне неэффективно (Windows 95 и Windows NT не могут одновременно эффективно обрабатывать тысячи потоков). Вместо этого, UnrealScript эмулирует потоки. Этот факт является прозрачным для кода на UnrealScript, но становится очень очевидным, когда вы пишете код на C++, взаимодействующий со сценариями UnrealScript.
Все сценарии UnrealScript выполняются независимо друг от друга. При наличии 100 монстров на уровне все 100 сценариев этих монстров выполняются одновременно и независимо от "тиков" акторов каждого монстра.
Иерархия объектов
Перед началом работы с UnrealScript важно понять высокоуровневые взаимоотношения между объектами Unreal. Архитектура Unreal серьезно отличается от большинства других игр: движок Unreal является полностью объектно-ориентированным (подобно COM или ActiveX), он имеет четко определенную модель объектов с поддержкой высокоуровневых объектно-ориентированных концепций, таких как графы объектов, сериализация, время жизни объекта и полиморфизм. Исторически, большинство игр разрабатывались монолитно, а их основные функции жестко привязывались на уровне объектов, хотя многие игры, такие как Doom и Quake, оказались расширяемыми на уровне контента. Основное преимущество модели объектно-ориентированного программирования Unreal в том, что новые функциональные возможности и типы объектов могут добавляться к Unreal во время выполнения. Расширение функциональности может осуществляться путем создания подклассов, а не, к примеру, путем модификации кучи существующего кода. Эта модель расширения является чрезвычайно мощной и привлекательной для сообщества Unreal, позволяя вносить в Unreal дополнительные модификации и усовершенствования.
Object - это базовый класс всех объектов Unreal. Все функции в класса Object доступны для всех объектов. Object - это абстрактный базовый класс, сам по себе он не имеет полезной функциональности. Вся функциональность обеспечивается подклассами, например, классами Texture (текстуры), TextBuffer (кусок текста) и Class (который описывает классы других объектов).
Actor (расширяет класс Object) является базовым классом для всех автономных игровых объектов Unreal. Класс Actor включает всю функциональность, необходимую для перемещения акторов, их взаимодействия между собой, воздействия на игровое окружение, для осуществления других полезных вещей, связанных с игрой.
Pawn (расширяет класс Actor) - это базовый класс класс всех созданий и персонажей Unreal, которые на контролируются высокоуровневым ИИ или управляются непосредственно игроком.
Class (расширяет класс Object) - это особый вид объекта, описывающий класс объектов. На первый взгляд он может показаться запутанным: класс объекта и класс описывает определенные объекты. Но в работе вам встретится множество случаев, когда Вы будете иметь дело с объектами Class. Например, при воплощении нового актора в UnrealScript, вы можете указать класс нового актера, применяя объект Class.
С помощью UnrealScript вы можете писать код для любого класса, расширяющего класс Object, но 99% времени вы будете писать код для классов, производных от класса Actor. Большинство полезных функций UnrealScript, связанных с игрой, - это действия с акторами.
Классы
Каждый сценарий соответствует одному отдельному классу и начинается с объявления класса, наследуемого класса и другой дополнительной информации, относящейся к классу. Простейшая форма:
class MyClass extends MyParentClass;
Здесь мы объявили новый класс с именем "MyClass", который наследует функциональность класса "MyParentClass". Кроме того, объявленный класс находится в пакете с именем "MyPackage".
Каждый класс наследует все переменные, функции и состояния от своего базового класса. Далее вы можете добавить новые переменные, новые функции (или переопределить существующие функции), новые состояния (или добавить функциональность к существующим).
Типичный подход к разработке класса UnrealScript заключается в создании нового класса (например чудовища минотавра), расширяющего существующий класс, который имеет большинство необходимых функций (например, класс Pawn, базовый класс для всех монстров). При таком подходе вам не нужно заново изобретать колесо - вы можете просто добавить новые необходимые вам функции, сохраняя при этом все существующие функции, не нуждающиеся в модификации. Этот подход особенно эффективен для реализации ИИ: встроенная система ИИ Unreal предоставляет огромное количество базовых функциональных возможностей, применимых в качестве строительных блоков для собственных существ.
Объявление класса может включать несколько дополнительных спецификаторов, которые влияют на класс:
Native(PackageName)
Указывает, что "этот класс использует за-кадром код на C++". Unreal ожидает наличие встроенных (native) классов в коде на C++, реализованных в основном исполняемом модуле (.exe). Объявлять встроенные функций или осуществлять реализацию встроенных интерфейсов могут только встроенные классы. Встроенные классы могут быть производными только от других встроенных классов. Для встроенных классов создается автоматически сгенерированный заголовочный файл C++ с описателями, необходимыми для взаимодействовия переменных и указанных функций сценария к кодом на C++. По умолчанию, PackageName - это имя пакета, в котором находится сценарий класса. Например, если в классе определено имя пакета Engine, то в результате автоматически сгенерированный заголовок будет называться EngineClasses.h.
NativeReplication
Указывает, что репликация значений переменных для данного класса осуществляется в реализации на C++. Допустимо только для встроенных классов.
DependsOn(ClassName[,ClassName,...])
Указывает, что класс ClassName компилируется перед компиляцией текущего класса. ClassName должно указывать на класс в текущем (или предыдущем) пакете. Несколько зависимостей можно определить с помощью DependsOn в одну строку, разделив имена классов запятыми, или же в несколько строк, определив DependsOn для каждого класса.
Abstract
Объявляет класс как "абстрактный базовый класс". Это позволяет пользователю добавлять акторов этого класса в мире с помощью UnrealEd или создвать экземпляры этого класса во время игры, потому что этот класс не имеет смысла сам по себе. Например, базовый класс "Actor" является абстрактным, в то время как подкласс "Ladder" не является абстрактным - Вы можете разместить Ladder в мире, но вы не можете поместить в мире актор. Это ключевое слово распространяется на внутренние дочерние классы, но не распространяется на дочерние классы сценариев.
Deprecated
Все объекты этого класса будут загружены, но не могут быть сохранены. Размещение экземпляра любого устаревшего (deprecated) актора будет выдавать дизайнерам уровней предупреждение при загрузке карты в редакторе. Это ключевое слово применимо к дочерним классам.
Transient
Указывает, что "объекты, принадлежащие к этому классу, никогда не должны сохраняться на диск". Имеет смысл только в сочетании с определенными видами встроенных классов, которые не являются устойчивыми по своей природе, например, как игроки или окна. Это ключевое слово распространяется на дочерние классы; дочерние классы могут переопределить этот флаг путем использования ключевого слова NonTransient.
NonTransient
Отменяет спецификатор Transient, унаследованный от базового класса.
Config(IniName)
Указывает, что этот класс сохраняет данные в .ini файл. При наличии в классе любых переменных конфигурации (объявленных с "config" или "globalconfig"), значения этих переменных будут сохраняться в указанный файл конфигурации. Этот флаг распространяется на все дочерние классы и не может быть отменен, но дочерние классы могут изменить имя .ini файла, применив спецификатор Config и указав другое значение IniName. Обычно IniName указывает имя .ini файла для сохранения данных, но несколько имен имеют специальное назначение:
Config(Engine): Используется файл конфигурации Engine, включающий имя вашей игры перед "Engine.ini". Например, для игры ExampleGame файл конфигурации будет назван ExampleEngine.ini.
Config(Editor): Используется файл конфигурации Editor, включающий имя вашей игры перед "Editor.ini". Например, для игры ExampleGame файл конфигурации будет назван ExampleEditor.ini.
Config(Game): Используется файл конфигурации Game, включающий имя вашей игры перед "Game.ini". Например, для игры ExampleGame файл конфигурации будет назван ExampleGame.ini.
Config(Input): Используется файл конфигурации Input, включающий имя вашей игры перед "Input.ini". Например, для игры ExampleGame файл конфигурации будет назван ExampleInput.ini.
PerObjectConfig
Информация о конфигурации для этого класса будут сохраняться пообъектно, при этом для каждого объекта создается отдельный раздел .ini файла в формате [ObjectName ClassName]. Этот спецификатор распространяется на дочерние классы.
PerObjectLocalized
Данные локализации для этого класса будут определны для каждого объекта, при этом для каждого объекта создается отдельный раздел в .ini файлах локализаций в формате [ObjectName ClassName]. Этот спецификатор распространяется на дочерние классы.
EditInlineNew
Применимо к редактору. Указывает, что объекты этого класса могут создваться через окно свойств UnrealEd (по умолчанию предполагается, что через окно свойств могут быть назначены только ссылки на существующие объекты). Этот флаг распространяется на все дочерние классы; дочерние классы могут переопределить этот флаг с использованием спецификатора NotEditInlineNew.
NotEditInlineNew
Применимо к редактору. Отменяет спецификатор EditInlineNew, унаследованный от базового класса. Не имеет эффекта, если для базового класса не указан спецификатор EditInlineNew.
Placeable
Применимо к редактору. Указывает, что этот класс может быть создан в UnrealEd и помещен на уровень, UI сцену, или в окно kismet (в зависимости от класса типа). Этот флаг распространяется на все дочерние классы; для дочерних классов можно переопределить этот флаг спецификатором NotPlaceable.
NotPlaceable
Применимо к редактору. Отменяет спецификатор Placeable, унаследованный от базового класса. Указывает, что этот класс не может быть создан в UnrealEd и помещен на уровень и т.д.
HideDropDown
Применимо к редактору. Предотвращает просмотр этого класса в полях списков (combo boxes) окна свойств UnrealEd.
HideCategories(Category[,Category,...])
Применимо к редактору. Задает одну или несколько категорий, которые должны быть спрятаны в окне свойств UnrealEd для объектов этого класса. Чтобы скрыть переменные, объявленные без каких-либо категории, используйте имя класса, в котором объявлены эти переменные.
ShowCategories(Category[,Category,...])
Применимо к редактору. Отменяет спецификатор HideCategories, унаследованный от базового класса.
AutoExpandCategories(Category[,Category,...])
Применимо к редактору. Задает одну или несколько категорий, которые должны быть автоматически развернуты в окне свойств UnrealEd для объектов этого класса. Для автоматического развертывания переменных, объявленных без каких-либо категорий, используйте имя класса, в котором объявлены эти переменные.
Collapsecategories
Применимо к редактору. Указывает, что свойства этого класса не должны быть сгруппированы по категориям в окне свойств UnrealEd. Это ключевое слово распространяется на дочерние классы; для дочерних классов можно переопределить этот флаг спецификатором DontCollapseCategories
DontCollapseCategories
Применимо к редактору. Отменяет спецификатор CollapseCatogories, унаследованный от базового класса.
ForceScriptOrder(true/false)
Применимо к редактору. Заставляет окно свойств отображать свойства и категории объекта этого класса в том порядке, в котором они определены в сценарии.
Within ClassName
Расширенный. Указывает, что объекты этого класса не могут существовать без экземпляра ClassName. Для того, чтобы создать объект этого класса, вы должны указать экземпляр ClassName как объект Outer. Это ключевое слово должно следовать первым после самого объявления класса.
Inherits(ClassName[,ClassName,...])
Расширенный. Используется для множественного наследования, определяя дополнительные базовые классы. Несколько базовых классов можно определить с помощью Inherits в одну строку, разделив имена классов запятыми, или же в несколько строк, определив Inherits для каждого класса. Применимо только к встроенным классам. Множественное наследование от двух классов, производных от UObject, не поддерживается.
Implements(ClassName[,ClassName,...])
Расширенный. Определяет один из несколько классов интерфейсов, которые этот класс реализует. Несколько интерфейсов можно определить с помощью Implements в одну строку, разделив имена интерфейсов запятыми, или же в несколько строк, определив Implements для каждого интерфейса. Встроенные интерфейсы могут быть реализованы только встроенными классами.
NoExport
Расширенный. Указывает, что объявление С++ для этого класса не должно быть включено в автоматически генерируемый компилятором сценариев заголовочный файл C++. C++ объявление класса должно быть определено вручную в отдельном заголовочном файле. Допустимо только для встроенных классов.
ClassGroup(Groupname[,Groupname,...])
Указывает, что Actor Browser редактора должен включить этот класс и любой подкласс этого класса в группу, указанную параметром Groupname, и отображать в соответствующей группе при включении опции Group View.
Переменные
Типы переменных
Встроенные типы
Вот некоторые примеры объявления переменных экземпляра в сценарии UnrealScript
var int a; // Declare an integer variable named "A".
var byte Table[64]; // Declare a static array of 64 bytes named "Table".
var string PlayerName; // Declare a string variable named "PlayerName".
var actor Other; // Declare a variable which can be assigned a reference to an Actor instance.
var() float MaxTargetDist; // Declare a float variable named "MaxTargetDist" and allow its value to be modified from an UnrealEd property window.
Переменные, объявляемые в сценариях UnrealScript, могут быть двух видов: переменные экземпляра, которые применимы ко весему объекту, размещаются сразу после объявления класса или структуры. Локальные переменные, размещаемые в теле функции, и действительные только во время выполнения этой функции. Переменные экземпляр объявляются с ключевым словом var. Локальные переменные объявляются с ключевым словом local, например:
<pre>
function int Foo()
{
local int Count;
Count = 1;
return Count;
}
Встроенные типы переменных, поддерживаемые сценариями UnrealScript
byte: Однобайтное значение с диапазоном значений с 0 по 255.
int: 32-битное целое.
bool: Булева переменная: может принимать значение true или false.
float: 32-биттное число с плавающей точкой.
string: Строка символов. (Смотрите Строки в сценариях UnrealScript)
constant: Константа. Переменная, значение которой не может быть изменено.
enumeration: Перечисление. Переменная, которая может принимать одно из нескольких предопределенных именами целых значений. Например, перечисление ELightType, объявленное в сценарии Actor, описывает динамическое освещение и может принимать значения LT_None, LT_Pulse, LT_Strobe и так далее.
Составные типы данных
array<Type>: Переменная, определяющая массив значений типа Type.
struct: Подобно структурам языка C, структуры UnrealScript позволяют создавать новые типы данных, которые включают в себя одну или несколько переменных. Например, двумя часто используемыми структурами Unreal являются vector, состоящий из компонентов X, Y и Z, и rotator, состоящий из компонентов pitch, yaw и roll. (Смотрите Применение структур в сценариях UnrealScript)
Типы Unreal
Имя: Имя элемента в сценарии UnrealScript (например, имя функции, состояния, класса и т.д.). Имена хранятся как индексы в глобальной таблице имен. Именам соответствуют простые строки до 64 символов. Имена отличаются от строк тем, что они не изменяются после создания (Смотрите Строки в сценариях UnrealScript)
Ссылки на Object и Actor: Переменные, ссылающиеся на другой объект или актор, размещенный в игровом мире. Например, если класс Pawn имеет ссылку на актор "Enemy", указывающую актор, который Pawn должен пытаться атаковать. Ссылки на объекты и акторы, - это очень мощные инструменты, потому что они позволяют получить доступ к переменным и функциям другого актора. Например, в сценарии Pawn, вы можете написать Enemy.Damage(123) для вызова функции Damage актора Enemy - в результате чего Enemy получает повреждения. Ссылки на объекты также могут содержать специальную величину, называемую None, эквивалентную указателю NULL в языке C и указывающую, что "эта переменная не ссылается ни на какой объект".
Делегат: Хранит ссылку на функцию сценария UnrealScript.
Спецификаторы переменных
Переменные также могут иметь дополнительные спецификаторы (такие как const), которые более подробно описывает переменную. На самом деле, есть довольно много спецификаторов, которые вы не ожидали бы увидеть в языке программирования общего назначения. Преимущественно спецификаторы UnrealScript предназначены для обеспечения встроенной поддержки концепций, специфичных для игр и игровых окружений:
config
Это переменная конфигурации. Текущее значение может быть сохранено в .ini файл и будет загружаться при создании экземпляра. Значение не может быть указано в свойствах по умолчанию. Подразумевается константа.
globalconfig
Работает также, как и переменная конфигурации, но вы не можете изменить переменную в производном классе. Значение не может быть указано в свойствах по умолчанию. Подразумевается константа.
localized
Эта переменная принимает значение, определенное для конкретной локализации. Преимущественно используется для строк. Подразумевается константа. Читайте подробнее на страницах Локализация. Справочное руководство External Link | UnrealScript. Справочное руководство и Строки в сценариях UnrealScript.
const
Значение переменной рассматривается как константа. В сценариях UnrealScript вы можете прочитать значения констант, но вы не можете присваивать им новые значения. Спецификатор "const" используется только для переменных, за обновление которых отвечает движок и которые не могут быть безопасно обновлены ??из сценария UnrealScript, то есть такие, как местоположение актора (которое может быть установлено только путем вызова функции MoveActor).
private
Переменная является закрытой и доступна только из текущего класса сценария, другие классов (в том числе дочерние) не имеют к ней доступа.
protected
Переменных доступна только из текущего класса и дочерних классов, но не из других классов.
repnotify
Акторы должны быть уведомлены (через функцию ReplicatedEvent), когда это значение этого свойства будет получено с помощью репликации.
deprecated
Указывает, что эта переменная будет удалена в ближайшем будущем не должна быть доступна в редакторе. Устаревшие (deprecated) свойства все же загружаются в редактор, но их значения не сохраняются.
instanced
Спецификатор применим только к свойствам объекта. При создании экземпляра класса, переменная будет ссылаться на ??уникальную копию объекта, связанную с этой переменной по умолчанию. Используется для экземпляров дочерних объектов, определенных в свойствах по умолчанию для класса.
databinding
Это свойство можно управляться системой хранения данных.
editoronly
Значение этого свойства будет загружаться только при запуске UnrealEd или commandlet. Во время игры значение этого свойства игнорируется.
notforconsole
Значение этого свойства будет загружаться только при работе на ПК. На консолях значение этого свойства игнорируется.
editconst
Применимо к редактору. Переменной можно просматривать в UnrealEd но не редактировать. Подразумевается константа.
editfixedsize
Применимо к редактору. Имеет смысл только для динамических массивов. Позволяет предотвратить изменение длины массива пользователями через окно свойств UnrealEd.
editinline
Применимо к редактору. Позволяет пользователям редактировать свойства объекта, на который ссылается эта переменная, в инспекторе свойств UnrealEd (имеет смысл только для ссылок на объекты, в том для числе массивов ссылок на объекты).
editinlineuse
Применимо к редактору. То же самое, что и editinline, но в редакторе добавляет кнопку "Use" рядом с ссылкой на объект.
noclear
Применимо к редактору. Предотвращает возможность в редакторе присвоить ссылке на объект значение None.
interp
Применимо к редактору. Указывает, что значение может использоваться в течение долгого времени объектами Float или Vector Property Track в Matinee.
input
Расширенный. Делает переменную доступной для системы ввода Unreal, при этом ввод (например, нажатие кнопки или движение джойстика) может быть непосредственно отражен в данной переменной. Применим только для переменных типов "byte" и "float".
transient
Расширенный. Объявляет, что переменная для временного использования и не является частью постоянного состояния объекта. Переходные переменные не сохраняются на диск, инициализируются в значение по умолчанию для класса этой переменной при загрузке объекта.
duplicatetransient
Расширенный. Указывает, что значение переменной должно быть сброшено в значение по умолчанию для класса при создании бинарных дубликатов объекта (через StaticDuplicateObject).
noimport
Расширенный. Указывает, что эта переменная должна быть пропущена при импорте T3D текста. Другими словами, значение этой переменной не будут передано новыи экземплярам объектов при импорте или копировании/вставки объектов.
native
Расширенный. Объявляет, что переменная загружается и сохраняется на из кода на C++, а не из сценария UnrealScript.
export
Расширенный. Имеет смысл только для свойств объекта (или массивов объектов). Указывает, что объект, связанный с этим свойством, при копировании объекта (для копирования/вставки) или экспорта в T3D должен быть экспортирован в полном объеме как блок подобъекта, а не просто как ссылка на объект.
noexport
Расширенный. Имеет смысл только для встроенных классов. Эта переменная не должна быть включена в автоматически генерируемое объявление класса.
nontransactional
Расширенный. Указывает, что изменение значения этой переменной не будет включено в историю правки (отмены и повтора) редактора.
pointer{type}
Расширенный. Эта переменная является указателем на type. (параметр type не является обязательным). Обратите внимание на синтаксис: pointer VarName {type}
init
Расширенный. Это свойство должно быть экспортировано в заголовочный файл как FString или TArray, а не как FStringNoInit или TArrayNoInit. Применяется только для строк и динамических массивов, объявляемых во встроенных классах. Свойствам "Init" не должны присваиваться значения по умолчанию, так как значения по умолчанию будут удалены при создании объекта. (Смотрите Строки в сценариях UnrealScript и Strings In Native Code External Link | UnrealScript. Справочное руководство)
repretry
Расширенный. Применим только к свойствам структур. Повторить репликацию этого свойства, если она не полностью завершена (например, если ссылки на объекты еще не доступны для сериализации по сети). Для простых ссылок спецификатор устанавливается по умолчанию, а для структур часто нежелателен из-за затрат, связанных с пропускной способностью сети, и по умолчанию отключен.
allowabstract
Расширенный. Применим только к ссылкам на класс. Позволяет в редакторе связывать с этой переменной абстрактные классы.
out
Этот спецификатор применим только к параметрам функций. Подробнее читайте в разделе Функции.
coerce
Этот спецификатор применим только к параметрам функций. Подробнее читайте в разделе Функции.
optional
Этот спецификатор применим только к параметрам функций. Подробнее читайте в разделе Функции.
skip
Этот спецификатор применим только к параметрам функций операторов. Он используется только для бинарных логических операторов (таких , как и & или ||). Сообщает компилятору о необходимости внедрить кусочек кода в поток. Таким образом, второй аргумент может быть пропущен (не проверяется), если результат логической операции может быть вычислен до конца из значения первого аргумента.
Это гарантирует поведение выражений в стиле C, например:
if( ++a==10 && ++b==10 )
Здесь, b++ пропускается, если первое выражение ложно.
Возможность редактирования
В сценариях UnrealScript вы можете сделать переменную экземпляра "редактируемой", то есть пользователи смогут изменять значение переменной в UnrealEd. Этот механизм отвечает формирование содержимого свойств актора в диалоговом окне Actor Properties редактора UnrealEd: все свойства акторов в редакторе, это просто переменные UnrealScript, объявленные в сценариях как редактируемые.
Синтаксис объявления редактируемой переменной следующий:
var() int MyInteger; // Declare an editable integer in the default
// category.
var(MyCategory) bool MyBool; // Declare an editable integer in
// "MyCategory".
Также вы можете объявить переменную как editconst, что сделает переменную видимой в редакторе, но не редактируемой средствами UnrealEd. Заметим, что при этом значение переменной может быть изменено в сценарии UnrealScript. Если вы хотите объявить константу, видимую в редакторе, вы должны объявить ее со спецификатором const editconst:
// MyBool is visible but not editable in UnrealEd
var(MyCategory) editconst bool MyBool;
// MyBool is visible but not editable in UnrealEd and
// not changeable in script
var(MyCategory) const editconst bool MyBool;
// MyBool is visible and can be set in UnrealEd but
// not changeable in script
var(MyCategory) const bool MyBool;
Массивы
Синтаксис объявления массива следующий:
var int MyArray[20]; // Declares an array of 20 ints.
UnrealScript поддерживает только одномерные массивы, хотя вы можете эмулировать многомерные массивы путем математического приведения строк/столбцов самостоятельно. Подробнее о динамических массивах читайте ниже в разделе Динамические массивы.
Структуры
Структура UnrealScript является инструментом объединения нескольких переменных в новый вид переменной - в структуру. Структуры UnrealScript очень похожи на структуры C, они также могут включать в себя переменные, массивы и другие структуры, но структуры UnrealScript не могут содержать функции.
Вы можете объявить структуру следующим образом:
// A point or direction vector in 3D space.
struct Vector
{
var float X;
var float Y;
var float Z;
};
После объявления структуры вы можете объявлять конкретные переменные, имеющие тип структуры:
// Declare a bunch of variables of type Vector.
var Vector Position;
var Vector Destination;
Для доступа к компонентам структуры используйте код, подобный следующему.
function MyFunction()
{
Local Vector A, B, C;
// Add some vectors.
C = A + B;
// Add just the x components of the vectors.
C.X = A.X + B.X;
// Pass vector C to a function.
SomeFunction( C );
// Pass certain vector components to a function.
OtherFunction( A.X, C.Z );
}
С переменным struct Вы можете сделать все то же, что что и с другими переменными: вы можете присваивать им значение, передать их в функции и получать доступ к их компонентам.
Есть несколько структур, определенных в классе Object, используемых во всех сценариях Unreal. Вы должны уметь ими пользоваться, так как они являются основными строительными блоками сценариев:
Vector
Уникальная точка или вектор в 3D пространстве, с компонентами X, Y и Z.
Plane
Определяет уникальную плоскость в 3D пространстве. Плоскость определяется компонентами X, Y и Z (которые предполагаются нормализованными), плюс компонент W, представляющий расстояние от плоскости от начала координат, вдоль нормали к плоскости (кратчайшей линии от плоскости к началу координат).
Rotator
Определяет вращение в ортогональной системе координат. Rotator включает в себя компоненты Pitch, Yaw и Roll.
Coords
Произвольная система координат в 3D пространстве.
Color
Значение цвета с компонентами RGB.
Region
Определяет уникальную выпуклую область в пределах уровня.
Спецификаторы структур
Также структуры могут иметь несколько спецификаторов, влияющих на все экземпляры структуры.
atomic
Указывает, что эта структура должна всегда быть сериализована как единое целое, и если какое-либо свойство в структуре отличается от значения по умолчанию, то все элементы структуры будут сериализованы.
atomicwhencooked
Применяет флаг 'atomic' только при работе с приготовленными (cooked) данными пакетов.
immutable
Указывает, что эта структура использует двоичную сериализацию (уменьшает расход дискового пространства и улучшает производительность сериализации), но это небезопасно при добавлении/удалении членов структуры без увеличения версии пакета.
immutablewhencooked
Применяет флаг 'immutable' только при работе с приготовленными (cooked) данными пакетов.
strictconfig
При использовании свойства 'config/globalconfig' для элементов структуры указывает, что из .ini файла будут инициализированы только элементы со спецификатором config/globalconfig (по умолчанию из .ini файла будут инициализированы значения всех элементов структуры)
Перечисления
Перечисления существуют в UnrealScript как удобный способ для объявления переменных, содерщих одно из нескольких ключевых слов. Например, класс актора содержит перечисление EPhysics, описывающее физику, которая должна применяться движком к актору. EPhysics может быть установлено в одно из предопределенных значений, например, в PHYS_None, PHYS_Walking, PHYS_Falling и так далее.
Внутренне, значения перечислени хранятся в виде байтовых переменных. При разработке на языке UnrealScript байтовых значений для перечислений не видно, так как в них нет необходимости, это делает код более читаемым, например, значение PHYS_Swimming для физики актора намного понятнее, чем (например) 3.
Пример объявления перечислений:
// Declare the EColor enumeration, with three values.
enum EColor
{
CO_Red,
CO_Green,
CO_Blue
};
// Now, declare two variables of type EColor.
var EColor ShirtColor, HatColor;
// Alternatively, you can declare variables and
// enumerations together like this:
var enum EFruit
{
FRUIT_Apple,
FRUIT_Orange,
FRUIT_Bannana
} FirstFruit, SecondFruit;
В исходном коде Unreal мы всегда объявляем значения перечисления, как LT_Steady, PHYS_Falling и так далее, а не как просто как "Steady" или "Falling". Это только вопрос стиля программирования, а не требование языка.
UnrealScript распознает теги перечислений (например, FRUIT_Apple) без указания принадлежности к типу перечисления в только классе, в котором перечисления определены, и в его подклассах. Если вам необходимо обратиться к тегу перечисления из другого класса, вы должны "указать принадлежность тега к типу перечисления":
FRUIT_Apple // If Unreal can't find this enum tag...
EFruit.FRUIT_Apple // Then qualify it like this.
Значения перечисления могут быть использованы в качестве размера статического массива. Кроме того, статический массив может быть определен размером, равным числу элементов в перечислении, если использовать имя типа перечисления в определении, например:
var int MyIntArray[EFruit];
Константы
В сценариях UnrealScript вы можете использовать как константы практически все типы данных:
Константы для целых чисел и байтов задаются с простыми числами, например: 123. Если вы должны задать константу, как число или байт в шестнадцатеричном формате, то используйте запись вида: 0x123.
Константы с плавающей точкой задаются десятичными числами, например: 456.789
Строковые константы должны быть заключены в двойные кавычки, например: "MyString"
Константы имен должны быть заключены в одиночные кавычки, например: 'MyName'
Константы Vector содержат значения X, Y и Z, например: vect(1.0,2.0,4.0)
Константы Rotator содержат значения Pitch, Yaw и Roll, например: Rot(0x8000,0x4000,0)
Константа None указывается для пустых ссылок на объекты или акторы.
Константа Self содержит ссылку на текущий объект или актор (эквивалент this в C++).
Базовые константы объектов задаются именем объекта, следующим за типом объекта, и заключаются одиночные кавычки, например: texture'Default'
Константа EnumCount возвращает количество элементов в перечислении, например: ELightType.EnumCount
Константа ArrayCount возвращает количество элементов в статическом массиве, например: ArrayCount(Touching)
Вы можете использовать ключевое слово "const" для объявления констант, к которым можно далее можно обращаться по имени. Например:
const LargeNumber=123456;
const PI=3.14159;
const MyName="Tim";
const Northeast=Vect(1.0,1.0,0.0);
Константы могут быть определены в пределах классов или структур.
Для доступа к константе, объявленой в другом классе, используйте синтаксис "class'classname'.const.constname", например:
class'Pawn'.const.LargeNumber
Переменные ссылок на объекты и акторы
Вы можете объявить переменную, ссылающуюся на актор или объект, например:
var actor A; // An actor reference.
var pawn P; // A reference to an actor in the Pawn class.
var texture T; // A reference to a texture object.
Переменная "Р" может ссылаться на актор в классе Pawn. Такая переменная может ссылаться на любой актор, производный от класса Pawn. Например, Р может обратиться к Brute, к Skaarj или к Manta, то есть к любому виду Pawn. Однако, P никогда не может ссылаться на актор Trigger (потому что Trigger не наследует класс Pawn).
Ссылки на акторы часто удобно использовать в случаях, когда один персонаж может атаковать другого, например в акторе каждого из персонажей можно объявить переменную Enemy, ссылку на класс Pawn, а значение этой переменной инициализировать ссылкой на актор ближайшего врага, которого можно атаковать.
Если у вас есть переменная, ссылающаяся на актор, то Вы можете получить доступ к переменным этого актера и вызывать его функции. Например:
// Declare two variables that refer to a pawns.
var pawn P, Q;
// Here is a function that makes use of P.
// It displays some information about P.
function MyFunction()
{
// Set P's enemy to Q.
P.Enemy = Q;
// Tell P to play his running animation.
P.PlayRunning();
}
Переменные, ссылающиеся на акторы всегда, либо ссылаются на существующий актор (любой актор, который действительно присутствует на уровне), либо содержат значение None. Значение None эквивалентно значению NULL для указателя в C/C++. Однако, в UnrealScript доступ к переменным и вызов функций по ссылке None полностью безопасен, результат всегда равен нулю.
Обратите внимание, что ссылка на объект или актор "указывает на" другой актор или объект, он не "содержит" сам актор или объект. Ссылка на актор в UnrealScript эквивалентна указателю на объект класса AActor в языке С (в C вы бы использовали запись AActor*). Например, у вас на уровне есть два монстра, Bob и Fred, которые сражаются между собой. В этом случае переменная "Enemy" монстра Bob будет ссылаться на актор Fred, а переменная "Enemy" монстра Fred будет ссылаться на актор Bob.
В отличие от указателей языка C, в языке UnrealScript ссылки на объекты всегда безопасны, то есть в UnrealScript невозможно создать ссылку на несуществующий объект или задать неправильное значение для ссылки (кроме случая с использованием значения None). В UnrealScript при уничтожении актора все ссылки на него автоматически устанавливаются в значение None.
Переменные ссылок на классы
В Unreal все объекты например, акторы, текстуры и звуки, - это объекты классов. Объектов классов относятся к классу под названием "class". Часто у вас будут случаи, когда вы захотите сохранить ссылку на объект класса, имя которого еще не объявлено (не известно на момент компиляции). Например:
var() class C;
var actor A;
A = Spawn( C ); // Spawn an actor belonging to some arbitrary class C.
Теперь убедитесь, что не путайте роли класса C и объекта О принадлежащего к классу C (называемого "экземпляром" класса С). Проводя аналогию обозначим класс, как измельчитель перца, а экземпляр этого класса, как молотый перец. Вы можете использовать измельчитель перца (класс) для создания молотого перца (объекта этого класса), повернув рукоятку (вызвав функцию Spawn)... Но, измельчитель перца (класс) - это не молотый перец (объект, принадлежащий классу), так что ВЫ ДОЛЖНЫ НЕ ПЫТАТЬСЯ ЕГО СЪЕСТЬ!
При объявлении переменных, ссылающихся на объекты классов, для ограничения набора классов (и их дочерних классов), на которые может ссылаться переменная, вы можете использовать синтаксис class<metaclass>. Например, в объявлении:
var class<actor> ActorClass;
Переменная ActorClass может ссылаться только на класс, наследующий класс "actor". Это полезно для улучшения проверки типов во время компиляции. Например, функция Spawn принимает класс в качестве параметра, но в данном случае будет иметь смысл только для классов, наследующих класс "actor", синтаксис class<classlimitor> обеспечивает выполнение этого требования компилятором.
Как и при динамическом преобразовании типов объектов вы можете динамически преобразовывать типы классов, например:
// casts the result of SomeFunctionCall() a class of type Actor (or subclasses of Actor)
class<actor>( SomeFunctionCall() )
Выражения
Присваивание
Чтобы присвоить значение переменной, используйте оператор "=" следующим образом:
function Test()
{
local int i;
local string s;
local vector v, q;
i = 10; // Assign a value to integer variable i.
s = "Hello!"; // Assign a value to string variable s.
v = q; // Copy value of vector q to v.
}
В языке UnrealScript, когда функция или другое выражение, требуют определенный тип данных (например, "float"), а вы указываете другой тип данных (например, "int"), компилятор будет пытаться автоматически привести значение к соответствующему типу. Преобразования между всеми числовыми типами данных (byte, int и float) происходит автоматически, без каких-либо усилий с вашей стороны.
UnrealScript также может конвертировать многие другие встроенные типы данных в другие типы, если вы явно указываете их преобразование преобразовать в коде. Синтаксис для явного преобразования следующий:
function Test()
{
local int i;
local string s;
local vector v, q;
local rotator r;
s = string(i); // Convert integer i to a string, and assign it to s.
s = string(v); // Convert vector v to a string, and assign it to s.
v = q + vector(r); // Convert rotator r to a vector, and add q.
}
Ниже приведен полный набор типов, для которых в языке UnrealScript можно использовать не автоматическое явное преобразование:
String в Byte, Int, Float: Осуществляется попытка преобразовать строку вида "123" в значение 123. Если строка не является текстовым представлением числа, то результатом будет 0.
Byte, Int, Float, Vector, Rotator в String: Число преобразуется в текстовое представление.
String в Vector, Rotator: Осуществляется попытка преобразовать содержимое строки в значения элементов вектора или ротатора, соответственно.
String в Bool: Преобразует строки "True" и "False" (без учета регистра) в значения True и False, соответственно; преобразует любое ненулевое значение строки в True, а если строка пуста, то в False.
Bool в String: результатом будет строковое значение "True" или "False".
Byte, Int, Float, Vector, Rotator в Bool: Преобразует ненулевое значение в True, а нулевое в False.
Bool в Byte, Int, Float: Преобразует True а 1, а False в 0.
Name в String: Преобразует имя в соответствующий текстовый эквивалент.
Rotator в Vector: Возвращает вектор "вперед" в соответствии со значением ротатора.
Vector в Rotator: Возвращает ротатор с компонентами Pitching и Yaw, указывающими в направление вектора, для Roll присваивается 0.
Object (или Actor) в Int: Возвращает целое число, гарантированно уникальное для этого объекта.
Object (или Actor) в Bool: Возвращает False если ссылка на объект равна None или True для ссылки на существующий объект.
Object (или Actor) в String: Возвращает текстовое представление объекта.
Преобразование ссылок на объекты и классы
Подобно функциям преобразования между различными типами данных, в языке UnrealScript есть возможность преобразования типов ссылок на объекты и акторы. например, у вас есть переменная, названная "Target", являющаяся ссылкой на актор. Скажем, вы пишете сценарий, где вам необходимо проверить, принадлежит ли актор, представленный ссылкой Target, классу "Pawn", а если принадлежит, то осуществить последовательность необходимых вам операций, имеющих смысл только для класса Pawn, например, вызвать определенную функцию. Это можно сделать путем применения операторов преобразования.
var actor Target;
//...
function TestActorConversions()
{
local Pawn P;
// Cast Target to Pawn and assign the result to P. If Target is not a Pawn (or subclass of Pawn), then the value assigned to P will be None.
P = Pawn(Target);
if( P != None )
{
// Target is a pawn, so set its Enemy to Self.
P.Enemy = Self;
}
else
{
// Target is not a pawn.
}
}
Для выполнения преобразования типа актора введите имя класса, в который необходимо преобразовать ссылку,а в скобках укажите имя переменной, ссылающейся на иходный тип актора. Результат преобразования зависит от возможности выполнения преобразования. В приведенном выше примере, если Target ссылается объект Trigger, а не Pawn, выражение Pawn(Target) вернет "None", так как Trigger не может быть преобразован в Pawn. Однако, если ваша цель ссылается объект Brute, преобразование успешно вернет Brute, потому что Brute наследует класс Pawn.
Таким образом, преобразование типов акторов имеет две цели: во-первых, вы можете использовать для проверки объекта на принадлежность определенному классу. Во-вторых, вы можете использовать его для преобразования ссылки на актор из одного класса в другой. Обратите внимание, что это преобразование не влияет на актор и не меняет класс актора, а просто приводить ссылку на актор к конкретному типу, позволяя получить доступ к свойствам и методам, объявленным для типа, к которому осуществляется приведение, например к свойствам и методам производного класса.
Другой пример преобразования находится в сценарий Inventory. Каждый актор сценария Inventory принадлежит к классу Pawn, даже если его переменная Owner ссылается на любой другой актор (Actor.Owner - это переменная типа Actor). Таким образом, общей темой в коде Inventory является приведение типа Owner к типу Pawn, например:
// Called by engine when destroyed.
function Destroyed()
{
// Remove from owner's inventory.
if( Pawn(Owner)!=None )
Pawn(Owner).DeleteInventory( Self );
}
-- 11 янв 2012, 13:50 --
Функции
Объявление функций
В языке UnrealScript вы можете объявлять новые функции и писать новые версии уже существующих (переписывать функции). Функции могут принимать один или несколько параметров (UnrealScript поддерживает переменные любых типов) и, опционально, могут возвращать значение. Хотя большинство функций пишутся непосредственно в UnrealScript, вы также можете объявлять функции, которые могут быть вызваны из UnrealScript, но реализованы на C++ и находятся в DLL. Технология Unreal поддерживает все возможные комбинации вызовов функций: из кода движка на C++ можно вызывать функции сценариев; из сценариев можно вызывать функций, реализованные на C++, и, естественно, из сценариев можно вызывать функции, реализованные в сценариях.
Ниже приведен пример определения простой функции. Эта функция в качестве параметра принимает вектор и возвращает число с плавающей точкой:
// Function to compute the size of a vector.
function float VectorSize( vector V )
{
return sqrt( V.X * V.X + V.Y * V.Y + V.Z * V.Z );
}
Объявление функции всегда начинается с ключевого слова function. За ним следуют тип возвращаемого значения функции (в данном случае float), имя функции, а затем список параметров функции в круглых скобках.
При вызове функции выполняется код, находящийся между фигурными скобками. Внутри функции можно объявлять локальные переменные (с использованием ключевого слова local) и разместить любой код UnrealScript. Необязательное ключевое слово return заставляет функцию немедленно вернуть значение.
В функции вы можете передавать любые типы UnrealScript (в том числе массивы), а функция может возвращать результат любого типа.
По умолчанию, любые локальные переменные, объявляемые в пределах функции, инициализируются нулем.
Вызов функции может быть рекурсивным. Например, следующая функция вычисляет факториал числа:
// Function to compute the factorial of a number.
function int Factorial( int Number )
{
if( Number <= 0 )
return 1;
else
return Number * Factorial( Number - 1 );
}
Некоторые функции UnrealScript вызываются движком при наступлении определенных событий. Например, когда актора коснулся другой актер, двигатель называет функцию Touch, сообщающую акторы, кто с ним соприкасается. При написании собственной функции Touch, вы можете выполнить необходимые действия, выполняемые при вызове Touch:
// Called when something touches this actor.
function Touch( actor Other )
{
Log( "I was touched!")
Other.Message( "You touched me!" );
}
Вышеприведенная функция иллюстрирует несколько моментов. Во-первых, функция записывает сообщение в лог-файл, используя командуLog (которая эквивалентна командам языка Basic "print" и языка C "printf", за исключением некоторых правил форматирования). Во-вторых, она вызывает функцию "Message", находящуюся в другов акторе. Вызов функций, определенных в других объектах, это распространенное действие в языке UnrealScript (как и в других объектно-ориентированных языках, например в Java), поскольку оно обеспечивает простое средство для "общения" акторов между собой.
Спецификаторы параметров функций
Когда вы просто вызываете функцию, UnrealScript делает локальные копии всех переданных вами параметров. Если функция изменяет некоторые параметры, то это не отражается на значениях переменных, которые вы передали. Например, следующая программа:
function int DoSomething( int x )
{
x = x * 2;
return x;
}
function int DoSomethingElse()
{
local int a, b;
a = 2;
log( "The value of a is " $ a );
b = DoSomething( a );
log( "The value of a is " $ a );
log( "The value of b is " $ b );
}
При вызове функции DoSomethingElse производится вывод следующих значений:
The value of a is 2
The value of a is 2
The value of b is 4
Другими словами, функция DoSomething работает с локальной копией переменной "a" и не влияет на реальное значение переменной "a".
Спецификатор out позволяет указать функции, что она должна изменить значение передаваемой переменной, вместо того значения локальной копии. Это полезно, например, если у вас есть функция, которая должна возвращать несколько значений. Вы можете указать спецификатор out для нескольких параметров функции, например:
// Compute the minimum and maximum components of a vector.
function VectorRange( vector V, out float Min, out float Max )
{
// Compute the minimum value.
if ( V.X<V.Y && V.X<V.Z ) Min = V.X;
else if( V.Y<V.Z ) Min = V.Y;
else Min = V.Z;
// Compute the maximum value.
if ( V.X>V.Y && V.X>V.Z ) Max = V.X;
else if( V.Y>V.Z ) Max = V.Y;
else Max = V.Z;
}
Без ключевого слова out будет сложно написать функцию, которая должна возвращать более одного значения. Выходные параметры передаются по ссылке, так что изменения значения параметра в функции отражаются и на значении оригинала. Это также может быть использовано для оптимизации передачи больших значений, подобно указанию "const out" в C++.
С ключевым словом optional вы можете сделать определенные параметры функции необязательными. В функциях языка UnrealScript необязательные параметры, для которых при вызове не указывается значение, устанавливаются в значения по умолчанию, указанное в объявлении функции, или в ноль (например, 0, false, "", none), если значения по умолчанию не были указаны в объявлении функции. Для встроенных функций значения по умолчанию для необязательных параметров зависят от функции. Например, функция Spawn принимает необязательные параметры location и rotation, которые принимают значения для размещения и ориентации актора. Значение по умолчанию для необязательных аргументов может быть указано добалением оператора присваивания и самого значения, например: function myFunc(optional int x = -1).
Ключевое слово coerce принудительно приводит передаваемые параметры к указанному типу (даже если в UnrealScript указанное преобразование не выполняется автоматически). Это полезно для функций, связанных со строками, где параметры автоматически преобразуются в строки. (Смотрите Строки в сценариях UnrealScript).
Переопределение функций
"Переопределение функции" - это изменение реализации функции базового класса для производного класса. Например, вы пишете сценарий для нового вида монстра называемого Demon. Создаваемый вами класс Demon наследует класса Pawn. Когда Pawn видит игрока в первый раз, вызывается функция "SeePlayer" класса Pawn, и Pawn может начать атаковать игрока. Это хорошая концепция, но говорят, что вам хотелсь бы изменить функцию "SeePlayer" для вашего нового класса Demon. Как вы это делаете? Конечно же, с помощью переопределения функции SeePlayer.
Чтобы переопределить функцию, можно просто скопировать определение функции из базового класса и вставить в новый класс. Например, вы можете добавить функцию SeePlayer в ваш класс Demon.
// New Demon class version of the SeePlayer function.
function SeePlayer( actor SeenPlayer )
{
log( "The demon saw a player" );
// Add new custom functionality here...
}
Переопределение функций является ключом к более эффективному созданию новых классов UnrealScript. Вы можете создать новый класс, который расширяет существующий класс. В этом случае все, что вам нужно сделать, это переопределить функции, алгоритм которых должен отличаться от функций базового класса. Это позволяет создавать новые типы объектов без написания множества строк кода.
Некоторые функции в UnrealScript объявляются со спецификатором final. Ключевое слово final (находящееся непосредственно перед словом function) указывает, что данная функция не может быть переопределена для дочернего класса. Этот спецификатор должен быть использован в функциях, для которых переопределение нежелательно, потому что может привести потерям производительности. Например, у вас есть функция VectorSize, которая вычисляет размер вектора. В ней совершенно нечего переопределять, то есть ее целесообразно его объявить как final. С другой стороны, конкретная реализация функций, подобных Touch, сильно зависит от контекста и не должна быть окончательной.
===Расширенные спецификаторы функций
Static
Статическая функция языка UnrealScript подобна статической функции языка C++, ее можно вызывать без ссылки на объект класса. Статические функции могут вызывать другие статические функции, может получать доступ к значениям по умолчанию для переменных. Статические функции не могу вызвать не статические функции и получать доступ переменным экземпляра (так как они не выполняются по отношению к экземпляру объекта). В отличие от языка C++, статические функции являются виртуальными и могут быть переопределены в дочерних классах. Это полезно в тех случаях, когда вы хотите вызывать статическую функции для переменной класса, а на момент компиляции класс не известен, но представлен переменной со ссылкой на класс.
Singular
Ключевое слово singular предотвращает возможность рекурсивного вызова функции. Правило в следующем: если ссылка на актор уже в пределах сингулярной функции, то любые последующие вызовы сингулярной функции будут пропущены. В некоторых случаях это позволять бесконечной рекурсии. Например, если вы попытаетесь перемещать актор внутри вашей реализации функции Bump, то есть вероятность, что во время своего движения актор врежется в другой актор, в результате чего произойдет вызов Bump еще раз, и так далее. Для предотвращении подобного поведения Вы должны быть очень осторожны, но если у вас нет полной уверенности в невозможности появления бесконечной рекурсии, то используйте ключевое слово singular.
Native
Вы можете объявить функцию UnrealScript как native, то есть указать, что функция вызывается из UnrealScript, но реализована в коде на C++. Например, класс Actor содержит много определений функций со спецификатором native, например:
native(266) final function bool Move( vector Delta );
Число в скобках, после ключевого слова native соответствует номеру функции, объявленной в коде на C++ (с использованием макроопределения AUTOREGISTER_NATIVE), и необходимо только для оператор-функции. Встроенные функции, как ожидается, находятся в DLL с именем, идентичным имени пакета классов, содержащего определение на языке UnrealScript.
NoExport
Используется только для встроенных функций. Указывает, что объявление функции на C++ для этой встроенной функции не должно быть экспортировано. При компиляции сценария в автоматически генерируемый заголовочный файл будет помещено только объявление этой функции.
Exec
Указывает, что эта функция может быть выполнена путем ввода ее имени в консоли. Применимо только для функций некоторых классов.
Latent
Указывает , что встроенная функция латентная, а это означает, что она может быть вызвана только из кода состояния и может вернуть значение через некоторое количество игрового времени.
Iterator
Указывает, что встроенная функция является итератором, который может быть использован для перебора списка акторов с помощью команды foreach.
Simulated
Указывает, что функция может выполняться на стороне клиента, когда актор эмулируется прокси или автономным прокси. Все встроенные функции как правило эмулируются автоматически. (Примечание: если вы переопределите виртуальную встроенную функцию функцией сценария, функция сценария НЕ будет эмулироваться, если вы укажете это ключевое слово)
Server
Указывает, что функция должна быть отправлена на сервер, а не выполняться на локальном клиенте.
Client
Указывает, что функция должны быть направлена для выполнения на клиенте-владельце, а не запускается на сервере. Этот флаг также неявно устанавливает для функции флаг simulated.
Reliable
Указывает, что реплицируемая функция (отмеченая флагом server или client) должна быть надежно передана, то есть должна гарантированно достичь адресата.
Unreliable
Указывает, что реплицируемая функция (отмеченая флагом server или client) должна быть отправлена ненадежно, что означает, что ее достижение адресата не гарантированно, ее передача может быть выполнена в любой удобный момент не выполняться вообще, если не позволяет пропускная способность сети.
Private, Protected
Эти спецификаторы имеют тот же смысл, что и соответствующие ключевые слова для переменных.
Operator, PreOperator, PostOperator
Эти ключевые слова предназначены для объявления особого вида функций, называемых операторами (эквивалентных операторам C++). Компилятор UnrealScript знает обо всех встроенных операторах, таких как "+", "-", "==", "||" и т.д. Если не вдаваяться в подробности, то операторы UnrealScript похожи на операторы C++ и вы можете объявить новые операторы, как функции UnrealScript или встроенные функции, применяя ключевые слова operator, preoperator и postoperator.
Event
Ключевое слово event в UnrealScript имеет то же значение, что и как функция function. Однако, при экспорте заголовочного файла C++ с использованием =unreal -make -h, UnrealEd автоматически генерирует для "события" заглушку вызова из C++ в UnrealScript, что автоматически синхронизирует код на C++ с функциями UnrealScript и исключает возможность передачи ошибочных параметров в функции UnrealScript. Рассмотрим следующий участок кода UnrealScript.:
event Touch( Actor Other )
{ ... }
Создает код на C++ в EngineClasses.h, подобный следующему:
void eventTouch(class AActor* Other)
{
FName N("Touch",FNAME_Intrinsic);
struct {class AActor* Other; } Parms;
Parms.Other=Other;
ProcessEvent(N, &Parms);
}
Это позволяет вам вызвать функцию UnrealScript из C++ следующим образом:
AActor *SomeActor, *OtherActor;
SomeActor->eventTouch(OtherActor);
Const
Это спецификатор добавляется после объявления функции и может быть использован только в объявлении встроенной функции. Указывает, что функция должна быть экспортирована в заголовочный файл как 'const'. Пример использования:
native function int doSomething(string myData) const;
Управляющие структуры
Язык UnrealScript поддерживает все стандартные операторы управления выполнением языков C, C++ и Java:
Структуры повторения
Циклы for
Циклы "For" позволяют выполнять цикл до тех пор, пока не будет выполнено определенное условие. Например:
// Example of "for" loop.
function ForExample()
{
local int i;
log( "Demonstrating the for loop" );
for( i=0; i<4; i++ )
{
log( "The value of i is " $ i );
}
log( "Completed with i=" $ i);
}
В результате работы этого цикла мы получим вывод:
Demonstrating the for loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
В цикле for вы должны указать три выражения, разделенные точкой с запятой. Первое выражение для инициализации переменной в исходное значение. Второе выражение содержит условие, проверяемое перед выполнением каждой итерации цикла, если это выражение истинно, цикл выполняется, а если оно ложно, цикл завершается. Третье условие содержит выражение, увеличивающее счетчик цикла.
Хотя большинство выражений цикла "for" просто обновляют счетчик, вы также можете использовать циклы "for" для более расширенных операций, например, для обхода связанных списков, с помощью соответствующей инициализации исходного значения, условия прекращения и величины прироста.
Во всех управляющих структурах вы можете выполнять одну операцию без расстановки фигурных скобок, например:
for( i=0; i<4; i++ )
log( "The value of i is " $ i );
Или же вы можете выполнить несколько операций, заключив их в фигурные скобки, например:
for( i=0; i<4; i++ )
{
log( "The value of i is" );
log( i );
}
Циклы do
Циклы "do" позволяют вам выполнять цикл пока указанное вами окончательное выражение истинно. Обратите внимание, что Unreal использует синтаксис do-until, который отличается от синтаксиса языков C и Java (использующих do-while).
// Example of "do" loop.
function DoExample()
{
local int i;
log( "Demonstrating the do loop" );
do
{
log( "The value of i is " $ i );
i = i + 1;
} until( i == 4 );
log( "Completed with i=" $ i);
}
В результате работы этого цикла мы получим вывод:
Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
Циклы while
Циклы "while" позволяют вам выполнять цикл пока указанное вами начальное выражение истинно.
// Example of "while" loop.
function WhileExample()
{
local int i;
log( "Demonstrating the while loop" );
while( i < 4 )
{
log( "The value of i is " $ i );
i = i + 1;
}
log( "Completed with i=" $ i);
}
В результате работы этого цикла мы получим вывод:
Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
Оператор continue
Команда "continue" перемещает поток выполнения в начало цикла, а все, что находится после нее, выполнено не будет. В некоторых случаях это может быть использовано для пропуска участка кода цикла.
function ContinueExample()
{
local int i;
log( "Demonstrating continue" );
for( i=0; i<4; i++ )
{
if( i == 2 )
continue;
log( "The value of i is " $ i );
}
log( "Completed with i=" $ i );
}
В результате работы этого цикла мы получим вывод:
Demonstrating continue
The value of i is 0
The value of i is 1
The value of i is 3
Completed with i=4
Оператор break
Команда "break" осуществляет выход из текущего цикла ("For", "Do" или "While").
function BreakExample()
{
local int i;
log( "Demonstrating break" );
for( i=0; i<10; i++ )
{
if( i == 3 )
break;
log( "The value of i is " $ i );
}
log( "Completed with i=" $ i );
}
В результате работы этого цикла мы получим вывод:
Demonstrating break
The value of i is 0
The value of i is 1
The value of i is 2
Completed with i=3
Обратите внимание, что команда "break" также может быть использована для пропуска оставшейся части условного оператора ("switch").
Структуры выбора
Выражения if-then-else
Операторы "if", "else if" и "else" позволяют вам выполнить код при выполнении определенных условий.
// Example of simple "if".
if( LightBrightness < 20 )
log( "My light is dim" );
// Example of "if-else".
if( LightBrightness < 20 )
log( "My light is dim" );
else
log( "My light is bright" );
// Example if "if-else if-else".
if( LightBrightness < 20 )
log( "My light is dim" );
else if( LightBrightness < 40 )
log( "My light is medium" );
else if( LightBrightness < 60 )
log( "My light is kinda bright" );
else
log( "My light is very bright" );
// Example if "if" with brackets.
if( LightType == LT_Steady )
{
log( "Light is steady" );
}
else
{
log( "Light is not steady" );
}
Выражения case
"Switch", "Case", "Default", and "Break" позволяют вам легко обрабатывать списки условий.
// Example of switch-case.
function TestSwitch()
{
// Executed one of the case statements below, based on
// the value in LightType.
switch( LightType )
{
case LT_None:
log( "There is no lighting" );
break;
case LT_Steady:
log( "There is steady lighting" );
break;
case LT_Backdrop:
log( "There is backdrop lighting" );
break;
default:
log( "There is dynamic" );
break;
}
}
Структура "switch" состоит из одного или более операторов "case", а также необязательного оператора "default". Управление передается оператору "case", совпадающему со значением оператора "switch". Оператор "switch" может включать любое количество экземпляров "case", но два оператора "case" не могут иметь одинаковое значение. Выполнение кода после оператора начинается с выбранного оператора и продолжается до тех пор, пока оператор break не передаст управление за пределы структуры case. Если ни одно выражение case не совпадает со значением оператора "switch", управление передается операторам, следующим за необязательным оператором "default". Если оператора "default" нет, то управление передается за пределы управляющей структуры "switch".
Оператор перехода "break", требуется после каждого блока case, включая последний блок, вне зависимости от того, какой из двух операторов ("case" или "default") там использован. Если вы не используете "break", то выполнение будет передано следующему оператору "case" (или "default").
// Example of switch-case.
function TestSwitch2()
{
switch( LightType )
{
case LT_None:
log( "There is no lighting" );
break;
case LT_Steady: // will "fall though" to the LT_Backdrop case
case LT_Backdrop:
log( "There is lighting" );
break;
default:
log( "Something else" );
break;
}
}
Оператор goto
Команда "goto" осуществляет переход потока выполнения к указанной метке.
// Example of "goto".
function GotoExample()
{
log( "Starting GotoExample" );
goto Hither;
Yon:
log( "At Yon" );
goto Elsewhere;
Hither:
log( "At Hither" );
goto Yon;
Elsewhere:
log( "At Elsewhere" );
}
В результате мы получим вывод:
Starting GotoExample
At Hither
At Yon
At Elsewhere
Функциональность языка
Встроенные операторы и их приоритет
Язык UnrealScript предоставляет широкий спектр операторов в стиле C, C++ и Java для таких операций, как сложение чисел, сравнение значений, и инкремент переменных. Полный набор операторов определен в пакете Object.u. Ниже описаны стандартные операторы, в порядке убывания приоритета. Обратите внимание, что все операторы в стиле С имеют тот же приоритет, что и в языке C.
Оператор Типы, к которым применим оператор Назначение
@ string Объединение строк с дополнительным пробелом между двумя строками. "string1"@"string2" = "string1 string2"
@= string Объединение строк, с дополнительным пробелом между двумя строками, объединить и присвоить. "string1"@="string2", "string1" => "string1 string2" (версия 3323 и выше)
$ string Объединение строк. "string1"$"string2" = "string1string2"
$= string Объединение строк, объединить и присвоить. "string1"$="string2", "string1" => "string1string2" (версия 3323 и выше)
*= byte, int, float, vector, rotator Умножить и присвоить
/= byte, int, float, vector, rotator Разделить и присвоить
+= byte, int, float, vector Сложить и присвоить
-= byte, int, float, vector Вычесть и присвоить
|| bool Логическое ИЛИ
&& bool Логическое И
^^ bool Логическое исключающее ИЛИ
& int Побитовое И
| int Побитовое ИЛИ
^ int Побитовое исключающее ИЛИ (XOR)
!= Все Провенить на неравенство
== Все Проверить на равенство
< byte, int, float, string Меньше, чем
> byte, int, float, string Больше, чем
<= byte, int, float, string Меньше или равно
>= byte, int, float, string Больше или равно
~= float, string Проверить на приблизительное равенство (с точностью 0.0001), сравнить без учета регистра (для строк).
<< int, vector Левый двоичный сдвиг (int), прямое преобразование вектора (vector)
>> int, vector Правый двоичный сдвиг (int), обратное преобразование вектора (vector)
>>> То же, что и >>
+ byte, int, float, vector Сложить
- byte, int, float, vector Вычесть
% float, int, byte По модулю (остаток от деления)
* byte, int, float, vector, rotator Умножить
/ byte, int, float, vector, rotator Разделить
Dot vector Скалярное произведение (dot product)
Cross vector Векторное произведение (cross product)
** float Возведение в степень
ClockwiseFrom int (элементы rotator) Возвращает истину, если первый аргумент расположен по часовой стрелке по отношению ко второму аргументу
В таблице, приведенной выше, перечислены операторы в порядке убывания их приоритета (для каждой группы операторов). При вводе сложного выражения, например: "1 * 2 + 3 * 4", UnrealScript автоматически группирует операторы по их приоритету. Так как умножение имеет более высокий приоритет, чем сложение, выражение рассматривается компилятором как "(1 * 2) + (3 * 4)".
Операторы "&&" (логическое И) и "||" (логическое ИЛИ) являются замкнутыми: если результат выражения может быть определен только из первого выражения (например, если первый аргумент операции && имеет значение false), то второе выражение не вычисляется.
Что касается операций прямого и обратного преобразования векторов, оператор >> переводит вектор из локального пространства в пространство мира, а оператор << производит обратное преобразование, то есть переводит вектор из пространства мира в локальное пространство.
Например, если у вас есть вектор перед, находящийся в 64 единицах перед игроком, vect(64,0,0) - это вектор локального пространства игрока. Если вы хотите перевести его в мировое пространство, то вы должны преобразовать его с использованием вектора ориентации игрока, вычислив вектор в пространстве мира следующим образом:
myWorldSpaceVect = my64Vect >> playerPawn.rotation;
Прямое преобразование вам нужно использовать в тех случаях, когда у вас есть вектор пространства мира, а вам необходимо преобразовать его в локальное пространство игрока. Например, вы можете преобразовать скорость актора автомобиля из пространства мира в локальное пространство автомобиля, таким образом вы можете получить вектор с компонентом x, равным скорости автомобиля, чтобы затем отобразить его скорость на HUD.
В дополнение к стандартным операторам язык UnrealScript поддерживает следующие унарные операторы:
! (bool) логическое отрицание.
- (int, float) математическое отрицание.
~ (int) двоичное отрицание.
++, -- префиксиые и постфиксные инкремент и декремент.
Время от времени в движок будут добавляться новые операторы. Полный список операторов вы найдете в исходном коде класса Object.
Функции общего назначения
Создание объектов
Для того чтобы создать новый экземпляр объекта в UnrealScript, вы будете использовать одну из двух функций в зависимости от того, наследует ли класс объекта класс Actor. Для акторов вы должны использовать функцию Spawn, которая объявлена в файле сценария Actor.uc. Для остальных классов вы должны использовать оператор new. Синтаксис оператора new в отличается от любой другой функции. В дополнение к необязательному списку параметров вы должны указать класс нового объекта и необязательный шаблон объекта. В сценариях UnrealScript нет объявления для оператора new, но если бы было, то выглядело примерно так new:
native final operator function coerce Object new
(
object InOuter,
name InName,
int InFlags,
class InClass,
object InTemplate
);
InOuter
(необязательный) Пакет, который необходимо назначить как "внешний" (Outer) для вновь созданного объекта. Если этот параметр не указан, "внешним" для объекта будет установлен специальный пакет, который существует только во время игры, называемый "переходным пакетом" (transient package).
InName
(необязательный) Имя для нового объекта. Если параметр на указан, объекту будет дано уникальное имя в формате ClassName_##, где ## - уникальный номер, увеличивающийся при создании каждого нового экземпляра этого класса.
InFlags
(необязательный, в настоящее время значение ограничивается 64 битными флагами) Флаги, уточняющие особенности применения объекта после его создания:
0x0000000100000000: Редактором поддерживается отмена/повтор операций с данным объектом. (RF_Transactional)
0x0000000400000000: Может ссылаться на внешние файлы. (RF_Public)
0x0000400000000000: Не может быть сохранен на диск. (RF_Transient)
0x0010000000000000: Не загружать объект на игровой клиент. (RF_NotForClient)
0x0020000000000000: Не загружать объект на игровой сервер. (RF_NotForServer)
0x0040000000000000: Не загружать объект в редактор. (RF_NotForEdit)
0x0008000000000000: Держать объект для редактирования, даже если он не на что не ссылается. (RF_Standalone)
InClass
класса, экземпляр которого создается
InTemplate
Объект, используемый для инициализации значений свойств нового объекта
Синтаксис применения оператора new выглядит следующим образом:
ObjectVar = new[(InOuter, InName, InFlags)] <class'InClass'>[(InTemplate)];
Создать объект класса LightFunction:
function CreateALight()
{
local LightFunction NewObj;
NewObj = new class'Engine.LightFunction';
}
Создаем новый объект класса LightFunction с именем "NewLight", указываем этот объект как внешний пакет.
function CreateALight()
{
local LightFunction NewObj;
NewObj = new(Self,'NewLight') class'Engine.LightFunction';
}
Создаем новый объект класса LightFunction с именем "NewLight" в переходный пакет, инициализируем значения свойств нового объекта, используя переменную LightFunctionTemplate:
var LightFunction LightFunctionTemplate;
function CreateALight()
{
local LightFunction NewObj;
NewObj = new(None,'NewLight') class'Engine.LightFunction' (LightFunctionTemplate);
}
defaultproperties
{
Begin Object Class=LightFunction Name=MyLightFunctionArchetype
End Object
LightFunctionTemplate=MyLightFunctionArchetype
}
Функции для операций над целыми числами
int Rand( int Max ); Возвращает случайное число в диапазоне с 0 по Max-1.
int Min( int A, int B ); Возвращает меньшее из двух чисел.
int Max( int A, int B ); Возвращает большее из двух чисел.
int Clamp( int V, int A, int B ); Возвращает первое число, значение которого ограничено пределом с A по B.
Предупреждение - в отличие эквивалентных функций языков C и C++, Min и Max работают только с целыми числами. При использовании этих функций для операций над числами с плавающей точкой вы не получите никаких предупреждений, числа просто будут округлены! Для операций над числами с плавающей точкой используйте функции FMin и FMax.
Функции для операций над числами с плавающей точкой
float Abs( float A ); Возвращает абсолютное значение числа.
float Sin( float A ); Возвращает синус величины, указанной в радианах.
float Cos( float A ); Возвращает косинус величины, указанной в радианах.
float Tan( float A ); Возвращает тангенс величины, указанной в радианах.
float ASin( float A ); Возвращает арксинус величины в радианах.
float ACos( float A ); Возвращает арккосинус величины в радианах.
float Atan( float A ); Возвращает арктангенс величины в радианах.
float Exp( float A ); Возвращает число "e", возведенное в степень A.
float Loge( float A ); Возвращает натуральный (по базе "e") логарифм числа A.
float Sqrt( float A ); Возвращает квадратный корень числа A.
float Square( float A ); Возвращает квадрат числа A = A*A.
float FRand(); Возвращает случайное число в диапазоне с 0.0 по 1.0.
float FMin( float A, float B ); Возвращает меньшее из двух чисел.
float FMax( float A, float B ); Возвращает большее из двух чисел.
float FClamp( float V, float A, float B ); возвращает первое число, значение которого ограничено пределом с A по B.
float Lerp( float A, float B, float Alpha ); Возвращает результат линейной интерполяции между A и B.
float Smerp( float Alpha, float A, float B ); Возвращает результат нелинейной Alpha-smooth интерполяции между A and B.
float Ceil ( float A ); Округляет число в большую сторону.
float Round ( float A ); Округляет число.
Функции для операций над строками
int Len( coerce string S ); Возвращает длину строки.
int InStr( coerce string S, coerce string t); Возвращает смещение второй строки в первой строке, если она существует, или -1 если нет.
string Mid ( coerce string S, int i, optional int j ); Возвращает среднюю часть строки S начиная от символа i и по символ j (или до конца, если символ j не указан).
string Left ( coerce string S, int i ); Возвращает левую часть строки S до символа i.
string Right ( coerce string] S, int i ); Возвращает правую часть строки S после символа i.
string Caps ( coerce string S ); Возвращает строку S в верхнем регистре.
string Locs ( coerce string S); Возвращает строку S в нижнем регистре. (версия 3323 и выше)
string Chr ( int i ); Возвращает символ из таблицы ASCII.
int Asc ( string S ); Возвращает индекс первого символа строки S в таблице ASCII.
string Repl ( coerce string Src, coerce string Match, coerce string With, optional bool bCaseSensitive ); Заменяет Match на With в строке Src. (версия 3323 и выше)
string Split(coerce string Text, coerce string SplitStr, optional bool bOmitSplitStr); Разбивает строку Text в месте первого вхождения SplitStr и возвращает оставшуюся часть Text. Если bOmitSplitStr истинно, SplitStr исключяется из возвращаемой строки.
array<string> SplitString( string Source, optional string Delimiter=",", optional bool bCullEmpty ); Врапер для разделения строки на массив строк с помощью указанного выражения.
JoinArray(array<string> StringArray, out string out_Result, optional string delim = ",", optional bool bIgnoreBlanks = true); Создать одну строку из массива строк, используя указанный разделитель, опционально игнорируя пустые строки.
ParseStringIntoArray(string BaseString, out array<string> Pieces, string Delim, bool bCullEmpty); Разбивает строку с разделителями на элементы массива строк.
A == B; Сравнивает две строки, в случае их равенства возвращает истину (С учетом регистра).
A ~= B; Сравнивает две строки, в случае их равенства возвращает истину (Без учетом регистра).
A != B; Сравнивает две строки, в случае, если строки отличаются, возвращает истину (С учетом регистра).
Полее подробно читайте на странице Строки в сценариях UnrealScript.
Функции для операций над векторами
vector vect( float X, float Y, float Z ); Создает новый вектор с указанными компонентами.
float VSize( vector A ); Возвращает евклидов размер вектора (квадратный корень суммы квадратов компонентов).
vector Normal( vector A ); Возвращает вектор размером 1,0, указывающий в направлении исходного вектора.
Invert ( out vector X, out vector Y, out vector Z ); Инвертирует систему координат, определенную тремя осями векторов.
vector VRand ( ); Возвращает равномерно распределенный случайный вектор.
vector MirrorVectorByNormal( vector Vect, vector Normal ); Отражает вектор по указанному вектору нормали.
Функции для работы с таймерами
Функции для работы с таймерами доступны только для классов, наследующих класс Actor.
Вы можете создать несколько таймеров, каждый из которых имеет разную частоту. Каждый таймер имеет уникальную функцию (по умолчанию Timer()).
function SetTimer(float inRate, optional bool inbLoop, optional Name inTimerFunc); Запускает таймер, который срабатывается после inRate секунд. Если inbLoop истинно, таймер зацикливается. inTimerFunc определяет функцию для вызова, по умолчанию это функция Timer(), это значение также используется для указания нескольких таймеров.
ClearTimer(optional Name inTimerFunc); Останавливает таймер
bool IsTimerActive(optional Name inTimerFunc); Возвращает true, если таймер активен
float GetTimerCount(optional Name inTimerFunc); возвращает значение счетчика таймера, например, количество секунд с момента последнего исполнения таймера. Возвращает -1, если таймер не активен.
float GetTimerRate(optional name TimerFuncName = 'Timer'); Возвращает скорость таймера, GetTimerRate('SomeTimer') - GetTimerCount('SomeTimer') возвращает оставшееся время таймера.
Функции для отладки
Следующие функции могут помочь вам в отладке кода.
LogEx( ELoggingSeverity Severity, name Category, coerce string Msg ); Выводит сообщение с заданными важностью и категорией. Эта функция обеспечивает больший контроль, чем стандартная функция log(). Она позволяет фильтровать сообщения журнала по важности и по категориям в режиме исполнения.
LogFatal( name Category, coerce string Msg ); Сокращенный вариант вызова LogEx(LOG_FATAL, Category, Msg)
LogError( name Category, coerce string Msg );
function LogWarn( name Category, coerce string Msg );
LogInfo( name Category, coerce string Msg );
LogDebug( name Category, coerce string Msg );
LogTrace( name Category, coerce string Msg );
Отметим, что по состоянию списка изменений 134102, функции протоколирования, указанные выше, больше не доступны. Они были заменены на соответствующие макроопределения, которые обрабатываются препроцессором UnrealScript.
ScriptTrace(); Помещает стек вызова текущего сценария в файл журнала
Name GetFuncName(); Возвращает имя текущей вызываемой функции
DumpStateStack(); Выводит текущее состояние стека
Препроцессор UnrealScript
Подробнее о препроцессоре читайте на странице Препроцессор Unrealscript.
Инструменты и утилиты UnrealScript
Профилирование сценариев
The Профайлер геймплея External Link | UnrealScript. Справочное руководство может помочь поиске сценариев, наиболее критичных к времени выполнения.
Отладчик сценариев
Подробнее об инструментах отладки Unreal читайте на странице Debugging Tools External Link | UnrealScript. Справочное руководство.
Взято с GameDev.ru
Автор: Vincent Barabus