US: Справочник

(Материалы по UDK)

Модератор: KrisGames

Аватара пользователя
Ryslan
Пилигрим
Сообщения: 16
Рег. Вс май 02, 2010 3:58 pm
Репутация: 1

US: Справочник

Сообщение Ryslan » Чт апр 29, 2010 9:08 pm

Назначение этого документа

Этот технический документ описывает язык 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

Аватара пользователя
Ryslan
Пилигрим
Сообщения: 16
Рег. Вс май 02, 2010 3:58 pm
Репутация: 1

Расширенные функции языка

Сообщение Ryslan » Вс май 02, 2010 3:50 pm

Расширенные функции языка
Таймеры
Таймеры используются в качестве механизма для планирования выполнения, повторения или завершения событий. Таким образом, актор может установить таймер, зарегистрировав себя в игровом движке и установив необходимый порядок вызова функции Timer().

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

Обычно игровой цикл обновляет состояние каждого актора один раз за кадр, а часть функции Tick() каждого актора в включает в себя вызов функции UpdateTimers(), которая проверяет все таймеры на истечение времени и вызывает функции UnrealScript, соответствующие таймерам.

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

Состояния
Обзор состояний
Исторически сложилось, что программисты игр используют концепцию состояний. Состояния (также известные как "машины программирования состояний") являются естественным путем управления поведением сложных объектов. Однако, пока поддержка состояний не была реализована на уровне языка UnrealScript, разработчикам для реализации состояний проиходилось создавать конструкции "switch" на языке C или C++. Такой код трудно было писать и обновлять.

Теперь UnrealScript поддерживает состояния на уровне языка

В UnrealScript каждый актор, расположенный в игровом мире, всегда находится в одном и только одном состоянии. Его состояние отражает действия, которые он должен выполнить. Например, перемещение кисти включает несколько состояний, например, "StandOpenTimed" и "BumpOpenTimed". Для объектов Pawn есть несколько состояний, таких как "Dying", "Attacking" и "Wandering".

В языке UnrealScript вы можете писать функции и код, относящиеся к определенным состояниям. Эти функции вызываются только тогда, когда актор пребывает в определенном состоянии. Например, при разработке сценария монстра, вы реализуете функцию "SeePlayer", которая вызывается в тот момент, когда монстр "видит" игрока. "Увидев" игрока монстр перейдет из состояния "Wandering" в состояние "Attacking", начав атаку.

Самый простой способ реализовать вышеописанное - это определить некоторые состояния (Wandering и Attacking), и написать различные версии "Touch" в каждом состоянии. Язык UnrealScript поддерживает эту идею.

Перед тем, как приступить к изучению состояний, обратите внимание, что существуют два основных преимущества использования состояний и одно осложнение:

Преимущество первое: Состояния обеспечивают простой способ записи функций, специфичных для состояний, так что вы можете вызывать одну и ту же функцию по-разному, в зависимости от того, что делает актор.
Преимущество второе: Для состоянием вы можете написать специальный "код состояния" с использованием обычных команд UnrealScript плюс нескольких специальных функций, известных как "латентные функции". Латентная функция выполняется "медленно" и может вернуть результат по истечении определенного количества "игрового времени". Это позволяет выполнять программирование, основанное на времени выполнения, - которое дает вам ощутимые преимущества, не доступные в языках C, C++ или Java. То есть вы можете описывать события именно, как вы их представляете, например, вы можете написать сценарий, являющийся эквивалентом фразы: "открыть эту дверь через 2 секунды; пауза; воспроизвести этот звуковой эффект; открыть эту дверь; освободить этого монстра и заставить его атаковать игрока". Все это вы можете реализовать с помощью простого, линейного кода, а движок Unreal позаботится о деталях управления выполнения кода, основанного на времени выполнения.
Осложнение: Вы можете иметь функции (например, Touch), переопределенные в нескольких состояниях, а также в дочерних классах, и вам необходимо будет выяснить, какие именно функции "Touch" будут вызываться в конкретной ситуации. Язык UnrealScript предусматривает правила, позволяющие четко разграничить этот процесс, и если вы создаете сложные иерархии классов и состояний, то эти правила вам необходимо знать.
Ниже приведены примеры состояний из сценария TriggerLight:

Код: Выделить всё

// 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' );
   }
}


Здесь мы объявили два разных состояния (TriggerTurnsOn и TriggerTurnsOff) и написали в каждом состоянии по версии функции Trigger. Хотя мы могли бы решить эту задачу и без использования состояний, применение состояний делает код гораздо более модульным и расширяемым: в UnrealScript, вы можете реализовать дочерний класс на базе существующего класса, добавить новые состояния, а также новые функции. Если вы попытаетесь реализовать этот сценарий без без использования состояний, то в результате код будет сложнее расширять.
Состояние может быть объявлено как редактируемое, что означает, что пользователь сможет установить состояние актора в UnrealEd. Объявить редактируемое состояние вы можете следующим образом:

Код: Выделить всё

state() MyState
{
   //...
}


Объявить нередактируемое состояние вы можете следующим образом:

Код: Выделить всё

state MyState
{
   //...
}


Вы можете также задать автоматическое или начальное состояние, в которое актер должен установлен, с помощью ключевого слова "auto". Автосостояние указывает, что все новые акторы при первом создании должны быть установлены в данное состояние:

Код: Выделить всё

auto state MyState
{
   //...
}


Метки состояний и латентные функции
В дополнение к функциям, состояние может включать одну или несколько меток с кодом на UnrealScript. Например:

Код: Выделить всё

auto state MyState
{
Begin:
   Log( "MyState has just begun!" );
   Sleep( 2.0 );
   Log( "MyState has finished sleeping" );
   goto('Begin');
}


Приведенный выше код состояния выводит сообщение "MyState has just begun!", затем пауза в течение двух секунд, а затем выводит сообщение "MyState has finished sleeping". Самое интересное в этом примере - это вызов латентной функции "Sleep": эта функция возвращает значение не сразу после вызова, а по истечении определенного количества игрового времени. Латентные функции могут быть вызваны только из кода состояний, а не из функций. Латентные функции позволяют управлять сложными цепочками событий, которые предусматривают на некоторых этапах истечение определенного количества времени.

