Accessing Other Entities
You can help by finishing the translation.
Also, please make sure the article tries to comply with the alternate languages guide.In Hammer kommunizieren Entities nur über vordefinierte Inputs und Outputs . 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 func_breakable)
- 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.
&
erzeugen. SomeFunc(&MyVar)
ist das gleiche, wie SomeType* pMyVar = MyVar; SomeFunc(pMyVar)
.Casting
Zeigerenden müssen von der gleichen Klasse, wie der jeweilige Pointer sein. Aber was ist mit touch-Funktionen , 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.
CBreakable* pWall = static_cast<CBreakable*>(pOther)
. 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 CHandle
(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.Hinweis:Das Sternchen ist in diesem Zusammenhang ein Suchplatzhalter.- eine Schleife betreten, die endet, wenn
pResult
ungültig wird. - unser
CBaseEntity*
,pResult
, in einenCAI_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 teuer , also sollte es nach Möglichkeit vermieden werden. Beispielsweise kann eine Entity in einem Memberpointer gespeichert werden, wenn ein späterer Zugriff erforderlich ist.