Nicht eingeloggt
Registrieren

UnrealScript Einstieg

UScript Einstieg für UT2k4 I - Grundlagen 1
Erstellt von: Ausbrecher ; Korrigiert von KillerVirus



Einleitung:

Mit diesem Tutorial möchte ich einen Einstieg in UScript für Unreal Tournament 2004 ermöglichen.
Als Programm benutze ich, damit jeder von euch es einfach nachmachen kann, den UnrealED welcher im UT2004\System\ Ordner zu finden ist.
Ich möchte hier erstmal die Grundlagen erklären, und warum der ActorBrowser so aussieht, wie er nun mal aussieht. Dahinter steckt nämlich eine recht ausgefallene Idee, welche wichtig ist, um das Scripten zu erlernen, und eine Ähnlichkeit mit der Programmiersprache Java aufweisst.
Nach diesem Tutorial könnt ihr zwar noch nicht unbedingt einen eigenen Mutator erstellen, aber das sollte auch nicht Ziel sein.
Das Ziel ist viel mehr euch den Aufbau zu erklären und euch Verständnis zu lehren.
Ich denke mal, es werden mehr Tutorials über's Scripten folgen, diese nützen euch aber absolut nichts, wenn ihr diese Grundlagen nicht könnt.
Auch wenn es langweilig erscheint, durch diese Grundlagen müsst ihr leider Gottes durch, und Grundlagen soll auch nicht bedeuten, dass ihr danach alle Grundlagen wisst. Es gibt sovieles was man wissen muss, das man das garnicht in ein Tutorial verfassen kann. Ihr werdet also auch in den weiteren Tutorials vermutlich auf Grundlagen stoßen. Hier will ich nun mal die Wichtigsten nennen.


Vorraussetzung:

Man sollte vielleicht schon hier und da mal eine Map für Unreal Tournament 2004 erstellt haben, den Actorbrowser zumindest gesehen haben, und das Wissen über einige wenige Actoren haben.
Das sind zwar keine Grundvorraussetzungen, aber dies macht das Erstellen von Scripten um ein Vielfaches einfacher.


Basiswissen:



UScript basiert auf der Stammbaumansicht (Treeview) welche bei der Klasse (class) "Object" beginnt. Diese Object Klasse hat mehrere Unterklassen (sub class), wie zum Beispiel die Actor-Klasse.
Diese Actor-Klasse hat nun weitere Unterklassen wie Pawn, Info, Decoration, ...
Jede dieser Unterklassen hat einen Sinn. Der Pawn zum Beispiel ist eine bewegliche Spielfigut (Monster / Fahrzeuge / Spieler [Bots und Player]) . Der Controller hat als Unterklasse zum Beispiel den Bot-KI, die Fähigkeiten der Spieler, einige Consolenbefehle, etc.

Die Klassen sind stets abhängig von Parent Klassen. Ich kann also Variablen von Parent Klasse wie z.B. von Objekt (welche ja der Parent ist) abfragen. Genauso kann ich auch die Parent Strukturen und Funktionen benutzen.
Falls ich aber nun in meiner Klasse den gleichen Funktionsnamen benutze wie in der Parent Klasse, wird die Funktion des Parent überschrieben.
Wenn nun in Object eine Funktion wäre, die "GetMyName" heißen würde, und ich in meiner Subclass ebenfalls diesen Funktionsnamen hätte, würde die Funktion aus der Object-Klasse erstmal überschrieben werden.
Diese kann man zwar noch aufrufen, es geht aber erst, nachdem man zusätzlichen Code eingefügt hat. Dieser Code würde in dem Falle "super.GetMyName();" heißen.
Nun erläutere ich mal unsere erste Code-Zeile. Super bedeutet, dass der Parent gemeint ist; GetMyName bedeutet, dass die Funktion des Parent gemeint ist.
Super ist also stehts der Parent.


Die Syntax:

Das ; (Semikolon) bedeutet, dass hier der Aufruf eines Befehls beendet wird.
Die runden Klammern () werden für mehrere Dinge benutzt. Einerseits kann man diese in mathematischen Formeln benutzen und damit andeuten, dass diese als erstes, wie in der Mathematik, berechnet werden sollen, andererseits werden diese bei Funktionsaufrufen benutzt, um klarzustellen, dass eine Funktion benötigt wird, und um benötigte Parameter beizulegen. Bei der Funktionsdeklaration werden diese auch benötigt um Parameter zu definieren.
Eckige Klammern werden dazu benutzt, um einem Array einen Index zu geben.
Geschwungene Klammern deuten auf einen Block. Bei Funktionen, welche immer die geschwungenen Klammern haben müssen, bedeutet dies, dass der Code zwischen den Klammern zu dieser Funktion gehört.
Als Beispiel erstelle ich hier eine Funktion:

Code:
function bool MeineFunktion(string Parameter1, string Parameter2) 
So deklariert man beispielsweise eine einfache Funktion. Beginnen wir am Anfang: function bedeutet, dass es sich um eine Funktions-Deklaration handelt, bool sagt aus, dass es sich um einen boolischen Rückgabewert handeln soll, MeineFunktion ist der Name der Funktion, ( ) heißt, dass zwischen diesem Block die Parameter stehen sollen, string Parameter1, string Parameter2 sind 2 Parameter des String Types. bedeutet, dass alles zwischen diesen Klammern zu dieser Funktion gehört.
Pro Klasse darf jede Variable und Funktion nur einmal Deklariert werden!

Wichtig zu merken ist, dass Funktionsnamen, Classnamen, Variablen, Parameter, Datentypes, Strukturen, etc. keine Leerzeichen oder Sonderzeiche mit Ausnahme von - und _ beinhalten dürfen! Um somit eine gute Worttrennung deuten zu können, schreiben die meisten Programmierer die Worte in Einem und betonen die Anfangsbuchstaben mit einem großgeschriebenen Buchstaben.
z.B.: MeineKlasse, HierGehtsLos, WhatToDoNext, ...

Auf Groß- und Kleinschreibung wird nicht geachtet. Dies bezeichnet man auch als "non Case Sensitive". Hierbei wird zwischen Groß- und Kleinschreibung nicht unterschieden. Somit ist die Variable Test auch gleich die Variable tESt. Es spielt auch keine Rolle, ob ich eine Funktion, die PostBeginPlay() heißt, mit postbeginplay() aufrufe.


Aufbau:

Der Aufbau einer Klasse hat einige Grundlagen. Im folgenden Beispiel habe ich eine Klasse erstellt, die "ErsteKlasse" heißt und eine Unterklasse von "Actor" ist.

Code:
class ErsteKlasse extends Actor;
defaultproperties 
Dies ist der Grundaufbau jeder Klasse. Er beginnt mit class gefolgt von dem Klassennamen. Anschließend wird der Parent nach einem extends angegeben.
Die Defaultproperties sind über den in UnrealED eingebauten Scripteditor nicht zu sehn!! Diese können aber auf anderem Wege eingestellt werden, zu dem ich in einem anderen Tutorial mehr sage. Wundert euch also nicht, wenn der Defaultpropertie Abschnitt fehlt.


Client / Server

