Thinking

From Valve Developer Community
Revision as of 09:49, 23 February 2008 by TomEdwards (talk | contribs) (created)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

An entity’s Think() function is the root gateway for executing its code. It is called once on spawn, with any subsequent calls decided by the programmer – automatically, as described below, or in response to input with standard procedure calls.

Note:Think functions should usually be added to the entity's DATADESC with DEFINE_THINKFUNC, to prevent changes in behaviour when restoring a saved game.
Tip:Use ClientThink() to have an entity think once per frame.

SetNextThink()

  • Defines when the entity next automatically thinks.
  • Accepts a floating point value.
  • New values override old.
void CMyEntity::Think()
{
	Msg( "I think, therefore I am.\n" );
	SetNextThink( gpGlobals->curtime + 1.0f ); // Think again in 1.0 seconds
}

This code causes the entity to print a message to the console once per second. Note the use of gpGlobals->curtime, which returns the time at execution, and f, which tells the C++ compiler that we are submitting a floating-point value and not an integer.

Tip:SetNextThink( 0 ) or SetNextThink( null ) will pause automatic thinking.

SetThink()

  • Changes the active automatic think function
  • Accepts a function pointer: add ‘&’ before the name and omit its closing parentheses.
  • New values override old.
void CMyEntity::Think()
{
	Msg( "I think, therefore I am.\n" );
	SetThink( &CMyEntity::Think2 ); // Think with this function next
	SetNextThink( gpGlobals->curtime + 1.0f );
}

void CMyEntity::Think2()
{
	Msg( "Variety is the spice of life.\n" );
	SetThink( &CMyEntity::Think ); // Think with this function next
	SetNextThink( gpGlobals->curtime + 1.0f );
}

This code switches thinking between two functions. A real-world application is to change an entity between various life stages: consider a buildable gun turret. One think function would run while it waits to be unpackaged, another while it is being built, another while it is active, and a fourth when it dies. Creating think functions for each discrete stage increases code stability and aids debugging.

GetLastThink()

A utility function that returns the time of the entity’s last think as a floating point value.

float dt = gpGlobals->curtime - GetLastThink();
SetAltitude( m_flAltitude + m_flBarnaclePullSpeed * dt ); // Change tongue altitude
npc_barnacle

This real-world code for npc_barnacle modulates the speed of tongue movement, even if the frequency at which the code driving it is called changes. dt is short for “delta time”.

Note:For smooth animation this think code would need to be executed every frame. This is exactly what happens, until the barnacle is no longer in the PVS and the rate is slowed down – thus requiring the above modulation.

ClientThink()

Thinking can also occur on the client, but its effects are limited. Additionally, only one think function is supported for each entity.

void C_MyEntity::ClientThink()
{
	Msg( "Don't put anything expensive in this function!\n" );
	SetNextClientThink( CLIENT_THINK_ALWAYS ); // Think every frame
}

Some examples of client-side thinking are:

  • Visual effects / particles
  • VGUI screen interaction
  • Modifying player speed (calculated on the client as well as server to avoid lag)
  • Striders’ legs snapping ropes (disabled by default)

SetNextClientThink()

Used to re-fire ClientThink(). In addition to normal float values, it accepts:

CLIENT_THINK_ALWAYS
Think on the client once every frame. Use with caution!
Replaces Simulate().
CLIENT_THINK_NEVER
Pause all automated client thinking.