Wie letzte Woche versprochen gehen wir dieses Mal ein wenig mehr in die Tiefe der Entwicklung für Google Cardboard. In dieser Anleitung zeige ich euch Skripte mit denen ihr euren Charakter Laufen, Fliegen und Interagieren lassen könnt, ohne dass ihr einen zusätzlichen Gamepad benötigt. Das tolle an der Cardboard ist ja die Einfachheit und die soll nicht durch zusätzliche Hardware genommen werden.
Unity Technologies stellt auch diverse kostenlose Beispiel Projekte zur Verfügung, jedoch sind die meisten davon nicht für Mobile optimiert und werden deshalb nicht ohne sofort fehlerfrei auf einem Handy laufen. Denn zum einen muss natürlich die Steuerung angepasst werden und zum anderen haben wir auf mobilen Endgeräten deutlich weniger Rechenleistung zur Verfügung. Die einzelnen Modelle aus den Beispielen können jedoch sicherlich auch gut in Mobile-Projekten benutzt werden. Ferner ist es wichtig, dass die Szenen keine Echtzeit Lichter enthalten, da diese die Hardware auf den meisten mobilen Endgeräten in die Knie zwingen. Im Asset Store gibt es auch einige Kostenlose Modelle und Umgebungen, welche ganz bequem aus Store heruntergeladen und Importiert werden können.
Ich habe euch für dieses Tutorial ein Unity Packge zusammen gestellt, welches ihr euch kostenlos hier herunterladen könnt. Es enthalt eine kleine Waldszene die ich soweit schon mal für Mobile Geräte angepasst habe. Die Szene ist gezielt minimal gehalten, damit sie auf den meisten Smartphones läuft. Wenn ihr stärkere Geräte habt, könnt ihr die Szene nach belieben erweitern, bist auch euer Smartphone an seine Grenzen stößt. In dieser Szene könnt ihr euch, wie schon beim letzten Mal, zunächst lediglich umsehen. Doch im Laufe dieses Tutorials werden wir eure Möglichkeiten um Laufen, Fliegen und Interagieren erweitern. Fangen wir also an:
Downloadet dieses Unitypackge. Mit einem Doppelklick auf die Datei öffnet ihr dann den Import Vorgang in das aktuell geöffnete Projekt. Unity zeigt euch dann die neuen Dateien an und mit einem Klick auf „Import“ kopiert ihr die Szene in das Projekt.
Öffnet die Szene unter „Bloculus/Scenes/“. Für einen ersten Test könnt ihr direkt einmal die Build Settings aufrufen. Falls unter „Scenes In Build“ noch die Szene vom letzten Tutorial aufgelistet ist, Entfernt den Haken vor dieser und klickt auf den Button „Add Current“ um Unity zu sagen, dass ihr die Aktuelle Szene dem Build hinzufügen wollt. Ist die Szenenliste leer, könnt ihr braucht ihr nicht ihr die aktuelle Szene nicht zwingend der Liste hinzufügen, denn in diesem Fall fügt Unity automatisch die aktuelle Szene dem Build bei. Aus Performancegründen könnt ihr zudem noch die „Texture Compression“ aktivieren. Dadurch dauert der Build Vorgang zwar einmalig (deutlich) länger, durch die geringere Texturgrößen können sie jedoch schneller in den Speicher des Geräte geladen werden, was zu einer besseren Performance führt.
Als erstes müssen wir uns ein Steuerungskonzept überlegen, dass mit nur einer einzigen Taste auskommt. Wir haben insgesamt drei Funktionen die wir unterstützen wollen: Interagieren, Laufen und Fliegen. Da wir nur eine Taste haben und ich zunächst nicht auf Headtracking Gesten eingehen möchte (da diese für Neulinge schnell komplex werden) haben wir per se folgende Interkations-Möglichkeiten Neben dem Headtracking:
- Schalter Drücken
- Schalter mehrmals schnell drücken (z.B. „Doppel-Drück“)
Wie erwähnt möchte ich die Funktionen Laufen, Fliegen und Interagieren realisieren, jedoch passen diese nicht auf die eben genannten Möglichkeiten. Deshalb müssen wir das ganze ein wenig anders gestalten und zwar in „Modus Wechseln“ und „Modus nutzen“.
Gemapped könnte das dann so aussehen:
- „Doppel-Drück“ = Wechsel zwischen Lauf-, Flieg- und Interaktionsmodus
- Einfaches Drücken = Modus Aktivieren (also Vorwärtsbewegung oder Interagieren) bzw. Modus beenden
Ein Gedrückt halten kann leider nicht realisiert werden. Der Magnetschalter funktioniert über den internen Kompass des Smartphones. Wird der Schalter bewegt, kann eine rapide Änderung am Kompass festgestellt werden. Ein Script beobachtet also die ganze Zeit den Kompass und gibt Bescheid, sobald dieser sich ungewöhnlich geändert hat. Hält man den Magneten gedrückt, kann keine Änderung mehr festgestellt werden und somit kann dieser Zustand nicht erkannt werden.
Als nächstes legen wir ein neues Script an. Im Unterordner „Bloculus/Scripts“ klicken wir in der Projekt Ansicht die rechte Maustaste und wählen Create -> C# Skript und geben dem neuen Script den Namen dk_ModeSelector.
Die nächsten Schritte machen wir nun im Code:
Ihr könnt einfach jeden hier genannten Abschnitt Stück für Stück Lesen, Verstehen und in das Script kopieren.
Zunächst deklarieren wir alle Variablen die wir im Laufe des Skriptes benötigen werden:
using UnityEngine; using System.Collections; public class dk_ModeSelector : MonoBehaviour { // Wir definieren einen neuen Typ "Modes" der die drei Modi "Interact","Walk" und "Fly" festlegt. public enum Modes { Interact = 0, Walk = 1, Fly = 2 }; private Transform mainCamera; private CharacterController charController; private dk_UI ui; public float flySpeed = 3f; // Wie schnell soll sich der Charackter im Flug bewegen public float walkSpeed = 1.25f; // Wie schnell soll ich der Charackter beim laufen Bewegen private bool gravity = false; // Speichert ob derzeit die Schwerkraft aktiviert ist private int triggerCounter = 0; // In dieser Variabel speichern wir wie oft der Trigger geklickt wurde private bool activated = false; // Ist die funktion des aktuellen Modus aktiviert? private Modes currentMode = Modes.Interact; // Der Aktuell ausgewaehlte Modus
Bei der Start Methode müssen wir nun die Referenzen zu der Hauptkamera und dem Charakter Controller finden und in die dafür deklarierten Variabel legen:
void Start(){ // Finde eine Referenz zur Haupt Kamera mainCamera = ((GameObject)GameObject.FindGameObjectWithTag ("MainCamera")).transform; // Finde eine Referenz zu dem Charackter Controller dieses GameObjects charController = GetComponent (); // Finde eine Referenz zu meinem UI Script ui = FindObjectOfType (); }
Nachdem wir nun den Anfang gemacht haben müssen wir noch die Programmlogik festlegen. Die Update Methode wird in jedem Frame ausgeführt:
// Wird in jedem Frame ausgefuert void Update() { checkForClick (); // Fuehre die Methode aus, die zum aktuellen Modus passt, wenn der Modus aktiviert wurde if (activated) { if (currentMode == Modes.Interact) { interact (); } else if (currentMode == Modes.Walk) { walk (); } else if (currentMode == Modes.Fly) { fly (); } } //Wenn schwerkraft aktiviert ist, dann lass den Characjter falllen wenn er nicht den Boden berührt if (gravity && !charController.isGrounded) { charController.Move (new Vector3 (0, -9.81f * Time.deltaTime, 0)); } }
Anschließend legen wir eine Methode an die überprüft ob der Spieler einen Klick oder einen Doppelklick mit dem Magnetschalter ausgeführt hat:
/* * Diese Methode ueberprueft ob der Magnetschalter gedrueckt wurde * wurde und unterscheidet zwischen Halten und Klicken */ private void checkForClick(){ if (!Cardboard.SDK.CardboardTriggered){ return; // Wenn der Schalter nicht gedrueckt ist und er auch nicht kuerzlich gedrueckt wurde, verlassen wir diese Methode } // An dieser stelle ist der Schalte also aufjedenfall getriggered! triggerCounter++; // Click zaehler erhoehen if (triggerCounter >= 2) { // Wenn der Zustand groeßer als 4 wird, also ein Doppelklick ausgefuehrt wurde, fuehren wir unseren Code aus OnDoubleClick (); ResetState (); } else { /* * Ein Doppelklick muss schnell passieren, drueckt man die Taste ueber 5 Minuten hinweg 2 mal, soll dies ja nicht als Doppelklick erkannt werden. * Also brauchen wir einen "Timeout", der festlegt wann der Abstand zwischen den Clicks zu gross ist: * Mit Invoke(string), kann das ausfuehren von Methoden geplant werden. * IsInvoking(string) prueft, ob wir eine ausfuehrung geplant haben * CancelInvoke(string), bricht eine Planung ab * * Ein Doppelklick muss schnell passieren, wir nutzen Invoke um einen Timeout zu erstellen: */ // wenn schon eine Planung aktiv ist, berchen wir sie ab um den Timeout neu zu starten. if (IsInvoking ("ResetState")) { CancelInvoke ("ResetState"); } //nach 0.5 Sekunden wird der State zuruck gesetzt und wir nehmen an, dass es sich nicht um einen doppelklick handelt. Invoke ("ResetState", .75f); } }
Die Bereits erwähnte Methode ResetState Methode sieht so aus:
/* * Setzt den Counter zurueck und erzwingt so schnelle Doppelklicks */ private void ResetState(){ if (triggerCounter == 1) { // Wenn der TimeOut aktiviert wird, die Taste aber bereits einmal gedrueckt wurde, dann ist es ein Klick! OnClick(); } triggerCounter = 0; }
Was soll passieren ein wir einen Klick oder einen Doppelklick ausführen?
private void OnClick(){ activated = !activated; // Den Modus in den jeweils gegenteiligen Modus umschalten Debug.Log ("Click! "+currentMode+"="+activated); } private void OnDoubleClick(){ // Change Mode activated = false; // Deaktiviere den aktuellen Modus currentMode = (Modes)((((int)currentMode)+1)%3); // Erhoeht den aktuellen Modus um 1 und wechselt zurueck zum ersten wenn der letzte erreicht wurde. /* % ist der Module Operator, er gibt den Rest einer Division zurueck. 1%3 = 1 (1/3 = 0 mit Rest 1), 3%3 = 0 (3/3 = 0 mit Rest 0), 5%3 = 3 (5/3 = 1 mit Rest 2) * wir erhoehen also die variabel "currentMode" bei jedem Doppelklick um 1 und wenn sie den Wert 3 hat, setzen wir sie wieder auf 0. */ Debug.Log ("Double Click: " + currentMode); // Zeige den aktuellen Modus kurz in der UI an: ui.Text = currentMode.ToString(); }
Zu guter Letzt legen wir noch Fest was passieren soll wenn die einzelnen Modi aktiviert sind:
/* * Wird ausgefuehrt wenn wir im Interactions Modus sind und dieser auch aktiviert ist * Schiesst einen Strahl in die Blickrichtung und wenn das getroffene Objekt Physik faehig ist (hat einen rigidbody), geben wir ihm eine vertikale kraft nach oben */ private void interact(){ RaycastHit info; if (Physics.Raycast (mainCamera.position, mainCamera.forward, out info, 5f)) { if(info.rigidbody != null){ info.rigidbody.AddForce(0,20*Time.deltaTime,0,ForceMode.Impulse); } } } /* * Wird ausgefuehrt wenn wir im Laufen Modus sind und dieser auch aktiviert ist * Bewegt den Charackter in Kamera Blickrichtung und aktiviert die schwerkraft */ private void walk(){ gravity = true; charController.Move (mainCamera.forward * Time.deltaTime * walkSpeed); } /* * Wird ausgefuehrt wenn wir im Fliegen Modus sind und dieser auch aktiviert ist * Bewegt den Charackter in Kamera Blickrichtung und deaktiviert die schwerkraft */ private void fly(){ gravity = false; charController.Move (mainCamera.forward * Time.deltaTime * flySpeed); } }
Ich habe dem CardboardMain Objekt bereits einen Charackter Controller hinzugefügt, ihr müsst jetzt also lediglich das neue Script ebenfalls dem Objekt hinzufügen. Ist das getan sind wir für heute auch schon fast fertigt. Wir erstellen also wie schon am Anfang dieses Tutorials einen Build und los geht’s.
Zieht ihr den Magnetschalter zweimal runter wechselt ihr den Modus. Beim Modus Wechsel wird der neue Modus kurz angezeigt. Beim einmaligen klicken aktiviert ihr den aktuellen Modus. Im Laufen Modus fallt ihr zu Boden und lauft los, im Fliegen Modus fliegt ihr Los und im Interact Modus könnt ihr die Fässer und Kisten neben dem Zelt schweben lassen in dem ihr sie ansieht.
Das ganze ist nur der erste Schritt. Fangt an das Script nach euren Wünschen zu verändern und lernt so Stück für Stück dazu.
Ich hoffe diese kleine Einführung hat euch gefallen!
Abschließend noch der Projekt Download:
Falls ihr euch das Projekt nicht vollständig von Hand zusammenbauen wollt, könnt ihr auch einfach mein Projekt herunterladen und ihr erhält ein Fertig konfiguriertes Projekt dass ihr nur noch Builden müsst!
Projekt Download: CardboardBloculusExamplePT2.zip via mega.co.nz
Im Unitypackage verwendete Assets:
Finde dieses Tutorial SUPER!! :D
Ist es viel komplizierter die Bewegungen mithilfe eines Bluetooth Controllers (Dualshock) zu steuern? Bzw. gibt es dafür schon scripts?
Die Downloads funktionieren leider nicht mehr, kannst du sie nochmal online stellen?
Hallo Andreas, ich habe die Links nochmal überprüft und zu diesem Zeitpunkt funktionieren sie ohne Einschränkungen. Eventuell hatte mega zu dem Zeitpunkt ein Server Problem o.ä. :)
Mit bestem Gruß, Daniel
Hallo, ich versuche vergeblich die Scene zu laden in Unity..kommt aber immer das die Version, zu alt ist?! Bitte um Hilfe, vielen Dank