UScript hat einen Grundlegenden Unterschied zu anderen Sprachen. Man kann Scripte und Klassen auf dem Server oder auf dem Client laufen lassen. Dies ist sehr performanceschonend.
Anhand eines Bots kann ich folgendes Beispiel dafür nennen: Der Server berechnet, welcher Weg der Klügste für den Bot ist, sendet die Daten an den Client, der Client verwaltet die Position und bestimmt dann die Animationen, die der Bot beim Laufen darstellen soll. Anhand der Waffe könnte man folgendes Beispiel nennen: Der Client betätigt die Maustaste -> Nun wird dem Server gesagt, dass der Spieler schießen will -> Der Server überprüft, ob noch genügend Munition vorhanden ist und ob er überhaupt schießen kann -> Wenn er schießen kann wird an den Client ein OK gegeben, der Client spielt die Schussanimation ab und lässt einen Emitter spawnen, der Server berechnet, wer getroffen wurde -> Alle Getroffenen werden an den Client übermittelt -> Der Client spielt bei den getroffenen die Animation ab.
Man sieht, da läuft eine ganze Menge im Hintergrund ab. Aber wie geht das im UScript?
Es gibt 2 Arten von Funktionsdeklarationen. Zum einen gibt es die Funktionen, die auf dem Server ausgeführt werden sollen, zum anderen gibt es die Funktionen, die auf dem Client ausgeführt werden sollen.
Diese Funktionen unterscheiden sich im Quelltext nur von der Deklaration.

Code:
function ServerSide() simulated function ClientSide() 
Wir sehen, der einzige Unterschid zwischen den beiden Funktionen ist, dass das eine ein simulated an sich hat. Dieses simulated bedeutet, dass die Funktion ClientSide ausgeführt wird.

Des Weiteren gibt es den so genannten Replication-Block. Zu den Replication-Block sage ich ein anderes Mal mehr. So sieht er ungefähr aus, ist aber noch recht uninteressant.

Code:
replication 
Übersicht:

Die Übersicht (auch Programmierstiel genannt) ist sehr wichtig. Stellt euch vor ihr habt einen Code mit 20.000 Zeichen. Da würdet ihr sehr schnell die Übersicht verlieren. Darum haben die Programmierer sich folgende Tricks angeeignet:
Sie machen des Öfteren Leerzeilen, zum Beispiel zwischen 2 Funktionen einfach mal 2 leere Zeilen, zwischen Variablendeklaration und Funktionen werden auch mal so 3-4 leere Zeilen gemacht, ...
Allein dies hilft schon mächtig.
Des Weiteren werden für fast alle Befehle und Funktionsaufrufe eine eigene Zeile benutzt.
Man könnte Theoretisch einen Quellcode welcher 5 Funktionen und 1000Zeichen in nur einer Zeile schreiben. Auch die oben gezeigten Codeblöcke könnte man in nur eine Zeile schreiben. Dies wäre allerdings recht unübersichtlich und man würde schnell durcheinander kommen. Also nach jedem Befehls/Funktionsaufruf (Semikolon) wird eine neue Zeile angefangen .
Auch bei Stukturblöcken wird für Übersicht gesorgt. Somit wird in jeder Zeile des Strukturblocks (eingeleitet mit einer geschweiften Klammer) 1x mit Tab eingerückt. Es werden auch Kommentare eingefügt. Kommentare sind oft kurz gefasste Texte, welchen den Ablauf beschreiben.
Eine Möglichkeit ist das Einfügen von Kommentaren über // . Alles, was nach den beiden Schrägstrichen steht, zählt als Kommentar, welcher bis zum Ende dieser Zeile gerht. In der nächsten Zeile ist der Kommentar automatisch beendet. Eine andere Möglichkeit ist das einfügen von Kommentaren mit /* zum Begin des Kommentares, und */ zum beenden. Somit kann man mehrzeilige Kommentare verfassen.

Code:
/* Hier beginnt ein
mehrzeiliges Kommentarfeld. Hier kann ich jetzt Text einfügen, der nicht
verarbeitet wird. Alle Funktionen, Variablen und Berechnungen die in einem
Kommentarfeld stehen, werden ignoriert.*/
function Uebersicht() blabla();
}
blablabla();
bla();
}
Wie ihr seht, wird nach jeder Zeile, die mit einer geschwungenen Klammer aufgehört hat, um ein Tab aufgerückt. Dies hilft ebenfalls gewaltig in der Übersicht.
Des Weiteren sollten viele Kommentare gesetzt werden.
Auf Wikipedia gibt es mehr zum Thema Programmierstiel


Der im UED eingebaute Scripteditor:

