Jump to content
Unity Insider Forum

Scripten in Unity für Scripterfahrene


Sascha
 Share

Recommended Posts

Einige Zeit nach dem Erstellen der ersten beiden Scripting-Tutorials kristallisiert sich heraus, dass noch ein drittes sinnvoll wäre.

Zwar kann man nach den ersten beiden schon ein wenig scripten, aber wie man mit diesen Skripts ein ganzes Spiel steuern kann, ist manchen Schleierhaft.

 

Für dieses Tutorial sollten "Scripten in Unity für Einsteiger" und "Scripten in Unity für Nicht-mehr-Einsteiger" gelsen worden sein.

 

Scripten in Unity für Scripterfahrene (an Objekte kommen)

Oder: Richtig Scripten in Unity

 

Um ein wenig Licht ins Dunkel zu bringen, noch einmal die Erinnerung aus dem ersten Scripting-Tutorial:

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

Ein Script ist dabei eine Komponentensorte, die einem GameObject zugewiesen werden kann.

 

Damit alles folgende verständlich ist, hier ein Grundsatz:

Der Grundgedanke des Systems der Unity Engine ist, dass sich jedes GameObject über seine Scripts autonom selbst steuert.

 

Ein Fehler, den ich immer häufiger sehe, ist, dass Anfänger versuchen, alle ihre Operationen über ein paar wenige Skripts zu erledigen.

Dafür ist Unity nicht ausgelegt, und so sollte man es dem entsprechend auch nicht machen.

 

Stattdessen soll man versuchen, jedem GameObject ein oder ein paar Scripts zuzuweisen, sodass sich möglichst ausschließlich diese Scripts um dieses GameObject kümmern.

Das wird auf Dauer nicht funktionieren, sollte aber angestrebt werden.

Falls das mal überhaupt nicht hin kommt (und das wird es), gibt es immernoch andere Methoden.

 

Beispiel 1: Der Spieler und die Box

Simples Beispiel:

Wie haben eine Spielfigur, Capsule, Character Controller, Bewegungsskript.

Außerdem steht da eine Box mit Namen "Cube", die, wenn man Space gedrückt hält, sich genau so bewegen soll wie der Spieler.

Für "Space gedrückt" nehmen wir

Input.GetButton("Jump")

 

Eine Idee wäre es, den PlayerMovement-Skript anzupassen:

function Update()
{
var bewegung : Vector3 = Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
GetComponent(CharacterController).Move(bewegung * Time.deltaTime);

//Die Box
if(Input.GetButton("Jump"))
 GameObject.Find("Cube").transform.Translate(bewegung * Time.deltaTime);
}

Sieht gut aus, eigentlich.

Das Problem: Es verstößt gegen den oben genannten Grundsatz, denn ein Skript, das dem Player-Objekt zugewiesen wird, steuert den Würfel.

GameObject.Find("Cube") ist nämlich ein Befehl, der ein Mal die komplette Objekthierarchie nach einem Objekt namens "Cube" durchsucht, und das in jedem Frame, weil er in der Update()-Funktion steht.

Man stelle sich nur die Performance-Einbrüche vor, wenn so ein Befehl durch 50 GameObjects in einer Hierarchie von weit mehr GameObjects ausgeführt wird - jeden Frame.

 

Versuchen wir es also gleich richtig und schreiben einen neuen Skript für den Würfel:

function Update()
{
 if(Input.GetButton("Jump"))
{
 var bewegung : Vector3 = Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
 transform.Translate(bewegung * Time.deltaTime);
}
}

...weisen den Skript der Box zu und streichen den Box-Teil komplett aus dem Player-Skript.

 

Sauber!

 

Beispiel 2: Die anhängliche Kamera

Der SmoothFollow-Skript in den Standard Asstes ist vielleicht nett zu benutzen, aber man möchste bestimmt mal etwas eigenes basteln.

Hier ein Beispiel:

Wieder eine steuerbare Kapsel, Character Controller, Player-Skript.

Dazu eine Kamera, die nun einen eigenen Skript kriegen soll, der die Kamera an die Fersen des Spielers heftet, aber ohne sich mitzudrehen, wie es der Fall wäre, wenn man die Kamera dem Spieler unterordnen würde.

 

Die erste Idee mit GameObject.Find("Player") schlagen wir uns gleich wieder aus dem Kopf.

Stattdessen denken wir an Beispiel 1, aber wir müssen feststellen, dass diese Technik schlecht funktioniert, da die Kamera irgendwie an die Transform-Infos des Players heran kommen muss, um die position festzustellen.

