Jump to content
Sign in to follow this  
zapat

loadable permanent GUI?

Recommended Posts

I have read a lot about GUIs, but there is one(two) questions I cannot cope with.

What is a good programflow-design decision for loadable permanent GUIs which can communicate with variables of the MissionNameSpace?

I would like a permanent GUI on my screen. This GUI is a feedback for a variable (eg. player's exhaustion).

I use RscTitles for this with onLoad = "uiNamespace setVariable ['myGui', _this select 0];";

I then start an endless loop, which updates the controls of the GUI every 1 second and eg. ctrlSetPosition / ctrlSetTextColor, like this:

_display = uiNamespace getVariable "myGui";

_myCtrl = _display displayCtrl 1200;

if (player getVariable "exhaustionLevel" > 100) then {_myCtrl ctrlSetTextColor RED;};

if (MissionVar > 1000) then {_myCtrl ctrlShow false;};

_myCtrl ctrlCommit 0.1;

-> This needs missionNameSpace variables (and specific object vars eg. player getVariable "exhaustionLevel" ) What is a good design decision for this? Is using missionNamespace vars mixed with UInamespace a good idea in a disabledserializaton sqf?

-> Since it is an UI file, I need to use disableserialization. This means my loop stops whenever I load the mission.

What is a good design decision for restarting the loop so I have a permanent GUI?

So how to do all this?

Edited by zapat

Share this post


Link to post
Share on other sites

I didnt work much with GUIs yet to be honest but I would write a lightweight GUI handler FSM that listens to the variables that have to be updated and/or is suspended for a fixed amount of time. But as I said, no experience with GUIs so this is propably the worst way to do it :D.

Share this post


Link to post
Share on other sites

Well what I did was saving the references of the child controls of my "display" into an global array so I could use them without having to switch between namespaces - I was forced to use disableSerialization nevertheless.

But I'd be interested how others do this :D

Share this post


Link to post
Share on other sites

I have SOME advance. But it resulted in more questions.

If I use only uinamespace variables (with uinamespace do{}) then no disableserialization is needed. So best practice seems to have the UI vars in the UI namespace, and don't use missionNamespace variables. Therefore I now save the vars -which are needed for communication- to the UI namespace too. It seems that these are saved and reloaded as well, so this separation does work good.

Still don't know how to handle loading with cutRsc though. Constantly calling cutRsc seem to make more and more layers upon themselves. On the other hand Cutrsc is stopped by loading a game, (at least my variables stay now. :) )

There is an idea at the bottom of the disableserializaiton page. Do I really need to use this workaround to find if game was loaded, and cutRsc again?

Share this post


Link to post
Share on other sites

After some hours of experimenting, I came up with the following solution - if somebody is interested. I guess I got the behaviour of the UIs after all. :) It is waaaaay lot more easier than I thought. All the shady disableserialization and all that detect load stuff is unnecessary. There are no complications at all. :)

By using this setup you can make a permanent and loadable GUI, which communicates well with your mission variables. No diableserialization is needed. This is advanced topic and does not include full rscTitles file. It is of course easily expandable with other variables.

rscTitles.hpp - without base classes

class RscTitles
{
   class MYUI_screen
   {
       idd = -1;
       duration = 1e+1000;   //important to achieve unlimited display
       fadeIn = 0;
       fadeOut = 0;
       name = "";
       onLoad = "uiNamespace setVariable ['MYUI_display', _this select 0];";      //important for later ID 

       class Controls
       {
           class MYUI_text: RscText    //parent class is not included
           {
               idc = 1000;    //important for later ID
               text = "0"; 
               x = 0.92 * safezoneW + safezoneX;
               y = 0.211 * safezoneH + safezoneY;
               w = 0.062 * safezoneW;
               h = 0.017 * safezoneH;
           };
       }
   }
}

the display loop sqf. ExecVM it in init phase. After that the UI will be shown until the mission is finished.

while {true} do
{
   sleep 1;

   //save your data variables in uiNamespace, so you won't need disableserialization and other shady stuff
   uiNamespace setVariable ["MYUI_data", damage player];

   //check if UI is alive. Reinit if not (eg. after loading) 
  if (isNull (uiNamespace getvariable ["MYUI_display",displayNull])) then 
   {
       cutRsc ["MYUI_screen", "PLAIN"];  //show Resource

       //you can save the controls for easy access later to uinamespace vars
       with uiNamespace do
       {
           MYUI_myctrl = MYUI_display displayCtrl 1000;
       };
   };

   //update controls. ONLY uinamespace from this point
   with uiNamespace do
   {
       MYUI_myctrl ctrlSetText str MYUI_data;
       MYUI_myctrl ctrlCommit 0.1; 
   };
};

Edited by zapat

Share this post


Link to post
Share on other sites

Very similar in approach zapat, is one I use from Deadfast:

Deadfast cutrsc

Although it does use disableSerialization... His example for thirst and color coding is similar to what you're after.