Код состояния начинается с определения метки, в приведенном выше примере метка называется "Begin". Метка указывает удобную точку входа в код состояния. Для меток кода состояний вы можете использовать любые имена, но метка "Begin" имеет особое значение: это начальная точка входа в код состояния.

Для всех акторов доступны три основные латентные функции:

Sleep( float Seconds ) Останавливает выполнение кода состояния на определенное время.

FinishAnim() Ожидает завершения текущей последовательности анимации. Эта функция позволяет писать сценарии, связанные с управлением последовательностями анимации, например, сценарии для анимации, управляемой ИИ (в отличие от анимации, управляемой временем). Реализация плавной анимации является основной целью системы ИИ.

FinishInterpolation() ожидает завершения текущего движения InterpolationPoint.

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

Три встроенных функции UnrealScript особенно полезны при написании кода состояния:

Функция "Goto('LabelName')" (аналогичная оператору goto языков C, C++ и Basic) осуществляет переход к метке состояния, указанной параметром 'LabelName'.

Специальная команда Goto('') останавливает выполнение текущего кода состояния. Выполнение не продолжится, пока вы не укажете новую метку для перехода.

Функция "GoToState" переводит актор в новое состояние, и, опционально, к указанной метке нового состояния (если вы не укажете метку, то будет осуществлен переход к метке "Begin"). Вы можете вызвать GoToState изнутри кода состояния и переход к новому состоянию произойдет немедленно. Вы также можете вызвать GoToState из любой функции в актора, но в этом случае переход будет осуществлен только после завершения выполнения текущей функции.

Ниже приведен пример код состояния, отражающий описанные выше концепции:

Код: Выделить всё

// This is the automatic state to execute.
auto state Idle
{
   // When touched by another actor...
   function Touch( actor Other )
   {
      log( "I was touched, so I'm going to Attacking" );
      GotoState( 'Attacking' );
      Log( "I have gone to the Attacking state" );
   }
Begin:
   log( "I am idle..." );
   sleep( 10 );
   goto 'Begin';
}
 
// Attacking state.
state Attacking
{
Begin:
   Log( "I am executing the attacking state code" );
   //...
}


Когда вы запустите эту программу, а затем коснетесь актар, вы получите вывод:
I am idle...
I am idle...
I am idle...
I was touched, so I'm going to Attacking
I have gone to the Attacking state
I am executing the attacking state code

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

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

Однако, помимо абстракции состояний, модель программирования языка UnrealScript включает дополнительные правила наследования и иерархии. Правила наследования следующие:

Новый класс наследует все переменные из базового класса.

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

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

Код: Выделить всё

// Here is an example parent class.
class MyParentClass extends Actor;
 
// A non-state function.
function MyInstanceFunction()
{
   log( "Executing MyInstanceFunction" );
}
 
// A state.
state MyState
{
   // A state function.
   function MyStateFunction()
   {
      Log( "Executing MyStateFunction" );
   }
// The "Begin" label.
Begin:
   Log("Beginning MyState");
}
 
// Here is an example child class.
class MyChildClass extends MyParentClass;
 
// Here I'm overriding a non-state function.
function MyInstanceFunction()
{
   Log( "Executing MyInstanceFunction in child class" );
}
 
// Here I'm redeclaring MyState so that I can override MyStateFunction.
state MyState
{
   // Here I'm overriding MyStateFunction.
   function MyStateFunction()
   {
      Log( "Executing MyStateFunction" );
   }
// Here I'm overriding the "Begin" label.
Begin:
   Log( "Beginning MyState in MyChildClass" );
}


Если у вас есть функция, которая реализована в глобально, в одном или нескольких состояниях и в одном или нескольких базовых классах, вы должны знать, какая именно версия функции будет вызвана в данном контексте. Правилами наследования и иерархии, разрешающие эти сложные ситуации, являются:
  • Если для класса определено состояние, а в этом состоянии реализована функция, совпадающая с глобальной функцией этого класса (в текущем или в одном из базовых классов), то выполняться будет наиболее поздняя версия функции, определенной в состоянии.
  • Если же с глобальной функцией не совпадает ни одна функция, реализованная для состояния (в текущем или в одном из базовых классов), то выполняться будет наиболее поздняя версия этой функции.

Расширенное программирование состояний
Если в производном классе вы не переопределяете состояние базового класса, то вы можете указать ключевое слово "extends", чтобы дополнить состояние базового класса в дочернем классе. Это полезно в тех случаях, когда у вас есть группа подобных состояний (например, MeleeAttacking и RangeAttacking), которые имеют общую функциональность с базовым состоянием Attacking. Состояние Attacking вы можете дополнить следующим образом:

Код: Выделить всё

// Base Attacking state.
state Attacking
{
   // Stick base functions here...
}
 
// Attacking up-close.
state MeleeAttacking extends Attacking
{
   // Stick specialized functions here...
}
 
// Attacking from a distance.
state RangeAttacking extends Attacking
{
   // Stick specialized functions here...
}


В состоянии может дополнительно быть использован спецификатор ignores для игнорирования определенных функций базового состояния, например:

Код: Выделить всё

// Declare a state.
state Retreating
{
   // Ignore the following messages...
   ignores Touch, UnTouch, MyFunction;
 
   // Stick functions here...
}


Функция GotoState('') позволяет вывести актор из всех состояний, то есть перевести его в состояние "no state". Когда актор пребывает в состоянии "no state", вызываются только его глобальные функции.

При каждом вызове функции GotoState для смены текущего состояния актора, движок может вызвать две специальные функции уведомления, если они определены: EndState() и BeginState(). EndState вызывается в текущем состоянии, непосредственно перед переходом в новое состояние, а BeginState вызывается сразу после перехода в новое государства. Эти функции предназначены для осуществления необходимых вам инициализации и очистки конкретных состояний.

