De/Accessing Other Entities

In kommunizieren nur über vordefinierte . Die gute Nachricht ist, dass in C++ diese Limitation verschwindet und man auf alles zugreifen kann, was nicht explizit "privat" ist. Die schlechte Nachricht ist, dass die Arbeit dafür ein wenig schwerer ist, um es in Gang zu bringen.
== Ein Beispiel ==
CBreakable* pWall = GetFuncBreakable(); // bestehende Funktion!
if (!pWall) return;
pWall->Break(this);
Im oberen Code wird:
# ein Pointer deklariert und die Entity zugewiesen (ein )
# sichergegangen, dass dem Pointer erfolgreich ein Wert zugewiesen wurde
# auf eine Memberfunktion des Zielentity zugegriffen
== Pointer ==
Ein Pointer (auch Zeiger) ist das C++ Äquivalent einer Desktopverknüpfung. Das aktuelle Ziel eines Pointers wird 'Zeigerende' genannt.
Ein Pointer hat 5 Hauptregeln:
# Pointer werden wie Variablen deklariert, abgesehen von dem Sternchen vor dem Namen (z. B. CBaseEntity* pOther). Verwende das Sternchen sonst nirgens - es ist nur für Deklarationen.
# Du musst eine Headerdatei einer jeden Klasse mit #include einbinden, für die ein Pointer erzeugt werden soll.
# Einem Pointer kann nur ein Objekt zugewiesen werden, welches eine passende klasse besitzt. Andernfalls wirft der Compiler einen Fehler.
# Beim Zugriff auf Member eines Zeigerendes muss ein -> statt eines Punkts verwendet werden.
# Jeder Versuch, auf einen nicht zugewiesenen ('null') Pointer zuzugreifen lässt die Modifikation sofort abstürzen. Willkommen zu C++!
Abgesehen davon ist die Syntax eines Pointer die gleiche, wie für jeden anderen Member der aktuellen Klasse.
=== Casting ===
Zeigerenden müssen von der gleichen Klasse, wie der jeweilige Pointer sein. Aber was ist mit , welche die andere beteiligte Entity als CBaseEntity* liefert? Es gibt keine reinen CBaseEntity, oder?
Das ist tatsächlich so. Der Zeigerende ist kein CBaseEntity, aber der Pointer wurde "gecastet", um sich so zu verhalten, als wäre dort eins. Das ist möglich, wenn eine Klasse von einer anderen erbt; Da alles von CBaseEntity erbt, kann alles in ein CBaseEntity* gecastet werden. Grundlegendes Wissen, Wenn man nicht weiß, mit welchen Entities der eigene Code umgehen wird!
Ein CBaseEntity*-Pointer wenn man auf etwas in CBaseEntity definiertes zugreifen will. Wenn man aber auf etwas tiefer liegendes zugreifen will, dann muss der Pointer in eine Klasse gecastet werden, die Zugriff besitzt. Dies wird mit dem dynamic_cast erreicht.
CMyEntity::Touch( CBaseEntity* pOther )
{
	CBreakable* pWall = dynamic_cast<CBreakable*>(pOther);
	if (!pWall) return;
	pWall->Break(this);
}
pOther wird in einen CBreakable* gecastet, dessen Ergebnis in pWall gespeichert wird. Dann kann auf die CBreakable-Funktion Break() zugegriffen werden.
Es ist doppelt vierfach wichtig, dass der Pointer dieses Mal zugewiesen ist, denn der Cast wird versucht, wannimmer die Entity etwas berührt. Wenn pOther kein CBreakable ist, dann wird die Durchführung fehlschalgen, welches zu einem gefährlichen Nullpointer führt.
=== CHandle ===
Pointer repräsentieren physiache Positionen im Systemspeicher. Wenn ein Pointer zwischen dem Client und dem server transportiert werden oder in einem Spielstand gespeichert werden soll, muss ein (aka EHANDLE) verwendet werden.
== Entities finden ==
Es gibt keinen großartigen Zweck, zu wissen, wie ein Pointer erstellt wird, wenn man keine entity zum Bearbeiten hat. Aus gEntList bekommt man diese. Es ist ein globales Objekt, welches über cbase.h verfügbar ist.
gEntList bietet diverse Suchfunktionen, deren Namen alle mit Find oder Next beginnen. Jede wird jedoch nur 1 CBaseEntity* gleichzeitig liefern, also ist eine gewisse Polsterung notwendig, wenn man über alle Ergebnisse iterieren will:
CBaseEntity* pResult = gEntList.FindEntityByClassname(NULL,"npc_*");
while (pResult)
{
	CAI_BaseNPC* pNPC = dynamic_cast<CAI_BaseNPC*>(pResult);
	if (pNPC)
		pNPC->SetState(NPC_STATE_IDLE);
	pResult = gEntList.FindEntityByClassname(pResult,"npc_*");
}
Hier wird:
# gEntList.FindEntityByClassname() aufgerufen, um die erste Entity mit dem Hammer-Klassennamen "npc_*" zu erhalten. 
# eine Schleife betreten, die endet, wenn pResult ungültig wird.
# unser CBaseEntity*, pResult, in einen CAI_BaseNPC* gecastet, damit Zugriff auf die KI-bezogenen Member besteht.
# sichergegangen, dass der Cast erfolgreich war.
# die gewünschte Operation durchgeführtPerform our desired operation.
# FindEntityByClassname() erneut für die suche nach "npc_*" aufgerufen, welche diese Mal die Suche in der Liste ab der Position von pResult's startet.
# zurück zum anfang der Schleife gesprungen.
Die gesamte Entity-Liste jedes Mal zu durchsuchen ist , also sollte es nach Möglichkeit vermieden werden. Beispielsweise kann eine Entity in einem Memberpointer gespeichert werden, wenn ein späterer Zugriff erforderlich ist.
== Siehe auch ==
* Wikipedia:Zeiger (Informatik)