Thinking

From Valve Developer Community
< De
Jump to navigation Jump to search
English (en)Deutsch (de)Español (es)Português do Brasil (pt-br)Русский (ru)Translate (Translate)
Info content.png
This page has not been fully translated.

You can help by finishing the translation.

Also, please make sure the article tries to comply with the alternate languages guide.

Think-Funktionen erlauben Entities, Code geplant später ausführen zu lassen. Durch das konstante neuplanen eines Thinks, kann eine automatisierte Schleife erzeugt werden, die die Entity autonom macht.

Planung („Scheduling“)

SetNextThink() wird verwendet, um einzustellen, wann der nächste Think einer Entity ausgeführt werden soll. Es nimmt float(en)-Werte an.

void CMyEntity::Spawn()
{
	BaseClass::Spawn();
	SetNextThink( gpGlobals->curtime ); // Think jetzt
}

void CMyEntity::Think()
{
	BaseClass::Think(); // Das muss immer gemacht werden, wenn Think() überschrieben wird

	Msg( "Ich denke, also bin ich.\n" );
	SetNextThink( gpGlobals->curtime + 1 ); // Think in 1 Sekunde nochmal
}

Beachte die Verwendung von gpGlobals->curtime, um den übergebenen Wert relativ zum ausführungszeitpunkt zu machen.

Tip.pngTipp:SetNextThink(0) oder SetNextThink(null) werden jeden zukünftigen Think abbrechen.

Neue Think-Funktionen

eine Entity kann eine beliebige Anzahl zusätzlicher Think-Funktionen haben. Um eine neue zu registrieren:

  1. Man muss sichergehen, dass die Funktion void ist.
  2. Hinzufügen zur DATADESC(en) der Entity mit DEFINE_THINKFUNC().
  3. SetThink() aufrufen und den Pointer zur Funktion übergeben (siehe Beispiel unten).
  4. Man muss sichergehen, dass DECLARE_DATADESC(); in der eigenen Klasse ist.
BEGIN_DATADESC( CMyEntity )
	DEFINE_THINKFUNC( MyThink ), // Die neue Think-Funktion registrieren
END_DATADESC()

void CMyEntity::Spawn()
{
	BaseClass::Spawn();
	SetThink( &CMyEntity::MyThink ); // Einen Funktionszeiger übergeben
	SetNextThink(gpGlobals->curtime);
}

void CMyEntity::MyThink()
{
	Msg( "Ich denke, also bin ich.\n" );
	SetNextThink( gpGlobals->curtime + 1 );
}

Der Think-Code einer Entity kann in verschiedene Funktionen aufgeteilt werden, um es wechseln zwischen dem Operationsmodi zu vereinfachen.

Tip.pngTipp:SetThink() kann auch aus einer Think-Funktion heraus aufgerufen werden. Der nächste Aufruf wird dann an die neue Funktion gehen.

Kontexte verwenden

Es ist möglich, eine beliebige Anzahl an Think-Funktionen mit einem „Think-Kontext“ Seite an Seite zu planen. Um einen neuen Kontext zu erzeugen:

  1. RegisterThinkContext(string(en) ContextName) aufrufen
  2. SetContextThink(void(en)* Function, float NextThinkTime, string ContextName) aufrufen
  3. SetNextThink(float NextThinkTime, string ContextName) für nachfolgende Thinks aufrufen
BEGIN_DATADESC( CMyEntity )
	DEFINE_THINKFUNC( ContextThink ),
END_DATADESC()

void CMyEntity::Spawn()
{
	SetNextThink( gpGlobals->curtime ); // Standard Think-Schleife - kein Kontext
	
	RegisterThinkContext( "TestContext" );
	SetContextThink( &CMyEntity::ContextThink, gpGlobals->curtime, "TestContext" );
}

void CMyEntity::Think()
{
	BaseClass::Think();

	Msg( "Think\n" );
	SetNextThink( gpGlobals->curtime + .1 );
}

void CMyEntity::ContextThink()
{
	Msg( "Kontext-Think\n" );
	SetNextThink(gpGlobals->curtime + .2, "TestContext" );
}

Dies erzeugt 2 gleichzeitige Think-Schleifen, die beide mit unterschiedlicher Rate Konsolenausgaben machen.

Tip.pngTipp:Einen neuen Kontext zu erzeugen ist ein großartiger weg, den Funktionsaufruf zu verzögern, ohne die Think-Schleife umwerfen zu müssen.

Utilities

Diese sollten selbsterklärend sein:

float	GetLastThink()
float	GetNextThink()
int	GetLastThinkTick()
int	GetNextThinkTick()
Barnacle.jpg

Die GetLast-Funktionen sind nützlich für die Kontrolle der Rate, in der etwas auftritt. Dieser Think-Code des npc_barnacle moduliert die Deschwindigkeit der Zungenbewegung, auch wenn die Think-Häufigkeit sich ändert:

float dt = gpGlobals->curtime - GetLastThink(); // dt ist "delta time" ("Zeitunterschied")
SetAltitude( m_flAltitude + m_flBarnaclePullSpeed * dt ); // Ändern der Zungenhöhe

Damit diese nicht-Skelett(en)-Animation weich ist, muss der Code jeden Frame ausgeführt werden. Genau das Passiert, bis das Barnacle nicht mehr in der PVS(en) des Spieler ist, wonach die Rate verringert wird – wofür der obige Code nötig ist.

ClientThink()

Think kann ebenfalls auf der Clientseite auftreten, aber dessen Auswirkungen sind limitiert. Nur eine Think-Funktion wird für jede Entity unterstützt.

void C_MyEntity::ClientThink()
{
	Msg( "Packe nichts teures(en) in diese Funktion!\n" );
	SetNextClientThink( CLIENT_THINK_ALWAYS ); // Think in jedem Frame
}

Ein paar Beispiele für Clientseitige Thinks sind:

  • visuelle Effekte / Partikel
  • VGUI-Interaktionen
  • Anpassung der Spielergeschwindigkeit (auf dem Client und auf dem Server, um Vorhersage(en)probleme zu vermeiden)
  • Fangseile der Strider (standardmäßig deaktiviert)

SetNextClientThink() wird zum Planen von ClientThink() verwendet. Es gibt 2 spezielle, akzeptierte Werte:

CLIENT_THINK_ALWAYS
Think auf Clientseite bei jedem Frame. Mit Vorsicht verwenden!
Tip.pngTipp:Man kann gpGlobals->frametime zur Regulierung der Geschwindigkeit verwenden.
CLIENT_THINK_NEVER
Pausiert automatisch alle Client-Thinks.