Стек состояний
При при обычном изменении состояния вы переходите из одного состояния в другое, не имея возможности вернуться в предыдущее состояние. С применением стека состояний это возможно. Вызов функции PushState осуществляет переход в новое состояние, поместив текущее состояние на вершину стека.
При этом выполнение текущего состояния останавливается. Вызов функции PopState осуществляет переход в предыдущее состояние и продолжает его исполнение от точки вызова PushState. Функция PushState по своему поведению похожа на латентные функции. Вызов из функции не будет прерывать выполнение кода (так же, как и вызов GoToState внутри функции), в то время как вызов ее из кода состояния приостановит выполнение текущего состояния до его исвлечения из стека (опять же, как и вызов GoToState внутри кода состояния).

Состояние может быть помещено в стек только один раз. Попытка поместить состояние стеке второрично не удастся. PushState работает как GotoState, она принимает имя состояния и, необязательно, метку точки входа в состояние. Новое состояние получит событие PushedState, текущее состояние получит событие PausedState. После вызова PopState текущее состояние получает события PoppedState, а новое состояние (то, что находилось рядом в стеке) получит событие ContinuedState.

Код: Выделить всё

state FirstState
{
   function Myfunction()
   {
      doSomething();
      PushState('SecondState');
      // this will be executed immediately since we're inside of a function (no latent functionality)
      JustPushedSecondState();
   }
   
Begin:
   doSomething();
   PushState('SecondState');
   // this will be executed once SecondState is popped since we're inside of a state code block (latent functionality)
   JustPoppedSecondState();
}

state SecondState
{
   event PushState()
   {
      // we got pushed, push back
      PopState();
   }
}


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

Код: Выделить всё

state BaseState
{
   ...
}

state ExtendedState extends BaseState
{
   ...
}


Если активно состояние ExtendedState, то IsInState('BaseState') вернет false. Конечно же, вызов IsInState('BaseState', true) вернет истину, если BaseState в стеке.

Итерация (ForEach)
Команда foreach языка UnrealScript упрощает работу с большими группами акторов, например, со всеми акторами на уровне или со всеми акторами на определенном расстоянии от указанного актора. "foreach" работает в сочетании с особой функцией, называемой "итератором" ("iterator"), назначением которой является перебор списка акторов.

Ниже приведен простой пример использования команды foreach:

Код: Выделить всё

// Display a list of all lights in the level.
function Something()
{
   local actor A;
 
   // Go through all actors in the level.
   log( "Lights:" );
   foreach AllActors( class 'Actor', A )
   {
      if( A.LightType != LT_None )
         log( A );
   }
}


Первым параметром во всех командах foreach является имя класса, определяющего тип акторов для поиска. Вы можете использовать этот параметр для ограничения диапазона поиска, например, перебирать только акторы, производные от класса Pawn.

Второй параметр команды foreach - это переменная, которой при каждой итерации цикла foreach присваивается ссылка на очередной актор из перебираемого списка.

Ниже приведены все функции итерации, которые работают с оператором "foreach".

AllActors ( class<actor> BaseClass, out actor Actor, optional name MatchTag )
Перебирает все акторы уровня. Если вы укажете дополнительный параметр MatchTag, то в поиск будут включены только акторы, переменная "Tag" которых соответствует указанному вами имени.
DynamicActors( class<actor> BaseClass, out actor Actor )
Перебирает все акторы, которые были созданы с начала запуска уровня, игнорируя те, что изначально размещены в уровне.
ChildActors( class<actor> BaseClass, out actor Actor )
Перебирает все акторы, принадлежащие указанному актору.
BasedActors( class<actor> BaseClass, out actor Actor )
Перебирает все акторы, использующие указанный актор в качестве базового.
TouchingActors( class<actor> BaseClass, out actor Actor )
Перебирает все акторы, которые касаются (или взаимопроникают) с указанным актором.
TraceActors( class<actor> BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent )
Перебирает все акторы, которые касаются линии, от ее начальной точки, определяемой параметром Start, до ее конечной точки, определяемой параметром End, с учетом степени касания, определяемой параметром Extent. При каждой итерации для HitLoc установливается координата касания, а для HitNorm - нормаль касания.
OverlappingActors( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden )
Перебирает все акторы, находящиеся в указанном радиусе от указанного места (или, если место не указано, то от местоположения указанного актора).
VisibleActors( class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc )
Перебирает все акторы, видимые с указанного места (или, если место не указано, то от местоположения вызывающего актора).
VisibleCollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden );
Возвращает все сталкивающиеся (bCollideActors==true) акторы в пределах указанного радиуса, видимые с места, указанного Loc (или, если место не указано, то видимые от местоположения вызывающего актора). Параметр bIgnoreHidden позволяет игнорировать все скрытые акторы. Работает быстрее, чем AllActors(), поскольку этот итератор использует хэш столкновений.
CollidingActors ( class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc );
Возвращает все сталкивающиеся (bCollideActors==true) акторы в пределах указанного радиуса от места, указанного параметром Loc (или, если место не указано, то от местоположения вызывающего актора). При достаточно малых радиусах работает быстрее, чем AllActors(), поскольку этот итератор использует хэш столкновений.
Обратите внимание: Все функции итерации являются методами классов, наследующих класс Actor. Если вам необходимо перебрать определенные классы, не наследующие класс Actor, то воспользуйтесь переменной, ссылающейся на класс, наследующий Actor, и следующим синтаксисом:

foreach ActorVar.DynamicActors(class'Pawn', P)
Таким образом, с помощью класса Interaction вы можете сделать следующее:

foreach ViewportOwner.Actor.DynamicActors(class'Pawn', P)
Обратите внимание: Итераторы теперь поддерживают и динамические массивы. Подробнее о динамических массивах читайте в разделе "Итераторы динамических массивов".

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

