Спецификаторы параметров функций

Выражения

Присваивание

Чтобы присвоить значение переменной, используйте оператор "=" следующим образом:

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 );

}

Функции

Объявление функций

В языке 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), поскольку оно обеспечивает простое средство для "общения" акторов между собой.

Переопределение функций

"Переопределение функции" - это изменение реализации функции базового класса для производного класса. Например, вы пишете сценарий для нового вида монстра называемого 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

Эти спецификаторы имеют тот же смысл, что и соответствующие ключевые слова для переменных.

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, вы будете использовать одну из двух функций в зависимости от того, наследует ли класс объекта класс 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 }

Функции для отладки

Следующие функции могут помочь вам в отладке кода.

  • 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 Спецификаторы параметров функций - student2.ru .

Профилирование сценариев

The Профайлер геймплея Спецификаторы параметров функций - student2.ru может помочь поиске сценариев, наиболее критичных к времени выполнения.

Отладчик сценариев

Подробнее об инструментах отладки Unreal читайте на странице Debugging Tools Спецификаторы параметров функций - student2.ru .


Продолжение...

Данный документ является переводом оригинального документа UnrealScript Language Reference.

UnrealScript. Справочное руководство. Частьвторая http://www.gamedev.ru/community/udk/articles/UnrealScriptReference2Автор: VincentBarabus Данный документ является переводом оригинального документа UnrealScript Language Reference Расширенные функции языка Таймеры Таймеры используются в качестве механизма для планирования выполнения, повторения или завершения событий. Таким образом, актор может установить таймер, зарегистрировав себя в игровом движке и установив необходимый порядок вызова функции Timer(). Таймеры UnrealScript реализованы как простой массив структур внутри каждого актора (актор может иметь несколько таймеров в стадии согласования). Структура включает в себя количество времени, оставшегося до истечения таймера, функцию для вызова по истечении определенного срока и т.д. Обычно игровой цикл обновляет состояние каждого актора Спецификаторы параметров функций - student2.ru один раз за кадр, а часть функции 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" ); //... } Когда вы запустите эту пр

Наши рекомендации