Der Scripteditor (ihr habt ihn sicherlich schonmal gesehn, indem ihr auf einen Actor 'nen Doppeklick gemacht habt) hat leider einige Fehler und muss sehr sensiebel behandelt werden.
Er ist auf keinen Fall für komplexe Mutatoren geeignet, reicht uns aber für's erste aus.
Was ihr auf keinen fall machen dürft, ist die vorgegebene Klassendefinition verändern. Ihr könnt die extras wie Config, placeable und ähnliche hinzufügen, aber macht am besten nichts an dem Klassennamen, an dem Parent, an dem class und an dem extends. Dies kann schnell das komplette Script für immer zerstören. Ist so eine Macke an dem Script-Editor, aber das ist ja auch nicht relevant da man da nie was ändern muss.
Des Weiteren hat der Editor so seine Probleme mit der farblichen Darstellung, was eigentlich nur der Übersicht dient. Wenn ihr die Darstellung aktualisieren wollt, macht einfach im linken Menu einen Doppelklick auf die Klasse, die ihr aktualisieren wollt und schon stimmt die Darstellung wieder.


Das erste Script wird erstellt:

Nun hatten wir genug Theorie. gehen wir mal ein Bisschen an die Praxis.
Nun wollen wir unser erstes Script erstellen. Erstellt dazu eine kleine Map. Sie muss nicht groß sein, sie sollte aber zumindest 1 PlayerStart und einen Substracted Brush beinhalten, damit wir auch die Map starten können. Nun öffnet den ActorBrowser. Unser erstes Script soll eine Subclass von Actor sein, also machen wir rechte austaste auf Actor und gehen auf "New". Es erscheint ein Fenster, in dem wir bei Package "myLevel" und bei Name "ErsteKlasse" eingeben. Dann drücken wir auf OK und nun sollte sich der Scripteditor öffnen. Nun gebt dort einfach den noch fehlenden Code ein, sodass das ganze ungefähr so aussieht:

Code:
class ErsteKlasse extends Actor
placeable;
var() string LogString;
function PostBeginPlay() 
Der Scripteditor hat oben eine Leiste mit verschiedenen Schaltflächen. Drückt dort auf "Compile Changed". Am unteren Rand sollte nun stehen, dass so und soviel Zeilen erfolgreich compiliert wurden.
Nun schliesst den ScriptEditor einfach, geht in den ActorBrowser, und nun sollte auch schon unter Actor irgendwo "ErsteKlasse" stehen. Ist dies nicht der Fall, aktualisiert die Liste, indem ihr im ActorBrowser einfach "Show placeable only" deaktiviert und wieder aktiviert. Spätestens jetzt sollte die Klasse angezeigt werden.
Markiert die Klasse und fügt diese einfach nun in eure Map ein.
Macht nun einen Rechtsklick auf den Actor, geht in die Properties und in der Liste dürfte nun eine Sektion sein, die "ErsteKlasse" heißt. Dort findet ihr auch die Einstellung "LogString", wo ihr nun einen BlindText eingeben könnt. Startet nun mal eure map, beendet danach sofort wieder UT, öffnet die UT2004.log Datei welche sich im UT2004\System\ Ordner befindet und dort könnt ihr nun euren Text irgendwo begutachten.

Nun gehen wir das Script mal durch.
Das placeable in der Classdefinition bedeutet, dass man diesen Actor in seiner Map plazieren kann.
var bedeutet, hier wird eine Variable definition begonnen, die () danach bedeuten, dass dies eine Variable ist, die über das Properties Menu einstellbar ist.
String heißt, dass es sich bei der Variable um einen Text handelt, LogString ist der Variablen Name, mit dem wir Zugriff auf den eingegebenen Text haben und das ; bedeutet das die Variablendefinition hier zu Ende ist.
Nun haben wir hier eine Funktion die PostBeginPlay heißt. Diese Funktion wird beim Laden eines jeden Actors aufgerufen. Da wir wollen, dass es auch richtig geladen wird, sorgen wir mit super.PostBeginPlay(); dafür, dass auch die Funktion, welche in den Parents festgelegt wurde, aufgerufen wird. Bei PostBeginPlay sollte eigentlich immer auch super.PostBeginPlay(); aufgerufen werden da es sonst zu Initialisierungsfehlern kommen könnte.
log(); ist eine in Object definierte Funktion die wir in unserer Klasse aufrufen. Diese Funktion veranlasst einen Parameter in die UT2004.log Datei zu schreiben.

Falls es euch aufgefallen ist, als ihr im Spiel wart, konnte man den Actor sehn. Da war plörtlich der blaue Adlerkopf bei euch in der Map. Das sieht natürlich nicht so toll aus. Das werden wir nun auch ändern, den jetzt lernen wir die Defaultproperties kennen. Geht also in den ActorBrowser und macht einen Rechtsklick auf euren Actor. Dort geht ihr nun im Menu auf "Default Properties". Hier könnt ihr nun Standardwerte eingeben, die die Actoren beim plazieren in die Map haben sollen. Als erstes wollen wir, dass der Actor nicht sichtbar ist. Dazu geht ihr in Advanced und stellt bHidden auf True. Nun wollen wir auch gleich einen Standard LogString Wert haben, also gehen wir auf ErsteKlasse und geben einen LogSting ein. Nun einfach das Defaultproperties Fenster schließen und ersetzt nun mal euren alten Actor mit dem neuem. Wenn ihr nun in die Properties geht, seht ihr gleich, dass die Standartwerte schon eingetagen sind.
Es gibt auch noch die fortgeschrittenen DefaultProperties. Um in diese zu gelangen, gebt in den UnrealED, wo ihr unten ein "Command" eingeben könnt, den Befehl ein: editdefault class=<package>.<class> ein. In unserem Fall würde also der Befehl "editdefault class=myLevel.ErsteKlasse heißen.
Soviel jetzt erstmal zu den DefaultProperties.

Dieses Script war doch schonmal sehr einfach. Nun machen wir das ganze mal bisschen komplexer.

Code:
class ErsteKlasse extends Actor
placeable;
var() string LogString;
var() string LogString2;
function PostBeginPlay()  else }

Wie gesagt, jetzt machen wir das ganz noch bisschen komplexer:
Wir haben nun eine zweite LogStringVariable, in der wir noch einen anderen BlindText haben können.
Des Weiteren hat sich unsere Funktion ein bischen erweitert.
Fangen wir mal bei der Funktion an. local bedeutet, dass wir hier eine Lokale Variable haben. Locale Variablen können nur innerhalb dieser Funktion benutzt werden und existieren außerhalb dieser funktion nicht mehr. Außerdem müssen Locale Variablen immer gleich nach der Funktionsdeklaration deklariert werden, und nicht erster weiter unten im Quellcode. Dies ist performanceschonender. Wir haben nun also 2 Variablen und eine lokale Variable.
Als nächstes machen wir was ganz komisches. rnd=frand();
frand(); ist, wie das geschulte Auge sehen kann, eine Funktion da es mit (); aufhört. Daran erkennen wir eine Funktion. Diese Funktion liefert uns einen zufälligen Rückgabewert zweischen 0.00 und 1.00
Dies ist also ein Zufallsgenerator. wir geben der lokalen Variable "rnd" also einen Wert, der von einem Zufallsgenerator generiert wurde.
In der nun folgenden Zeile lernen wir etwas Wichtiges kennen. Den sogenannten IF-Block. Hier stellen wir eine Abfrage. Für die, die nicht so fit in Englisch sind, If bedeutet auf deutsch "Wenn" und Else bedeutet "Ansonsten"
Wenn also das was in der Klamma steht zutrifft, soll der darunterfolgende Code ausgeführt weden, ansonsten soll der Code unter dem Else ausgeführt werden. Bei uns heißt das im Klartext also "Wenn die Zufallszahl größer ist als 0,5 , dann soll log(LogString1); ausgeführt werden, ansonsten soll log(LogString2); ausgeführt werden.
Compiliert einfach das Script, fügt den Actor in eure Map ein, stellt die Properties ein, testet die Map, schaut euch die LogFile an, und ihr werdet seh'n, das zufällig LogString1 oder LogString2 in der LogDatei steht.
Ihr könnt gerne die Map auch nochmal und nochmal testen. Ihr habt soeben einen kleinen Zufallsgenerator erstellt der euch einen von 2 Zufälligen Texten in eine LogDatei schreibt.



Fachbegriffe, Datentypen und Strukturen:

Script: Ein Script besteht aus Syntax wie bei dem Programmieren. Jedoch benötigt ein Script einen Interpreter (In diesem Fall UT) der das Script verarbeitet
Programm: Ein Programm braucht im Gegensatz zu einem Script keinen Interpreter sondern läuft Selbstständig.
Quelltext / Quellcode / Sourcecode: Der Quellcode ist der für Menschen lesbare Programmiertext.
Syntax: Die Syntax ist der Aufbau der Sprache. Manche Sprachen erfordern ein Semikolon am ende eines Befehls, manche nicht. Bei manchen wird der Index eines Arrays in eckiger Klammer geschrieben, bei manchen in Runden. Dies ist alles Teil der Syntax.
Maschinencode: Der Maschinencode ist der für Menschen nicht lesbare Text.
Compiler: Ein Compiler wandelt den Quellcode in Maschinencode um.
Methode: Eine Methode ist ein Aufruf von einer Verarbeitung von Daten.
Function: Eine Funktion ist ein Methode MIT einem Rückgabewert.
Event: Ein Event wird von einem Ereignishandler aufgerufen.
Variable: Ein Datenbehälter in dem man Datensätze hinterlegen kann.
Datentyp: Jede Variable und Rückgabewert hat einen Datentyp. Es gibt verschiedene Datentypen auf die ich noch eingehen werde.
Class: Eine Class (im Deutschen Klasse) ist ein Teil des UnrealScripts und wird beim Objektorientiertem Programmieren benutzt.
Instanz: Eine Instanz ist eine Variable welche eine Klasse als Datentyp beinhaltet. (Bsp: Im Actorbrowser hat man die Klasse Light, wenn man diese in die Map einfügt hat man eine Instanz von dieser Klasse eingefügt und nicht die Klasse an sich.)
SubClass: Eine Unterklasse einer bereits vorhandenen Klasse.
Deklarieren / definieren: Funktionen, Strukturen, Variablen, .. müssen erster erstellt werden damit man diese Nutzen kann. Dies nennt man deklarieren.
Parameter: Ein Parameter sind Variablen mit denen man Werte eine Function / Methode oder an ein Event weiterleiten kann.
Actor: Als Actor bezeichnet man Rang niedrigere Klasse die den Actor als Parent hat.
Parent: Der Parent ist eine Rang höhere Klasse. Die Höchste Klasse ist in UT2004 die Object-Klasse welche an aller erster stelle steht und auf der alles Beruht.
BlindText: Ein X-Beliebiger Text der grad zum Testen ist wie "gdsjgjgsdfa jsfk nja" oder auch "Test 1"