Global Вызывает наиболее позднюю версию глобальной (не включенной в состояние) функции.
Super Вызывает соответствующую версию функции базового класса. Вызываемая функция может быть как функцией состояния, так и глобальной функцией, в зависимости от контекста.
Super(classname) Вызывает соответствующую версию функции, находящуюся в указанном классе, или версию ближайшего из базовых классов. Вызываемая функция может быть как функцией состояния, так и глобальной функцией, в зависимости от контекста.

Объединение нескольких спецификаторов вызова (например, Super(Actor).Global.Touch) является недопустимым.

Ниже приведено несколько примеров применения спецификаторов вызова:

Код: Выделить всё

class MyClass extends Pawn;
 
function MyExample( actor Other )
{
   Super(Pawn).Touch( Other );
   Global.Touch( Other );
   Super.Touch( Other );
}


Также, например, функция BeginPlay() вызывается, когда актор входит в игровой процесс. Функция BeginPlay() реализована в классе Actor и включает некоторые важные операции, которые должны быть выполнены. Теперь, скажем, вы хотите переопределить функцию BeginPlay() в вашем новом классе MyClass, чтобы добавить новую функциональность. Чтобы сделать это безопасно, необходимо вызвать версию BeginPlay() из базового класса:
class MyClass extends Pawn;

Код: Выделить всё

function BeginPlay()
{
   // Call the version of BeginPlay in the parent class (important).
   Super.BeginPlay();
 
   // Now do custom BeginPlay stuff.
   //...
}


Доступ к статическим функциям из переменной класса
Статические функции из переменной класса могут быть вызваны следующим образом:

Код: Выделить всё

var class C;
var class<Pawn> PC;

class'SkaarjTrooper'.static.SomeFunction(); // Call a static function
                                            // in a specific class.

PC.static.SomeFunction(); // Call a static function in a variable class.

class<Pawn>(C).static.SomeFunction(); // Call a static function in a
                                      //casted class expression.


Значения по умолчанию для пременных

Доступ к значениям по умолчанию для переменных
UnrealEd позволяет дизайнерам уровней редактировать переменные "по умолчанию" класса объекта. Когда создается новый актор, все его переменные инициализируются значениями по умолчанию. Иногда это полезно для ручного сброса переменной в значение по умолчанию. Например, когда игрок бросает предмет инвентаря, код предмета инвентаря должен сбросить некоторые из значений актора в значения по умолчанию. В UnrealScript можно получить доступ к переменным по умолчанию класса с использованием ключевго слова "Default.". Например:

Код: Выделить всё

var() float Health, Stamina;
//...
 
// Reset some variables to their defaults.
function ResetToDefaults()
{
   // Reset health, and stamina.
   Health = Default.Health;
   Stamina = Default.Stamina;
}


Доступ к значениям по умолчанию для переменных через ссылку на класс
Если у вас есть ссылка на класс (переменная типа class или class<classlimitor>), то вы можете получить доступ к свойствам по умолчанию для класса, на который она ссылается, не имея самого объекта этого класса. Этот синтаксис работает с любыми выражениями типа class.

Код: Выделить всё

var class C;
var class<Pawn> PC;

Health = class'Spotlight'.default.LightBrightness; // Access the default value of
                                                   // LightBrightness in the Spotlight class.

Health = PC.default.Health; // Access the default value of Health in
                            // a variable class identified by PC.

Health = class<Pawn>(C).default.Health; // Access the default value
                                        // of Health in a casted class
                                        // expression.


Определение значений по умолчанию с использованием блока defaultproperties
В дополнение к установке значений по умолчанию для свойств актора с использованией окна свойств в UnrealEd, вы также можете присвоить значения по умолчанию для переменных членов классов путем размещения специальных выоажений внутри блока класса defaultproperties.

В блоке defaultproperties не допустимо размещение операций, за исключением операций над динамическими массивами
Запятые могут быть размещены в конце каждой строки, но это не обязательно
Значения по умолчанию наследуются дочерними классами. Значения, указанные в defaultproperties дочернего класса переопределяют значения, указанные для базового классов.

Синтаксис
Синтаксис блока defaultproperties немного отличается от стандартного синтаксиса UnrealScript

Простые типы (Ints, Floats, Bools, Bytes):
VarName=Value
Статические массивы:
ArrayProp(0)=Value1
ArrayProp(1)=Value2
или
ArrayProp[0]=Value1
ArrayProp[1]=Value2

Динамические массивы:
ArrayProp=(Value1,Value2,Value3)
или
ArrayProp(0)=Value1
ArrayProp(1)=Value2
ArrayProp(2)=Value3
или
ArrayProp.Add(Value1)
ArrayProp.Add(Value2)
ArrayProp.Add(Value3)

Имена
NameProp='Value'
или
NameProp=Value

Объекты
ObjectProp=ObjectClass'ObjectName'

Подобъекты
Begin Object Class=ObjectClass Name=ObjectName
VarName=Value
...
End Object
ObjectProperty=ObjectName
Структуры (включая векторы):
StructProperty=(InnerStructPropertyA=Value1,InnerStructPropertyB=Value2)
или
StructProperty={(
InnerStructPropertyA=Value1,
InnerStructPropertyB=Value2
)}

Обратите внимание: Внутри структуры значений по умолчанию некоторые типы используют несколько иной синтаксис.
Встраиваемые (inline) статические массивы должны быть объявлены как (Обратите внимание, что здесь для доступа к элементам массива используются скобки "[]", вместо круглых скобок "()"):
StructProperty=(StaticArray[0]=Value,StaticArrayProp[1]=Value)

Встраиваемые (inline) динамические массивы должны быть объявлены с использованием следующего синтаксиса:
StructProperty=(DynamicArray=(Value,Value))

Встраиваемые (inline) переменные имен должны быть заключены в кавычки:
StructProperty=(NameProperty="Value")

