Jump to content
Unity Insider Forum

Scripten in Unity mit Unity


Sascha
 Share

Recommended Posts

Wer die vorherigen zwei Tutorials gelesen hat, kann zwar einige Sturkturen eines JavaScripts anfertigen, hat aber eventuell noch keine große Vorstellung davon, wie man diese zum Entwickeln eines Spiels in Unity zu benutzen hat.

Dieses Tutorial, quasi nachträglich geliefert, soll dafür sorgen, dass jeder Leser mit den erworbenen Grundkenntnissen in JavaScript auch in Unity scripten kann.

 

Ergo: Bitte auf jeden Fall die ersten beiden Scripting-Tutorials lesen, dann dieses hier.

 

Scripten in Unity mit Unity

 

Teil 1 - Unity-Dinge

Die Unity Engine muss man sich, wie vieles andere auch, so vorstellen:

Der Kern der Engine ist eine so genannte "Black Box".

Man weiß als Benutzer erst einmal nicht, wie sie intern funktioniert.

Wie sie letztenendes die Grafikkarte anspricht und ein 3D-Objekt damit zeichnet, ist einem schleierhaft und das ist auch in Ordnung so.

Ein großer Teil der Engine ist dagegen nämlich Teil der "Schnittstelle" der Engine, also des sichtbaren Teils.

Schreibt man ein Script für die Unity Engine, dann benutzt man neben den grundlegenden Sprachkonstrukten der Sprache (also den Keywords var, function, if usw., den Klammern und allem anderen) eben diese Elemente der Schnittstelle.

Diese Elemente sind die sichtbaren Teile vieler Klassen, aus denen Unity besteht, wie z.B. Transform, Mathf, Physics oder GameObject.

Diese Schnittstelle durch und durch zu kennen ist optimal, immerhin ihren groben Aufbau zu verstehen essenziell.

 

Im Folgenden werde ich diesen groben Aufbau einmal erläutern.

Ein perfektes Verständnis wird dabei garantiert nicht vermittelt, das erhält man nur durch lange Zeit des Übens.

 

Allem voran steht die Scripting Reference, die man auch lokal auf seinem Rechner hat, wenn man Unity installiert hat.

Sie ist das Nachschlagewerk für die gesamte Schnittstelle und begleitet den Entwickler durchgehend.

Auch nach Jahren der Erfahrung mit Unity kommt man ohne noch nicht aus.

Wann immer man also einen Begriff nicht kennt, eine Methode sucht oder eine Parameter-Reihenfolge vergessen hat - im Scirpt-Editor F1 drücken, und man erhält (hoffentlich) die Reference.

 

Teil 2 - Das grundlegende Prinzip

Man kann es nicht oft genug nennen, darum auch hier noch einmal:

  • Ein Spiel besteht aus mehreren Szenen (Scenes)
  • Eine Szene besteht aus mehreren GameObjects
  • Ein GameObject besitzt mehrere Komponenten (Component)

Scenes gibt es in der Schnittstelle so erst einmal nicht, aber GameObjects und Komponenten.

GameObject ist einfach eine Klasse in der Schnittstelle, fertig.

Component dagegen ist eine Superklasse (der Begriff muss einem nichts sagen) für eine Menge Komponenten.

Jede dieser Komponenten ist eine Klasse.

Man sollte sich mit der Zeit aneignen, welche Komponenten was können und wofür sie zuständig sind.

 

Neben GameObject und den Komponenten gibt es noch eine dritte Art von Klassen, die (erstmal!) wichtig ist: Statische Klassen.

Die Klasse GameObject wird instanziiert, wenn man ein GameObject erzeugt. Dann hat man ein so genanntes "Exemplar" der GameObject-Klasse.

Mit diesem Exemplar arbeitet man dann in der Regel, weniger mit der Klasse selbst.

Statische Klassen dagegen bieten Funktionalität innerhalb der Klasse selbst an.

Als erstes und wichtigstes Beiepiel: Mathf.

Diese Klasse wird nicht instanziiert, man erzeugt nie ein neues "Mathf-Objekt".

Stattdessen benutzt man die Klassenmethoden direkt:

Mathf.Methodenname();

Mehr zu statischen Strukturen gibt es hier.

Andere statische Klassen kommen später hinzu.

 

So viel zu den "Kategorien".

 

Teil 3.1 - Komponenten