String: Zeichenkette
Byte: Positive Zahl zwischen 0-255 (ohne Kommastellen)
Integer (int): Positive und Negative Zahl zwischen -2147483648 und 2147483648
Float: Positive und negative Zahl mit Kommazeichen
Bool: Boolischer Wert. Entweder 1 (True) oder 0 (False)
Name*: Ein Identifizierer

Rotator*: Rotierungswert in Pitch, Yaw und Roll (int)
Vector*: Ein Vector-Wert mit X,Y und Z Koordinaten (Float)
Color*: Ein 32bit Farbwert mit R(Rot), G(Grün), B(Blau) und A(Alpha) (Byte)
Plane*: Rotator mit zusätzlicher W-Koordinate (Float)
GUID*: Beinhaltet das A,B,C und D (Int)
Coords*: Beinhaltet Origin, XAxis, YAxis und ZAxis (Vector)
Range*: Beinhaltet Min und Max (Float)
RangeVector*: Beinhaltet X, Y und Z (Range)
Weitere Strukturen auf BeyondUnreal

* Nur in UScript vorhaden



Erstellt von: Ausbrecher ; Korrigiert von KillerVirus

 

UScript Einstieg für UT2k4 II - Grundlagen 2


Voraussetzung:

Man sollte zuvor UScript Einstieg für UT2k4 II - Grundlagen 2 gelesen haben.


Fortsetzung:

Wir haben uns soeben in dem Tutorial "UScript Einstieg für UT2k4 I - Grundlagen 1" einen Zufallsgenerator gebaut. Dies ist für den Anfang schon recht nett aber das reicht uns natürlich nicht. Leider ist das Grundwissen noch nicht so hoch das wir jetzt gleich an das Mod-Scripten gehen können, also eignen wir uns den Rest des Grundwissens an.

Code:
class ErsteKlasse extends Actor
placeable;
var() string LogString;
var() string LogString2;
var(Zahler) int Min;
var(Zahler) int Max;
function PostBeginPlay()  else LogDieZahlen();
}
function LogDieZahlen()                
}
Auch wenn es auf dem ersten Blick nicht danach aussieht, hier ist eine ganze Menge neues Zeug dabei. Fangen wir als erstes mal mit den neuen 2 Variable an.
var(Zahler) int Min; Wir haben wie bei jeder Variable die für das ganze Script ist, ein var was dem Script sagt das hier eine Variable definiert wird.
Des weiteren haben wir nun wieder Klammern daneben, was bedeutet das diese in den DefaultProperties zu finden ist. Diesmal steht allerdings Zahler zwischen den Klammern. Dies bedeutet, das wir eine neue Sektion in den DefaultProperties erstellen wollen welche Zahler heisst, und in dieser Sektion soll diese Variable stehen. Als Datentyp benutzen wir den int (Integer) welche ja positive und negative aber keine Kommazahnelwerte sind. Das gleiche haben wir mit der zweiten Variable.

Nun gucken wir in die Funktion "PostBeginPlay()" hier sehen wir, das ich etwas am ende eingefügt habe. LogDieZahlen(); bedeutet, das wir eine Funktion aufrufen wollen, welche "LogDieZahlen" heisst. Diese Funktion existiert noch nicht, da es sich um eine eigene Funktion von mir handelt, also habe ich die in den Nächsten Zeilen mal erstellt.
Nun verlassen wir die Funktion "PostBeginPlay()" und gucken uns den nächsten Funktions-Block an. Wir haben mir einem function begonnen, Das bedeutet das wir hier eine Funktion deklarieren wollen. Das darauf folgende LogDieZahlen steht für den Funktions-Namen mit dem wir auch diese Funktion aufrufen können. Die Klammern Starten den Parameter-Block. Da wir aber keine Parameter habe schliessen wir auch diesen Block gleich.
Nun kommt die geschwungene Klammer welche ja bedeutet das hier der Inhalt der Funktion beginnt.
Wir haben wieder eine Lokale Variable erstellt, diesmal mit dem Datentyp eines Integers, und als Variablenname i . Um die Übersicht zu behalten, haben sich die Programmierer dazu entschlossen das i als Zählervariable in ihren Richtlinien festzulegen. Wenn ihr also eine Variable seht, die i heisst, dann ist das ui 99% eine Zählervariable des Datentyps int.
In der nächsten Zeilen haben wir das sogenannte for. For ist eine Schleife, welche eine Variable Zählt bis ein Ergebnis zutrifft. So auch hier. Wenn wir nun mal in die Klammern rein gucken finden wir 3 Blöcke welche mit einem ; getrennt wurden.
Als erstes haben wir i=Min; Hier legen wir den Startwert fest. i Soll also den Wert der Min Variable haben. Im Nächsten Block finden wir die Bedingung welche der Schleife sagt, bis wann die Schleife Zählen soll.
Wir haben hier gesagt, die Schleife soll solange Zählen solange i kleiner oder gleich Max ist. Sobald i also grösser ist als der Maximalwert, soll die Schleife beendet werden. Im letzten Block sagen wir der Schleife, wie er mit der i Variable umgehen soll. ++ wird bei Integer Variablen benutzt und bedeutet soviel wie i + 1. Also er soll immer 1 Addieren.
Im gerammten bedeutet die Schleife also Fange bei dem Minimum Wert an, Mach solange bis bis wir über den Maximalwert drüber sind und beende dann, und Addiere immer 1 zu unserer Zahl.
Nun haben wir hier wieder geschweifte Klammern, welche den Schleifen block festlegen. Alles was zwischen diesen Klammern ist, gehört zu unserer Schleife.
Als nächstes haben wir ein einen Aufruf der log Funktion. Diesmal sieht unser Log aber schon anders aus. Wir teilen den Inhalt mal auf. Als erstes haben wir hier mal den Teil "Es wurde die Zahl". Wir haben absichtlich diesen Teil in Anführungszeichen gesetzt. Dies bedeutet das hier eine Zeichenkette steht, und kein Quellcode. Diese Anführungszeichen sind Pflicht bei festgelegten Texten. als nächstens haben wir den Teil @ . Das @ bedeutet, das die zu loggende Zeichenkette noch nicht beendet ist. Alternativ gibt es auch das $. Beide dieser Zeichen bedeuten, das die Zeichenkette noch nicht ihr Ende gefunden hat, jedoch gibt es einen Unterschied. Bei @ wird ein Leerzeichen gesetzt, bei $ geht die Zeichenkette direkt weiter. Nun haben wir unsere Variable i. Wir brauchen hier keine Anführungszeichen da dies ja Quellcode ist, und kein Festgelegter Text. Nun haben wir wieder ein @ gefolgt von einer festgelegten Zeichenkette "geloggt".
Diesen Code Compilieren wir, setzen den Actor in unsere Map, legen in den Properties die Min und Max Variable fest welche nun in der Sektion Zahler zu finden sind, und starten dann die Map.
Wenn wir nun in die LogDatei gucken, sehen wir, wenn wir als Min = 3 und Max = 6 gesetzt haben folgendes:

Zitat:
ScriptLog: Es wurde die Zahl 3 geloggt
ScriptLog: Es wurde die Zahl 4 geloggt
ScriptLog: Es wurde die Zahl 5 geloggt
ScriptLog: Es wurde die Zahl 6 geloggt
Wenn ihr das bisher kapiert habt, könnt ihr richtig stolz gucken. Ihr habt bisher eine ganze Menge gelernt und wenn ihr euch mal die Scripte von Epic anguckt, dürftet ihr diese nun schon zum Teil kapieren.


Die erste Wirkung:
Wir haben unseren ersten Actor erfolgreich beendet. Nun wollen wir aber auch einen Actor, der einen Sinn ergibt.
Erstellt euch nun einen zweiten Actor in myLevel mit dem Namen "ZweiterActor" und als Parent nehmen wir "Actor".

Dieser Actor soll folgenden Sinn haben. Er soll nach x Sekunden, ein Monster erscheinen lassen, diesem Monster eine Vordefinierte Gesundheit geben, und die Geschwindigkeit Skalieren, mit der sich das Monster fortbewegt. Es wird also eine Menge neues Zeit dazu kommen.

Code:
class ZweiterActor extends Actor
placeable;
var() class<Monster> MonsterTyp;
var() float Zeit;
var() int Gesundheit;
var() float Geschwindichkeit;
Dies sind die Varaiblen die wir benötigen. Hier haben wir schon unsere erste neue Variable, nehmlich die class gefolgt von dem Klassenname in spitzen Klammern. Dies bedeutet das wir hier die Klasse in diese Variable setzen. Man bemerke aber, das wir hier die Klasse, und keine Instanz der Klasse haben. Mit Hilfe dieser Klassendefinition kann man Über UScript Instanzen dieser Klasse erstellen. Dies werden wir nachher auch gleich machen. Da dies eine Property Variable ist, können wir nachher über das Property-Fenster die genaue Klasse welche eine Unterklasse von Monster (Object -> Actor -> Pawn -> UnrealPawn -> xPawn -> Monster) sein muss, auswählen.

Dann machen wir mal weiter.

Code:
class ZweiterActor extends Actor
placeable;
var() class<Monster> MonsterTyp;
var() float Zeit;
var() int Gesundheit;
var() float Geschwindichkeit;
function PostBeginPlay() function Timer() 
Wir haben hier ebenfalls unste PostBeginPlay() Wir rufen wieder um Initialisierungsfehler zu vermeiden die Super Funktion von PostBeginPlay auf. Als nächstes rufen wir die Funktion setTimer() auf. SetTimer ist eine Funktion welche nach einer angebenen Zeitspanne automatisch die Funktion Timer() aufruft. setTimer benötigt 2 Parameter. Zum ersten eine Sekundenanzahl (als float Variable), zum anderem einen Booleschen Wert. Ist dieser Wert auf False, wird nur einmal die Zeit ablaufen und nur einmal Timer() aufgerufen, steht der Wert auf True, wird immer und immer wieder diese Zeit ablaufen und auch immer und immer wieder Timer() aufgerufen.

Nun schauen wir uns mal die Timer() Funktion genauer an. In dieser Timer() Funktion wird nun sich alles abspielen. Wir wollen nun ein Monster erscheinen lassen, und die Werte dieses Monsters ändern.

Code:
function Timer() 
Wenn wir auch dieses Script testen sehen wir, das Des ausgewählte Monster tatsächlich schon erscheint. Dieses Script geht also schon. Nun schauen wir uns das ganze mal an.
Wir haben eine lokale Variable welche ein Monster ist und M heisst. wir brauchen hier kein class<Monster> Da wir ja keine Klasse wollen, sondern wir wollen eine Instanz dieser Klasse. Wir wollen ja nicht den Quellcode des Monsters erscheinen lassen, sondern das endgültige Monster. Die Variable M ist also ein komplett selbst denkendes Monster welches in unser Script eingebunden wird.
Im Script nützt es uns also nichts.
Mit der Funktion spawn() können wir eine Instanz einer Klasse erscheinen/spawnen lassen. Nun wollen wir auch Zugriff auf diese Instanz haben, also weissen wir das erschienene Monster der Variable M zu. M ist nun das Monster das auch auf der Karte herumläuft. Nun schauen wir uns aber auch mal die spawn() funktion an. Diese Funktion hat mehrere Parameter wobei die meisten Optional sind. Optionale Parameter bedeuten, man muss hier keine angeabe machen.
Der erste Parameter ist Pflicht. Hier muss man die klasse angeben welche man spawnen will. Dies muss eine Variable des Types class sein, darum auch oben die Property-Variable. Die weiteren Parameter sind alle Optional, ist eine Instanz dessen Klasse ein Actor ist, gefolgt von dem Parameter SpawnTag. SpawnTag ist eine Parameter des Types 'name' und ist für den Tag des erscheinenden Actors zuständig. Der nächste Parameter ist für den Event zuständig. Diese Beiden Angaben interessieren uns derzeit aber nicht. uns interessieren viel mehr die nächsten beiden Angaben.
Der 4 Parameter sagt an welcher Possition der Map der Actor erscheinen soll, und der 5 Parameter sagt aus wie die Rotierung sein soll.
Wir haben hier je eine Variable angegeben. Einmal location und einmal rotation. Diese Variablen hat jeder Actor, da diese schon unter Actor deklariert wurden. Diese bezeichnen die Possotion unseres Actors, und die Rotation.
Unser Script sorgt also dafür, das genau an der Stelle wo unsere Klasse ist, ein Monster erscheint und genau die gleiche Rotierung (=Blickrichtung) hat.
Nun haben wir ein Monster, aber dieses Monster hat noch alle Standardeigenschaften.
Dies wollen wir nun auch ändern.
Um nun mal einen Einblick zu haben geht mal in das Script der Klasse Monster und in alle Parent Klassen und schaut euch mal die Parameter an.
Unter Pawn finden wir insgesamt 4 Variablen die für uns relevant sind. Wir finden nämlich Health, GroundSpeed, WaterSpeed und die Variable AirSpeed.
Da diese Variablen ja in einem Parent von Monster sind, hat auch Monster diese Variablen.