Операции над динамическими массивами. Они могут быть использованы для модификации содержимого динамического массива, который может быть унаследован от бызового класса.
Array.Empty - полностью очищает массив
Array.Add(element) - добавляет элемент к концу массива
Array.Remove(element) - удаляет элемент из массива, эта процедура может удалить все вхождения элемента
Array.RemoveIndex(index) - удаляет элемент по указанному индексу
Array.Replace(elm1, elm2) - заменяет elm1 на elm2. Все вхождения будут заменены. Если elm1 будет не найден, то выводится предупреждение.

Ознакомьтесь со следующим примером (основанном на Actor.uc):

Код: Выделить всё

defaultproperties
{
   // objects
   MessageClass=class'LocalMessage'

    // declare an inline subobject of class SpriteComponent named "Sprite"
   Begin Object Class=SpriteComponent Name=Sprite
       // values specified here override SpriteComponent's own defaultproperties
      Sprite=Texture2D'EngineResources.S_Actor'
      HiddenGame=true
   End Object
   //todo
   Components.Add(Sprite)

    // declare an inline subobject of class CylinderComponent named "CollisionCylinder"
   Begin Object Class=CylinderComponent Name=CollisionCylinder
       // values specified here override CylinderComponent's own defaultproperties
      CollisionRadius=10
      CollisionHeight=10
      AlwaysLoadOnClient=True
      AlwaysLoadOnServer=True
   End Object
   //todo
   Components.Add(CollisionCylinder)
   
   CollisionComponent=CollisionCylinder

    // floats (leading '+' and trailing 'f' characters are ignored)
   DrawScale=00001.000000
   Mass=+00100.000000
   NetPriority=00001.f

    // ints
   NetUpdateFrequency=100
   
    // enumerations
   Role=ROLE_Authority
   RemoteRole=ROLE_None
   
    // structs
   DrawScale3D=(X=1,Y=1,Z=1)
   
    // bools
   bJustTeleported=true
   bMovable=true
   bHiddenEdGroup=false
   bReplicateMovement=true
   
   // names
   InitialState=None
   
   // dynamic array (in this case, a dynamic class array)
   SupportedEvents(0)=class'SeqEvent_Touch'
   SupportedEvents(1)=class'SeqEvent_UnTouch'
   SupportedEvents(2)=class'SeqEvent_Destroyed'
   SupportedEvents(3)=class'SeqEvent_TakeDamage'
}


Значения по умолчанию для структур
При объявлении структуры в UnrealScript можно дополнительно указать значения по умолчанию для свойства структуры. При каждом использовании структуры в UnrealScript ее члены будут инициализироваться этими значениями. Синтаксис идентичен блоку defaultproperties для класса - единственное исключение в том, что вы должны назвать блок structdefaultproperties. Например:

Код: Выделить всё

struct LinearColor
{
   var() config float R, G, B, A;

   structdefaultproperties
   {
      A=1.f
   }
};


При каждом определении переменной LinearColor в UnrealScript, значение ее члена A будет установлено в 1.f. Также важно учесть, что блок defaultproperties класса переопределяет свойства по умолчанию для структуры. Если в вашем классе есть переменная типа LinearColor, любое значение, присвоенное ей в блоке defaultproperties заменит значение, определенное в блоке structdefaultproperties.

Код: Выделить всё

defaultproperties
{
    NormalColor=(R=1.f,B=1.f,G=1.f)       // value of A will be 1.0f for this property
    DarkColor=(R=1.f,B=1.f,G=1.f,A=0.2f)  // value of A will be 0.2f for this property
}


Динамические массивы
Ранее мы рассмотрели статические массивы. Это означает, что размер (количество элементов в массиве) устанавливается во время компиляции и не может быть изменен. Динамические и статические массивы имеют следующие общие характеристики:

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

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

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

Первое - это объявление переменной. Объявление динамического массива очень похоже на объявление любой другой переменной UnrealScript (то есть имеет вид var/local type varname). Для объявления динамического массива указызается тип array, а затем тип массива, заключенный в угловые скобки. Если тип массива содержит скобки (например class<Actor>), то вы должны поставить пробел между закрывающей скобкой типа и закрывающей скобкой объявления массива, иначе компилятор интерпретирует двойную угловую скобку как оператор >>.

Примеры:
Объявление динамического массива целых чисел с именем IntList:
var array<int> IntList;
Объявление динамического массива типа class<PlayerController> с именем Players:
var array<class<PlayerController> > Players;

При запуске сценария IntList будет содержать 0 элементов. Динамическими массивами поддерживаются методы, позволяющие добавлять элементы в массив, изымать элементы из массива, а также произвольно увеличивать или уменьшать длину массива. Синтаксис вызова этих методов (использованием переменной IntList): IntList.MethodName().

Для динамических массивов доступны следующие методы:

Add(int Count): увеличивает длину массива на Count элементов, идентично вызову FArray::AddZeroed().
Insert(int Index, int Count): где Index - это индекс массива для вставки элементов, а Count число элементов для вставки. Все существующие элементы в этом месте массива смещаются вверх, а новые элементы создаются и вставляются в указанном месте. Вставка 5-ти элементов по индексу 3 смещтит вверх (на значение индекса) все элементы массива, начиная от индекс 3 на 5 элементов. Элемент, ранее распологавшийся по индексу 3 теперь будет расположен по индексу 8, элемент 4 теперь будет элементом 9 и так далее. Все добавленные элементы инициализируются значениями по умолчанию (ноль/нуль для всех типов, кроме структур, имеющих structdefaultproperties).
Remove(int Index, int Count): где Index - это начальный индекс для удаления элементов из массива, и Count - это число удаляемых элементов. Это позволяет удалить группу элементов из массива, начиная с любого допустимого индекса. Обратите внимание, что значения индексов оставшихся элементов (начиная с индекса, равного Index+Count) изменятся в меньшую сторону. Имейте это в виду, если вы храните значения индексов динамических массивов.
AddItem(Item): добавляет Item в конец массива, увеличивая длину массива на один элемент.
RemoveItem(Item): удаляет все экземпляры Item, используя линейный поиск.
InsertItem(int Index, Item): вставляет Item в массив по индексу Index, увеличивая длину массива на один элемент.
Find(...) - находит индекс элемента в массиве. Есть две версии Find: стандартный поиск для элемента по значению, и специализированная версия для поиска структуры по значению одного из свойств структуры
Find(Value): где Value - это значение для поиска. Возвращает индекс первого найденного элемента в массиве, который соответствует указанному значению, или -1, если это значение не было найдено. Value может быть представлено ??любым допустимым выражением.
Find(PropertyName, Value): где PropertyName - это имя свойства структуры для поиска (должно иметь тип 'Name'), а Value - это искомое значение. Возвращает индекс первой найденной структуры в массиве, свойство с именем PropertyName которой соответствует значению, указанному Value, или -1, если это значение не было найдено. Value может быть представлено ??любым допустимым выражением.
Sort(SortDelegate) - SortDelegate - это делегат для сортировки содержимого массива. SortDelegate должен иметь следующий вид:
delegate int ExampleSort(ArrayType A, ArrayType B) { return A < B ? -1 : 0; } // отрицательное возвращаемое значение указывает, элементы должны поменяться местами

