Sie haben wahrscheinlich schon von einem der vielen benutzerdefinierten Spielmodi gehört oder sogar gespielt, die es für die Source-Engine gibt – Attentat, Gungam, Free-for-all etc. Alle diese Spielmodi erfordern derzeit eine serverseitige Modifikation wie z.B. SourceMod, um korrekt zu funktionieren und dem Mapper sehr wenig Kontrolle zu geben. All dies ändert sich jedoch mit VScripts.
Was ist eigentlich ein VScript?
Ein VScript ist ein kleines Stück Code, das direkt in ihre Map implementiert wird und effektiv als Brücke zwischen ihren Map-Elementen und dem Innenleben des Spiels fungiert. Dieses kann unglaublich leistungsfähig sein und erlaubt es ihnen, viele Sachen zu tun, die vorher unmöglich gewesen sind.
Um etwas detaillierter darauf einzugehen, werden VScripts in einer Sprache mit der Bezeichnung Squirrel verfasst. Es ist eine ziemlich einfache Sprache mit vielen der gleichen dynamischen Mechaniken wir Lua, während eine traditionelle Syntax, die der von C++ oder Java ähnelt, beibehalten werden kann. VScripts wurde ausgiebig für L4D2 verwendet, wo sie es Mappern ermöglichen, mit dem Director zu interagieren (der Teil des Codes, der das Spawnen von Zombies steuert), aber sie sind in allen aktuellen Source-Spielen verfügbar (die derzeit nur L4D2, Nuclear Dwan, Alien Swarm, Portal 2 und CS:GO abdecken).
Das Einzigartige an VScripts ist, dass es vollständig in ihrem .bsp existiert, d.h. die Mapper, erhalten die volle Kontrolle über das, was die VScripts tun sollen, anstatt sich auf SourceMod-Plugins verlassen zu müssen (die möglicherweise nicht einmal vorhanden sind, wenn ein Benutzer die Map abspielt) und dass niemand damit herumspielen kann, nachdem Sie fertig sind. Das bedeutet auch, dass du alle neuen Dinge tun kannst, egal wie klein oder groß, die vorher nicht möglich gewesen wären – wir werden damit beginnen, eines dieser Skripte zu erstellen und umzusetzen.
Leider ist die Dokumentation für uns so gut wie gar nicht existent für VScripts und es gibt nichts anderes als eine einfache Funktionsliste und eine Reihe von Valve-eigenen Skripten. Dieses Tutorial wird daher hauptsächlich auf bestehende L4D2-Skripte verweisen und Sie durch die Erstellung eines Free-for-All-Gamemodus mit Hilfe von VScripts führen.
Scripting.
Wir werden dieses Tutorial in zwei Abschnitte aufteilen – die Mapping-Seite, die so alles darstellt, was in der .VMF vor sich geht und die Coding-Seite, die das eigentliche .nut-Skript ist. Diese beiden werden sich natürlich irgendwo auf der ganzen Linie zusammenschließen, aber im Moment werden wir uns auf das Scripting konzentrieren.
Als Erstes müssen wir jedoch herausfinden, was wir eigentlich tun möchten. Da sich dieses Tutorial auf die Möglichkeiten von VScripts konzentriert, insbesondere, wie es verwendet werden kann, um SourceMod für Gamemodes zu ersetzen, versuchen wir nicht, einen existierenden Spielmodus neu zu erstellen – wie zum Beispiel hide `n` seek. Für jeden, der es nicht weiß, besteht hide `n` seek aus zwei Teams, den Requisiten und den Suchern. Die Requisiten (Terroristen) werden in eine zufällige Requisite verwandelt und erhalten dann eine bestimmte Zeitspanne zum Verstecken. Nach Ablauf dieser Zeit werden die Sucher (die Anti-Terroristen) losgeschickt, um sie zu finden. Ziemlich einfache Sachen, aber angesichts einer einfachen Map ohne VScripts erfordert es SourceMod, Spieler in Modelle zu verwandeln. Wir werden etwas dagegen tun.
Öffnen Sie also ihre bevorzugten Texteditor (Sie können auch Notepad verwenden, wenn Sie es wünschen. Es ist so gut wie alles andere. Es fehlt ihm nur noch die Syntaxhervorhebung), um eine komplett leere Datei zu erstellen. Diese Datei wird unsere Skriptdatei sein und der Ort, an dem wir unseren gesamten Code schreiben werden. Speichern Sie die Datei (denken Sie daran, den Typ auf „Alle Dateien“ umzuschalten und sie mit .nut am Ende zu benennen. Dadurch wird es zu einer .nut-Datei, die von CS:GO als Squirrel-Skript gelesen wird) – wobei dies nicht wirklich von Belang ist, aber wir schlagen vor, das gleiche Verzeichnis zu verwenden, in dem sich alle offiziellen Skripte befinden: Steam/SteamApps/Common/ GameStrike/csgo/go/skripte/vskripte/videoplatte. Das Setup ist nun aus dem Weg. Bevor wir anfangen in unseren 1337 hax0ring einzusteigen, sollten wir eine kleine Planung machen, um genau zu verstehen, was wir vom Skript erwarten. Wir werden eine kleine Liste mit denjenigen erstellen, die wir dann ankreuzen können, wenn wir fertig sind.
Verwandle beim Rundenstart alle Spieler auf T in eine zufällige Requiste.
Das klingt doch gar nicht so übel, oder? Das Entwaffnen von Waffen, das Verriegeln von Cts, all das kann mit ein wenig Entitätsarbeit vollständig über die Map abgewickelt werden. Ich schätze, es gibt nicht viel zu sagen, außer, dass wir anfangen sollten.
Dieses Tutorial geht davon aus, dass Sie keine oder kaum Erfahrung mit der Programmierung haben und erklärt einige der Grundprinzipien der Programmierung. Da es sich jedoch um ein einfaches Tutorial handelt, wird es nicht sehr in die Tiefe gehen, wie wir es gerne sehen würden und Sie müssen daher vielleicht ein paar Dinge googeln, sofern unsere Erklärungen nicht ausreichen. Als Erstes stellen wir die Funktionen vor.
Programmierung ist etwas Besonderes, da der Computer alle Befehle wie _exactly_ korrekt lesen und verarbeiten kann. Das bedeutet aber auch, dass Sie nicht einfach aufschreiben können, was sie zum Beispiel einem anderen Menschen sagen würden. Wenn Sie alle Spieler im Terroristenteam finden wollen, können Sie nicht einfach „Computer – alle Terroristen“ schreiben, geschweige denn, wo man nach ihnen suchen soll. Stattdessen muss man das, was man will, in kleine Schritte aufschlüsseln.
Drei Zeilen lang. Lasst Sie es uns aufschlüsseln.
Dies ist der Ort, an dem wir dem Computer mitteilen, dass das, worauf er folgen soll, eine Funktion ist. Wir tun dies mit einer sehr spezifischen Syntax. Das erste Wort auf der Leitung ist „Funktion“. Dies teilt dem Computer mit, dass genau das, was folgen soll, eine Funktion ist. Das nächste Wort ist, wo wir dem Computer tatsächlich etwas Bestimmtes zu dieser Funktion sagen – seine Bezeichnung. Das Wort direkt nach „Funktion“ ist die Bezeichnung und das, was es im Code als „Funktion“ bezeichnet wird. Sie können dir das als Namensfindung für eine Entität vorstellen. Ziemlich einfaches Zeug.
Etwas, das ihnen vielleicht aufgefallen ist, das Sie vielleicht als etwas merkwürdig empfunden haben, sind die beiden Paranthen am Ende dieser Bezeichnung. Alle Funktionen verfügen über diese. Sie werden manchmal verwendet, um die Übergabe von Werten von außerhalb der Funktion an den Code innerhalb der Funktion zuzulassen, aber im Moment sind sie leer. Es wird nur darauf hingewiesen, dass der Name eine Funktion ist und keine Variable. Das Wichtigste ist, was als nächstes kommt: die geschwungenen Klammern. Diese geschweiften Klammern definieren den _Anfang_ und das _Ende_ der Funktion. Alles zwischen diesen beiden geschweiften Klammern ist der Code, der ausgeführt wird, wenn die Funktion aufgerufen wird. Kurz gesagt, die Syntax für das Erstellen einer Funktion ist
Das ist ein Teil des Codes. Auch dieser Code ist ziemlich einfach. Erinnerst du dich, wie wir ihnen erklärt haben, dass Paranthen verwendet werden, um Werte außerhalb einer Funktion an den Code im Inneren weiterzugeben und um darauf hinzuweisen, dass das, worüber wir sprachen, tatsächlich eine Funktion war? Die Scharfsinnigen von euch werden merken, dass es sich hier um eine Funktion mit der Bezeichnung „printl“ handelt. Was du hier siehst, ist, wie du eine Funktion „callst“ wie du den Computer aufforderst, den darin enthaltenen Code auszuführen. Du schreibst den Namen der Funktion, dicht gefolgt von einer Paranthese und einem Wert, den du an sie weitergeben möchtest. Printl ist zufällig eine der Standardfunktionen, eine Funktion, die von überall her aufgerufen werden kann und sie schreibt einfach jeden beliebigen Wert, den Sie an sie weitergeben an die Konsole. In diesem Fall haben wir Folgendes weitergegeben: „Eine neue Runde hat gerade erst begonnen. Die Anführungszeichen sind sehr wichtig, da sie dem Computer sagen. „Dieser Text ist kein Code. Alles, was sich zwischen den Anführungszeichen befindet, wird als eine Zeichenkette gelesen – ein Stück Text, das genau das ist – als Text. Eine weitere wichtige Sache ist das Semikolon am Ende. Alle Code-Zeilen, die nicht in die nächste Zeile weitergehen sollen, müssen dies am Ende haben. Es macht das Debuggen wesentlich einfacher und viele Sprachen haben es nicht erforderlich gemacht. Wenn du das vergisst, wird dies der Computer als Fehler ausgeben.
Dies ist einfach die schließende geschweifte Klammer für die Funktion, d.h. die Funktion endet hier. Alles, was nach diesem Punkt kommt, ist nicht Teil der Funktion und sollte nicht ausgeführt werden, wenn die Funktion aufgerufen wird.
Wenn Sie sich einfach dieses 3-Zeilen lange Beispiel durchlesen, werden Sie feststellen, dass nichts in der Programmierung willkürlich ist. Alles, was Sie schreiben, hat seine eigene Bedeutung, bis hinunter zum kleinsten Symbol und das Schreiben des falschen Symbols kann deinen Code zum Absturz des Spiels bringen. Es sollte jedoch kein Problem mit diesem Code geben, also lasst uns zum Mapping-Teil kommen.
Mapping.
Schnappen Sie sich die Map, die Sie für dieses Experiment verwenden möchten und fügen Sie zwei neue Entitäten hinzu – ein logic\_auto und unsere wichtigste Entität – das logic\_script. Sie sollten bereits mit der logic\_auto-Entität vertraut sein, aber die logic\_script-Entität sollte für Sie ganz neu sein. Obwohl VScripts von fast jeder Entität aus ausgeführt werden können, ist die logic\_script Entität die _main_Entität. Es ermöglicht eine Vielzahl von verschiedenen Eingaben, die alle irgendwie mit dem angegebenen Skript kommunizieren oder geladen werden können. Die Entität hat drei verschiedene KeyValues – Namen, Entity-Skript und Skript Think Function. Der Name ist ziemlich selbsterklärend; es ist der Name, mit dem sich andere Entitäten auf ihn beziehen, genau wie bei jeder anderen Entität. Das Interesse daran sind die beiden nächsten KeyValues.
Zuerst einmal unsere Entity-Skriptdatei. Dies ist die .nut-Datei, die wir gerade oben erstellt haben. Wenn Sie unsere Anweisungen befolgen, wird es in „ Steam/SteamApps/Common/ GameStrike/csgo/go/scripts/vscripts“ gespeichert – wir haben unseres als „hns.nut“ gespeichert. Durch Anklicken des Buttons „Manage“ wird eine Liste aller von ihnen hinzugefügten Skriptdateien eingeblendet. Es sollte leer sein, da Sie noch keine hinzugefügt haben – was Sie durch Anklicken des „+“-Buttons tun können. Navigieren Sie zu ihrer .nut-Datei und öffnen Sie sie. Dadurch wird es in die Liste aufgenommen. Klicken Sie auf den entsprechenden Eintrag in der Liste und klicken Sie auf „Ok“. Sie sollten nun den Namen der Datei und Schlüsselwert des Entity-Skripts sehen. Fürs Erste ist das Alles, was wir wirklich tun müssen, wenn es um die logic\_script-Entität geht.
Bei der logic\_auto ist es dann so. Für diejenigen unter euch, die es noch nicht herausgefunden haben, ist dies die Entität, mit der wir die Funktion ausführen, die wir gerade zu Beginn einer jeden Runde geschrieben haben. Deaktiviere das Flag „Remove on Fire“ und springe in die Registerkarte „Outputs“. Erstelle eine neue Ausgabe, setze den Ausgabe-Namen auf „OnMultiNewRound“ und setzte das Ziel auf unsere logic\_script-Entität. Die Eingabe sollte „RunScriptCode“ lauten. Was diese Eingabe bewirkt, ist, dass sie eine einzelne Codezeile genau so ausführt, als ob sie in die .nut-Datei geschreiben worden wäre. Aufgrund eines Fehlers im Hammer wird die Verwendung von Anführungszeichen die VMF beschädigen, aber wenn das nicht der Fall wäre, könnten wir einfach „print“ schreiben und damit fertig werden. Wir können das aber nicht, also müssen wir stattdessen unsere eigene Funktion (startRound() aufrufen.
Erinnern Sie sich, wie wir die „printl“-Funktion durch die Eingabe von „printl()“ aufgerufen haben? Wir werden genau das Gleiche tun, um die startRound-Funktion aufzurufen. Stellen Sie daher den Parameter auf Override auf „startRound()“. Dadurch wird der Computer angewiesen, „die Funktion mit der Bezeichnung startRound zu finden und sie auszuführen“. Das ist wirklich alles, was es dazu zu sagen gibt. Kompilieren Sie die Map und führen Sie sie aus. Wir haben das gemacht und haben damit eine neue Runde gestartet.
Wir wissen jetzt, dass unsere startRoundFunction bei jedem Rundenstart aufgerufen wird, was großartig ist. Zeit, es so zu bearbeiten, dass es alle Spieler auf T in zufällige Requisiten verwandeln. Bevor wir das aber tun, sollten Sie vielleicht eine kurze Pause einlegen. Das Erlernen der Programmierung ist schwer und es wird in der Folge nur noch schlimmer. Deshalb sollten Sie sich am besten eine kleine Pause gönnen.
Scripting (Teil 2).
Jetzt ist es an der Zeit, den nächsten Programmierbegriff zu erläutern. Diesmal sind wir bei den Variablen angelangt. Variablen sind relativ einfach. Eine nette Analogie wären Schubladen, mit einer winzigen Note, die nach außen geklebt sind. Sie können eine Schublade mit der Bezeichnung „Bank“ haben, in die Sie alle ihre Bankpapiere einlegen oder Sie können eine Schublade mit der Bezeichnung „Kabel“ haben, in der Sie alle Kabel aufbewahren, die Sie im Laufe der Jahre gesammelt haben. Variablen in der Programmierung folgen im Großen und Ganzen dem gleichen Prinzip – Sie haben eine Variable mit einer Bezeichnung, die etwas in sich trägt. Mit dem einfachen Begriff „Kabel“ bezeichnen Sie die Kabel innerhalb der _Kabelschublade_. Indem Sie eine Variable mit der Bezeichnung „modelName“ erstellen und sie auf „modelss/player/tm\_phoenix.mdl“ setzen, können Sie einfach „modelName“ sagen und der Computer wird sofort wissen, dass Sie tatsächlich „modelss/player/tm\_phoenix.mdl“ meinen. Damit kann man sogar etwas verändern, dass sich in einer Schublade befindet, ohne den Namen ändern zu müssen.
Variablen werden mit der Syntax „local=“ erstellt – zum Beispiel „local squareOfFour = 16“. Das lokale Schlüsselwort besagt im Grunde genommen: „Wir erstellen eine neue Variable und sie kann nur innerhalb dieses Skriptes aus gelesen werden“, während der Name ziemlich selbsterklärend ist. Das Interessante daran ist, was sich hinter dem Gleichheitszeichen verbirgt. Es gibt viele verschiedene Arten von Variablen. Was du gerade oben gesehen hast, ist das, was als Ganzzahl bekannt ist. Eine ganze Zahl, wie z.B. 16, das ist nicht mehr als das. Wenn Sie Dezimalstellen (z.B. 12,89) wünschen, arbeiten Sie mit einem Fließkomma, das fast genauso funktioniert. Andere gängige Arten von Variablen sind Zeichenketten (über die wir vorhin gesprochen haben – es ist ein einfacher Text, der immer in Anführungszeichen eingeschlossen ist und dem Computer mitteilt, dass es sich um eine Zeichenkette und keinen Code handelt). Booleans (die entweder wahr oder falsch sind) und Arrays (die Listen von Variablen sind). Sie können beispielsweise ein Array der ersten 5 Nummern in der Fibonacci-Sequenz erstellen, indem sie „local fib = [0,1,1,1,1,2,3]“ tippen. Beachten Sie, wie die Liste mit einer eckigen Klammer beginnt und endet und wie jeder einzelne Wert durch ein Komma aufgeteilt wird). Eine weitere interessante Sache ist, dass eine Variable, die nichts in sich birgt, den Inhalt von „null“ hat.
Zurück zu unserem VScript. Dein Code sollte jetzt so ähnlich wie hier aussehen:
Woran wir jetzt arbeiten, ist, alle Spieler im Terroristenteam zu finden und sie zu einem zufälligen Requisitenobjekt zu machen.
Um dies zu erreichen, müssen wir uns die _Funktionsliste_ der VScripte in CS:GO kurz anschauen, Dies ist eine Liste aller _globalen Funktionen_, die überall in ihrem Code verfügbar sind und die Sie vor der Verwendung nicht erst schreiben müssen. Konkret möchten wir einen Blick auf die FindByModel()-Funktion werfen. Wenn Sie einen Blick darauf werfen, werden Sie sehen, dass es das Folgende über die FindByModel()-Funktion aussagt:
CEntities: Jede Funktion hat einen Elternteil. In unserem Fall hat die startRound()-Funktion unser hns.nut-Skript als Elternteil. Zufällig hat FindByModel „Entities“ als übergeordnetes Element. Wenn wir uns auf eine Funktion beziehen wollen, die sich außerhalb unseres eigenen Skripts befindet, müssen wir dem Computer genau mitteilen, wo er nach ihr suchen soll. Dies geschieht mit der Syntax von „.“, wobei der Punkt darauf hindeutet, dass was auch immer vor ihm ist, das übergeordnete Element von was auch immer nach ihm ist. Auf dieser Basis stellen wir sicher, dass der Computer die Funktion finden kann und damit keinen bösen Fehler macht.
FindByModel: Das ist einfach unser Funktionsname, wie wir bereits erfahren haben. Inzwischen wissen wir, dass wir, um diese spezifische Funktion zu finden, „Entities. FindByModel()“ schreiben müssen.
(handle, string)
Hier kommt es auf das Interessante an. Was wir hier haben, sind die beiden Werte, die wir an die FindByModel()-Funktion weitergeben – ein Handle und eine Zeichenkette. Wir haben bereits über Zeichenketten gesprochen, aber Handles sind eine neue Sache. Ein Handle ist nicht so sehr eine Variable, sondern ein Verweis auf etwas. Variablen können unglaublich fortschrittlich sein (z.B. können wir einen ganzen Player in einer Variablen speichern). Dieser Player hätte einfach seine eigenen Subvariablen (wie Velocity, Model, Size etc.), aber je weiter sie fortgeschritten sind, desto mehr Platz nehmen sie ein,
Daher ist es oft sinnvoll, einfach eine neue Variable zu speichern, die besagt: „Hier finden Sie das, worauf ich mich beziehe“, anstatt die vorhandene Variable zu kopieren. Genau das ist es, was hier passiert. Nach einem kurzen Test stellten wir fest, dass das Handle auf Entitäten verweist. Wenn Sie die FindByModel()-Funktion aufrufen, ist es offensichtlich, dass es mehr als eine Entität mit einem bestimmten Modell geben kann und deshalb brauchen wir eine Möglichkeit, jede einzelne dieser Entitäten zu durchlaufen.
Aufgrund von Einschränkungen der Programmiersprache ist es der Funktion nicht möglich, einfach zu sagen: „Hier ist ein Array aller Entitäten mit diesem Modell“ – sie kann nur mit einer Entität nach der anderen antworten. Valve hat sich entschieden, dies auf eine sehr einfache Weise zu umgehen. Das Handle, das Sie weitergeben, kann entweder Null (Nichts) sein oder eine Entität referenzieren. Wenn es Null ist, gibt die Funktionen ihnen einfach die allereste Entität im Array zurück. Wenn es sich jedoch um eine Referenz auf eine Entität handelt, findet die Funktion diese Entität im Array und zeigt ihnen stattdessen die nächste an.
Dies ist eine eher abstrakte Idee, so dass es vielleicht besser sein könnte, ein praktisches Beispiel dafür zu geben. Nehmen wir an, wir wollen jeden Spieler finden, der das Standard-Terroristenspielermodell hat. (Models/Spieler/tm\_phoenix.mdl). Als Erstes bitten wir die Funktion, die erste Entität mit diesem Modell zu finden, indem Sie „Entities.FindByModel(null, „modelss/player\_phoenix.mdl“) eingeben. Das wird uns unseren ersten Spieler geben. Aber was ist nun, wenn wir auch den zweiten Spieler kennenlernen müssen? Nun, das ist ziemlich simpel. Wir müssen nur einen Verweis auf den ersten Spieler speichern (z.B. mit der Aufschrift „local first = Entities.FindByModel (null, „modells/player/tm\_phoenix.mdl“)“. Der Computer liest den Funktionsnamen als Antwort der Funktion und setzt die Variable „first“ auf diese Antwort – die zufällig der erste Spieler ist. Wir schreiben dann „Entities.FindByModel(first, „modelss/player/tm\_phoenix.mdl“)“ und sagen der Funktion, dass sie mit dem ersten Spieler beginnen und den nächsten im Array nehmen soll. Wie können wir das im Code konvertieren?
Wir müssen nur weiter die zweite Zeile aufrufen und wir fangen mit dem ersten Spieler an und gehen bis zu letzten Zeile vor (beim Aufruf der FindByModel()-Funktion mit einem Handle, das sich auf den letzten Spieler bezieht, wird uns eine Antwort von „Null“ gegeben, da es keine weiteren Spieler mehr gibt. Ein erneuter Aufruf mit einem „Null“-Handle startet ihn von vorne und macht ihn so zu einer Schleife). Die programmiertechnische Art, dies zu schreiben, sieht wie folgt aus:
Lassen Sie uns den Code aufschlüsseln.
Inzwischen solltest du das wissen. Es wird einfach eine neue Variable mit der Bezeichnung „ply“ (kurz für „player“) erstellt und ihr Wert wird auf Null (oder gar nichts) gesetzt.
Das hier ist interessant. Vereinfachen wir das Ganze ein wenig.
Das macht es viel lesefreundlicher. Was ihr hier seht, ist ein While in einer Schleife. Wie der Name schon sagt, handelt es sich um eine Schleife – etwas, das immer und immer wieder abläuft. Das Besondere an der While-Schleife ist, dass sie so lange läuft, wie das, was in den Paranthen geschrieben wird, wahr ist. Wenn der Code innerhalb der While-Schleife ausgeführt wurde, kehrt er wieder nach oben in die While-Schleife zurück. Es wird dann geprüft, ob die Bedienung noch korrekt ist und in diesem Fall wird der Code erneut ausgeführt. Wenn es falsch ist, springt es an das Ende der While-Schleife und blickt nie wieder zurück.
Wie können wir das nutzen? Wie wir bereits gesehen haben, führt uns die wiederholte Ausführung der FindByModel()-Funktion durch jede einzelne Entität, die dieses Modell verwendet hat. Am Ende gibt er „Null“ zurück. Wenn die Funktion erneut mit einem Handle von „Null“ ausgeführt wird, beginnt alles von Neuem. Wir wollen nicht, dass so etwas passiert. Daher ist es einfach: Wir überprüfen, ob die Antwort der FindByModel()-Funktion Null ist und wenn ja, dann stoppen wir die Ausführung des Codes ( der Vergleich „!=“ bedeutet „ungleich“). Der Computer liest das Bedingte als „while“, d.h. der Code wird für die Dauer der Antwort ausgeführt, solange die Antwort etwas anderes als Null ist).
Das ist ziemlich einfach. Es speichert die Antwort der FindByModel()-Funktion in unseren Ply-Variablen, so dass es beim nächsten Aufruf der Funktion das nächste Modell im Array finden wird. Es wird dann der Text „Ein Spieler mit dem Terroristenmodell wurde gefunden. Dies ist der Code, der immer und immer wieder ausgeführt wird, bis jeder einzelne Spieler mit dem Terroristenmodell ihn durchgespielt hat. Das bedeutet, dass der Text genau so oft gedruckt wird, wie es Spieler mit dem Terroristenmodell gibt. Probiere es aus, speichere den folgenden Code und überprüfe es selbst. (es sei angemerkt, dass die „!=null“ vom Bedingten weg gekürzt wurde – der Computer liest bestimmte Dinge, einschließlich „null“, „0“ und andere ähnliche Wertem due „nichts“ bedeuten, als falsch, was bedeutet, dass der Computer sie immer noch gleich liest).
Als wir es umgesetzt haben, mit 5 Spielern in jedem Team, schrieben wir Folgendes auf die Konsole (mit 5 Spielern in jedem team):
Eine neue Runde hat gerade erst begonnen.
Ein Spieler mit dem Terroristenmodell wurde gefunden.
Ein Spieler mit dem Terroristenmodell wurde gefunden.
Ein Spieler mit dem Terroristenmodell wurde gefunden.
Ein Spieler mit dem Terroristenmodell wurde gefunden.
Ein Spieler mit dem Terroristenmodell wurde gefunden.
Erfolgreich. Wir wissen jetzt, dass wir bei einem Rundenstart jedem Terroristen etwas antun können. Super!
Schauen wir uns die Funktionsliste an, um zu sehen, wie wir das Modell des Players dann bearbeiten können. Das haben wir getan und wir haben das Folgende gefunden:
Das sieht schon so aus, wie wir es benötigen. Was würden wir also tun, wenn wir jeden Terroristen in eine Antenne verwandeln wollten („models/props/de\_dust/du\_antenna\_a_a.mdl“)?
Wie Sie sehen, wurde diese einfache Code-Zeile hinzugefügt:
Und was macht das hier? Nun, jede einzelne Entität in Source hat eine Funktion mit der Bezeichnung „SetModel“. Was wir hier tun, ist, die besagte Funktion eines jeden Spielers einfach als „models/props/de\_dust/du\_antenna\a.mdl“ zu bezeichnen. Dabei sollte das Spielermodell auf das Modell der Antenne eingestellt werden, das wir dann später so ändern können, dass es ein Modell nach dem Zufallsprinzip auswählt. Testen wir es aus.
Leider ist das Spiel abestürzt. Schauen wir es uns noch einmal an.
Schaut euch die Fehlermeldung des Spiels an („2/ – Spieler: UTIL/_SetModel; not precached; modelss/props/de\_dust/du\_antenna\_a.mdl“), wir können es ausprobieren. Jetzt stellt sich natürlich die Frage, wie wir das geschafft haben? Um das zu beantworten, müssen wir einen Blick darauf werfen, wie die Source-Engine mit Modellen umgeht. Wann immer eine Map geladen wird, schaut Source durch die Map und findet jedes benötigte Modell. Dazu gehören Spielermodelle, Requisiten in der Map, jedes Modell, das irgendwo verwendet werden kann. Es liest die Daten für alle diese Modelle von ihrer Festplatte und speichert sie in ihrem RAM – ein Prozess, der als Precaching bezeichnet wird. Da der Arbeitsspeicher um ein Vielfaches schneller ist als ihre Festplatte, bedeutet dies, dass Source in der Lage ist, neue Kopien dieses Modells viel, viel schneller zu erstellen, was die Ladezeit drastisch verkürzt. Optimiert für den allgemeinen Gebrauch.
Leider gibt es keine Funktion, um ein Modell mit Hilfe von VScripts vorab zu cachieren, so dass wir darauf zurückgreifen müssen, ein wenig hackig zu werden. Um dies zu tun, müssen wir Hammer wieder öffnen.
Mapping (Teil 2).
Wir wissen, dass Source jedes Modell, das in der Map zu finden ist, mit Precaches versehen hat. Wir können dies leicht missbrauchen, indem wir einfach eine Entität (verwenden wir prop\_dynamic für dieses Beispiel – es sei darauf hingewiesen, dass prop\_static nicht zu funktionieren scheint) mit dem gleichen Modell in unserer Map platzieren und Source zwingen kann, es vorab zu speichern. Für hide „n“ seek sollte das kein großes Problem sein, da alle Requisiten, in die der Spieler verwandelt werden soll, bereits auf der Map vorhanden sein sollten, aber für unsere kleine Beispielmap müssen wir sichergehen, dass wir bereits jeden Requisiten, den wir irgendwo auf der Map verwenden, haben. Wir wollen jedoch nicht, dass diese Template-Modelle für die Spieler sichtbar sind, also schaffen wir einen winzigen No-Draw-Raum außerhalb der Map, in dem wir die Modelle platzieren (um ein Auslaufen der Modelle zu vermeiden). Andernfalls könnten wir sie einfach in die Map einfügen, wenn es um die Ästhetik nicht geht oder wenn wir eine tatsächliche Map erstellen, in die sie passen würde).
Sie können ein Beispiel für einen solchen Raum in der unter den Ressourcen verknüpften Beispielmap sehen oder Sie können einfach selbst einen 64×64-Raum mit der Nodraw-Textur erstellen und alle Modelle dort platzieren. Vergewissern Sie sich, dass die von ihnen platzierte prop\_statische Entität genau das gewünschte Modell hat („modelss/props/de\_dust/du\_antenna\_b.mdl“ reicht zum Beispiel nicht, wenn wir das Spielermodell auf „modelss/props/de\_dust/du\_antenna\_a.mdl“ einstellen wollen). Kompiliere die Map neu und versuche es noch einmal. Nun klappt es.
Damit ist unser Tutorial zu Ende. Ich hoffe, dass wir ihnen einen kurzen Einblick in VScripts bieten konnten.
Vielen Dank für ihren Besuch.
Leave A Comment