Code:
function Timer() 
So, wir rufen nun unser Monster auf, und sagen ihm über diese Variablen, wie viel Health es haben soll. Die Health des Monsters soll also unserem Gesundheitswert gleich sein.
Wir wollen das die Geschwindichkeit aber nicht unserem Wert gleichgesetzt ist, sondern wir wollen Skalieren. Also den Wert mit einer Zahl Multiplizieren.
Wir könnten hier also Theoretisch auch M.GroundSpeed = M.GroundSpeed * Geschwindichkeit; aber den weg, den ich benutze ist üblicher und schneller, bewirkt aber das gleiche. Dies ist jetzt das komplette Script das auch tadellos funktioniert.
Nun gibt aber ein Problem! Das Script an sich beinhaltet keine Fehler. Aber der Benutzer dieses Scripts kann mit den Einstellungen einen Fehler machen! Es können bei der Ausführung 2 fehler entstehen. Was wäre nun, wenn der Benutzer kein Monster angegeben hat das erscheinen soll. Also wenn MonsterTyp = none ist.
Dann wird kein Monster erstellt, und wir würden einem Nichts werte zuweisen. Dies ergibt dann eine Fehlermeldung "Access None" die wir in der UT2004.log Datei nachlesen können.
Oder was wäre, wenn es ein Fehler ergibt beim Spawnen. Wenn bei der Location schon ein Monster ist. Da kann man kein Monster einfach drüber Spawnen lassen. Dann ist M auch wieder None und es gibt auch den Fehler "Access None".
Den Fehler beheben wir ganz einfach und schnell mit einer IF Abfrage:

Code:
function Timer() }
Jetzt haben wir eine Abfrage, die Abfragt ob sich hinter M auch wirklich ein Monster befindet. Falls ja, werden die Werte übermittelt, wenn nein, dann nicht.
Nun haben wir auch keine Fehler mehr, auch wenn der Benutzer einen Fehler bei der Einstellung gemacht hat oder falls das Script das Monster nicht erscheinen lassen konnte.

Was wir nun noch wollen ist die Performance schonen. Wenn kein Monster angegeben wurde, warum soll dann überhaupt ein Timer gestellt werden der dann jedes mal eine Funktion aufruft die eh nichts bringt. Also fragen wir ab, ob eine Monsterklasse gesetzt wurde, bevor wir den Timer setzen. Des weiteren wollen wir Wissen, ob überhaupt eine Zeit angegeben wurde, oder ob die Zeit = 0 oder gar im Minusbereich ist.
Hier ist unser endgültiges Script das komplett fertig ist.

Code:
class ZweiterActor extends Actor
placeable;
var() class<Monster> MonsterTyp;
var() float Zeit;
var() int Gesundheit;
var() float Geschwindichkeit;
function PostBeginPlay() }
function Timer() }
Das ein zigste was und hier nun neu erscheint, ist das wir 2 Abfragen in einem IF haben, getrennt von && . Dies würde in einem Satz bedeuten: "Wenn Monstertyp ungleich none ist UND Zeit grösser als 0, dann ..."
Wo ein UND ist, gibt es auch ein ODER. Das ODER besteht aus 2 Senkrechten Strichen || .
Dieses Script ist 100% funktionstüchtig, jedoch gibt es noch eine Sache die man kürzen könnte.
Bei Schleifen und IF Blöcke kann man die geschleiften Klammern weglassen, wenn es nur um eine Zeile geht.
Hier haben wir jetzt in PostBeginPlay einen IF-Block, der nur für eine Zeile verantwortlich ist. Das bedeutet, wir könnten diese geschweiften Klammern bei dem IF-Block einfach weglassen, um die Übersicht zu befahren rücken wir dennoch den Text ein.

Code:
class ZweiterActor extends Actor
placeable;
var() class<Monster> MonsterTyp;
var() float Zeit;
var() int Gesundheit;
var() float Geschwindichkeit;
function PostBeginPlay() function Timer() }
Die so ziemlich letzte Grundlage die ich noch in diesem Tutorial ansprechen möchte ist das spezifischere Zugreifen auf eine Klasse.
Warlords fangen immer mit einem schuss auf die rechte Seite an. Also die erste Rakete geht immer nach rechts. Wir wollen jetzt aber, das der erste schuss des Warlords nach Links geht. Dafür ist im Warlordscript eine Variable zuständig welche var bool bRocketDir; lautet.
Nun wollen wir also testen, ob das gespawnte Monster ein Warlord ist, und wenn ja wollen wir für den Warlord diese Variable ändern.
Das Problem was wir nun hier haben ist, das wir M als Monster deklariert haben. Dies bedeutet wir haben auf alle Monster und Parent-Variablen den Zugriff, nicht aber auf die Child, und Warlord ist nun mal ein Child von Monster.
Dazu benutzen wir nun folgendes Script:

Code:
class ZweiterActor extends Actor
placeable;
var() class<Monster> MonsterTyp;
var() float Zeit;
var() int Gesundheit;
var() float Geschwindichkeit;
function PostBeginPlay() function Timer() }
Diese 2 zusätzlichen Zeilen die ich am Ende der Timer() Funktion eingefügt habe machen nun folgendes. Mit Warlord(M) teste ich, ob es sich um ein Warlord handelt. Wenn also die WarlordKlasse mit M verknüpft werden kann, und dies ungleich none ergibt dann können wir direkt auf die WarlordKlasse in verknüpfung mit M zugreifen und auch dort die Variable ändern.



So das war es von diesem Grundlage-Tutorial.


Erstellt von Ausbrecher

 

UScript Einstieg für UT2k4 III - ForEach


Einleitung:

In diesem Tutorial möchte ich näher auf eine recht wichtige und oft benötigte Art der Schleife eingehen.
Es handelt sich hierbei um die so genannte ForEach Schleife. Mit Hilfe dieser Schleife kann man auf die Suche von bereits vorhandenen Actoren gehen, die sich in der Map befinden.


Voraussetzung:

Man sollte die Grundlagen Tutorials I und II gelesen und verstanden haben.


Was bewirkt ForEach?

ForEach ist eine Schleife, die zusammen mit einer Funktion solange durchläuft bis alle Suchmuster durchlaufen wurden. Es gibt da verschiedene vorgefertigte Funktionen, die mit ForEach genutzt werden können. Dies sind ganz besondere Funktionen nämlich iterator function. Diese vorgefertigten iterator function sind zum grössten Teil in der Klasse Actor, aber auch in anderen Klassen vorhanden.

Eine simple ForEach Schleife sieht z.B. so aus:

Code:
local NavigationPoint NP;
ForEach AllActors (class'NavigationPoint', NP) 
In diesem Beispiel durchsucht unser Script unser Map nach allen Actoren, die als Parent den NavigationPoint haben. Gefunden werden also Teleporter, PlayerStart, PathNodes, ...
Jeder gefundene NavigationPoint wird fürs erste in die Variable NP eingetragen. Somit erhalten wir kompletten Zugriff auf diesen NavigationPoint. Das was wir in dieser Schleife machen, ist den Instanznamen wie z.B. PathNode305 zu loggen.
Der Aufbau ist recht simpel.
Als erstes brauchen wir eine Variable welche die gleiche Instanz speichern kann, aus der die Suchklasse besteht. Somit könnte ich auch local Actor NP; benutzen, da Actor die NavigationPoint speichern kann.