Переменная Length

Динамические массивы имеют переменную, называемую Length, значением которой является текущая длина (количество элементов) динамического массива. Для получения доступа к Length, применительно к нашему массиву IntList используется запись: IntList.Length. Мы можем не только прочитать, но и непосредственно установить значение переменной Length, что позволяет нам изменить количество элементов в массиве. При изменении переменной Length изменяется и длина массива. Например, если мы установим IntList.Length = 5, а затем установим IntList.Length = 10, дополнительные 5 элементов, добавленных последней операцией, добавляются в конец массива с сохранением первоначальных 5 элементов и их значений. Если мы уменьшим длину, то элементы будут сняты с конца массива. Обратите внимание, что при добавлении элементов в массив операцией Insert() или путем увеличения длины, элементы инициализируются в значения по умолчанию для типов переменных (0 для целых, None для ссылок на класс и т.д.). Следует также отметить, что вы можете увеличить длину динамического массива, обратившить по большему индексу, чем текущее значение длины массива. Это позволяет увеличивать массив так же, как если бы вы установили большее значение для Length.

OldLength = Array.length
Array.Length = OldLength + 1
Array[OldLength] = NewValue

Array[Array.Length] = NewValue

Array.AddItem(NewValue)
- это эквивалентные формы одной и той же операции.

Заметим, однако, что вы не можете увеличивать длину массива и получать доступ к его членам одновременно. Выражение:

Array[Array.length].myStructVariable = newVal
не будет работать.

Предостережение: переменная Length динамического массива никогда не должна увеличиваться или уменьшаться операторами '++','--','+=' или'-=', и вы не должны передавать Length в функцию в качестве параметра (если функция может изменить ее значение). Выполнение этих операций приведет к утечке памяти и сбоям. Устанавливайте значение Length только через оператор '=' (или через обращение по большему индексу, чем текущее значение длины массива).

Обратите внимание: array<bool> не поддерживается!

Последнее замечание: динамические массивы не реплицируются. Вы можете обойти это с помощью функции, реплицирующей индексы динамического массива и соответствующие значения элементов. Однако, при этом вы также должны учитывать временное рассогласование между клиентом и сервером.

Итераторы динамических массивов
Динамические массивы теперь поддерживает оператор 'foreach'. Базовый синтаксис следующий: 'foreach ArrayVariable(out ArrayItem,optional out ItemIndex) {}', при каждой итерации осуществляется инкремент индекса и возвращение элемента, а также возвращение индекса, если указан соответствующий параметр.

Код: Выделить всё

function IterateThroughArray(array<string> SomeArray)
{
    local string ArrayItem;
    local int Index;
    foreach SomeArray(ArrayItem)
    {
       `log("Array iterator test #1:"@ArrayItem);
    }
    foreach SomeArray(ArrayItem,Index)
    {
        `log("Array iterator test #2:"@ArrayItem@Index);
    }
}




Поддержка метаданных
В игре и в редакторе функциональность может быть расширена с помощью свойств метаданных.

Обзор метаданных
Произвольные метаданные могут быть связаны со свойством UnrealScript следующим образом:

Для переменной:

var float MyVar<TAG=VALUE>
Для перечисления:
enum EMyEnum
{
EME_ValA<TAG=VALUE>,
EME_ValB<TAG=VALUE>,
};

Использование нескольких спецификаций метаданных
Вы можете использовать несколько спецификаций метаданных для одного свойства, разделив их символом |.

Например:

var() LinearColor DrawColor<DisplayName=Draw Color|EditCondition=bOverrideDrawColor>;
Доступные спецификации метаданных
Ниже приведены теги, поддерживаемые в настоящее время, и их описание:

<ToolTip=TEXT_STRING>

Делает TEXT_STRING подсказкой, всплывающей при наведении мыши на соответствующее свойство в окне редактора свойств.

Обратите внимание: Недавно была добавлена поддержка комментариев вида /** VALUE */, которые автоматически переводятся компилятором сценариев в тег ToolTip метаданных.

<DisplayName=TEXT_STRING>

В окне редактора свойств имя свойства отображается как TEXT_STRING, а не настоящее имя свойства.

Пример:

Var() bool bEnableSpawning<DisplayName=Spawning Enabled>;
Внимание: Использование DisplayName для перечислений создаст проблемы, если вы измените параметр UPropertyInputCombo для сортировки перечислений в комбинированном списке редактора.

<EditCondition=ConditionalPropertyName>

Это позволяет менять статус редактируемости свойства в редакторе свойств в зависимости от значения другого (булевого) свойства.

Например, вы можете сделать следующие настройки в классе MyPackage.MyClass:

/** Enable or disable spawning */
Var() bool bEnableSpawning;

