[Neuling] Roll a Ball Extended Version - Lektion 3 - Den Spieler bewegen

  • Willkommen bei "[Neuling] Roll a Ball Extended Version - Lektion 3 - Den Spieler bewegen" 8)

    Diese Lektion setzt Lektion 2 dieser Reihe voraus und baut entsprechend darauf auf.


    Wir wollen uns nun um den Spieler kümmern.

    Hierzu wählt den "Player" aus und fügt im Inspector über die Schaltfläche "Add Component" => "Physics" => "Rigidbody" hinzu.

    Es sollte nun eine weitere Komponente erschienen sein. Eine Alternative wäre diese Komponente über das Hauptmenü (oben) hinzu zu fügen.

    Klickt nun auf das Zahnrad (Settings) in der Rigidbody Komponente und wählt "Move Up", bis es unter der "Transform" Komponente sich befindet.

    Ihr wisst nun, wie man Komponenten untereinander organisieren könnt. Gewöhnt euch eine möglichst gleiche Struktur solcher Komponente in euren Projekten an, sodass ihr schnell die jeweilige Komponente im Objekt findet.


    Klickt nun auf das Buch-Icon der Rigidbody-Komponente.

    Es sollte sich ein neues Fenster öffnen, in dem ihr zur Unity Anleitung geleitet werdet.

    Laut Unity wird der Rigidbody folgendermaßen beschrieben:

    Rigidbodies enable your GameObjects to act under the control of physics. The Rigidbody can receive forces and torque to make your objects move in a realistic way. Any GameObject must contain a Rigidbody to be influenced by gravity, act under added forces via scripting, or interact with other objects through the NVIDIA PhysX physics engine


    Kurz gesagt: Diese Komponente (der Rigidbody) stellt euren Objekt die Möglichkeit zur Verfügung auf physikalische Änderungen z.b. durch einen Stoß, Bewegung, Gravitation, etc. zu reagieren.

    Es lassen sich ebenso Masse, physikalische Berechnungen, etc. damit bewerkstelligen.


    Hinweis: Um den Überblick bei sehr vielen Komponenten zu bewahren, können diese mit dem kleinen Pfeil auf der jeweils linken Seite eingeklappt werden. Dies hat keine Auswirkung auf das Objekt. Die Komponente funktioniert natürlich auch weiterhin.


    Als nächstes wollen wir ein Skript erstellen - unser erstes C# Skript, welches Funktionalität in unser Objekt bringt.

    Ein Skript kann auf verschiedene Arten erstellt werden. Einerseits in unserem Skript Ordner oder in anderen (was jedoch keinen Sinn machen würde) per Rechtsklick im "Project" Fenster => "Create" => "C# Script". Oder als Komponente für ein Objekt, wie unseren Player.

    In diesem Fall macht es mehr Sinn das Skript dem Spieler als Komponente direkt zu übergeben, da wir die Funktionalität auf dieses Objekt haben wollen.


    Wählt den Player an, klickt: "Add Component" => "New script" und wählt als Name "ControllerPlayer" => "Create and Add".

    Wichtiger Hinweis: Ihr könnt auch versuchen über die Suchfunktion bei "Add Component" einen Namen einzugeben, wie "SkriptXYZ". Dies ist als Komponente noch nicht realisiert und Unity schlussfolgert somit automatisch, dass es als Skript (C#) angedacht ist. Sprich: es ist eine schnelle Art ein Skript neu zu erstellen. Der obige Weg ist jedoch genauso gut.


    Habt ihr die Komponente hinzugefügt, sollte diese eurem Objekt hinzugefügt sein.


    Geht nun über das "Project" Fenster in euer Root Ordner "Assets", wo alle euren Ordner sind (FBX, Materials, ...).

    Hier sollte nun das ControllerPlayer.cs (C# Skript) liegen. Zieht dieses nun in den "Scripts" Ordner um Ordnung zu wahren.


    Wechselt nun in den Scripts Ordner und wählt das erstellt Skript an. Im Inspector sehr ihr nun eine Vorschau von dem Default erzeugten Code.

    Zum Bearbeiten klickt nun per Doppelklick auf das Skript.

    Je nach Entwicklungsumgebung (IDE) öffnet sich nun diese und zeigt den zuvor per Vorschau gesehenen Code an.


    Anmerkung: Ich nutze Visual Studio, solltet ihr MonoDevelop nutzen, kann die farbliche Formatierung ggf. anders aussehen. Dies tut jedoch hierbei nichts zur Sache.


    Löscht nun den folgenden Code:

    Csharp
    1. // Use this for initialization
    2. void Start () {
    3. }
    4. // Update is called once per frame
    5. void Update () {
    6. }

    Euer Skript sollte nun in etwa so aussehen:

    Csharp
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ControllerPlayer : MonoBehaviour {
    5. }


    Unser Code kommt ab jetzt nur in diesen Teil / Klasse / Block:

    Csharp
    1. public class ControllerPlayer : MonoBehaviour {
    2. }


    Kurze Erwähnung:

    Es gibt mehrere Arten den Code bzw. euer Spiel 'aktuell' zu halten, sprich z.B. bevor Frames geladen werden den Code auszuführen.


    Meist wird hierfür void Update() verwendet:

    Csharp
    1. void Update () {
    2. }

    void Update() wird jedesmal bevor ein Frame angezeigt wird durchlaufen und führt den Code eures Programmes aus. Hier landet der meiste Spiele-relevante Code.


    Hingegen z.B. void FixedUpdate() ist für physikalische Berechnungen zuständig und wird vor jeder physikalischen Berechnung ausgeführt.

    Csharp
    1. void FixedUpdate () {
    2. }

    Da wir eine physikalische Berechnung wollen, wird diese Funktion von uns benötigt.

    Es gibt weitere Update Varianten wie z.B. LateUpdate, welches immer zuletzt geladen wird, ehe das Spiel gerendert wird.

    Mehr hier unter der Unity Referenz.


    Unser Code sollte nun so aussehen:

    Csharp
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ControllerPlayer : MonoBehaviour {
    5. void FixedUpdate () {
    6. }
    7. }


    Schreibt nun in die FixedUpdate Funktion "Input".

    Wir wissen, wir brauchen einen Input, aber welchen genau, ist ja unbekannt.

    Es lässt sich die Unity Referenz hierzu hernehmen.

    Hier finden wir eine Fülle von Informationen über Eigenschaften, Methoden, etc. die Unity uns wie Update() und FixedUpdate() bereitstellt.

    Uns interessiert hierbei jedoch vorerst nur die statische Methode "Input.GetAxis".

    Input.GetAxis(string axisName) erwartet also einen Parameter vom Typen string.


    Wem das jetzt hier noch nichts sagt, sollte unbedingt meine C# Lektionen durchlesen und sich damit beschäftigen. Das Tutorial / Projekt hier ließe sich auch ohne dem Wissen fortsetzen, jedoch wird das Verständnis vermutlich nicht so stark aufgebaut / fehlen, um die Aktionen dahinter zu verstehen.


    In dem Beispiel auf der Seite von Unity unter Input.GetAxis sehen wir folgende Verwendung:

    Csharp
    1. float translation = Input.GetAxis("Vertical") * speed;
    2. float rotation = Input.GetAxis("Horizontal") * rotationSpeed;

    Wir können dies als ersten Anhaltspunkt hernehmen, wandeln es jedoch für uns etwas um.


    Geht nun zurück in den Skript Editor.

    Ergänzt eure FixedUpdate() Funktion um folgenden Code:

    Csharp
    1. void FixedUpdate ()
    2. {
    3. float moveHorizontal = Input.GetAxis("Horizontal");
    4. float moveVertical = Input.GetAxis("Vertical");
    5. }


    Um es jetzt klar auszudrücken: Unity weiß nun, welche Achse im Spiel die Vertikale und welche die Horizontale ist.

    Sprich, welche Pfeiltasten welchen Achsen entsprechen. Aber woher weiß Unity das?

    Hierzu schauen wir uns in Unity die Input Einstellungen an.

    Hier sehen wir also, dass der Name der horizontalen Achse gleich "Horizontal" und der vertikalen Achse gleich "Vertical" ist.

    (Also die englischen Namen) Die Tasten (Pfeil und alternative Tasten) weiß Unity ebenso durch die entsprechende Zuweisung.


    Daher ist es wichtig das euer Code exakt diese Namen zwischen den " " Zeichen enthält. Andernfalls weiß Unity nicht, welche Achsen gemeint sind.

    Stünde in den Eigenschaften bei der horizontalen Achse "Blubb" (was nicht sinnvoll wäre(!), so müsste im Code entsprechendes stehen:

    Code
    1. void FixedUpdate ()
    2. { float moveHorizontal = Input.GetAxis("Blubb");
    3. float moveVertical = Input.GetAxis("Vertical");
    4. }

    Dies wollen wir jedoch nicht, es diente nur dem Verständnis - am Besten ihr behaltet die von Unity voreingestellten Namen bei.

    Solltet ihr zukünftig andere Achsen wissen wollen, wisst ihr, wo diese zu finden sind.



    Wir haben nun die Achsen und den Input. Für die Physik benötigen wir nun jedoch den "Rigidbody".

    Also tippen wir dies entsprechend ebenso in unseren Code.


    Wir wissen jedoch nicht, was wir zur Verfügung haben.

    Daher suchen wir wieder in der Unity Scripting API nach "Rigidbody" und finden unter anderem einen Eintrag wie "Rigidbody.AddForce".
    In dem gezeigten Beispiel sehen wir auch die Verwendung von diesem:

    Anhand diesem Beispiel sehen wir in etwa, wie es zu verwenden ist. Auf der Hauptseite, die wir in der Scripting API gefunden haben, sehen wir außerdem folgendes:

    public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force);


    D.h. unsere AddForce Methode vom Rigidbody benötigt einen 3D Vektor und einen optionalen ForceMode.

    Wir belassen dies erstmal im Raum stehen. Der 3D Vektor kann separat erzeugt werden wobei es nach dem Format " X, Y, Z " -Achse definiert wird. Er kann aber auch in der Methode direkt geschrieben werden.

    Sollte dies an dieser Stelle noch nicht klar sein, wird es das hoffentlich im folgenden Beispiel anhand unseres Codes.


    Erweitert nun den Code um folgendes (Erklärung folgt) :


    Erklärung:

    Csharp
    1. private Rigidbody rb;

    Deklariert eine Variable des Types Rigidbody mit dem Namen rb.

    Diese Variable soll unseren Rigidbody beinhalten, der unserem "Player" (Kugel) Objekt in Unity als Komponente zugewiesen wurde.

    Doch... woher weiß Unity (das Skript) nun, welcher Rigidbody gemeint ist und mit welchem er sich 'verlinken' soll / es in die Variable speichern soll?


    Hierfür ist folgendes notwendig:

    Csharp
    1. void Start()
    2. {
    3. rb = GetComponent<Rigidbody>();
    4. }

    Erstmal: void Start() ist ähnlich wie Update() und FixedUpdate() eine Funktion von Unity, die das Spiel und deren Logik von elementarer Wichtigkeit sind.

    void Start() und der Inhalt, also hier die "rb = Get....." Zeile wird einmalig beim Aufruf des Skriptes ausgeführt. Meist ist dies auch gleich der Spiel-Start.

    Es wird somit nicht jeden Frame neu geladen, was hier ja nicht notwendig ist. Wir wollen nur einmalig Unity sagen, was in unserer Rigidbody Variable gespeichert sein soll.

    Dies tuen wir mit der Zeile: rb = GetComponent<Rigidbody>();


    Unity weiß nun, dass die Rigidbody Komponente unseres Players in der "rb" Variable vom Typ Rigidbody gespeichert ist.


    Bleiben noch folgende Zeilen:

    Csharp
    1. Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    2. rb.AddForce(movement);


    Erstere Zeile mit dem "Vector3" initialisiert einen neuen Vector3 (also alle 3 Achsen), in dem wir gemäß dem Format "X, Y, Z" Achse die Daten übergeben.

    Wir wissen oder sehen anhand des Unity Gizmos (Bunte Kegel-Achsen im Scene View in Unity), dass die "Y-Achse" die Achse für oben und unten ist.

    Unser Spieler soll sich nicht nach oben oder unten bewegen, weswegen wir dies mit 0 setzen. unsere anderen Input Variablen tragen wir an entsprechender Stelle ein.


    Nun übergeben wir den Vector3 (movement) an unseren Rigidbody über "rb.AddForce(movement);"


    Speichert das Skript ab und kehrt zu Unity zurück.

    Es kann kurzzeitig dauern, bis Unity darauf reagiert. Ist im "Console" Tab neben dem "Project" Tab (unten) kein Fehler, so habt ihr alles bisher richtig gemacht.


    Startet nun das Spiel mit Klick auf dem "Play-Pfeil" oben in der Mitte.


    Ihr befindet euch nun im Spiel Modus (Play-Mode).
    Achtung!!! => Alle Änderungen, die ihr in diesem Modus macht, gehen verloren, sobald ihr wieder in den normalen Modus zurück geht!


    Ihr könnt nun die Pfeiltasten drücken und die Kugel / der "Player" sollte sich entsprechend (wenn auch langsam) bewegen.

    Derzeit ist es fast noch zu langsam. Das wollen wir nun ändern.


    Beendet den Play-Mode durch erneutes Drücken auf den "Play-Button" oben in der Leiste.

    Hinweis: Testet ruhig immer wieder euer Projekt zwischendurch um größere Fehler ausschließen zu können.


    Öffnet nun erneut das Skript des Player's. Wir könnten nun folgende Zeile verändern:

    Csharp
    1. rb.AddForce(movement);

    zu:

    Csharp
    1. rb.AddForce(movement * 50);


    dies ist aber eine sehr unelegante Methode. Denn... jedesmal, wenn wir diesen Wert ändern wollten, müssten wir in das Skript und diesen dort entsprechend abändern.

    Daher verändern wir diese Zeile nicht und erweitern unseren Code entsprechend so, wie im folgenden gezeigt:


    Mit "public float speed;" führen wir eine neue Variable ein, in der unsere Geschwindigkeit gespeichert ist. Diese wird mit "rb.AddForce(movement * speed);" multipliziert und auf unser Objekt übertragen.

    Das Gute an der "public" Deklaration unserer Variable ist, dass diese nun in Unity als Feld bei der Skript-Komponente dem Player-Objekt zur Verfügung steht. Wir können also hier den Wert eintragen, ohne im Skript 'pfuschen' zu müssen.


    Speichert das Skript und wechselt in den Unity Editor.

    Wählt den Player an und schaut die Skript Komponente an. Diese sollte nun um ein Feld "speed" mit einem Zahlenwert von "0" ergänzt worden sein.

    (Es kann etwas dauern, bis Unity das Feld anzeigt, nachdem ihr vom Skript Editor zum Unity Editor wechselt.)


    Tragt nun statt 0 den Wert "10" ein.


    Wechselt nun erneut in den Play-Mode.

    Beim Bewegen des Spielers mittels den Pfeiltasten sollte dieser nun wesentlich schneller über den Bildschirm sich bewegen.

    Beendet den Play-Mode und speichert das Prjekt ab.



    Weiter geht es in: "[Neuling] Roll a Ball Extended Version - Lektion 4 - Bewegung der Kamera"

    :s_info: Ehemalig in dieser Community als Toothless bekannt, habe ich nun meinen Künstlernamen angenommen: Cryptogene

    So manch einer kennt ja ohnehin schon meine 3D Szenen.


    ninja_pointright Ihr wollt euch für das Forum bedanken und dieses zu 100% unterstützen? Ihr könnt uns finanziell durch eine Schenkung unterstützen. Danke für euren Support!