Thinking: Difference between revisions
No edit summary |
TomEdwards (talk | contribs) (corrections) |
||
Line 1: | Line 1: | ||
{{toc-right}} | |||
An entity's '''Think()''' function is the root gateway for processing without external [[input]]. Some entities (such as <code>[[CAI_BaseNPC]]</code>) think automatically when they are spawned, but others [[#SetNextThink()|need prompting]] first. | |||
This article describes all of the functions that play a part in thinking. | |||
=== SetNextThink() === | == Creating a think function == | ||
A new think function: | |||
* Must be <code>void</code> | |||
* Must be added to the entity's [[DATADESC]] with <code>DEFINE_THINKFUNC</code> | |||
* Should probably make a call to <code>SetNextThink()</code> | |||
Otherwise it is like any other function. | |||
{{note|Overrides of <code>Think()</code> itself should include a call to <code>BaseClass::Think()</code>, or you may find that <code>SetThink()</code> breaks.}} | |||
== SetNextThink() == | |||
* Defines when the entity next automatically thinks. | * Defines when the entity next automatically thinks. | ||
Line 15: | Line 27: | ||
Msg( <span style="color:brown;">"I think, therefore I am.\n"</span> ); | Msg( <span style="color:brown;">"I think, therefore I am.\n"</span> ); | ||
SetNextThink( gpGlobals->curtime + 1 ); <span style="color:green;">// Think again in 1 second</span> | SetNextThink( gpGlobals->curtime + 1 ); <span style="color:green;">// Think again in 1 second</span> | ||
BaseClass::Think(); | |||
} | } | ||
This code causes the entity to print a message to the console once per second. Note the use of <code>[[gpGlobals]]->curtime</code> | This code causes the entity to print a message to the console once per second. Note the use of <code>[[gpGlobals]]->curtime</code> to make the NextThink time relative to now. | ||
{{tip|<code>SetNextThink(0)</code> or <code>SetNextThink(null)</code> will pause automatic thinking.}} | |||
{{tip| | {{tip|Use [[#ClientThink()|ClientThink()]] to have an entity think exactly once per screen frame.}} | ||
== SetThink() == | |||
* Changes the active automatic think function | * Changes the active automatic think function | ||
* Accepts a function [[ | * Accepts a function [[pointer]]: add <code>&</code> in front and omit the parentheses. | ||
* New values override old. | * New values override old. | ||
Line 32: | Line 47: | ||
SetThink( &CMyEntity::Think2 ); <span style="color:green;">// Think with this function next</span> | SetThink( &CMyEntity::Think2 ); <span style="color:green;">// Think with this function next</span> | ||
SetNextThink( gpGlobals->curtime + 1 ); | SetNextThink( gpGlobals->curtime + 1 ); | ||
BaseClass::Think(); | |||
} | } | ||
Line 43: | Line 59: | ||
This code switches thinking between two functions. A real-world application is to change an entity between various life stages: consider [http://forums.gamedesign.net/viewtopic.php?t=3702 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. | This code switches thinking between two functions. A real-world application is to change an entity between various life stages: consider [http://forums.gamedesign.net/viewtopic.php?t=3702 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. | ||
== SetContextThink() == | |||
{{ | {{todo|Confirm all of this.}} | ||
Fires a think function once, at a defined time. The third parameter is a "context", a [[string]] value that seems to be used to group multiple thinkfuncs under a given heading. | Fires a think function once, at a defined time. The third parameter is a "context", a [[string]] value that seems to be used to group multiple thinkfuncs under a given heading. | ||
Line 61: | Line 77: | ||
} | } | ||
== GetLastThink() == | |||
A utility function that returns the time of the entity’s last think as a float. | A utility function that returns the time of the entity’s last think as a float. | ||
Line 80: | Line 96: | ||
*<code><span style="color:blue;">int</span> GetLastThinkTick()</code> | *<code><span style="color:blue;">int</span> GetLastThinkTick()</code> | ||
== ClientThink() == | |||
Thinking can also occur on the client, but its effects are limited. Additionally, only one think function is supported for each entity. | Thinking can also occur on the client, but its effects are limited. Additionally, only one think function is supported for each entity. | ||
Line 97: | Line 113: | ||
* Striders’ legs snapping ropes (disabled by default) | * Striders’ legs snapping ropes (disabled by default) | ||
=== SetNextClientThink() === | |||
Used to re-fire <code>ClientThink()</code>. In addition to normal float values, it accepts: | Used to re-fire <code>ClientThink()</code>. In addition to normal float values, it accepts: | ||
Line 103: | Line 119: | ||
;CLIENT_THINK_ALWAYS | ;CLIENT_THINK_ALWAYS | ||
:Think on the client once every frame. Use with caution! | :Think on the client once every frame. Use with caution! | ||
;CLIENT_THINK_NEVER | ;CLIENT_THINK_NEVER | ||
:Pause all automated client thinking. | :Pause all automated client thinking. | ||
[[Category:Programming]] | [[Category:Programming]] | ||
[[Category:AI]] | |||
[[Category:Functions]] | [[Category:Functions]] |
Revision as of 08:31, 7 August 2009
An entity's Think() function is the root gateway for processing without external input. Some entities (such as CAI_BaseNPC
) think automatically when they are spawned, but others need prompting first.
This article describes all of the functions that play a part in thinking.
Creating a think function
A new think function:
- Must be
void
- Must be added to the entity's DATADESC with
DEFINE_THINKFUNC
- Should probably make a call to
SetNextThink()
Otherwise it is like any other function.

Think()
itself should include a call to BaseClass::Think()
, or you may find that SetThink()
breaks.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 ); // Think again in 1 second BaseClass::Think(); }
This code causes the entity to print a message to the console once per second. Note the use of gpGlobals->curtime
to make the NextThink time relative to now.

SetNextThink(0)
or SetNextThink(null)
will pause automatic thinking.
SetThink()
- Changes the active automatic think function
- Accepts a function pointer: add
&
in front and omit the 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 ); BaseClass::Think(); } void CMyEntity::Think2() { Msg( "Variety is the spice of life.\n" ); SetThink( &CMyEntity::Think ); // Think with this function next SetNextThink( gpGlobals->curtime + 1 ); }
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.
SetContextThink()
Fires a think function once, at a defined time. The third parameter is a "context", a string value that seems to be used to group multiple thinkfuncs under a given heading.
CAI_ActBusyQueueGoal
provides good examples, such as:
void CAI_ActBusyQueueGoal::MoveQueueUp() { // Find the node the NPC has arrived at, and tell the guy behind him to move forward if ( GetNextThink( QUEUE_MOVEUP_THINK_CONTEXT ) < gpGlobals->curtime ) { float flTime = gpGlobals->curtime + RandomFloat( 0.3, 0.5 ); SetContextThink( &CAI_ActBusyQueueGoal::MoveQueueUpThink, flTime, QUEUE_MOVEUP_THINK_CONTEXT ); } }
GetLastThink()
A utility function that returns the time of the entity’s last think as a float.
float dt = gpGlobals->curtime - GetLastThink(); SetAltitude( m_flAltitude + m_flBarnaclePullSpeed * dt ); // Change tongue altitude
This real-world think code for npc_barnacle modulates the speed of tongue movement, even if the frequency of thinking changes. dt
is short for “delta time”.

There are also:
float GetNextThink()
int GetNextThinkTick()
int GetLastThinkTick()
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!
- CLIENT_THINK_NEVER
- Pause all automated client thinking.