Система Flash UI
Материал из CryWiki Russia
DCamer (Обсуждение | вклад) |
Alex626 (Обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
+ | [[Категория:CryEngine 3 Free SDK:Игра]] | ||
Система '''UI Action''' позволяет настраивать и контролировать любые Flash-элементы UI в потоковом графе. | Система '''UI Action''' позволяет настраивать и контролировать любые Flash-элементы UI в потоковом графе. | ||
Версия 12:30, 9 марта 2012
Система UI Action позволяет настраивать и контролировать любые Flash-элементы UI в потоковом графе.
Для этого каждый Flash-ассет может быть определена в XML-файле со всеми событиями, функциями и переменными, которые должны быть доступны в потоковом графе.
Содержание |
Настройка XML для Flash-ассета
Система Flash UI считывает все XML-файлы, которые находятся в Game/Libs/UI/UIElements и создаёт потоковые ноды для контролирования Flash-ассетов.
Откройте существующий XML-файл или создайте новый, чтобы определить новый UIElement.
Основное определение Flash-ассета:
<UIElements name="HudElements"> <!-- название группы для этого элемента --> <!-- определение элемента UI под названием "HUD" --> <UIElement name="HUD"> <!-- GFX/SWF-файл для этого элемента UI --> <GFx file="HUD.gfx" layer="1"> <!-- режим выравнивание этого элемента --> <Constraints> <Align mode="dynamic" halign="center" valign="center" scale="1" max="1" /> </Constraints> </GFx> <!-- доступные функции--> <functions> </functions> <!-- доступные события, которые вызываются Flash-ассетом --> <events> </events> <!-- доступные переменные--> <variables> </variables> <!-- доступные массивы --> <arrays> </arrays> <!-- доступные мувиклипы --> <movieclips> </movieclips> </UIElement> <!-- определение другого элемента UI под названием "LoadingScreen" --> <UIElement name="LoadingScreen"> ... </UIElement> </UIElements>
Функции
Функции, которые должны будут вызывать извне, нужна определить в списке <functions> элемента.
Например, функция ActionScript, для установки полосы здоровья:
function setHealth(iHealth:Number):void { // установка полосы здоровья }
Может быть определена в XML так:
<function name="SetHealth" funcname="setHealth"> <param name="Health" desc="Players current health"/> </function>
Где name="…" — название функции, отображаемое в потоковом графе, а funcname="…" — название функции в самом ActionScript.
Система автоматически создаст нод, чтобы вызывать эту функцию.
Примечание:
Кроме того, можно определить функции, которые находятся не в корневом пространстве Flash-файла, например:
<function name="SetHealth" funcname="myHealthMc.mysubmc.setHealth"> <param name="Health" desc="Players current health"/> </function>
События
Чтобы получить уведомления о каком-либо взаимодействии с пользователем, например, если была нажата кнопка, можно определить события в списке <events>. Чтобы вызвать срабатывание события в коде ActionScript, необходимо вызвать fscommand(«строкаКоманда»). Эти fscommands перехватываются движком.
Например, в функции onPress у кнопки, вы можете вызвать fscommand со строкой «onMyButtonPressed» и несколькими аргументами.
myButton.onPress = function() { var args:Array = new Array(); args.push(argument1); args.push(argument2); fscommand("onMyButtonPressed", args); }
Чтобы перехватить это событие, добавьте тег <event> в список <events>:
<event name="OnButton1" fscommand="onMyButtonPressed"> <param name="Arg1" desc="Some argument"/> <param name="Arg2" desc="Another argument"/> </event>
Система создаст нод для перехвата этого события.
Переменные, массивы и мувиклипы
Доступ к массиву или переменной также могут быть определены в XML-файле.
Просто добавьте тег <variable> в список <variables>, тег <array> в список <arrays> или тег <movieclip> в список <movieclips>.
<variables> <variable name="SomeVariable" varname="someVariable"/> <variable name="TextField" varname="_root.myTextfield.text"/> </variables> <arrays> <array name="SomeArray" varname="_root.mc2.someArray"/> </arrays> <movieclips> <movieclip name="MovieClip1" instancename="_root.Mc1"/> <movieclip name="MovieClip2" instancename="_root.Mc1.subMc"/> </movieclips>
Чтобы получить или задать переменные, выберите в выпадающем списке потоковый нод UI:Variable:Var или UI:Variable:Array.
Примечание:
Массивы представляют собой строки разделённые запятой.
Вы также можете получить доступ к определённым мувиклипам через потоковые ноды.
Отображение/скрытие и настройка GFX-файлов
Чтобы отобразить или скрыть Flash-ассет используйте нод UI:Display:Display. Вы можете выбрать элемент в выпадающем списке.
Чтобы настроить поведение и ограничения используйте ноды UI:Display:Config и UI:Display:Constraints соответственно.
Также можно инициализировать все эти настройки в XML-файле.
<UIElement name="HUD" mouseevents="1" keyevents="1" cursor="1" console_mouse="1" console_cursor="1"> <GFx file="HUD.gfx" layer="1" alpha="0.5"> <!-- режим выравнивание этого элемента --> <Constraints> <Align mode="fullscreen" /> <!-- <Align mode="dynamic" halign="center" valign="center" scale="1" max="1" /> --> <!-- <Align mode="fixed" /> --> <!-- <Position top="20" left="20" width="200" height="200" /> --> </Constraints> </GFx> … </UIElement>
Конфигурация
• mouseevents• cursor
• keyevents
• console_mouse
• console_cursor
• layer
• alpha
Ограничения
Доступно три режима размещения ассетов на экране:
• "fixed"• "dynamic"
Если scale равно 1, элемент будет максимально масштабирован, не деформируя пропорции. Если scale равно 0, Flash-ассет не будет масштабироваться.
Если max равно 1, то элемент будет максимизирован, так, что 100% экрана будут закрыты (это может привести к тому, что некоторые части элемента выйдут за экран) В противном случае асстер будет подогнан под экран, возможны некоторые незакрытые пространства слева/справа или сверху/снизу.
• "fullscreen"Экземпляры элементов
У каждого нода есть порт InstanceID. Благодаря этому идентификатору экземпляра, у вас может быть более одного экземпляра любого Flash-ассета. Порт InstanceID любого нода UI определяет, на какой экземпляр будет влиять этот нод. Если вы используете нод с новым идентификатором экземпляра, автоматически будет создан новый экземпляр этого Flash-ассета. Если вы использует -1 в качестве идентификатора экземпляра, нод будет влиять на все экземпляры этого Flash-элемента.
Полезные функции
Доступно несколько специальных функции ActionScript, которые автоматически вызываются системой UI.
Эти функции могут быть определены в ActionScript в корневом пространстве и не нужно определять в XML-файле.
Функции
Функции ActionScript
- cry_onSetup
- Если эта функция существует, она вызывается однократно когда GFX/SWF-файл загружен движком.
function cry_onSetup(_bIsConsole) // истинно если запущено на консоле, ложь если на ПК
- cry_onShow
- Если эта функция существует, она вызывается однократно когда GFX/SWF-файл показан.
function cry_onShow()
- cry_onHide
- Если эта функция существует, она вызывается однократно когда GFX/SWF-файл скрыт.
function cry_onHide()
- cry_onResize
- Если эта функция существует, она вызывается однократно когда разрешение движка изменено.
function cry_onResize(_intWidth, _intHeight)
- cry_onBack
- Если эта функция существует, она вызывается однократно, если игрок нажал кнопку Back на контроллере.
function cry_onBack()
- cry_requestHide
- Эта функция вызывается, если сработал порт RequestHide у нода UI:Display:Display (или через код/Lua). Может быть использовано для плавного исчезновения элемента.
function cry_requestHide()
FSCommand
Доступно несколько строк FSCommand, которые могут быть вызваны вашим ActionScript.
- cry_hideElement
- Если FSCommand выполнен с этой строкой, он укажет системе UI скрыть элемент.
fsCommand("cry_hideElement");
Пример
Плавное появление/исчезновение элемента UI.
// создаём функцию onEnterFrame для плавного появления корневого элемента (если показан элемент UI) function cry_onShow() { _root._alpha = 0; onEnterFrame = function() { if (_root._alpha < 100) { _root._alpha++; } else { // очищаем функцию onEnterFrame onEnterFrame = function() {}; } } } // создаём функцию onEnterFrame для плавного исчезновения корневого элемента (если запрошено системой UI) function cry_requestHide() { onEnterFrame = function() { if (_root._alpha > 0) { _root._alpha--; } else { // очищаем функцию onEnterFrame и уведомляем систему UI, что этот элемент больше не надо отрисовывать. onEnterFrame = function() {}; fscommand("cry_hideElement"); } } }
Теперь вы можете делать, чтобы элемент плавно появлялся/исчезал при срабатывании про show/requestHide у нода UI:Display:Display.
Потоковые графы UI
Чтобы создать новый потоковый граф для UI, просто откройте Flow Graph и выберите File->New UI Action….
Все действия UI размещены в потоковых графах, а все XML-файлы должны быть сохранены в Game/Libs/UI/UIActions.
Примечание:
Все действия UI сохранены отдельно от уровня. Их нужно сохранять через File->Save в меню потокового графа. Убедитесь, что сохраняете каждый раз после изменений!
Все потоковые ноды для UI в списке компонентов UI.
Пример
Пример простого потокового графа для показа/скрытия экрана загрузки и определения названия уровня и полосы прогресса.
Связанный элемент определён в Game/Libs/UI/UIElements/Menus.xml:
<UIElement name="LoadingScreen"> <GFx file="LoadingScreen.gfx" layer="2" alpha="1" > <Constraints> <Align mode="fullscreen" /> </Constraints> </GFx> <variables> <variable name="Percent" varname="LoadingPanel.Percent.text"/> <variable name="LevelName" varname="LoadingPanel.Level.text"/> </variables> </UIElement>
UI Action
Иногда бывает нужно вызывать сложные UI Action (действия UI) несколько раз без перестраивания всякий раз всего графа.
Для этих целей можно создать UI Action с помощью нодов UI:Action:Start и UI:Action:End. Эти ноды находятся в разделе UI:Action.
Название потокового графа определяет название действия.
Нод UI:Action:Control позволяет начать UI Action и получить уведомления, если действие было начато или остановлено.
Также можно провести несколько аргументов UI Action через нод UI:Action:Control.
В качестве примера, UI Action для отображения USM-видео:
Это действие может быть использовано, например, для воспроизведения видеоролика при входе в Proximity Trigger:
Примечание:
Вы можете управлять любым действием UI из любого потокового графа. Если вы отключите UI Action, будет отключен полностью весь потоковый граф (а не только ноды между нодами начала и конца). Также можно испоользоватьь ноды StartAction и EndAction более одного раза в одном потоковом графе. Каждый нод StartAction сработает, если действие начато, и первый достигший нода EndAction инициирует порт OnEnd у любого нода UI:Action:Control, который «прослушивает» потоковый граф.
Система LUA и Flash UI
Также можно управлять системой UI через LUA.
Example
// Отображаем элемент UI UIAction.ShowElement("MyUIElement", 0); // Вызываем ActionScript UIAction.CallFunction("MyUIElement", 0, "Foo", "paramStr1", "paramStr2", 123, 12.3, false); // задаём переменную UIAction.SetVariable("MyUIElement", 0, "MyVariable", 23); // получаем переменную local var1 = UIAction.GetVariable("MyUIElement", 0, "MyVariable"); if (var1) then Log("Var1: %d", var1); end // set array local newValues = { "val1", "val2", "val3", }; UIAction.SetArray("MyUIElement", 0, "MyArray", newValues); // получаем массив local values = UIAction.GetArray("MyUIElement", 0, "MyArray"); local value; if (values) then for i,value in ipairs(values) do Log("Value: %s", value); end end
Коммуникация между C++ и потоковым графом UI
Вы можете просто создать свои собственные потоковые ноды и использовать их вызова/получения функций/событий UI. Подробная информация в статье Создание нового потокового нода».
Система UI Event
В системе UI есть система событий, которая также используется для коммуникации между C++ и потоковым графом UI.
Система событий предоставляет легкий метод для определения нодов функций и событий и перехват их из C++.
Ноды функций
Ноды функций доступны для всех потоковых графов и позволяют вызывать код C++.
Создайте новый файл .h и назовите его UIFunctions.h:
#ifndef __UIFunctions_H__ #define __UIFunctions_H__ #include <IFlashUI.h> class CUIFunctions : public IUIEventListener { public: CUIFunctions(); ~CUIFunctions(); // IUIEventListener virtual void OnEvent( const SUIEvent& event ); // ~IUIEventListener private: SUIEventHelper<CUIFunctions> m_Dispatcher; IUIEventSystem* m_pGameEvents; void OnFoo( const SUIEvent& event ); void OnBar( const SUIEvent& event ); }; #endif
Создайте новый файл .cpp и назовите его UIFunctions.cpp:
#include "StdAfx.h" #include "UIFunctions.h" CUIFunctions::CUIFunctions() : m_pGameEvents(NULL) { if (gEnv->pFlashUI) { // создаём новую систему событий под названием "Game" // вводим ноды eEST_UI_TO_SYSTEM; для этой системы событий из раздела UI:Functions // регистрируем как листенер событий m_pGameEvents = gEnv->pFlashUI->CreateEventSystem( "Game", IUIEventSystem::eEST_UI_TO_SYSTEM ); m_pGameEvents->RegisterListener( this ); // создаём новое описание функции для "Foo" SUIEventDesc fooDesc( "Foo", "Foo", "Call a function named foo" ); // добавляем описание параметра "Arg1" к описанию функции fooDesc.Params.push_back( SUIParameterDesc( "Arg1", "Arg1", "First Arg" ) ); // регистрируем описание функции в диспачере m_Dispatcher.RegisterEvent( m_pGameEvents, fooDesc, &CUIFunctions::OnFoo ); // создаём другое описание функции для "Bar" SUIEventDesc barDesc( "Bar", "Bar", "Call a function named bar" ); barDesc.Params.push_back( SUIParameterDesc( "Arg1", "Arg1", "First Arg" ) ); barDesc.Params.push_back( SUIParameterDesc( "Arg2", "Arg2", "Second Arg" ) ); m_Dispatcher.RegisterEvent( m_pGameEvents, barDesc, &CUIFunctions::OnBar ); } } CUIFunctions::~CUIFunctions() { if ( m_pGameEvents ) m_pGameEvents->UnregisterListener( this ); } void CUIFunctions::OnEvent( const SUIEvent& event ) { // используем диспачер для диспача события, чтобы откорректировать функцию m_Dispatcher.Dispatch( this, event ); } void CUIFunctions::OnFoo( const SUIEvent& event ) { int arg1 = 0; if ( !event.args.GetArg(0, arg1) ) { CryLogAlways("Foo: argument 1 has wrong type (should be int)"); return; } // делаем что-нибудь } void CUIFunctions::OnBar( const SUIEvent& event ) { bool arg1 = false; float arg2 = 0; if ( !event.args.GetArg(0, arg1) ) { CryLogAlways("Foo: argument 1 has wrong type (should be bool)"); return; } if ( !event.args.GetArg(1, arg2) ) { CryLogAlways("Foo: argument 2 has wrong type (should be float)"); return; } // делаем что-нибудь }
Откройте Game.h, объявляем свой класс и добавляем частный член в CGame:
#ifndef __GAME_H__ #define __GAME_H__ ... class CUIFunctions; ... class CGame : public IGame, public IGameFrameworkListener, public ILevelSystemListener { ... private: ... CUIFunctions * m_pUIFunctions; ... } ...
Откройте Game.cpp инициализируйте m_pUIFunctions с NULL в конструкторе, удалить m_pUIFunctions в деконструкторе и создайте объект CUIFunction в конце функции инициализирования.
#include "StdAfx.h" #include "Game.h" ... #include "UIFunctions.h" CGame::CGame() ... { m_pUIFunctions = NULL; } CGame::~CGame() { ... SAFE_DELETE(m_pUIFunctions); } ... bool CGame::Init(IGameFramework *pFramework) { ... if (m_ pUIFunctions == NULL) { m_ pUIFunctions = new CUIFunctions(); } } ...
Примечание:
Очень важно создать все ваши классы, которые использует система событий UI, прежде, чем вызван CompleteInit.
Этот в результате создаст два новых нода UI:Functions:Game:Foo и UI:Functions:Game:Bar.
Если порт send инициирован, он вызовет код C++ в UIFunctions.cpp.
Ноды событий
Ноды событий очень похожи на ноды функций. Они используются для отправки событий из C++ в любой потоковый граф UI. Например, вы можете создать одноэлементный класс для вызова событий из любой части вашего evenкода.
Создайте H-файл UIGameEvents.h:
#ifndef __UIGameEvents_H__ #define __UIGameEvents_H__ #include <IFlashUI.h> class CUIGameEvents { public: // получаем доступ к экземпляру static CUIGameEvents* GetInstance() { static CUIGameEvents inst; return &inst; } // инициализируем игровые события void Init(); // события enum EUIGameEvents { eUIGE_FooEvent, eUIGE_BarEvent, }; void SendEvent( EUIGameEvents event, const SUIArguments& args ); private: CUIGameEvents() : m_pGameEvents(NULL) {}; ~CUIGameEvents() {}; IUIEventSystem* m_pGameEvents; std::map<EUIGameEvents, uint> m_EventMap; }; #endif
Создайте CPP-файл UIGameEvents.cpp:
#include "StdAfx.h" #include "UIGameEvents.h" void CUIGameEvents::Init() { if (gEnv->pFlashUI) { // создаём новую систему событий под названием "Game" // вводим ноды eEST_UI_TO_SYSTEM; для этой системы событий из раздела UI:Events m_pGameEvents = gEnv->pFlashUI->CreateEventSystem( "Game", IUIEventSystem::eEST_SYSTEM_TO_UI ); // создаём новое описание события для "Foo" SUIEventDesc fooDesc( "Foo", "Foo", "Event named foo" ); // добавляем описание параметра "Arg1" к описанию события fooDesc.Params.push_back( SUIParameterDesc( "Arg1", "Arg1", "First Arg" ) ); // регистрируем событие в системе событий и связываем id с перечислителем события m_EventMap[ eUIGE_FooEvent ] = m_pGameEvents->RegisterEvent( fooDesc ); // создаём другое описание события для "Bar" SUIEventDesc barDesc( "Bar", "Bar", "Event named bar" ); barDesc.Params.push_back( SUIParameterDesc( "Arg1", "Arg1", "First Arg" ) ); barDesc.Params.push_back( SUIParameterDesc( "Arg2", "Arg2", "Second Arg" ) ); m_EventMap[ eUIGE_BarEvent ] = m_pGameEvents->RegisterEvent( barDesc ); } } void CUIGameEvents::SendEvent( EUIGameEvents event, const SUIArguments& args ) { // отправляем событием if (m_pGameEvents) { m_pGameEvents->SendEvent( SUIEvent(m_EventMap[event], args) ); } }
Откройте Game.cpp, включите «UIGameEvents.h» и добавьте в конец функции Init:
bool CGame::Init(IGameFramework *pFramework) { ... CUIGameEvents::GetInstance()->Init(); } ...
Код создаст два нода для потокового графа:
Теперь вы можете вызвать из любой части вашего кода функцию SendEvent для вызова, к примеру, нода UI:Events:Game:Bar:
int arg1 = 12; float arg2 = 1.4f; SUIArguments args; args.AddArgument( arg1 ); args.AddArgument( arg2 ); CUIGameEvents::GetInstance()->SendEvent( CUIGameEvents::eUIGE_BarEvent, args );