I will give your method a shot.

Here's some more info for you, if you have not seen it:

New to ArmA 2: (ui)Namespace

Share this post


Link to post
Share on other sites

Thanks panther!

I've just checked Deadfast's solution. That is not made for SP as his while cycle would stop on a mission load. Which is not an issue in MP, but you load a lot in SP.

This is why I wanted to avoid disableserialization. I just don't know what it really does... (I mean DF's while loop is inside a disabled sqf, so it behaves like an UI if you know what I mean (alive as long as current session is alive). What I did was that I used a normal sqf for looping and display checking (and re cutRsc if needed) and used the UI vars only where the UI was actually updated. )

So it comes to the same in the end again: it depends what do I want to do, right? :)

Edited by zapat

Share this post


Link to post
Share on other sites

from what I understand, and part of Deadfast's notes, disableSerialization allows you to store UI information in normal variables...

_display = uiNamespace getVariable "TAG_RscWhatever_display"; (information "stored" in _display)

I'm not sure I understand fully your issue with SP and mission loading. (RE: That is not made for SP)

Found another good explanation here. See responses from Spooner:

OFPEC thread - disableSerialization

Looks like your "way" would be the preferred method according to Spooner...

Edited by panther42
Added additional link

Share this post


Link to post
Share on other sites

UI information is not saved with a game save. Thus it is not loaded. It is what Disableserialization does:it stops the vars of that sqf from saving in a savegame. (It disables serialization, which means the saving of variables to a file). Although I am not sure how exactly. What is disabled exactly? Vars after the command? Vars of the whole sqf? Called or spawned? UIvars, missionNamespace globals, local vars? How does it affect inherited (called) vars? Etc. There is no info about it anywhere.

There is a While (!isNull _display) in DF's script. _display will be null when game is exited. Since this variable is not saved, _display will be null when you load a game. This means the cycle is finished by a game load.

In MP you don't save and load games. You start a new one. In SP you do.

Anyways, my code is safe. I always know what is happening with wich variable. They are separated. if something is not saved, it is reinitialized. Mission namespace vars are left alone totally, so no question there.

Edited by zapat

Share this post


Link to post
Share on other sites

I changed a line in my code. This is important, and it is in connection with what I said earlier. It is hard to follow how uivariables are saved/not saved.

So if anyone is using my code, change the line:

if (isNull (uiNamespace getvariable ["MYUI_display",displayNull])) then

(this both detects if isNull and if isNil)

Without this your display won't show, if you restart the arma.exe application. This means the UIvariables stay until the arma application is running. No matter what mission you run.

The display variable will be NULL (alive, without a value) once you restart the mission, but stays NULL(alive, without a value) and not NIL (not alive) until the application is restarted! It gets NIL if the application is restarted.

1. You start your arma application and load a saved mission. UIvariables are NON EXISTENT. Your MYUI_display is NON EXISTENT - (you cannot even check for isNull).

2. You detect it in your code (this has to be an endless loop as you need to detect it WHEN the application starts which can be anytime when a user saves your mission and exits the app.) , and reinit your UIvariables. Your MYUI_display is now alive and gets a value.

3. You die and reload a previous savegame. Your UIvariables EXIST and remember their previous state! Your display becomes NULL though!

So you need to detect if your display is null (mission reloaded without application restart), and reinit your UI.

This is very important info for a GUI. Probably it is known by many, but still it is needed for a permanent loadable GUI, so I handle this as important info. :) I don't dare to go into what is happening with disableserialized local/missionnamespace vars. :)

Edited by zapat

Share this post


Link to post
Share on other sites

No, you can't. as it would only detect application restart and not loaded game. it only detects nil (not alive) and not null (alive, but without value).

Share this post


Link to post
Share on other sites

Eh? What alive? if MYUI_display variable is undefined it will be nil. What you do is you assign controlNull IF it is nil and then test if it is null, where as in my example you test if it is nil straight away.

Share this post


Link to post
Share on other sites

Yes, you are right. But you only test if it is nil. It will only be nil if the application is restarted. UIvars stays not nil but null if the mission is exited. So you need to check for both nil and null.

My line does that, yours only checks for nil.

What I am talking about is the following scenario. You START a SP game. All the init takes place. No problems here. MYUI_display gets a value, so it is not nil, nor null.

BUT!

The user can save and load a previous save any time. This is not a mission restart, so init does not take place. missionnamespace vars get loaded. Still MYUI_display becomes null.

The user can exit the arma application anytime after saving. Then after a day start the application and load a previous savegame. Now, MYUI_display will be nil, and not null.

This is my experience. I am not arguing if your line check for nil or null. You are of course right. You are testing for nil. What I am saying it is not enough.

Share this post


Link to post
Share on other sites

with uiNamespace do {

if (isNil "MYUI_display" || {isNull MYUI_display}) then ....

};

Share this post


Link to post
Share on other sites

Okay, you win, man. :) That is definitely a lot better....

Edited by zapat

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×