Hier ein paar der wichtigsten Komponenten:

  • Transform
    Jedes GameObject hat immer genau eine Transform-Komponente.
    Sie beinhaltet
    • Position
    • Rotation
    • Skalierung

    des Objekts und Methoden, um diese Werte zu verändern.

    Objekte verschieben, drehen oder die Größe ändern geht also über dessen Transform-Komponente.[*]Renderer

    Eine abstrakte Basisklasse. Von ihr sind mehrere Rendererklassen abgeleitet, z.B. MeshRenderer oder LineRenderer.

    Ein Renderer kennt eine Liste von Materialien, die auf das Objekt gepackt werden, das der Renderer rendern soll.

    [*]Collider

    Auch eine abstrakte Basisklasse. Von ihr abgeleitet sind z.B. BoxCollider, SphereCollider oder MeshCollider.

    Kann auf Trigger gestellt werden, stellt sonst ein Hindernis für bewegliche Objekte dar.

    [*]Rigidbody

    Sorgt dafür, dass PhysX das GameObject als Physik-Objekt behandelt, dass von Kräften, wie z.B. der Gravitation, beeinflusst werden kann.

    Hat Eigenschaften für Masse, Trägheit und Beschränkungen und Methoden zum Hinzufügen von Kräften.

    [*]Light

    Beleuchtet Meshes, also die 3D-Objekte der Szene.

    Hat Eigenschaften für Lichtfarbe und -Stärke, Schatten und Lightmapping.

    [*]Camera

    Sorgt dafür, dass die Renderer ein Bild Rendern, als würde es durch diese Kamera aufgenommen werden.

    Hat Eigenschaften zum Thema Field Of View, Hintergrundfarbe, Skybox oder RenderTarget.

    [*]Animation

    Animiert das GameObject und seine untergeordneten GameObjects nach Vorgaben einer Animation in den Assets.

    Hat Methoden zum Abspielen, Überblenden oder Stoppen von Animationen.

    [*]AudioSource

    Kann Klänge abspielen.

    Hat Eigenschaften über die Audio-Datei, Geschwindigkeit, Tonhöhe oder Doppler-Effekt und Methoden zum Abspielen oder Stoppen der Klänge.

    [*]MonoBehaviour

    Die abstrakte Basisklasse für Skripts.

    Jede Klasse, die man mit einem Skript definiert, ist davon abgeleitet.

    Hat eine Menge Methoden wie Start(), Update() und Events wie OnTriggerEnter(), OnEnable() oder OnMouseDown().

Gehen wir mal auf den letzten Punkt ein.

MonoBehaviour hat eine Menge Methoden. Die Events sind auch Methoden.

Besonders interessant sind die "Overridable Functions", wie sie in der Reference betitelt werden.

Im ersten Tutorial erwähnte ich spezielle Methoden, die die von sich Engine aufruft.

Genau um dise Methoden handelt es sich hierbei.

Erst schlägt man nach, wann sie ausgelöst werden, dann benutzt man sie dem entsprechend.

So wird z.B. OnApplicationFocus() ausgelöst, wenn das Spiel den Fokus erhält oder verliert.

Also überschreiben wir die Methode in unserem Skript:

function OnApplicationFocus(focus : boolean)
{
if(focus)
	PauseZuende();
else
	Pause();
}

 

So viel zu den Overridable Functions.

Was aber viel interessanter sein dürfte: Wie benutze ich jetzt die verschiedenen Komponeten in meinen Scripts?

Hierzu gibt es zuerst einmal GetComponent().

Mit dieser Methode lässt man sich eine Komponente eines bestimmten Typs von einem GameObject zurück geben:

var licht : Light = einGameObject.GetComponent(Light);
//"licht" ist jetzt die Lichtkomponente von einGameObject.
//Wenn dieses GameObject kein Licht hat, ist licht null
licht.intensity = 3; //Mach was mit dem Licht

Lässt man das GameObject vor dem Punkt weg, meint man immer das GameObject, das auch das Script hat, in dem GetComponent() aufgerufen wird.

Geben wir also einem GameObject ein Licht und dieses Skript:

function Start()
{
GetComponent(Light).intensity = 0;
}

...dann heisst es: Licht aus, sobald das Spiel losgeht.

 

Einige Komponenten, genauer, die, die jedes GameObject maximal ein Mal haben kann, sind direkt ansprechbar:

transform // = GetComponent(Transform)
renderer // = GetComponent(Renderer)
light // = GetComopnent(Light)
rigidbody // = GetComponent(Rigibody)
camera  // = GetComponent(Camera)
animation  // = GetComponent(Animation)
audio  // = GetComponent(AudioSource)
//und weitere

Ich habe irgendwann mal irgendwo gelesen, dass diese Shortcuts nur wieder GetComponent() auslösen, also nicht performancefreundlicher sind (warum auch immer).

Deswegen speichere ich auch diese Komponenten oft irgendwo zwischen (siehe unten). Ist aber nicht so wichtig.

 