Gleich danach beginnt auch schon unsere ForEach Schleife. Wir beginnen diese immer mit einem ForEach, gefolgt von der iterator function und deren Parameter. Hier ist der letzte Parameter ein so genannter out-Parameter. Hier gibt man eine Variable ein. Aus normalen Parametern wird normalerweise gelesen. Hier schreibt aber die Funktion. Man befördert also keinen Wert in die Variable, sondern man bekommt einen Parameter aus der Variable.

Ich werde nun mal mehr auf die iterator function eingehen.


AllObjects
Parameter: class baseClass, out Object obj
Beschreibung: Mit dieser iterator function können wir restlos Alle Klassen suchen. Diese Funktion braucht allerdings eine menge Speicher und Berechnungszeit.

AllActors
Parameter: class<actor> BaseClass, out actor Actor, optional name MatchTag
Beschreibung: Dies ist eine sehr oft benutzte Funktion in ForEach Schleifen. Wir durchsuchen hier alle Klassen der Actoren. Dies ist auf jeden Fall schon um ein vielfaches Performance schonender. Hier wird das ganze fast genauso gehandhabt, wie in AllObjects, nur das wir hier einen optionalen Parameter mehr haben. Wir können nun gezielter nach Actoren suchen, die den Tag, den sie mein Spawnen oder im Editor erhalten haben, beinhalten.

DynamicActors
Parameter: class<actor> BaseClass, out actor Actor, optional name MatchTag
Beschreibung: Dies ist die wohl am häufigsten genutzte Funktion, da wir hier gezielt auf Dynamische Actoren suchen. Dynamische Actoren sind zum Beispiel Spieler, Monster, Bots, und alle Actoren die die Eigenschaft bStatic = False haben. Diese Methode sollte am öftesten benutzt werden und auf jeden Fall bevorzugt von AllActors, meint auch Epic.
Zitat: "This should be used in most cases and replaces AllActors in most of Epic's game code."

ChildActors
Parameter: class<actor> BaseClass, out actor Actor
Beschreibung: Gibt alle Actoren zurück dessen Besitzer, der beim Spawnen als zweit-Parameter angegeben wurde, unser Actor ist. Diese Funktion ist langsamer als AllActors!

BasedActors
Parameter: class<actor> BaseClass, out actor Actor
Beschreibung: Gibt alle Actoren zurück, die auf unserem Actor basieren. Diese Funktion ist ebenfalls langsamer als AllActors!

TouchingActors
Parameter: class<actor> BaseClass, out actor Actor
Beschreibung: Gibt alle Actoren zurück, die unseren Actor berühren (festgelegt mit Collsion Radius / Collision Height). Diese Funktion ist sehr schnell!

TraceActors
Parameter: class<actor> BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent
Beschreibung: Nun wird es schon komplizierter. Diese Funktion gibt alle Alle Actoren zurück, die sich in einer Linie zwischen Start und Endpunkt befinden. Hierfür muss man 2 weitere Variablen definieren, nehmlich HitNormal und HitLoc, Außerdem muss man eine Vector Variable angeben, welche den Endpunkt unserer Linie darstellt. Optional kann man auch einen Startpunkt angeben, falls dieser nicht angegeben wird, wird die Position unseres Actors benutzt. Als Extend kann man die Breite/Höhe noch angeben in der gesucht werden soll.
HitLoc gibt uns die Position zurück, an der sich der gesuchte Actor mit unserer Linie getroffen hat.
Ein Beispiel für die Benutzung ist dies:

Code:
local Pawn P;
local vector HitNormal, HitLocation, EndPoint;
EndPoint = vect(0,0,0);
ForEach TraceActors (class'Pawn', P, HitLocation, HitNormal, EndPoint) 
Hier werden jetzt alle Actoren der Klasse Pawn aufgelistet, die sich zwischen unserem Punkt und dem 0-Punkt der Map befinden. Der 0-Punkt der Map ist genau der Punkt im Editor, an der sich beim Starten der Builder-Brush befindet, also dort wo im Koordinatensystem die dicken Linien sich kreuzen. Diese Funktion ist relativ schnell.

RadiusActors
Parameter: class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc
Beschreibung: Hier werden alle Actoren aufgelistet, die sich im angegebenem Radius rund um die Location (Loc) befinden. Loc ist ein optionaler Parameter und muss nicht angegeben werden. Als Standardwert wird hierfür die Location unseres Actors benutzt. Diese Funktion ist sehr langsam. Benutzt anstelle dieser eher CollidingActors oder für sichtbare (deren Eigenschaft bHidden = false ist) lieber VisibleCollidingActors. Diese Funktion lohnt nur, wenn die Eigenschaft bCollideActors auf false gesetzt wurde.

VisibleActors
Parameter: class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc
Beschreibung: Diese Funktion bewirkt das gleiche wie RadiusActors, nur das der Actor bHidden=false sein muss, um gefunden zu werden. Diese Funktion ist recht langsam und ihr solltet lieber VisibleCollidingActors benutzen, wenn der gesuchte Actor eine Collision hat.

VisibleCollidingActors
Parameter: class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden
Beschreibung: Diese Funktion ist die schnellste von allen. Sie sucht gezielt alle Actoren die bHidden = false und bCollidate = true haben und gibt diese zurück. Diese eignet sich also am besten wenn ihr alle beispielsweise alle Monster auflisten wollt, die sich in einem Radius befinden.

CollidingActors
Parameter: class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc
Beschreibung: Diese Funktion sucht nach allen Actoren, die eine Collision haben, und sich im angegebenen Radius befinden. Die Geschwindigkeit dieser Funktion ist nur gering schlechter als VisibleCollidingActors und sollte daher bei nicht sichtbaren Actoren benutzt werden.

AllDataObjects
Parameter: class objClass, out Object obj, string packageName
Beschreibung: DataObjects sind eine Unterklasse von Object welche zum Hinterlegen von speziellen Daten gedacht ist. Auf die Schnelle fällt mir hier nur der UT2004RPG Mutator ein, der diese benutzt. Hier wird für jeden Spieler ein DataObject angelegt, das Level, Erfahrung, Startpunkte, ... eingetragen und in der UT2004RPG.ini abgespeichert. Hiermit kann man alle DataObjects durchlaufen.
Diese Funktion ist nur für GameInfo und Unterkategorien möglich!

ZoneActors
Parameter: class<actor> BaseClass, out actor Actor
Beschreibung: Gibt alle Actoren zurück, die sich in dieser Zone befinden.
Diese Funktion ist nur für ZoneInfo und Unterkategorien möglich!


Ein Actor mit ForEach-Schleife

