Autor: Nicolai 'Prefect' Haehnle
Hier dreht sich alles um Linien. Fangen wir mit dem einfachsten Fall an:
Für eine Linie im HUD (ähnlich wie zwischen der Lebenspunkte und Armor-Anzeige) ist die Funktion FillRGBA() auf der Clientseite zuständig. Du musst dazu im entsprechenden HUD-Element (wie CHudHealth, CHudAmmo) in der Draw()-Funktion eine einfache Zeile einfügen:
FillRGBA(x, y, width, height, r, g, b, a);
D.h. eine Linie die für kurze Zeit zwei Koordinaten im eigentlich simulierten Raums verbindet (wobei die Koordinaten durchaus auch Entities sein können). Für diesen Zweck eignen sich am besten TENTs (Temporary ENTities), also eine Art Entities, die, wie der Name schon sagt, nur temporär existieren. Hier gibt's eine sehr große Auswahl an Möglichkeiten, und das Beste ist eigentlich, wenn du einfach in die common/const.h schaust und dir eine passende aussuchst. Ein Beispiel hier trotzdem, es muss natürlich entsprechend angepasst werden. Ich verwende in dem Beispiel TE_LIGHTNING, das wie folgt in der common/const.h definiert ist:
#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters // coord, coord, coord (start) // coord, coord, coord (end) // byte (life in 0.1's) // byte (width in 0.1's) // byte (amplitude in 0.01's) // short (sprite model index)
Das Meiste von dem Obigen sollte leicht verständlich sein, abgesehen vielleicht vom Sprite Model Index. Um den zu kriegen erstellst du am Besten eine neue globale Variable, z.B.
int g_iYourSpriteIndex = 0;
In einer der Precache()-Funktionen (bei ner Waffe in der Precache()-Funktion der Waffe) baust du dann sowas in der Art ein:
g_iYourSpriteIndex = PRECACHE_SPRITE("sprites/spritename.spr");
Jetzt aber zum eigentlichen Code:
Vector vecStart, vecEnd; // Hier kommt Code rein um vecStart und vecEnd irgendwelchen sinnvollen Werten zuzuweisen MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecStart); WRITE_BYTE(TE_LIGHTNING); WRITE_COORD(vecStart.x); WRITE_COORD(vecStart.y); WRITE_COORD(vecStart.z); WRITE_COORD(vecEnd.x); WRITE_COORD(vecEnd.y); WRITE_COORD(vecEnd.z); WRITE_BYTE(10); // der beam besteht 1 Sekunde lang WRITE_BYTE(10); // ist eine Einheit dick WRITE_BYTE(0); // und sollte eigentlich ganz gerade sein WRITE_SHORT(g_iYourSpriteIndex); // damit die Engine weiß, was sie für ein Sprite verwenden soll MESSAGE_END();
So… und was heißt das jetzt alles?
Danach kommt das erste Byte der Message, bei Tempentities immer die ID der Entity.
Dann folgen die TENT-Parameter, in der gleichen Reihenfolge und Typ wie sie in const.h beschrieben wurden.\\
Wenn es ein Effekt wie bei der Gauss sein soll, kommen eigentlich nur TENTs in Frage. Entweder ein TE_LIGHTING, oder ein TE_BEAMENTPOINT. Der Vorteil beim TE_BEAMENTPOINT ist, dass einer der Endpunkte eine Entity ist, im Fall einer Waffe wäre das netürlich der Spieler. Noch vorteilhafter ist, daß du sogar einen der Attachements als Endpunkt verwenden kannst, indem du statt den Entindex einfach nur als WRITE_SHORT(entindex()); anzugeben WRITE_SHORT(attachmentid « 12) | m_pPlayer→ entindex(); verwendest. Damit kannst du verhindern, dass der Beam von der aus dem Bauch des Spielers herauskommt anstatt aus der Waffe. Du kannst den Trick natürlich auch für TE_LIGHTNING verwenden, es gibt nämlich die Funktion GetAttachment(), die die Koordinaten eines Attachmentpunkts zurückgibt (in CBaseAnimating nachschauen!).
Wenn du eine Linie in der Welt haben willst, die du auch noch hinterher verändern willst (Startpunkte, etc…) ist ein CBeam höchst empfehlenswert. Allerdings ist der Code recht umfangreich, also musst du dir Beispiele anschauen (z.B. die Egon, vor allem CEgon::CreateEffect und CEgon::UpdateEffect sowie direkt die Funktionen von CBeam in der effects.cpp/.h).
Ja, auch das ist möglich. Eröffnet natürlich ganz neue Möglichkeiten… allerdings will ich hier nicht verheimlichen, dass ich nicht besonders heiß darauf bin, den Egoneffekt auf die Clientseite zu bringen… allen die sich daran versuchen kann ich nur viel Glück wünschen!
Die Vektor-Klasse in HL wird für zwei verschiedene Dinge genutzt. Entweder sie repräsentiert bloß einen Punkt im 3D-Raum (die x, y und z-Koordinaten), oder sie steht für einen Vektor. Ein Vektor steht für eine Größe und eine Richtung von einem Punkt im Raum. Zum Beispiel würde ein Vektor (0, 0, 10) 10 Einheiten in z-Richtung bedeuten und ein Vektor (-5, 3, 0) würde -5 Einheiten entlang der x-Achse und 3 Einheiten entlang der y-Achse bedeuten. Um den Endpunkt eines Vektors im Raum zu bestimmen, musst du den Startpunkt des Vektors wissen (der nicht Teil des Vektors selbst ist). Also wenn dein Startpunkt (10, 10, 10) ist und du denn Vektor (-5, 3, 0) addierst, dann erhältst du als Endpunkt (5, 13, 10). Du findest den Endpunkt, in dem du den Startpunkt und den Vektor addierst. Du kannst einen Vektor erstellen, in dem du 2 Punkte im Raum von einander abziehst. Wie du siehst, können Vektoren negative Werte beinhalten (das tun sie auch oft!).
UTIL_Traceline und UTIL_TraceHull werden benutzt um zu bestimmen ob da etwas ist, das die direkte Linie zwischen zwei Punkten im Raum blockiert. Du übergibst TraceLine oder TraceHull einen Start- und einen Endpunkt (die wirklich nur Punkte im Raum sind) und die Engine gibt dir eine TraceResult-Struktur zurück, die dir sagt, ob du etwas getroffen hast oder nicht. flFraction ist eine Variable in dieser Struktur, die dir sagt, wie lange der Trace ging bevor etwas getroffen wurde. Wenn du zum Beispiel zwischen zwei Punkten tracen lässt, die 100 Einheiten auseinanderliegen und TraceLine flFraction = 0.75 zurückgibt, dann weißt du, dass 3/4 des Weges zwischen den zwei Punkten etwas im Weg war. Wenn du wissen willst, welches Entity du getroffen hast, dann kannst du den pHit→v.classname Teil des TraceResults angucken, um den classname des Entities (func_button, func_wall, worldspawn, player, etc.) herauszufinden.
TraceHull ist ein Spezialfall von Traceline wo du die bounding box des Spielers tracen willst um zu sehen, ob irgendein Teil des Spielers von irgendwas blöckiert wird. Du musst ein zusätzliches Argument übergeben, „point_hull“, „human_hull“, „large_hull“ oder „head_hull“. human_hull wird für einen stehenden Spieler benutzt. head_hull wird für die bounding box des Kopfes benutzt. Ich nehme an, dass point_hull ein einziger Punkt ist (also das selbe wie TraceLine), aber es gibt keine Beispiel von point_hull im StandardSDK. Was large_hull ist, weiß ich nicht (wieder keine Beispiele).
Dieses Tutorial stammt aus der ehemaligen Sammlung des resourcecode.de und konnte dank der freundlichen Zustimmung des Autors in das thewall-Wiki übertragen werden.