Thinking: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
(corrections)
Line 1: Line 1:
An entity’s '''Think()''' function is the root gateway for executing its code without the need of external [[inputs]]. It is called once on spawn, with any subsequent calls decided by the programmer. This page describes the functions needed to set up an automated "think loop".
{{toc-right}}


{{note|Think functions should always be <code>void</code>, and must be added to the entity's [[DATADESC]] with <code>DEFINE_THINKFUNC</code>.}}
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.


{{tip|Use [[#ClientThink()|ClientThink()]] to have an entity think exactly once per frame.}}
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>, which returns the time of execution.
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|<code>SetNextThink( 0 )</code> or <code>SetNextThink( null )</code> will pause automatic thinking.}}
{{tip|Use [[#ClientThink()|ClientThink()]] to have an entity think exactly once per screen frame.}}


=== SetThink() ===
== SetThink() ==


* Changes the active automatic think function
* Changes the active automatic think function
* Accepts a function [[Wikipedia: Pointer (computing)|pointer]]: add &’ before the name and omit its closing parentheses.
* 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() ===
== SetContextThink() ==


{{TODO|Confirm all of this.}}  
{{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() ===
== 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()  ===
==  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() ====
=== 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!
:''Replaces Simulate().''
;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.

Note.pngNote:Overrides of 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.

Tip.pngTip:SetNextThink(0) or SetNextThink(null) will pause automatic thinking.
Tip.pngTip:Use ClientThink() to have an entity think exactly once per screen frame.

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()

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.

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
npc_barnacle

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”.

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

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.