/** Set the rate at which AIs are spawned. Has no effect unless bEnableSpawning = TRUE */
Var() float RespawnsPerSecond<EditCondition=bEnableSpawning>;
После чего RespawnsPerSecond в редакторе будет серым, когда bEnableSpawning ложно. Это помогает сделать наборы свойств для дизайнеров менее запутанными.
Важно: Эти настройки метаданных требуют наличия контролируемой переменной (RespawnsPerSecond) и обязательное наличие настраиваемого элемента свойства (WxCustomPropertyItem_ConditionalItem).

Для их включения необходимо внести соответствующие изменения в файле Editor.ini:

[UnrealEd.CustomPropertyItemBindings]
CustomPropertyClasses=(PropertyPathName=" MyPackage.MyClass: RespawnsPerSecond ",PropertyItemClassName="WxCustomPropertyItem_ConditionalItem")

<FriendlyName=TEXT_STRING>

Используется Редактором UI .

<AllowAbstract>

При наличии в свойстве Class, выпадающие списки для редактирования свойств будут включать в себя и абстрактные классы. Если спецификация не указана, то списки содержат только конкретные (не абстрактные) классы. Для этой спецификации метаданных не нужно указывать такие значения, как True или False.

<AutoComment=BOOLEAN_VALUE>

При добавлении к свойству Kismet Sequence Action, свойство и его текущее значение будет автоматически появляться в сценарии в качестве комментария над действием. Чтобы увидеть это в действии поместите новую последовательность действий "Gate" в сценарий. В данном классе эта опция метаданных используется как bOpen и AutoCloseCount.

Расширенные технические вопросы

Реализация UnrealScript
Для получения более подробной информации о том, как UnrealScript работает, посетите страницы Compile Process , Execution , Byte Code и UnrealScript Implementation .

Вопросы двоичной совместимости UnrealScript
UnrealScript разрабатывается таким образом, что классы в файлах пакетов могут развиваться в течение долгого времени без потерь двоичной совместимости. В данном случае двоичная совместимость означает, что "зависимые двоичные файлы могут быть загружены и слинкованы без ошибок"; наличие различных версий и модификаций ваших функций и классов - это уже отдельный вопрос. В частности, видами модификаций, которые могут быть произведены без риска потери совместимости, являются:

.uc файлы сценариев в пакете могут быть перекомпилированы без потери двоичной совместимости.
Добавление в пакет новых классов.
Добавление к классам новых функций.
Добавление к классам новых состояний.
Добавление к классам новых переменных.
Удаление из класса закрытых (private) переменных.

Другие преобразования, как правило, небезопасны, в том числе (но не ограничиваясь):
Добавление новых членов структур.
Удаление класса из пакета.
Изменение типа любой переменной, параметра или возвращаемого значения функции.
Изменение числа параметров функции.

Технические примечания
Сборка мусора. Все объекты и акторы в Unreal обрабатываются системой сборки мусора аналогично Java VM. Сборщик мусора Unreal использует функциональность сериализации класса UObject и рекурсивно определяет ссылки на другие объекты каждого активного объекта. В результате, объект не удаляется явно, сборщик мусора отслеживает их использование и удаляет объект только в случае его неиспользования. Этот подход имеет побочный эффект в виде скрытого удаления неиспользуемых объектов, однако это гораздо более эффективно, чем подсчет ссылок и удаление отдельных объектов. Подробнее читайте на странице Сборка мусора .

UnrealScript основан на байт-коде. Код языка UnrealScript компилируется в серии байт-кода, похожие на серии P-кода или байт-кода Java. Это делает UnrealScript независимым от платформы, позволяет осуществлять перенос компонентов клиента и сервера Unreal на другие платформы, то есть на Macintosh или на Unix.

Unreal, как виртуальная машина. Движок Unreal можно рассматривать как виртуальную машину для 3D-игр, как и язык и встроенные иерархии классов Java составляют виртуальную машину для сценариев веб-страниц. Виртуальная машина Unreal изначально портативна (благодаря разделению кода, зависимого от платформы, на отдельные модули) и расширяема (благодаря расширяемой иерархии классов). Однако, у нас нет никаких планов на документирование Unreal VM в объеме, необходимом для ее реализации других на платформах сторонними разработчиками.

Компилятор UnrealScript является трехпроходным. В отличие от C++, компиляция сценариев UnrealScript осуществляется в три разных прохода. В первый проход обрабатываются переменные, структуры, перечисления, константы, состояния, обявления функций и строится каркас каждого класса. На втором проходе код сценариев компилируется в байт-код. Два прохода позволяют собрать и связать сложные иерархии сценариев с круговыми зависимостями без отдельной фазы линковки. Третий проход анализирует и импортирует свойства по умолчанию для классов с использованием значений, указанных в блоке defaultproperties .uc файла.

Устойчивое состояние актора. Важно отметить, что в играх на двежке Unreal пользователь может сохранять игру в любое время, а состояния всех акторов, включая состояния выполнения их сценариев, могут быть сохранены только в то время, когда все акторы находятся на низком уровне стека UnrealScript. В целях устойчивости введено ограничение: латентные функции могут вызываться только из кода состояний. Код состояния выполняется на низком уровне стека, и, таким образом, состояние может быть легко сериализовано. Код функций, особенно, код встроенных функций на C++, может находиться на любом уровне стека, и их состояние не всегда возможно сохранить на диске, а затем восстановить.

Файлы Unreal имеют встроенный (native) двоичный формат. Файлы Unreal содержат индексы, сериализованные в дамп объектов, в частности, в пакеты Unreal. Файлы Unreal аналогичны DLL, они могут включать в себя ссылки на другие объекты, хранящиеся в других файлах Unreal. Такой подход сокращает времян загрузки (каждый пакет не загружается более одного раза), позволяет распространять контент Unreal в подготовленных "пакетах" через Интернет.

