Future Earth
Future Earth ist ein VR-Abenteuerspiel in einer dystopischen Zukunft, in der der Spieler Bäume pflanzen, Droiden bekämpfen und Teile sammeln muss. Über 3.000 Downloads und 4 Sterne auf SideQuest.
Typ
VR-Spiel
Jahr
2020
Angewandte Fähigkeiten
- VR
- Spielentwicklung
- Projektmanagement
- UX / UI
Team
- Robert Michels (PM)
- 3 teammates (SFU IAT)
Die Aufgabe
Future Earth wurde als Übung für die Gestaltung und Entwicklung immersiver Umgebungen entwickelt.
Mit dem Konzeptentwurf wollten wir ein VR-Abenteuerspiel schaffen, das die Spieler zu nachhaltigen Verhaltensweisen anregt. Dies wird erreicht, indem wir den Spieler in eine dystopische, futuristische Umgebung versetzen und ihm die Macht geben, diese durch das Spiel radikal zum Besseren zu verändern.
Das Ergebnis
Geschichte
In einer dystopischen Zukunft bist du der erste Mensch, der seit langer Zeit die Erde bewandert.
Ihr Missionsziel ist es, 100 Bäume zu pflanzen und zu erhalten. Von Ihrem Erfolg hängt es ab, ob die Menschheit versuchen wird, zur Erde zurückzukehren oder nicht.
Erfolg
Unser Projekt gewann den Preis für das beste Gesamtprojekt, das beste Interaktionsdesign, das beste Projekt für die Wirkung und das beste VR/AR-Projekt in unserer Klasse.
Prozess
Das Projekt wurde in 4 Monaten von einem 4-köpfigen Team entwickelt. Ich war der Projektleiter sowie einer der VR-/Spieldesigner und -entwickler.
Nachdem wir beschlossen hatten, ein Impact-Projekt zum Thema Nachhaltigkeit zu entwickeln, begannen wir mit der Erstellung eines Spiels. Unser Ziel war es, ein VR-Spiel zu entwickeln, das auch Spieler ohne Interesse an Nachhaltigkeit anspricht, um ein breiteres Publikum zu erreichen.
Als PM teilte ich das Konzeptdesign in eine Liste von Kern- und Bonusfunktionen auf. Der Kernentwurf beinhaltete ein kleines Spiel, in dem die Spieler gegen bösartige Roboter kämpfen, deren Teile sammeln und eine Wasserfiltertechnologie bauen, die Bäumen beim Wachsen hilft. Dieser Kernprototyp wurde innerhalb der ersten drei Monate umgesetzt.
Nach einer Runde von Benutzertests haben wir als Reaktion auf die Testergebnisse aktualisierte Funktionen implementiert. Dazu gehörten Maßnahmen zur besseren Vermittlung der Geschichte, wie z. B. eine umfassende Aktualisierung des Leveldesigns und die Implementierung einer ästhetischen Veränderung, wenn der Spieler mehr Bäume pflanzt. Wir hatten auch Zeit, einige unserer Bonus-Features zu implementieren, bei denen es sich hauptsächlich um ästhetische Verbesserungen handelte, wie z. B. das Hinzufügen von VFX, bessere Modelle und mehrere sammelbare Teile.
Der Code
Meine Programmierarbeit für dieses Projekt konzentrierte sich auf die ungewöhnlicheren Funktionen unseres Spiels, wie z. B. das Sammeln von Ersatzteilen, das Bauen von Gegenständen, die Interaktion mit der Benutzeroberfläche, die Abfolge der Geschichte und mehr. Eine weitere interessante Funktion, die ich implementiert habe, waren die Bäume, die Animationen für das Wachstum und die Überwachung ihres Gesundheitszustands auf der Grundlage der verfügbaren Wasservorräte benötigten. Um eine feine Kontrolle über das Wachstumsverhalten zu erreichen, habe ich eine Coroutine verwendet, die auch zur Kontrolle sterbender Bäume eingesetzt wird. Anstatt eine Unity-Animation zu verwenden, implementierte ich ein sanftes Wachstum, indem ich Skalierung und Rotation definierte mit Hilfe von Mathf.SmoothStep() und Mathf.Sin()
// CODE SNIPPET, NOT THE FULL FILE ////Coroutine that controls growth / death animation, colliders, presence and perch targetsIEnumerator TreeLerpSize (float time, bool death){ ////////////// before main loop float startSize, endSize; float elapsedTime = 0; if (death) { startSize = 1; endSize = 0; } else { growing = true; startSize = 0; endSize = 1; //Activate collider treeCollider.enabled = true; } ////////////// Main loop: Handle growth / shrinking while (elapsedTime < time) { //Transition between start and end size (0 to 1) float curSize = Mathf.SmoothStep(startSize, endSize, (elapsedTime / time)); //Rotations work different. It's best to define the rate of rotation, the sine function is handy for this (between 0, 1, back to 0) float curRotationRate = Mathf.Sin(Util.mapVal((elapsedTime / time), 0, 1, 0, Mathf.PI)); //Apply size & rotation treeTransform.localScale = new Vector3(curSize, curSize, curSize); treeTransform.RotateAround(treeTransform.position, Vector3.up, curRotationRate * turnDirection); elapsedTime += Time.deltaTime; yield return new WaitForEndOfFrame(); } ////////////// after main loop if (death) { Destroy(gameObject); } else { //Activate perch targets once growth complete Util.FindInactiveChild(gameObject, "perchParent").SetActive(true); growing = false; if (dead) { StartDeath(); } }}Eine der interessanten Änderungen, die sich aus unseren Benutzertests ergaben, war die drastischere Veränderung der Umgebung in Abhängigkeit vom Fortschritt des Spielers. Die Umgebung verändert sich, wenn der Spieler mehr Bäume pflanzt. Um die gewünschte emotionale Wirkung beim Spieler zu erzielen, haben wir viele variable Ebenen eingebaut: die Skybox, Nebel, Staubpartikel, Wasser, Beleuchtung und Nachbearbeitung. All diese Aspekte der Umgebung werden zwischen einem Start- und einem Endzustand interpoliert, und zwar mit der Methode SetEnvironment() die ich implementierte in GameCtrl.cs. Zum Beispiel hat der Nebel Definitionen für seine Farbe, die Startdistanz und die Enddistanz sowohl für den Anfang als auch für das Ende des Nebels.
// CODE SNIPPET, NOT THE FULL FILE ////Sets the mood of the environment (skybox, fog, dust, water). Input a value between 0 (beginning: grim) and 1 (ending: bright).private int startDust = 600;private int endDust = 0;private int startFogLimit = 300;private int endFogLimit = 1250;private int startFog = 0;private int endFog = 50;private void SetEnvironment (float lerp){ lerp = Util.capVal(lerp, 0, 1); Color horizonCol = Color.Lerp(startColors[1], endColors[1], lerp); //sky skyBox.SetColor("_TopColor", Color.Lerp(startColors[0], endColors[0], lerp)); skyBox.SetColor("_HorizonColor", horizonCol); skyBox.SetColor("_BottomColor", Color.Lerp(startColors[2], endColors[2], lerp)); //fog RenderSettings.fogColor = horizonCol; RenderSettings.fogEndDistance = Util.mapVal(lerp, 0, 1, startFogLimit, endFogLimit); RenderSettings.fogStartDistance = Util.mapVal(lerp, 0, 1, startFog, endFog); //dust int dustCount = (int)Mathf.SmoothStep(startDust, endDust, lerp); dust1.maxParticles = dustCount; dust2.maxParticles = dustCount / 12; //water waterMat.SetColor("_Color", Color.Lerp(waterColors[0], waterColors[1], lerp)); //Post Processing (disabled for mobile) ppBefore.weight = Util.mapVal(lerp, 0, 1, 1, 0); ppAfter.weight = Util.mapVal(lerp, 0, 1, 0, .4f); //sun sun.intensity = Util.mapVal(lerp, 0, 1, 0.5f, 1.1f);}