Sascha Geschrieben 6. September 2010 Melden Share Geschrieben 6. September 2010 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! 15 Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Bowserkoopa Geschrieben 11. Februar 2011 Melden Share Geschrieben 11. Februar 2011 Nett gemacht Übrigens steckt beim neuen Würfel-Skript noch das "Eine Idee wäre es, den PlayerMovement-Skript anzupassen:" drinen, was unschön aussieht. " Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 11. Februar 2011 Autor Melden Share Geschrieben 11. Februar 2011 Hups, irgendwie sind die ganzen Tags corrupted gewesen... merkwürdig. Müsste jetzt alles wieder stimmen. Danke für den Hinweis! Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Damon93 Geschrieben 9. September 2011 Melden Share Geschrieben 9. September 2011 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^^ Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 9. September 2011 Autor Melden Share Geschrieben 9. September 2011 Ist auch nicht für reine Anfänger gedacht, steht da aber auch Jump ist eine Input-Achse, die in Unitys Input Manager festgelegt wurde - standard ist da Space. Ich schau aber bei Gelegenheit mal den Workflow der Tutorials durch. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Damon93 Geschrieben 10. September 2011 Melden Share Geschrieben 10. September 2011 ja das wäre nett da ich persönlich finde, wenn man teil 1 und 2 gemacht hat... mit teil 3 nicht so gut klar kommt. Da einem einfach die ganzen begriffe fehlen. 1 Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
mauruco Geschrieben 11. September 2012 Melden Share Geschrieben 11. September 2012 Wollte mich an dieser Stelle mal für die tollen Tutorials bedanken . Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
forki Geschrieben 15. Juli 2014 Melden Share Geschrieben 15. Juli 2014 Super Tutorial! Ich muss sagen, ich bediene mit der Engine seit ca. 2 Monaten und es wurde mir durch die Tutorials einiges klarer, als hätte ich es mir autodidaktisch beigebracht. 1A Anlaufstelle gefunden . Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 16. Juli 2014 Autor Melden Share Geschrieben 16. Juli 2014 Freut mich Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Ugeen Geschrieben 23. Juli 2014 Melden Share Geschrieben 23. Juli 2014 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? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hrungdak Geschrieben 23. Juli 2014 Melden Share Geschrieben 23. Juli 2014 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 Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 23. Juli 2014 Autor Melden Share Geschrieben 23. Juli 2014 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; Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Ugeen Geschrieben 24. Juli 2014 Melden Share Geschrieben 24. Juli 2014 Danke nochmal, ich habe mir den FPS-Controler angesehen, da wird in den ersten zeilen auch nichts anderes gemacht als den Motor als Variable zu laden. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.