Почему UnrealScript не поддерживает статические переменные. C++ поддерживает статические переменные потому, что берет свои корни от низкоуровневого языка. Язык Java поддерживает статические переменные по причинам, которые кажутся не очень хорошо продумаными. Но таким переменным нет места в языке UnrealScript из-за неопределенностей, связанных с сериализацией, выводом и множеством уровней: статические переменные должны иметь "глобальные" семантики, а это означает, что все статические переменные во всех активных уровнях Unreal имеют одинаковое значение. Должны ли они быть в пакете? Должны ли они быть на уровне? Если да, то как они должны быть сериализованы: с классом в его .u файл, или с уровнем в .unr файл? Являются ли они уникальными для базового класса или модифицированные варианты классов имеют свои собственные значения статических переменных? В UnrealScript мы обошли эти проблемы, отказавшись от статических переменных, как от особенности языка, и позволили программистам управлять глобальными переменными путем создания классов. Если вам нужны переменные, которые доступны для всех объектов уровня, то вы можете создать новый класс для хранения этих переменных и, при этом, все переменные будут сериализованы вместе с уровнем. Таким образом исключается неопределенность. Примерами таких классов являются LevelInfo и GameInfo.

Стратегия программирования на UnrealScript
Здесь мы рассмотрим несколько моментов, касающихся эффективной разработки кода на UnrealScript, использования сильных сторон UnrealScript, и способов предотвращения возможных ошибок.

Код на UnrealScript медленее, чем код на C или C++. Обычная программа на C++ работает примерно в 20 раз быстрее, чем сценарий на UnrealScript. Философия программирования при написании наших собственных сценариев заключается в следующем: создавать сценарии, которые почти всегда простаивают. Другими словами, используйте UnrealScript только для обработки "интересных" событий, реакции на которые вы хотите настроить, а не для выполнения механических задач, таких как расчет движения, которые решаются физической подсистемой движка Unreal гораздо эффективнее.

Например, при написании сценария снаряда обычно пишутся функции HitWall(), Bounce() и Touch (), описывающие действия, выполняемые при наступлении основных событий. Таким образом, 95% времени сценарий для вашего снаряда не выполняется, а только ждет уведомления от физической подсистемы. Такой подход по своей сути очень эффективен. Хотя UnrealScript гораздо медленнее, чем C++, код на UnrealScript в среднем занимает 5-10% процессорного времени.

Используйте как можно больше латентных функций (таких, как FinishAnim и Sleep). Основной поток ваших сценариев состоит из них, вы создаете код для управления анимацией или код, выполнение которого основано на течении времени. Подобный код является достаточно эффективным в UnrealScript.

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

Будьте осторожны с кодом, который может привести к бесконечной рекурсии. Например, команда "Move" перемещает актор и вызывает функцию Bump() при столкновении. Поэтому, если вы используете команду Move в функции Bump, вы рискуете получить бесконечную рекурсию. Будьте осторожны. Бесконечные рекурсии и бесконечные циклы - это две ошибки, которые UnrealScript не обрабатывает корректно.

Создание и уничтожение акторов на стороне сервера - это довольно дорогостоящие операции, и крайне дорогостоящие операции в сетевой игре, потому что создание и уничтожение расходует пропускную способность сети. Используйте их разумно и рассматривайте актор как "тяжеловесный" объект. К примеру, не пытайтесь создавать системы частиц путем создания 100 уникальных акторов и их отправления по различным траекториям с использованием физической подсистемы. Это будет ОЧЕНЬ медленно.

Используйте объектно-ориентированные возможности UnrealScript как можно более полно. Создание новой функциональности путем переопределения существующих функций и состояний приводит к чистому коду, который легко модифицировать и легко интегрировать для разработки с участием других людей. Избегайте использования традиционных методов C, при обработке состояний объектов и акторов не используйте конструкции switch(), потому что такой код бесконтрольно разрастается по мере добавления новых классов.

.u пакеты UnrealScript собираются строго в порядке, установленном в списке EditPackages .ini файла, при этом каждый пакет может ссылаться только на объекты, входящие в состав текущего пакета, в состав ранее скомпилированных пакетов, но никогда не может ссылаться на пакеты, собираемые после текущего. Если вы обнаружите возникновение циклических ссылок между пакетами, то есть два решения:

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

Заметим, что если класс C должен ссылаться на класс или объект O в пакете, компилируемом позднее, то вы можете разбить класс на две части: абстрактный базовый класс определить как С (но не включать значения по умолчанию для его переменных в свойства по умолчанию), а производный класс D поместить во второй пакет, для которого указываются правильные значения по умолчанию.

Если два .u пакета неразрывно связаны между собой ссылками, то объедините их в один пакет. Это будет более разумно, потому что пакеты предназначены только для разделения кода на модули и вы не получите реальной пользы (например, экономии памяти) от разделения классов на несколько пакетов.


Взято с GameDev.ru
Автор: Vincent Barabus

Аватара пользователя
Ryslan
Пилигрим
Сообщения: 16
Рег. Вс май 02, 2010 3:58 pm
Репутация: 1

Сообщение Ryslan » Пн май 03, 2010 9:06 pm

Если найду еще материалы брошу сюда)

Аватара пользователя
Planer

Маэстро
Странник
Сообщения: 156
Рег. Сб май 08, 2010 12:37 pm
Награды: 1
Репутация: 23
Вклад в проект: 11
Откуда: Москва

Сообщение Planer » Сб май 08, 2010 12:58 pm

Только не забывай автора указывать со ссылью на первоисточник, а то плагиатором станешь)

Аватара пользователя
Buxyr

Коммандо Активист Друг форума Кот Леопольд
Рейнджер
Сообщения: 316
Рег. Вс май 02, 2010 10:07 am
Награды: 4
Репутация: 86
Вклад в проект: 27
Откуда: Владимир

Сообщение Buxyr » Сб мар 26, 2011 7:22 am

Спасибо отличный материал
Искусство это жизнь

Аватара пользователя
Evgenii
Пилигрим
Сообщения: 30
Рег. Пт фев 05, 2010 11:19 am
Репутация: 0

Сообщение Evgenii » Пт май 06, 2011 10:11 am

Примеров готовых скриптов бы не мешало побольше


Вернуться в «Конструктор UDK»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей

 

 

cron