Difference between revisions of "VGUI Creating A Custom Screen"

From Valve Developer Community
Jump to: navigation, search
(Quick Briefing on Employed Techniques)
Line 1: Line 1:
== Quick Briefing on Employed Techniques ==
+
== Quick Briefing ==
 
 
The Source Engine SDK is very accessible and allows for dramatic mutations without invading or intruding the primary code. I would highly advice avoiding changes to the source engine's code (and by that I mean, changing source\header files that are already in your solution when you first started creating your mod) unless it is required. In other words, exhaust all other methods before resorting to editing the main code modules of the engine. Thankfully, additional VGUI screens can be made without altering any of the engine's code.
 
 
 
 
 
The reason for this is to make sure we all have a common code-base to build off of & also to keep the engine's design solid.
 
  
 
We will be trying to achieve the act of rendering vgui components in to our world. This is done via VGUI screens. A much more polished example is what you might see below:
 
We will be trying to achieve the act of rendering vgui components in to our world. This is done via VGUI screens. A much more polished example is what you might see below:
 
https://developer.valvesoftware.com/wiki/File:Granted_vgui.jpg
 
https://developer.valvesoftware.com/wiki/File:Granted_vgui.jpg
  
== Setting up the solution ==
+
===TestScreen.h===
  
The first step is to, if you haven't already, create a seperate filter for your mod named "<your mod>" - the name is insignificant, as long as you can identify it and you think others would be able to as well. (To create a filter, right click on your project, and select '''Add->New Filter''') In this filter you will place your mod's enitites, UI components, Weapon code, etc... I also strongly advice placing in that filter a text document named "patchesToHL2Client.txt" to list any changes you have made to the source sdk engine and for what purpose as you may want to take note of them later.
+
In this filter create a header file, "TestScreen.h"
  
===CJTestScreen.h===
+
*Create a new class, called TestScreen, which inherits from CVGuiScreenPanel found in c_vguiscreen.h. As described in the header, this is the base class for all vgui screens.
 
 
In this filter create a header file, "CJTestScreen.h"
 
 
 
*Create a new class, called CJTestScreen (the "CJ" prefix is just a prefix I give all my game source\header files as not to lose them), which inherits from CVGuiScreenPanel found in c_vguiscreen.h. As described in the header, this is the base class for all vgui screens.
 
  
 
*Use DECLARE_CLASS to give the source sdk some recognition to your class if need be later.
 
*Use DECLARE_CLASS to give the source sdk some recognition to your class if need be later.
Line 29: Line 20:
 
*Declare a new unsigned integer, which will keep track of what number out counter is currently reading.
 
*Declare a new unsigned integer, which will keep track of what number out counter is currently reading.
  
CJTestScreen.h
+
TestScreen.h
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
 
#pragma once
 
#pragma once
Line 37: Line 28:
 
#include "vgui_controls/Label.h"
 
#include "vgui_controls/Label.h"
  
class CJTestScreen : public CVGuiScreenPanel
+
class TestScreen : public CVGuiScreenPanel
 
{
 
{
 
private:
 
private:
DECLARE_CLASS(CJTestScreen, CVGuiScreenPanel);
+
DECLARE_CLASS(TestScreen, CVGuiScreenPanel);
  
 
vgui::Label* m_pLblCount;
 
vgui::Label* m_pLblCount;
Line 47: Line 38:
  
 
public:
 
public:
CJTestScreen(vgui::Panel *pParent, const char *pMetaClassName);
+
TestScreen(vgui::Panel *pParent, const char *pMetaClassName);
virtual ~CJTestScreen();
+
virtual ~TestScreen();
  
 
virtual void OnTick();
 
virtual void OnTick();
Line 58: Line 49:
  
  
===CJTestScreen.cpp===
+
===TestScreen.cpp===
  
 
*At the end (or start) of your source file, place a macro DECLARE_VGUI_SCREEN_FACTORY. The first argument is the class which will be acting as a screen factory, and the second argument is the name of your screen factory. Later, you will reference this name in vgui_screens.txt when you are describing your screens.
 
*At the end (or start) of your source file, place a macro DECLARE_VGUI_SCREEN_FACTORY. The first argument is the class which will be acting as a screen factory, and the second argument is the name of your screen factory. Later, you will reference this name in vgui_screens.txt when you are describing your screens.
Line 66: Line 57:
 
*Per every tick, as can be seen in our implementation of OnTick, we call the base class's OnTick routine, and then increment our counter. We also set the text for our label (and format it using a stringstream.)
 
*Per every tick, as can be seen in our implementation of OnTick, we call the base class's OnTick routine, and then increment our counter. We also set the text for our label (and format it using a stringstream.)
  
*In CJTestScreen::Init, we simply setup our frame's Tick signal, and have it tick at intervals of 1 second (1000 ms)
+
*In TestScreen::Init, we simply setup our frame's Tick signal, and have it tick at intervals of 1 second (1000 ms)
  
 
*The general trend within the source sdk engine (or so I have seen) is that the pMetaClassName member in our constructor is simply ignored and passed down to the super class. With that said, you can make several different kinds of forms with your factory depending on the "MetaClassName" argument (we'll get in to what this actually is when we edit vgui_screens.txt).  
 
*The general trend within the source sdk engine (or so I have seen) is that the pMetaClassName member in our constructor is simply ignored and passed down to the super class. With that said, you can make several different kinds of forms with your factory depending on the "MetaClassName" argument (we'll get in to what this actually is when we edit vgui_screens.txt).  
  
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
DECLARE_VGUI_SCREEN_FACTORY( CJTestScreen, "CJTestScreen" );
+
DECLARE_VGUI_SCREEN_FACTORY( TestScreen, "TestScreen" );
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 78: Line 69:
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
 
#include "cbase.h"
 
#include "cbase.h"
#include "CJTestScreen.h"
+
#include "TestScreen.h"
 
#include "vgui/IVGui.h"
 
#include "vgui/IVGui.h"
  
 
#include <sstream>
 
#include <sstream>
  
CJTestScreen::CJTestScreen(vgui::Panel *pParent, const char *pMetaClassName) :  
+
TestScreen::TestScreen(vgui::Panel *pParent, const char *pMetaClassName) :  
 
CVGuiScreenPanel(pParent, pMetaClassName)
 
CVGuiScreenPanel(pParent, pMetaClassName)
 
{
 
{
Line 90: Line 81:
 
}
 
}
  
CJTestScreen::~CJTestScreen()
+
TestScreen::~TestScreen()
 
{
 
{
 
}
 
}
  
bool CJTestScreen::Init(KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData)
+
bool TestScreen::Init(KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData)
 
{
 
{
  
Line 106: Line 97:
 
}
 
}
  
void CJTestScreen::OnTick()
+
void TestScreen::OnTick()
 
{
 
{
  
Line 114: Line 105:
 
return;
 
return;
 
 
std::stringstream ssBuffer;
+
m_pLblCount->SetText(VarArgs("Count: %i", m_uiCounter % 21));
ssBuffer<<"Count: "<<m_uiCounter % 21;
 
 
 
m_pLblCount->SetText(ssBuffer.str().c_str());
 
 
 
 
m_uiCounter++;
 
m_uiCounter++;
 
}
 
}
  
DECLARE_VGUI_SCREEN_FACTORY( CJTestScreen, "CJTestScreen" );
+
DECLARE_VGUI_SCREEN_FACTORY( TestScreen, "TestScreen" );
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 133: Line 120:
 
In here, add the following under "VGUI_Screens";
 
In here, add the following under "VGUI_Screens";
  
*The "CJTestScreen" you see is the name of the factory that will be constructing the screen.  
+
*The "TestScreen" you see is the name of the factory that will be constructing the screen.  
  
*We reference the resource file that describes the appearence of our screen in the "resfile" property. As you can see, we will be storing this resource file in "<your mod>/resource/screens/CJCount.res" once we create it.
+
*We reference the resource file that describes the appearence of our screen in the "resfile" property. As you can see, we will be storing this resource file in "<your mod>/resource/screens/Count.res" once we create it.
  
 
*Also notice the pixelswide and pixelshigh members which describe the dimensions of our screen.
 
*Also notice the pixelswide and pixelshigh members which describe the dimensions of our screen.
Line 142: Line 129:
 
...
 
...
 
...
 
...
"CJTestScreen"
+
"TestScreen"
 
{
 
{
"type" "CJTestScreen"
+
"type" "TestScreen"
  
 
// These describe the dimensions of the screen *in pixels*
 
// These describe the dimensions of the screen *in pixels*
Line 151: Line 138:
  
 
// This is the name of the .res file to load up and apply to the vgui panel
 
// This is the name of the .res file to load up and apply to the vgui panel
"resfile" "resource/screens/CJCount.res"
+
"resfile" "resource/screens/Count.res"
 
}
 
}
 
...
 
...
Line 157: Line 144:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== CJCount.res ===
+
=== Count.res ===
  
 
This file should be saved in "<your mod>/resource/screens/"
 
This file should be saved in "<your mod>/resource/screens/"
Line 202: Line 189:
  
 
== Creating the panel in Hammer ==
 
== Creating the panel in Hammer ==
Finally, open Hammer, build yourself a simple map and place a vgui_screen entity. Set the "Panel Name" property to CJTestScreen, and you should have a working counter (with an annoying background image loaded in to your map. Beware that the opposite side to your screen does not render - so be sure to circle the entity before you figure that what you've done hasn't worked.
+
Finally, open Hammer, build yourself a simple map and place a vgui_screen entity. Set the "Panel Name" property to TestScreen, and you should have a working counter (with an annoying background image loaded in to your map. Beware that the opposite side to your screen does not render - so be sure to circle the entity before you figure that what you've done hasn't worked.

Revision as of 02:38, 2 April 2012

Quick Briefing

We will be trying to achieve the act of rendering vgui components in to our world. This is done via VGUI screens. A much more polished example is what you might see below: https://developer.valvesoftware.com/wiki/File:Granted_vgui.jpg

TestScreen.h

In this filter create a header file, "TestScreen.h"

  • Create a new class, called TestScreen, which inherits from CVGuiScreenPanel found in c_vguiscreen.h. As described in the header, this is the base class for all vgui screens.
  • Use DECLARE_CLASS to give the source sdk some recognition to your class if need be later.
  • Define your class constructor and destructor as you see below. Your constructor is expected to take two arguments, one is a pointer to the panel which is going to be your screen's parent. The other is string describing the meta class name you are to be creating (We'll get in to what these are a little later.)
  • Declare two virtual functions which overload that of our super class. OnTick (so we can keep track of time...) as well as the function Init.
  • Declare a pointer to a label, counter, that we will be incrementing at intervals of one second in length. When Init is called, we will initilize this pointer such that it points to the actual Label in our panel.
  • Declare a new unsigned integer, which will keep track of what number out counter is currently reading.

TestScreen.h

#pragma once

#include "cbase.h"
#include "c_vguiscreen.h"
#include "vgui_controls/Label.h"

class TestScreen : public CVGuiScreenPanel
{
private:
	DECLARE_CLASS(TestScreen, CVGuiScreenPanel);	

	vgui::Label* m_pLblCount;

	unsigned int m_uiCounter;

public:
	TestScreen(vgui::Panel *pParent, const char *pMetaClassName);
	virtual ~TestScreen();

	virtual void OnTick();
	virtual bool Init(KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
};


TestScreen.cpp

  • At the end (or start) of your source file, place a macro DECLARE_VGUI_SCREEN_FACTORY. The first argument is the class which will be acting as a screen factory, and the second argument is the name of your screen factory. Later, you will reference this name in vgui_screens.txt when you are describing your screens.
  • In the constructor, we simply initilize our label pointer & our counter to zero and call the base class's constructor.
  • Per every tick, as can be seen in our implementation of OnTick, we call the base class's OnTick routine, and then increment our counter. We also set the text for our label (and format it using a stringstream.)
  • In TestScreen::Init, we simply setup our frame's Tick signal, and have it tick at intervals of 1 second (1000 ms)
  • The general trend within the source sdk engine (or so I have seen) is that the pMetaClassName member in our constructor is simply ignored and passed down to the super class. With that said, you can make several different kinds of forms with your factory depending on the "MetaClassName" argument (we'll get in to what this actually is when we edit vgui_screens.txt).
DECLARE_VGUI_SCREEN_FACTORY( TestScreen, "TestScreen" );
  • In your constructor, initilize your
#include "cbase.h"
#include "TestScreen.h"
#include "vgui/IVGui.h"

#include <sstream>

TestScreen::TestScreen(vgui::Panel *pParent, const char *pMetaClassName) : 
	CVGuiScreenPanel(pParent, pMetaClassName)
{
	m_pLblCount = 0;
	m_uiCounter = 0;
}

TestScreen::~TestScreen()
{
}

bool TestScreen::Init(KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData)
{

	if(!CVGuiScreenPanel::Init(pKeyValues, pInitData))
		return false;
	
	vgui::ivgui()->AddTickSignal(GetVPanel(), 1000);
	
	m_pLblCount = dynamic_cast<vgui::Label*>(FindChildByName("lblCount"));
	return true;
}

void TestScreen::OnTick()
{

	CVGuiScreenPanel::OnTick();

	if(!m_pLblCount)
		return;
	
	m_pLblCount->SetText(VarArgs("Count: %i", m_uiCounter % 21));
	m_uiCounter++;
}

DECLARE_VGUI_SCREEN_FACTORY( TestScreen, "TestScreen" );

Setting up vgui_screens.txt

Found in the directory: <your mod>\scripts. I.e, mine is:

C:\Program Files (x86)\Steam\steamapps\sourcemods\cj\scripts

In here, add the following under "VGUI_Screens";

  • The "TestScreen" you see is the name of the factory that will be constructing the screen.
  • We reference the resource file that describes the appearence of our screen in the "resfile" property. As you can see, we will be storing this resource file in "<your mod>/resource/screens/Count.res" once we create it.
  • Also notice the pixelswide and pixelshigh members which describe the dimensions of our screen.
...
...
	"TestScreen"
	{
		"type"			"TestScreen"

		// These describe the dimensions of the screen *in pixels*
		"pixelswide"	480
		"pixelshigh"	240

		// This is the name of the .res file to load up and apply to the vgui panel
		"resfile"		"resource/screens/Count.res"
	}
...
...

Count.res

This file should be saved in "<your mod>/resource/screens/"

I simply took another screens res file and changed it up a bit; but you can use the in game vgui editor and cut the excess components (i.e the title bar, etc...), save it, and then plug it in to your screen.

"screen_basic.res"
{
	"Background"
	{
		"ControlName"	"MaterialImage"
		"fieldName"		"Background"
		"xpos"			"0"
		"ypos"			"0"
		"zpos"			"-2"
		"wide"			"480"
		"tall"			"240"

		"material"		"vgui/screens/vgui_bg"
	}

	"lblCount"
	{
		"ControlName"	"Label"
		"fieldName"		"lblCount"
		"xpos"			"120"
		"ypos"			"110"
		"wide"			"240"
		"tall"			"34"
		"autoResize"	"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"	"0"
		"labelText"		"0"
		"textAlignment"	"center"
		"dulltext"		"0"
		"paintBackground" "0"
	}
}

Creating the panel in Hammer

Finally, open Hammer, build yourself a simple map and place a vgui_screen entity. Set the "Panel Name" property to TestScreen, and you should have a working counter (with an annoying background image loaded in to your map. Beware that the opposite side to your screen does not render - so be sure to circle the entity before you figure that what you've done hasn't worked.