VGUI Screen Creation: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
mNo edit summary
Line 135: Line 135:


== Getting Input to work ==
== Getting Input to work ==
Here is the proper way of fixing vgui input:


Most of the information in this section came from Helk over at [http://www.hostile-planet.com HostilePlanet]
In '''in_buttons.h''', where the other flags are defined add:
    #define IN_VALIDVGUIINPUT     (1 << 23) //bitflag for vgui fix


First I will explain how the code is set up.
next, in '''in_main.cpp''' inside the method - CInput::CreateMove(...)
add:
    cmd->buttons |= IN_VALIDVGUIINPUT;
right above:
    g_pClientMode->CreateMove( input_sample_frametime, cmd );


1) Input is gathered and sent to in_main.cpp


2) CInput::CreateMove is called to handle the input.
next, in '''c_baseplayer.cpp''' inside the method - C_BasePlayer::CreateMove( ... )
add:
    if(pCmd->buttons & IN_VALIDVGUIINPUT)
right above:
    DetermineVguiInputMode( pCmd );
''(So it only calls DetermineVguiInputMode if the buttons include our flag)''


3) After some other classes look at the input, it goes to C_BasePlayer::CreateMove
and finally, inside the method - C_BasePlayer::DetermineVguiInputMode(...)
change '''''both''''' instances of:
    pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2);
to read:
    pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT);


4) After all other input handling in C_BasePlayer, it is sent to C_BasePlayer::DetermineVguiInputMode.


5) DetermineVguiInputMode then checks for a vgui_screen, and sends the input to SetVGuiScreenButtonState which is defined in c_vguiscreen.cpp
The move is created about 4 or 5 times from various points in code. What we've done, is added a flag to mark the buttons as valid for handling the vgui input in the only place where the buttons haven't manipulated and are still valid. This is a far better method of fixing the vgui input rather than using some counter.


6) SetVGuiScreenButtonState then sends to input to C_VGuiScreen::SetButtonState
''Note: No offense TJMonk but if you're going to use my code/suggestions and "give me credit" atleast do it properly so I don't look like I've done a poor job. I hope no mods employed the previous "fix" posted here.''


7) C_VGuiScreen::SetButtonState determines which buttons have been released and which have been pressed and saves them.
I hope this helps some people.


8) C_VGuiScreen::ClientThink is called every x ticks, like most Think Functions, and acts on the input.
[http://www.jakoavain.net/helk/granted.jpg Screenshot of working panels in the mod Hostile Planet]


The problem with the input is that it is generated several times per '''tick''' and the input gets flooded to C_VguiScreen. This wouldn't be a problem except only 1 out of every 4 or so input messages contains the actual input. Because the C_VguiScreen is flooded with bad data( the bad data that is sent is a zero ), the buttonstate that gets saved is garbage and when it is used in C_VguiScreen:ClientThink, it does nothing.
-Helkus@gmail.com
 
I have found one very easy way to solve this. All my code changes are in c_vguiscreen.cpp and mostly involve SetVGuiScreenButtonState. Here is a general overview:
 
1) Start a counter at zero.
 
2) Increment the counter.
 
3) Look at the input.
 
4) If the input is non-zero(ie. we have valid data) or if the counter has hit the max(I used 5), reset the counter and send the input to C_VguiScreen::SetButtonState.
 
My final code looks like:
 
#define MAX_VGUI_SCREEN_INPUT_TICK 5
static int tickCount=0;
void SetVGuiScreenButtonState( C_BaseEntity *pVguiScreenEnt, int nButtonState )
{
    tickCount++;
    if( nButtonState !=0 || tickCount == MAX_VGUI_SCREEN_INPUT_TICK )
    {
        DevMsg( "Button state = %d tick = %d\n", nButtonState, tickCount );
        if ( pVguiScreenEnt )
        {
            Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) );
            C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt);
            pVguiScreen->SetButtonState( nButtonState );
        }
        tickCount = 0;
    }
}


== Further Possibilities ==
== Further Possibilities ==

Revision as of 23:23, 22 July 2005


Getting it to load

( Originally from jim_the_coder from TheWavelength )

1) Create a .res file in steamdir\SourceMods\yourmod\scripts\screens ex. vgui_test_screen.res:

"screen_basic.res"
{
   "Background"
   {
       "ControlName" "MaterialImage"
       "fieldName"  "Background"
       "xpos"   "0"
       "ypos"   "0"
       "zpos"   "-2"
       "wide"   "480"
       "tall"   "240"
       "material"  "vgui/screens/vgui_overlay"
   }
   "OwnerReadout"
   {
       "ControlName" "Label"
       "fieldName"  "OwnerReadout"
       "xpos"   "10"
       "ypos"   "20"
       "wide"   "240"
       "tall"   "34"
       "autoResize" "0"
       "pinCorner"  "0"
       "visible"  "1"
       "enabled"  "1"
       "tabPosition" "0"
       "labelText"  "No Owner"
       "textAlignment" "center"
       "dulltext"  "0"
       "paintBackground" "0"
   }
   "HealthReadout"
   {
       "ControlName" "Label"
       "fieldName"  "HealthReadout"
       "xpos"   "240"
       "ypos"   "20"
       "wide"   "240"
       "tall"   "34"
       "autoResize" "0"
       "pinCorner"  "0"
       "visible"  "1"
       "enabled"  "1"
       "tabPosition" "0"
       "labelText"  "Health: 100%"
       "textAlignment" "center"
       "dulltext"  "0"
       "paintBackground" "0"
   }
   "DismantleButton"
   {
       "ControlName" "MaterialButton"
       "fieldName"  "Dismantle"
       "xpos"   "78"
       "ypos"   "160"
       "wide"   "324"
       "tall"   "48"
       "autoResize" "0"
       "pinCorner"  "0"
       "visible"  "1"
       "enabled"  "1"
       "tabPosition" "2"
       "labelText"  "Dismantle"
       "textAlignment" "center"
       "dulltext"  "0"
       "brighttext" "0"
       "Default"  "0"
       "command"  "quit"
       "paintborder" "0"
       "enabledImage"
       {
           "material" "vgui/screens/vgui_button_enabled"
           "color" "255 255 255 255"
       }
       "mouseOverImage"
       {
           "material" "vgui/screens/vgui_button_hover"
           "color" "255 255 255 255"
       }
       "pressedImage"
       {
           "material" "vgui/screens/vgui_button_pushed"
           "color" "255 255 255 255"
       }
       "disabledImage"
       {
           "material" "vgui/screens/vgui_button_disabled"
           "color" "255 255 255 255"
       }
   }
}

2) The materials used in the .res file can be found in source material.gcf using Nem's GCFScape. Extract the files in hl2\materials\vgui\screens to steamdir\SourceMods\moddir\materials\vgui\screens.

3) In steamdir\SourceMods\moddir\scripts create or open the text file vgui_screens.txt and add a new entry for the screen. ex.

"VGUI_Screens"
{
   "vgui_test_screen"
   {
       // This is our example screen
       "type"		"vgui_screen_panel"
       "pixelswide"	480
       "pixelshigh"	240
       // This must be the file you created in step 1
       "resfile"	"scripts/screens/vgui_test_screen.res"
   } 
   "teleport_countdown_screen"
   {
       "type"		"teleport_countdown_screen"
       "pixelswide"	480
       "pixelshigh"	240
       "resfile"	"scripts/screens/teleport_countdown_screen.res"
   }
}

4) Open Hammer create a small square map with a spawn, a light, and a vgui_screen. Change the vgui_screen's properties like so: Panel Name = vgui_test_screen Panel Width in World = 64 Panel Height in World = 32

Height and Width can be changed to whatever you need. Those values define the actual size of the screen in the world.

5) Save your files, build your map, launch your mod, and whala! you have a vgui_screen in game.

Getting Input to work

Here is the proper way of fixing vgui input:

In in_buttons.h, where the other flags are defined add:

   #define IN_VALIDVGUIINPUT		    (1 << 23) //bitflag for vgui fix

next, in in_main.cpp inside the method - CInput::CreateMove(...) add:

   cmd->buttons |= IN_VALIDVGUIINPUT;

right above:

   g_pClientMode->CreateMove( input_sample_frametime, cmd );


next, in c_baseplayer.cpp inside the method - C_BasePlayer::CreateMove( ... ) add:

   if(pCmd->buttons & IN_VALIDVGUIINPUT)

right above:

   DetermineVguiInputMode( pCmd );

(So it only calls DetermineVguiInputMode if the buttons include our flag)

and finally, inside the method - C_BasePlayer::DetermineVguiInputMode(...) change both instances of:

   pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2);

to read:

   pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT);


The move is created about 4 or 5 times from various points in code. What we've done, is added a flag to mark the buttons as valid for handling the vgui input in the only place where the buttons haven't manipulated and are still valid. This is a far better method of fixing the vgui input rather than using some counter.

Note: No offense TJMonk but if you're going to use my code/suggestions and "give me credit" atleast do it properly so I don't look like I've done a poor job. I hope no mods employed the previous "fix" posted here.

I hope this helps some people.

Screenshot of working panels in the mod Hostile Planet

-Helkus@gmail.com

Further Possibilities

1) Make it so that a player has to hold down a button to use the screen.

2) Make it so that when a player is viewing the screen, all input is sent to the screen, including movement keys. The player will not be able to move again, until they look outside the bounds of the screen.

3) Change the MAX_VGUI_SCREEN_INPUT_TICK to a convar like cl_screenmaxtick to allow clients control over how often the input should be updated. This could help laggy systems use the screen, and fast systems quicker response times.

Revision History

Created: 7/13/05 by TJMonk15