GetComponent() durchsucht immer die ganze Komponentenliste und ist deswegen nicht soo performant (auch wenn man's wohl nicht merkt).

Deswegen ist es eine saubere Sache, den Rückgabewert zu speichern:

private var filter : MeshFilter; //private, damit kein anderes Script Zugriff hat, und damit es nicht im Editor angezeigt wird (wozu auch)

function Awake()
{
filter = GetComponent(MeshFilter);
}

function Update()
{
//Mach Dinge mit "filter"
}

 

So, jetzt wenden wir das alles einmal an.

Wir haben zwei GameObjects:

  1. DER TRIGGER
    • Transform (logisch)
    • Collider (auf isTrigger gestellt)
    • Ein Script (kommt gleich)

  • DIE TÜR
    • Transform (klar)
    • MeshRenderer (für's sichtabare, arbeitet mit MeshFilter zusammen)
    • MeshFilter (kennt den zu rendernden Mesh)
    • Collider (damit man auch nicht durchkann, wenn sie im weg ist)
    • Animation (hat eine Animation, die die Tür öffnet)

    Der Trigger soll jetzt die Tür öffnen (sprich: Die Animation auslösen), wenn ein Objekt hinein gerät.

     

    Das Script des Triggers muss dann so oder so ähnlich aussehen:

    var tuer : GameObject; //Hier wird im Editor die Tür rein gezogen
    
    function OnTriggerEnter()
    {
    var tuerAnimation : Animation = tuer.GetComponent(Animation); // oder "= tuer.animation;"
    tuerAnimation.Play(); //löst die Animation aus.
    }

    Tadaa!

     

    Auf welche Arten man alles an andere GameObjects heran kommt, wird im nächsten Tutorial behandelt.

     

    Teil 3.2 - Statische Klassen

    Wie erwähnt gibt es Klassen in der Schnittstelle, die statische Methoden anbieten.

    Allen voran die Klasse Mathf.

    Sie bietet alle möglichen mathematischen Operationen an, darunter:

  • Runden
  • Eingrenzen
  • x hoch y
  • Logarithmen
  • Trigonometrie
  • Interpolation
  • PI
  • weitere

Einfach zu benutzen:

var tausendvierundzwanzig : float = Mathf.Pow(2, 10); // 2^10 = 1024
var zweiMalPi : float = Mathf.PI * 2;

 

Dann gibt es als weitere wichtige statische Klasse Physics.

Sie ist Teil der Schnittstelle zur PhysX-Engine und enthält wichtige Eigenschaften und Methoden für alles, was mit Physik und Kollision zu tun hat.

So z.B. die globale Gravitation:

Physics.gravity

Sehr wichtig sind auch die Raycast-Methoden.

Sie simulieren einen Strahl oder ähnliches in der Scene und geben zurück, ob dieser irgendetwas an Collidern getroffen hat.

Die Methoden der Physics-Klasse sind die ersten Anlaufstellen, wenn man wissen will, ob und wo irgendwo ein Objekt ist, oder wo man dagegen Platz hat.

 

Ebenfalls sehr wichtig ist die Klasse Input.

Sie hat jede Menge Eigenschaften und Methoden, die einen ermitteln lassen, was der Spieler gerade so an seiner Hardware macht.

Sprich: Status der Tastatur, der Maus, irgendwelchen GameControllern oder des Touch-Bildschirms und des Accelerometers bei iOS oder Android.

Bevor ich hier aber in's Detail gehe, verweise ich lieber auf diese Manual-Seite.

Mit dem Input-Manager kann man sich Achsen zusammenstellen, die in der Regel einen Wert von -1 bis 1 haben können und einen Namen haben.

Bei einem Joystick hieße das: Ist der Stick zur einen Seite geneigt, gibt die Achse für diesen Stick -1 zurück, auf der anderen 1, in der Mitte 0 und ansonsten irgendetwas dazwischen.

Bei einer Tastatur-Achse gibt man zwei Tasten an, z.B. Pfeiltaste links und rechts. Ist links gedrückt, gibt's -1, bei rechts +1 und bei keinem von beiden oder beiden gibt's 0.

Den momentanen Wert der Achse lässt man sich dann mit Input.GetAxis(Name_der_Achse) zurück geben:

var speed : float = 10;

function Update()
{
transform.Translate(Input.GetAxis("Horizontal") * Time.deltaTime * speed, 0, 0); //Bis zu 10m pro Sekunde auf der X-Achse bewegen, abhängig von der Achse "Horizontal" (Standard-Achse für links/rechts bzw. A/D)
}

 

Teil 4 - Was wir daraus lernen

So, was haben wir jetzt von alledem?

Zwei Dinge:

  • Du weisst jetzt grob, was Unity dir an Funktionen anbietet und kennst einige wichtige Komponenten und statische Klassen.
  • Du weisst, dass Du, um die richtige Funktion für einen Zweck zu finden, in der Scripting Reference nachschauen musst.
    Und durch diesen Text weisst Du hoffentlich auch, wo Du darin zu suchen anfangen kannst.

So, auf zum nächsten Tutorial tongue.gif

  • Like 18
Link to comment
Share on other sites

  • 2 years later...

Hi Sascha,

 

ich hab deine Tutorials gelesen und bedanke mich für den Aufwand, den du getrieben hast. Ich weiß natürlich, dass dieser Thread vier Jahre alt ist, trotzdem eine kurze Frage:

 

Teil 3.1 Komponenten:

 

Sind da die zwei GameObjects vertauscht? Sollte nicht die Tür einen Renderer haben und der Trigger einen Collider, der auf Trigger gestellt ist? Oder hab ich da was falsch verstanden?

 

 

Alex

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...