Also basteln wir uns folgenden Skript:

var player : GameObject;

function Update()
{
if(player)
 transform.position = player.transform.position + Vector3(0,10,40);
}

"Vector3(0,10,40)" bestimmt hierbei den Abstand der Kamera zur Spielfigur, da könnte man stattdessen auch eine Variable nehmen.

Wenn man nun diesen Skript der Kamera zuweist, dann muss man noch die Spielfigur aus der Scene View oder der Szenenhierarchie heraus in die Eigenschaft "Player" der Kamera im Inspektor ziehen.

Der Code "if(player)" sorgt dabei dafür, dass keine Fehlermeldung entsteht, wenn der Kamera mal aus irgendeinem Grund die Player-Variable nicht richtig zugewiesen wurde, oder der Player im Laufe des Spiels verschwindet. Mehr über den "Existenzoperator" in diesem späteren Tutorial.

 

Auf diese Weise erspart man der Engine das Suchen nach dem Objekt, denn wenn das gewünschte Objekt als Variable vorhanden ist, dann weiß die Engine sofort, an welcher Stelle das Objekt ist.

Es ist genau so wie beim Menschen: Wenn man ihm sagt, wo etwas ist, muss er nicht suchen.

 

Komponenten anstatt GameObject nehmen

Im letzten Beispiel habe ich eine Variable vom Typ GameObject deklariert, schön und gut.

Aber da gibt es noch eine wesentlich schönere Methode: Eine Komponente stattdessen nehmen!

Anstatt GameObject schreibe ich z.B. Transform.

Eine Variable wie diese:

var player : Transform;

erwartet auch ein GameObject als Wert, sodass man den Player hineinziehen kann, Bedingung ist aber, dass das Objekt eine Komponente des Typs "Transform" hat, was natürlich immer der Fall ist.

Beim Typ "Light" z.B. ist das aber eine richtige Bedingung, wenn der Player keine Light-Komponente hat, kann man ihn hier dann nicht zuweisen.

 

Der Vorteil bei der Sache ist: Man kann direkt auf die Komponente zugreifen.

Anstatt des vorherigen

player.transform.position

können wir nun einfach

player.position

schreiben, da "player" ja jetzt der Transform des Objekts ist anstatt das Objekt selbst.

 

Beispiel 3: Die hinterhältige Taschenlampe

Manchmal wird es etwas problematisch, die Technik aus Beispiel 2 zu benutzen.

Dazu hier ein etwas komplexeres Beispiel: Ein Spieler rennt mir einer Taschanlampe (nur Licht, kein Modell) durch die Gegend.

Die Lichtstärke der Lampe ist proportional zur Energie des Spielers, wenn man also ganz gesund ist, leuchtet sie am hellsten und wenn man schwächer wird, wird es auch das Licht der Lampe.

In diesem Fall muss der Player-Skript auf die Taschenlampe zugreifen und ihr, abhängig von einer eigenen Variable "health" eine neue Lichtstärke "light.intensity" geben.

 

Nun könnte man an Beispiel 2 denken, aber da gibt es ein Problem: Der Inspektor ist bei solchen Aktionen irgendwann voll von Eigenschaften, die nicht geändert werden (es ist immer die selbe Taschenlampe), und das ist auf Dauer unschön und unübersichtlich.

In diesem Fall kann man dann ruhig zur dritten Methode greifen:

var health : float = 100;
private var lampe : Light;

function Awake()
{
lampe = Transform.Find("Lampe");
}

function Update()
{
lampe.intensity = health / 100.0;
}

In diesem Skript sucht er sich mit Transform.Find() (dazu gleich mehr) seine Lampe (sie muss auch den Namen "Lampe" haben) heraus und speichert sie sich ganz am Anfang, bevor das erste Update aufgerufen wird.

Auf diese Weise kann man die ansonsten zu vermeidenden Funktionen wie GameObject.Find() benutzen, ohne dafür ärger kriegen zu müssen, denn ein, zwei mal darf man sich die Verwendung ruhig erlauben.

 

Folgendes ist hier interessant:

"private" vor der Variable

Eine als "private" deklarierte Variable kann nicht von anderen Skripts aus ausgelesen oder verändert werden.

Des weiteren erscheint diese Variable nicht als Eigenschaft im Inspektor, was die in diesem Falle gewünschte übersicht erhält.

Awake() benutzt

Awake ist genau die richtige Funktion, um anfängliche Wertzuweisungen wie diese zu machen, da man dann in Start(), was danach aufgerufen wird, damit weiter arbeiten kann.

