Im ersten Teil des Blogbeitrages zur Entwicklung der Virtual Reality-App haben wir das Backend vorbereitet. Es wurde ein HANA Cloud Trial Account eingerichtet und darin die Datenbanken und Services geschaffen, die wir mit unserer Virtual Reality-Anwendung auslesen wollen. Auch jetzt fangen wir mit der Einrichtung der notwendigen Software an.
Installation von Unity und des Software Development Kits für Android
Zunächst benötigen wir für die Entwicklung der Virtual Reality-App die Unity-Entwicklungsplattform. Der Code der Anwendung wurde auf Unity-Version 2018.3.7 erstellt. Um die Kompatibilität ganz sicherzustellen, sollte diese Version aus dem Archiv geladen werden:
https://unity3d.com/de/get-unity/download/archive
Während der Installation gibt es die Möglichkeit, verschiedene Module auszuwählen. Hier muss unbedingt der Android Build Support aktiviert werden, falls dies nicht automatisch erfolgt ist. Ansonsten ist die Installation selbsterklärend.
Nach der Installation von Unity muss noch das Software Development Kit für Android installiert werden, damit wir die Anwendung als .APK Datei builden und auf Android-Geräten installieren können.
Es empfiehlt sich, das Android Studio zu nutzen. Hier hat man dank einer GUI eine gute Übersicht über die installierten Android SDKs. Alternativ kann das SDK über Command Line Tools installiert werden. Mehr Informationen über das Android SDK und dessen Installation für die spezifische Unity-Version gibt es hier:
https://docs.unity3d.com/2018.3/Documentation/Manual/android-sdksetup.html
Es reicht in der Regel, das aktuellste verfügbare SDK herunterzuladen, da diese abwärtskompatibel sind. Wir werden mindestens API Level 19 (Android 4.4 'KitKat') benötigen.
Download Google Cardboard SDK
Bevor wir Unity öffnen und loslegen können, laden wir noch das Google Cardboard SDK herunter, das wir hier bekommen:
https://github.com/googlevr/gvr-unity-sdk/releases.
Wir suchen das „GVR SDK for Unity v1.200.1“ und laden davon die Datei mit der Endung „.unitypackage“ herunter.
Eine Beschreibung der hier vorgenommenen Schritte zu Download und Import findet sich auch unter https://developers.google.com/vr/develop/unity/get-started-android.
Virtual Reality-App: Richtig starten mit Unity
Nach dieser Vorbereitung können wir starten. Wir öffnen Unity und legen ein neues Projekt mit einem beliebigen Namen in einem beliebigen Ordner an. Wichtig ist nur, dass als Template 3D ausgewählt ist:
Wir finden uns auf der Unity-Oberfläche wieder. Unity ist ein sehr umfangreiches Werkzeug an dieser Stelle, daher eine kleine Erläuterung der Basics.
Die Unity-Basics kurz erklärt
Grundsätzlich ist eine Unity-Anwendung so strukturiert, dass Objekte, „GameObjects“ genannt, in unterschiedlichen Umgebungen agieren, den Scenes. Im Spiele-Bereich entspricht eine Scene etwa einem Level oder einem Menübildschirm. In der Web-Entwicklung wäre das Pendant in etwa eine View. In der Mitte der GUI sehen wir die Szenenansicht. Unity hat beim Starten des Projektes eine Scene mit dem Namen "Sample Scene" erstellt und in dieser standardmäßig zwei GameObjects platziert: eine Kamera und eine Lichtquelle. Die Kamera hat eine wichtige Bedeutung, denn sie bestimmt, welchen Ausschnitt der dreidimensionalen Welt der Anwender sieht. Wenn der „Play“-Button oberhalb der Szenenansicht gedrückt wird, startet die Anwendung und wir sehen den Renderbereich der Kamera.
Um im Editor durch die Szene zu navigieren, kann man die WASD-Tasten und die Maus nutzen. Wenn wir auf der Szenenansicht die rechte Maustaste festhalten, können wir die Sicht drehen und mit dem Mausrad reinzoomen. Ein Doppelklick auf ein GameObject in der Hierachie fokussiert es in der Szenenansicht. Das ist nützlich, wenn man die Ansicht ungünstig verstellt hat.
Der Weg zu den GameObjects
Bevor wir für die Virtual Reality-App eigene GameObjects und eine Logik in Form von Scripts erzeugen, müssen wir noch einige Vorbereitungen treffen. Unter dem Menü-Eintrag Edit > Preferences überprüfen wir erst einmal, ob der Pfad zum Android SDK richtig ist und tragen ihn gegebenenfalls nach:
Wenn dem so ist, importieren wir unser Google Cardboard SDK. Unter Assets > Import Package > Custom Package wählen wir Google Cardboard SDK GVR SDK for Unity v1.200.1. In dem erscheinenden Importfenster sollte bereits alles ausgewählt sein, ansonsten klicken wir auf „All“ und dann auf „Import“.
Wir wollen unsere fertige Anwendung als .APK Datei für Android Smartphones speichern und dort auch auf das Google Cardboard SDK im Gerät zugreifen. Dafür müssen wir noch einige Einstellungen für den Build vornehmen.
Unter File > Build Settings wählen wir Android aus und klicken auf „Switch Platform“. Gegebenenfalls dauert es einen Augenblick, bis das Build Target geändert wird.
Ebenfalls unter Build Settings wählen wir die Player Settings. Unter Other Settings wählen wir (frei) einen „Package Name“, als Beispiel „com.cpro.hanavr“. Mit diesem identifiziert das Android-Gerät unsere Anwendung. Das „Minimum API Level“ stellen wir auf „Android 4.4 ‚KitKat‘ (API Level 19)“. Unter XR Settings setzen wir den Haken bei „Virtual Reality Supported“ und wählen dort unter Virtual Reality SDKs „Cardboard“.
Als nächstes wählen wir aus dem Reiter über der Szenenansicht den Unity Asset Store und suchen nach einem Paket namens „JSON Object“. Dieses werden wir nutzen, um die Antwort des OData-Services komfortabel verarbeiten zu können. Erst „kaufen“ wir das Paket (es ist gratis), dann downloaden und importieren wir es.
Der Projektordner unten im Unity UI zeigt eine Ordnerstruktur, die mit einem „Asset“-Ordner beginnt. In diesem werden beliebige Ressourcen abgelegt (u.a. Texturen, 3D Objekte und die später näher erläuterten Prefabs). Während wir in dem Szenenbaum links neben der Szenenansicht szenenspezifische GameObjects sehen, haben wir unten die Ressourcen, aus denen wir in jeder beliebigen Szene GameObjects erzeugen können - entweder im Editor beim Aufbau der Szene oder dynamisch während der Anwendung. Im Folgenden sprechen wir von Szenenbaum und Ressourcen.
Umgebungsgrafik mit Google VR
Das Google SDK kommt mit einigen Beispielszenen und aus diesen „leihen“ wir uns die Umgebungsgrafik für unsere Virtual Reality-Anwendung. Nachdem wir das Google VR SDK importiert haben, erscheint der Ordner „Google VR“ in unseren Projektressourcen. Wir gehen in den Ordner „Google VR->Demos->Environment“ und ziehen das Prefab „CubeRoom“ per Drag and Drop in die Szene. Prefabs sind so etwas wie Blaupausen für GameObjects, die frei konfiguriert und dann immer wieder genutzt werden können. Bei den anderen beiden Objekten in dem Ordner handelt es sich um ein 3D-Model mit dem gleichen Namen sowie eine Textur für den Raum. In dem Prefab sind Textur und Model bereits in einen Zusammenhang gebracht. Nachdem wir das Prefab in die Scene gezogen haben, taucht ein neues GameObject in der Szenenhierarchie links auf. Wir wählen das neue Objekt aus. Rechts im Bild ist der Inspector zu sehen. Über diesen können Eigenschaften von GameObjects betrachtet und manipuliert werden. Die modular aufgebauten Eigenschaften von GameObjects nennen sich Components. Hinter jeder Component steckt ein Script.
Jedes GameObject hat mindestens die Component „Transform“, welche die Position (als Koordinaten) im Raum sowie die Rotation und die Skalierung beinhaltet. Wir ändern in der Transform Component des CubeRoom die Positionen X,Y und Z in 0,0,0, um den Raum auf dem Nullpunkt des Koordinatensystemes zu zentrieren.
Wer möchte, kann jetzt ein wenig mit dem Kamera-GameObject spielen, sie an verschiedene Positionen bringen und „Play“ drücken.
Weitere Objekte kreieren
Nun benötigen wir weitere Objekte aus dem Google VR SDK. Zunächst wählen wir die Kamera im Szenenbaum und löschen sie mit Rechtsklick > Delete aus der Szenenhierarchie, da wir sie mit einer VR-Kamera ersetzen wollen.
Mit Rechtsklick > CreateEmpty auf dem Szenenbaum (während wir uns nicht auf einem Objekt befinden) schaffen wir ein neues GameObject mit einer Transform Component. Wir benennen dieses Objekt im Inspector in „Player“ um und setzen die Position auf (0, 1.8, 0). Mit Rechtsklick > CreateEmpty auf das Player-Objekt erschaffen wir ein Child-Objekt des Players. Die Child-Parent-Beziehung von Unity GameObjects hat nichts mit Vererbung zu tun, sondern mit der Beziehung der Transform Component. Ändern wir eine der Transform-Eigenschaften im Parent-Objekt, ändert sich das Child-Objekt ebenfalls. Wenn wir nun im Child-Objekt in die Transform Component schauen, sehen wir dort die lokalen Eigenschaften relativ zum Parent-Objekt. Wenn wir also beispielsweise die Position auf (1,1,1) ändern würden, bedeutet das immer (1,1,1) relativ zur transform.position des Parent-Objektes. Wir nennen das neue GameObject „Main Camera“.
Camera-Component hinzufügen
Components werden in Skripten definiert und lassen sich beliebig GameObjects zuteilen. In der Regel handelt es sich bei einer Component um eine Klasse, wir werden das später in den Skripten sehen.
Im Inspector fügen wir der „Main Camera“ jetzt eine Component „Camera“ zu. Dazu drücken wir die Schaltfläche „Add Component und suchen über das Suchfeld nach „Camera“. In der Component ändern wir „Clear Flags“ in dem Dropdown in „SolidColor“. Das Tag des GameObjects (Dropdown unter dem Namen) ändern wir in „main camera“. Da wir die VR-Unterstützung in unserem Projekt aktiviert haben, sehen wir in der „Camera“ spezifische Eigenschaften wie „Stereo Seperation“.
Wir fügen der „Main Camera“ eine weitere Component „GVR Pointer Physics Raycaster“ hinzu. In unseren Ressourcen navigieren wir in den Ordner Assets > GoogleVR > Prefabs > Cardboard und ziehen das Prefab „GvrReticlePointer“ aus den Ressourcen in den Szenenbaum auf die „Main Camera“, so dass das Prefab ein Child der Kamera wird.
Funktionen der Virtual Reality-App testen
Außerdem ziehen wir aus Assets > GoogleVR > Prefabs > EventSystem das „GVREventSystem" auf die oberste Ebene des Szenenbaumes sowie aus Assets > GoogleVR > Prefabs den „GvrEditorEmulator“. Das Event-System ermöglicht VR-spezifische Events wie die Blicksteuerung. Mit dem EditorEmulator können wir diese und andere Funktionen der App im Unity Editor testen. Wir werden im Laufe der Entwicklung selten Zwischenschritte testen können, da viele Scripte und Objekte aufeinander aufbauen und Unity das Starten der Anwendung nicht erlaubt sobald es Scriptfehler (u.a. auch fehlende Referenzen) gibt.
Wer jetzt „Play“ drückt, kann bei gedrückter „Alt“-Taste mit seiner Maus die Kamera steuern und damit die Bewegungen simulieren, die wir in der fertigen Anwendung mit dem Kopf machen.
Die ersten eigenen Klassen bzw. Components
Wir schreiben ein Script, das aus dem OData-Service die Kategorien ausliest. Dazu legen wir in unseren Ressourcen einen Ordner „Scripts“ an und fügen in diesen per Rechtsklick > Create > C# Script ein neues Script ein. C# ist in Unity die Sprache der Wahl und inzwischen auch die einzige unterstützte.
Wir nennen das Script „CategoryReader“ und fügen folgenden Inhalt ein (die Datei lässt sich mit jedem beliebigen Texteditor öffnen):
Diese Script-Datei ist ausführlich kommentiert, um den Code und die Herangehensweise bei Unity nachvollziehbar zu machen. An dieser Stelle bekommen wir in der Unity GUI noch eine Fehlermeldung, da es die Klasse „CategoryButton“ noch nicht gibt, auf die wir verweisen. Ebenso haben wir noch kein Prefab „CategoryButton Prefab“ definiert. Dieses soll unser GameObject sein, das eine Art Button für die Auswahl einer Produktkategorie darstellt. Da wir in unserer Google Cardboard-App keinen Controller vorgesehen haben, müssen wir die Auswahl über eine reine Blicksteuerung ermöglichen. Es wäre ebenso möglich, den Input eines Bluetooth-Controllers zu verwenden oder den Button, den manche Cardboard Brillen eingebaut haben, aber hier wollen wir ein minimales Hardware-Setup verwenden, um den Nachbau zu erleichtern.
Wir werden in unserer Anwendung also mehrere Buttons haben, die auf Blick reagieren. Wenn wir dafür ein separates Script schreiben, müssten wir immer zwischen den Components hin und her referenzieren (in dem Beispiel oben mit„GetComponent“), was möglich, aber schnell unübersichtlich wird. Natürlich wollen wir die Logik aber auch nicht wiederholt in jedes Script schreiben, also machen wir uns die Vererbung von C# zunutze. Unity-Klassen erben von der Basisklasse MonoBehaviour (z.B.: public class CategoryReader : MonoBehaviour). Diese sorgt dafür, dass wir LifeCycle-Methoden wie „Start“ (bei Erzeugung des GameObjects) oder „Update“ (einmal pro Frame) haben. Wir können aber auch von anderen Klassen erben, die wiederum von MonoBehaviour erben und damit sämtliche Methoden innerhalb der Vererbungslinie behalten. Wir erzeugen in unserem Ordner „Scripts“ ein neues C# Script und nennen es „GazeButton“. Dieses sieht wie folgt aus (die Datei lässt sich mit jedem beliebigen Texteditor öffnen):
Das Script überprüft in der Update Methode, ob das GameObject mit dem Blick ausgewählt wurde und zählt einen Timer runter. Wenn der Timer abgelaufen ist, wird eine Funktion ausgelöst. Diese definieren wir dann in den erbenden Klassen aus. Wir können in den erbenden Klassen auch Variablen überschreiben, etwa wenn wir die Dauer des Blickkontaktes für das Auslösen der Funktion erhöhen wollen, der hier mit einer Sekunde definiert ist.
Eine wichtige Funktion fehlt noch. Wenn wir im dreidimensionalen Raum Objekte erzeugen, müssen diese in Richtung unserer Kamera ausgerichtet werden. Die Berechnung der richtigen Rotation ist mit Hilfe von Unity-eigenen Hilfsfunktionen vereinfacht. Da wir wieder mehrere Objekte haben, die diese Funktionalität benötigen, sie aber von der restlichen Logik unabhängig ist, schreiben wir dieses mal keine Klasse zum Vererben, sondern eine Klasse, die wir als Component an die entsprechenden Objekte anhängen können. Wir nennen das Script „QuadToCam“ (die Datei lässt sich mit jedem beliebigen Texteditor öffnen):
Mit dieser Vorarbeit schreiben wir jetzt unser Script für den Auswahl-Button der Kategorien. Im Script-Ordner fügen wir ein Script „CategoryButton“ mit folgendem Inhalt ein (die Datei lässt sich mit jedem beliebigen Texteditor öffnen):
Eine Zeile mit Referenz auf ein noch erzeugtes Script haben wir auskommentiert, damit wir zwischendurch testen können. Dieses muss später wieder aktiviert werden!
Jetzt, wo die Scripte für die Anzeige der Kategorien da sind, müssen wir noch die entsprechenden GameObjects einrichten. Wir erzeugen in unserem Szenenbaum ein GameObject, nennen es „CategoryReader“, und ordnen diesem im Inspector das gleichnamige Script „CategoryReader“ als Component zu.
Jetzt erzeugen wir das Prefab für die Category Buttons. Buttons werden wir hier mit Quads realisieren, einem der in Unity enthaltenen 3D-Objekte. Ein Quad ist vereinfacht eine Ebene, die in eine Richtung eine Textur zeigt. Das sollte man im Hinterkopf behalten: Falls man ein Quad in der Szene nicht sehen kann, betrachtet man es oft von der falschen Seite.
Wir erzeugen im Szenenbaum mit Rechtsklick > 3D Object > Quad ein Quad und nennen es „CategoryButton“.
Dann erstellen wir in den Ressourcen einen neuen Ordner „Prefabs“ und ziehen das GameObject dort rein. Damit haben wir automatisch ein Prefab erzeugt, das wir ab jetzt nicht mehr in der Szenenansicht, sondern im Prefab-Editor bearbeiten wollen. Das können wir mit Rechtsklick > Open auf das Objekt in den Ressourcen tun.
Dem CategoryButton fügen wir die Components „CategoryButton“ und „QuadToCam“ hinzu. In der vorhandenen Component „MeshCollider“ setzen wir den Haken bei „Convex“.
Per Rechtsklick > CreateEmpty fügen wir dem Button ein leeres Child-Objekt hinzu. In diesem Textfeld soll der Kategorie-Name angezeigt werden. Wir nennen es „CatName“. Diese Bezeichnung nutzen wir im Script, um das Objekt zu finden und den Text zu setzen. Wir fügen eine Component „TextMeshPro“ hinzu. Beim ersten Hinzufügen einer TextMeshPro Component öffnet sich ein Fenster und wir wählen die Option, die TMPRo Essentials zu importieren.
Wir stellen in der Component „RectTransform“ „Width“ auf 2 und „Height auf 0.5 und verschieben das Object relativ zum Parent Object, so dass der Text unter dem Bild der Kategorie zu sehen ist. In der Component „TextMeshPro“ stellen wir die „FontSize“ auf 2 und das „Alignment“ auf Center.
So sollte unser Prefab jetzt aussehen:
Mit Klick auf den kleinen Pfeil links oben in dem Hierarchiebaum gelangen wir zurück zur Szenenansicht. Hier löschen wir das GameObject „CategoryButton“, das wir als Prefab erzeugt haben und dynamisch generieren möchten. Wir wählen den „CategoryReader“ und ziehen das erzeugte Prefab auf das Feld „CategoryButtonPrefab“. Wir haben dieses Feld „public“ definiert, damit wir es aus dem Inspector befüllen können. Das ist einfacher als eine dynamische Zuordnung des Prefabs, wenn sich dieses im Programmverlauf nicht mehr ändert.
Langsam kommen wir den Virtual Reality-Erlebnis näher: Wenn wir jetzt auf Play drücken, können wir die Funktionalität bis jetzt testen und sollten die drei Kategorien sehen:
Nun haben wir als Zwischenschritt erfolgreich mit Unity aus der HANA Datenbank gelesen und das Ergebnis dreidimensional dargestellt. Im letzten Teil zur Entwicklung der Virtual Reality-App fügen wir die Blicksteuerung, die Produkte, und die Datenbank-Update-Funktion hinzu.