Nun wollen wir aber auch mal einen Actor erstellen, der solch eine ForEach-Schleife besitzt. Ich habe mir überlegt, was man da als Beispiel scripten könnte, und mir kam die Idee, Ein Actor der allen Spielern alle par Sekunden eine Anzahl von Health zurück gibt, solange der Spieler unter einem Minimalwert an Health ist.
Als Erstes benötigen wir einen Actor, also macht rechte Maustaste auf Actor, geht auf New und gebt als Package myLevel ein. Wir nennen unseren Actor ganz einfach HealActor.
Als Nächstes müssen wir uns überlegen, was für Variablen Wir brauchen. wir brauchen 3 Property Variablen, ich nenne diese einfach mal StopHealing, HealthBonus und HealTime.

Code:
class HealActor extends Actor
placeable;
var() int StopHealing;
var() int HealthBonus;
var() float HealTime;
function PostBeginPlay() function LogDieZahlen() 

Das ist unser komplettes Script dafür. Wir sehen hier schon bisschen was Neues. In der foreach-Schleife haben wir einen IF-Block eingebaut der 3 Abfragen stellt. Zum ersten wird abgefragt ob die Health der Spielfigur überhaupt kleiner ist als StopHealing, den wir wollen ja nicht, das der Spieler Health dazu bekommt, wenn er bereits genug hat. Die zweite Abfrage überprüft den so genannten State. Dies ist mehr oder weniger der Status, und wenn der Status auf 'Dying' steht, also die Spielfigur am sterben ist, dann soll er natürlich keine Health dazu bekommen. Die dritte Abfrage ist was sehr nützliches. Ich habe diese eingebaut, weil euch das in Zukunft noch oft begegnen wird. Wenn wir einen Klassennamen gefolgt von einer Actor-Variable in Klammern benutzen, heisst das, das wir auf die Eigenschaften dieser Klasse und nicht nur der VariablenKlasse zugreifen können. Voraussetzung ist allerdings, das von der eingegebenen Klasse der Parent die Variablenklasse sein muss.
Hier haben wir ja Pawn und Monster. Die Hierarchie des ActorBrowsers ist ja, wenn wir dort mal nach gucken:
Object - Actor - Pawn - UnrealPawn - XPawn - Monster - ...
Somit ist Monster immer auch ein Pawn.
Nun haben wir in unserer Abfrage stehen, ob P auch der Klasse Monster zugehört, da wir den Monstern keine Gesundheit geben wollen.
Falls die Abfrage zutrifft, geben wir in der letzten Zeile der Spielfigur auch ihre wohlverdiente Gesundheit.


Soviel zu diesem Tutorial. Ich hoffe ihr konntet was lernen und das Gelernte behalten.


Erstellt von Ausbrecher

 

UScript Einstieg für UT2k4 IV - WOTGreal
Erstellt von: Ausbrecher ; Korrigiert von KillerVirus




Einleitung:

In diesem Tutorial wollen wir uns einen Editor einrichten, mit dem wir einfacher in UScript arbeiten können. Von dieser Art gibt es mehrere, ich jedoch habe die besten Erfahrungen mit WOTGreal gemacht.
Da ich in den weiteren Tutorials auch mit WOTGreal arbeiten werde, beschreib ich hier, wie ihr die Einrichtungen am besten vornehmt.


Vorraussetzung:

Unreal Tournament 2004


Was ist WOTGreal:

WotGreal ist eine Entwicklerumgebung (IDE) mit der ihr Programmieren/Scripten könnt. Man kann WOTGreal auch zum erstellen von Visual Basic, C++, C#, PHP, ... Anwendungen, jedoch wollen wir UScript benutzen.
Es werden dem Entwickler eine menge Arbeiten abgenommen. Somit ist es möglich alle Klassen nach einem Suchmuster zu durchsuchen, eine Treeview der Klassenhirachie und Packagehirachie ist möglich, man kann Klassen löschen, man hat Textzugriff auf die Defaultproperties, ...
Es bietet eine Menge Vorteile gegenüber dem in UnrealED eingebauten UScript Editor, jedoch gibt es auch Nachteile!
Ein Nachteil ist z.B. das ihr keinen direkten Zugriff auf das myLevel habt und hierzu einen Umweg gehen müsst.


Woher bekomme ich WOTGreal:

WOTGreal ist eine Trial Applikation. Das bedeutet, ihr könnt das Programm kosten los, jedoch eingeschrängt gegenüber der kostenpflichtigen Version, benutzen.
Die Trail-Version ist kostenlos, Die Standardlizens kostet 25$ USD und die Profesional 45$ USD.
Für den Anfang reicht die Trailversion vollkommen aus.

Homepage: www.WOTgreal.com


Installation und Konfiguration:

Als erstes downloadet und installiert ihr WOTGreal. Das Starten lohnt derzeit noch nicht wirklich, den wir brauchen zuerst die ganzen Quellcode-Daten der *.u Dateien, die sich im UT2004\System\ Ordner befinden.
Es gibt mehrere Möglichkeiten an diese ran zu kommen. Teils bieten Webseiten diese zum Download an, aber ich beschreibe hier nun eine Möglichkeit, die sehr umstritten ist.
Diese Möglichkeit geht recht schnell, aber es kann zu komischen, jedoch nicht störenden Fehlern kommen. Bei dieser Methode finden sich oftmals komische DefaultProperties. Hin und wieder wird ein KarmaObject eingefügt, das gar nicht existiert. Diese Fehler spielen aber keine Rolle. Sie bringen einen nur zum wundern, woher plötzlich diese DefaultProperty kommt. Aber es gibt auch Vorteile gegenüber eines Internet-Downloads. Zum Beispiel ist man hier nicht auf die Standard Packages beschränkt, sondern kommt auch auf Custom Packages, also Mods/Mutatoren/... welche nicht zu UnrealTournament direkt gehören, sondern gedownloadet wurden.
Eine weitere Möglichkeit ist es, über die Datei "UCC.exe" den Quellcode der Packages zu extrahieren, jedoch ist dies erstmal zu kompliziert.
Falls ihr euch aber zum Download entscheiden solltet, kopiert die Order und Dateien direkt in euer UT2004\ Ordner und überspringt den nächsten Absatz.

Also fangen wir mal an mit der Methode an, welche ich am häufisten benutze. Dazu starten wir UnrealED.exe und gehen dort in den ActorBrowser. Nun gehen wir zuerst auf File -> Open Package... und öffnen alle *.u Dateien, von denen wir den Quellcode haben wollen.
Nachdem wir alle ausgewählt und geöffnet haben, gehen wir auf File -> Export All Scripts um diese in Quellcode umzuwandeln.

Dieser Vorgang kann einige Minuten dauern....

Nun habt ihr all eure Quelldateien, die wir brauchen. Nachdem dieser Schritt getätigt wurde, können wir UnrealED.exe auch wieder schließen. Jetzt starten wir WOTGreal. In der Trial-Version erwartet uns ein kleines Fenster, wo wir auf Order/ auf Bestell Informationen und ähnliches stoßen. Nach einer kurzen Zeit erscheint ein weiterer Schaltknopf namens "OK", den wir auch benutzen. Nun schließt sich auch das Fenster und wir sehen zum ersten mal die IDE, in der wir uns von nun an aufhalten werden.
Wir können derzeit aber noch nichts in WOT