Transform.Find() benutzt

Transform.Find() ist ähnlich wie GameObject.Find(), mit dem Unterschied, dass es nur in den Objekten sucht, die dem GameObject untergeordnet sind, zu dem die angegebene Transform-Komponente gehört.

 

Alternativ könnte die Taschenlampe auch auf den Player zugreifen, aber das läuft auf den selben Aufwand hinaus.

 

Abschließend

Jetzt kennt ihr die drei Methoden, mit denen man arbeiten muss, damit man nicht in die falsche Richtung programmiert.

Versucht, sie der Reihenfolge nach anzuwenden:

  • Jedes GameObject wird nur durch die eigenen Scripts gesteuert, wenn das nicht geht, dann
  • Variable deklarieren und im Inspektor zuweisen, aber wenn das nicht gut ist, dann
  • Private Variablen deklarieren und in Awake() zuweisen

Beachtet diese Regeln immer, dann programmiert ihr schonmal viel sauberer, und eure Performance wird bei komplexen Projekten auch mithalten!

  • Like 15
Link to comment
Share on other sites

  • 5 months later...
  • 6 months later...

Leider muss ich sagen, dass ich bei diesem Tut nur Bahnhof verstehe xD

 

Input.GetButton("Jump") <---> damit wird ja die Taste Space belegt laut des tutorials!? heißt die taste space dann in der programmiersprache Jump oder wie^^?

 

Genauso hat es ja gehiesen das die tuts für anfänger sind.

 

 

function Update()

{

var bewegung : Vector3 = Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));

GetComponent(CharacterController).Move(bewegung * Time.deltaTime);

 

//Die Box

if(Input.GetButton("Jump"))

GameObject.Find("Cube").transform.Translate(bewegung * Time.deltaTime);

}

 

ich glaube nicht das ein anfänger das rafft :) jedenfalls gehts mir so^^ z.b. wird die variable bewegung mit dem typ vector3 benannt. in den vorherigen tuts gab es aber nur string int double un so^^

vllt könntest du mir sagen wo man diese ganzen begriffe her bekommt, damit man sich die einfach reinpauken kann... wenn du verstehst was ich meine^^

Link to comment
Share on other sites

  • 1 year later...
  • 1 year later...

Danke für die Tutorials.

Habe eine Frage zum Taschenlampenbeispiel u.z.:

Wie ist es wenn man nicht auf "light" sondern auf eine Variable im Skript zugreifen will?

Also wenn das Licht der Taschenlampe im eigenen Skript berechnet wird und Health ist ein Parameter davon?

Link to comment
Share on other sites

Danke für die Tutorials.

Habe eine Frage zum Taschenlampenbeispiel u.z.:

Wie ist es wenn man nicht auf "light" sondern auf eine Variable im Skript zugreifen will?

Also wenn das Licht der Taschenlampe im eigenen Skript berechnet wird und Health ist ein Parameter davon?

So weit ich das verstanden habe, wird das ja schon so gemacht:

var health : float = 100;
private var lampe : Light;
function Awake()
{
lampe = Transform.Find("Lampe");
}
function Update()
{
lampe.intensity = health / 100.0;
}

Die Variable lampe ist vom Typ Light, Light ist dabei ein Prefab, denke ich. Diese Variable wird im Awake mit dem Objekt belegt, dass die Lampe repräsentiert.

 

In Update wird die Lampe dann mit der Variable health gesteuert. Diese regelt die Lichtintensität. Health kannst du dann natürlich selbst steuern, beispielsweise auch im Update:

 

private float healthVerlustProSekunde = 1f;


void Update() {
if (lampeLeuchtet)
{
	health = health - (healthVerlustProSekunde * Time.deltaTime);
	if (health < 0)
		health = 0;
}

lampe.intensity = health / 100.0;
}

 

Das ist jetzt in C#, sollte aber so etwa auch in Java funktionieren.

 

Alex

Link to comment
Share on other sites

Das ist jetzt in C#, sollte aber so etwa auch in Java funktionieren.

 

JavaScript ist nicht Java! Ganz und gar nicht!

 

Wie ist es wenn man nicht auf "light" sondern auf eine Variable im Skript zugreifen will?

Dann schreibst du eben diese Variable hin (ist jetzt wieder JavaScript):

var anderesScript = GetComponent.<NameDesAnderenScripts>();
anderesScript.health = 10;
anderesScript.power += 20;
anderesScript.wurstbrot = true;

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...