Ein Game in 2½ Stunden

Hallo Leute,
Vor drei Tagen habe ich versucht mit Unity ein Mobile-Spiel in 2 ½ Stunden zu entwickeln. Vorher hatte ich mir schon ein simples Spielkonzept überlegt.Der Spieler sollte mit einem Joystick eine langgezogene Kugel steuern. Mit dem anderen Joystick sollte er die Kugel und damit den an ihr befestigten Gewehrlauf( nur ein Zylinder ) drehen können. Außerdem sollte der Gewehrlauf kleine Kugeln schießen, wenn man den 2. Joystick benutzt.

Zuerst schrieb ich ein simples Movement-Script für den ersten Joystick. Für die Joysticks benutzte ich Joystick-Pack aus dem Unity-Assetstore. Dann erstellte ich das das Drehen bzw. Schießen des Spielers. Das war dann schon schwieriger. Vom Joystick-Pack habe ich die Daten als Richtungsvektor und Horizontale bzw. Vertikale Achse. Ich musste erst mal ein bisschen herumsuchen wie ich einen Richtungsvektor als Rotation für ein Objekt angebe. Schließlich schaffte ich es aber mit Quaternion.LookRotation.

Dann fragte ich danach ab, ob der Vektor nicht 0 beträgt und instanziere wenn ja eine kleine Kugel (mit Rigidbody-Komponente) am Lauf der Waffe und gebe ihr einen Schub nach vorn.

Die Kraftausübung hat leider irgendwie nicht funktioniert. Deshalb fallen die Kugeln einfach auf den Boden oder prallen aufeinander und erhalten dadurch eine leichte Kraft in beliebige Richtung.

Nun brauchte ich noch eine Map. Ich modellierte sie in Blender, indem ich einen Würfel nahm, seine Faces extrudierte, LoopCuts setzte und die neu entstandenen Faces wieder extrudierte. Dadurch entstand eine Art sehr offenen Labyrinth. Dann noch eine Fläche als Boden darunter und fertig.

Ich importierte die Map in Blender und ließ ein NavMesh erstellen. Dann nahm ich mir einen Zylinder und bereitete ihn als NavMeshAgent vor. Ich schrieb ein Script, das den Gegner einfach immer dem Spieler hinterherlaufen ließ.

Nun blieben mir noch 20 Minuten, inden ich das Sterben der Gegner und des Spielers sowie das Spawnen der Gegner entwickeln musste.

Zuerst schrieb ich ein Script, dass überprüft, ob ein Gegner den Spieler berührt. Wenn das der Fall ist, wird ein GameOverScreen angezeigt, auf dem der Spieler das Spiel neustarten kann.

Dann war das Spawnerscript an der Reihe. Dieses sollte auf leeren Objekten in der Szene arbeiten und alle 15 Sekunden einen Gegner instanzieren. Dabei wird nach jedem Spawn die Zekt bis zum nächsten Spawnen um eine Sekunde verkürzt. Später hatte ich das Problem, dass der original Gegner wenn vom Spieler beseitigt wurde, nicht mehr instanziert werden konnte. Dafür hätte man sicherlich eine schöne Lösung finden können ( zBs. mit Prefabs) aber dar ich unter Zeitdruck war erstellte ich einfach einen Würfel unter der Map und Stellte das Original darauf. Nach einer Weile kam das Problem wieder, weil scheinbar ein paar Kugeln durch den Boden rutschen. Also setzte ich noch einen Würfel über das Original und das Problem war gelöst. Dar ich es in 2 ½ Stunden nicht mehr geschafft habe den Spieler anzufangen, wenn er von der Map fällt, kann man das Original unter der Map immer noch sehen, wenn man herunter fällt.

Das Sterben der Gegner war eine schwierige Angelegenheit, denn ich musste überprüfen ob irgendeine Kugel mit irgendeinem Gegner kollidiert. Dafür müsste ich eigentlich eine Liste mit allen Kugeln oder mit allen Gegnern machen. Ich stand aber unter starkem Zeitdruck. Ich 3Minuten würde ich das nie schaffen. Dann fiel mir auf, dass alle instanzierten Kugeln Bullet Clone heißen. Ich fügte dem Gegnerscript das eine Methode hinzu, die alle Kollisionen durchgeht und guckt, ob der Name des kollidierenden Objekts Bullet enthält. Wenn ja zerstört sich der Gegner selbst.

Leider funktionierte die Selbstzerstörung nicht. Unity Objekten liegt scheinbar nicht viel am Tod. Ich löste das Problem, indem ich im Bulletscript abfragte, ob ein Objekt berührt wird, das Enemy im Namen trägt und gegebenenfalls auch die Zerstörung des Gegners übernimmt.

Endlich wieder Visual Studio

Hallo Leute,
Vor Monaten habe ich ja begonnen mit Visual-Basic zu programmieren. In letzter Zeit habe ich zu VB allerdings nichts mehr veröffentlicht. Das lag daran, dass Visual Studio mir erklärte, dass mein 30Tage Testzugang nun vorbei ware und ich ab nun ein Microsoft Konto brauche. Ich habe mich dann dazu entschieden KEIN Microsoft Konto anzulegen ( Aus Datenschutzgründen ) Das war eine schwere Entscheidung, da Visual Studio mit seinem ganzen Unter-Die-Arme-Greifen unglaublich praktisch ist. Somit hatte ich lange keinen Kontakt mit Visual-Studio.

Im Sommer bin ich auf das Gymnasium gekommen. Dieses Gymnasium nutzt Microsoft 365 für alle Schüler und Lehrer. Grundsätzlich ist das extrem praktisch, dar somit jeder an jedem Smartboard oder PC seine Daten abrufen kann.

Nun habe ich einfach mal probiert mich in Visual Studio mit meinem Schullkonto einzuloggen. Es hat funktioniert! Wow, was für eine Erleichterung. Nebenbei habe ich jetzt auch noch den perfekten Code-Editor für Unity.

Mit VB werde ich wahrscheinlich trotzdem nichts mehr machen, weil ich jetzt einfach aus der Übung bin und mit C# viel besser umgehen kann.

Blender Viewport-Settings

Hallo Leute,
Heute habe ich mal ein wenig die Blender-Vieport-Einstellungen ausprobiert.
Dabei ist meiner Meinung nach etwas sehr schönes herrausgekommen :

Solltet ihr das auch haben wollen, dann habe ich hier die Einstellungen dafür :

Unnoticed Devlog #4

Hallo Leute,
Auch in dieser Woche ist eine Menge passiert.

Gefahr für den Spieler

Zuerst habe ich Schießen auf den Spieler implementiert. Dafür habe ich eine Funktion geschrieben, die einen Ray auf den Spieler schießt. Wenn der Ray den Spieler trifft stirbt der Spieler. Diese Funktion wird durch invokeRepeating alle zwei Sekunden aufgerufen.

Dann habe ich einen mit SFXR erstellten Schuss-Sound hinzugefügt und mit einem Partikelsystem einen Muzzle Flash erstellt.

Am darauf folgenden Tag habe ich eine Todes-Animation für die Kamera erstellt und das Script so erweitert, dass die Animation bei einem Treffer des Gegners abgespielt wird und das Movement-Script des Spielers deaktiviert wird. Nun funktionierte das Schießen nur noch sehr merkwürdig. Der Gegner schoss überhaupt nicht mehr, nur wenn man ihn berührte würde plötzlich die TodesAnimation abgespielt.

Später fand ich heraus, dass invoke in der Update() Funktion nur sehr eingeschränkt funktioniert. Also erstellte ich mir mein eigenes Invoke. Dafür habe ich eine Variable erstellt, die wenn man sie auf 1 setzt jeden Frame um 1/120 Sekunde verringert wird. Wenn diese Variable 0 erreicht, wird die SchussFunktion ausgeführt. Damit schoss der Gegner schon mal wieder zuverlässig alle zwei Sekunden. Nur das Geräusch und der Muzzle Flash fehlten. Dieses Problem konnte ich auch schnell lösen.

Dann gab es noch das Problem, dass der Gegner perfekt zielen konnte man somit immer vom ersten Schuss des Gegners erfasst wurde. Zuerst dachte ich, dass ich das so lassen würde, denn in Unnoticed geht es ja schließlich darum unbemerkt zu bleiben und nicht darum dem Gegner zu entkommen. Schließlich habe ich mit meinem Vater darüber gesprochen und er sagte, dass der Gegner immer auf ein Objekt Taregt zielen sollte das dem Spieler zeitverzögert folgt. Somit ist es für den Gegner schwieriger den Spieler zu treffen wenn der Spieler sich viel bewegt. Diese Idee ließ sich dank Lerp sehr leicht umsetzten. Lerp ist eine Funktion, die einen Zwischenwert von zwei Floats oder Vektoren errechnet. Wen das interessiert, dem empfehle ich dieses YoutubeVideo. Mit dem Folgenden Script lasse ich das Target verzögert dem Spieler folgen.

public transform Player;
public float value = 0.2;
void Update(){
	transform.postion = Vector3.Lerp(transform.position, Player.position,  value) ;
}

So setzte ich die position des Targets in jedem Frame auf 20% zwischen der eigenen und der Spieler position. Jetzt wo ich so darüber nachdenke sollte ich das doch besser nicht jeden Frame, sondern jeden PhysikFrame tun, denn PhysikFrames werden immer nach einer einheitlichen Zeit ausgeführt. Also ersetzten wir Update einfach durch FixedUpdate. Denn sonst hätte der Gegner es schwerer zu zielen, wenn man auf besserer Hardware spielt. Ich denke allerdings, dass man dabei auch nicht so genau sein muss, schließlich sieht der Spieler ja von alldem garnichts.

Pause Menü

Ein paar Tage später habe ich ein PauseMenü erstellt. Das Problem ist nur, dass die Buttons aus irgendeinem Grund nicht funktionieren. Ich recherchierte und bemerkte, dass daran der FPS-CharacterController schuld ist. Der FPS-Controller hat in seinen Einstellungen eine BooleanVariable names Lock Cursor. Ich habe gelesen, dass ich diese wenn pausiert wird deaktivieren sollte. Bis jetzt schaffte ich dass allerdings noch nicht.

Builden von Version0.2

Nun dachte ich mir, ich sollte mal wieder eine Standalone-Version meines Spiels builden und diese hier hochladen. Als ich dies versuchte, erhielt ich einen Compilererror, der mich auf falsch gesetzte geschweifte Klammern in dem nicht von mir erstellten IK-Skript hinwies. Zu erst war ich verwundert, denn im Test-Modus entstand dieser Fehler ja auch nicht. Egal. Ich öffnete das IK-Script und sah mir die Klammern an. Mir fiel nichts falsches auf. Da der Compiler ja sagte etwas Stimme mit den (äußersten) Klammern nicht, probierte ich einfach aus noch eine Klammer hinzuzufügen oder wegzunehmen. Doch nichts half.

IK ohne IK

Zuerst war ich für ein paar Tage frustriert und arbeitete nicht weiter. Irgendwann recherchierte ich mal wieder ein bisschen zum Thema IK und fand etwas interessantes. Jemand benutzte statt Animation-Rigging oder anderem einfach ein transform.LookAt in der LateUpdate-Funktion. Dadurch wird in jedem Frame einfach nachdem der Animator seine Änderungen vorgenommen hat der transform eines Knochens so eingestellt, dass er direkt auf eine bestimmte Position zeigt. In meinem Fall auf den Spieler.

Am nächsten Tag probierte ich dies aus. Ich dachte ich müsste nur die Rotation des Oberarms verändern und die untereren Knochen auf 0 setzten, weil die Transformationsveränderung durch den Parent sich nicht auf die Werte der Transformation auswirkt. Damit lag ich falsch. Leider merkte ich das zu spät. Stattdessen probierte ich dieses ‘IK-System’ mithilfe von Offsets richtig einzustellen. Nach einer Menge Arbeit hatte ich den Oberarm so eingestellt, dass er immer in die Richtung des Spieler zielt. Nun versuchte ich den Unterarm und die Hand richtig rotieren zu lassen. Nach einer Weile gelang mir auch das. Doch als ich dies im Spiel ausprobieren wollte, fiel mir auf, dass der Unterarm nur aus einer bestimmten Perspektive funktionierte.

Gerade denke ich darüber nach, ob ich nicht auch Unterarm und Hand mit LookAt auf den Spieler zeigen lassen könnte. Das sollte ich vielleicht das nächste mal, wenn ich meinen Rechner an habe ausprobieren. Sollte das nicht klappen mache ich erstmal eine kleine Pause mit der Entwicklung von Unnoticed.

Am nächsten Tag

Ich habe das Problem jetzt lösen können. Mir fiel auf, dass ich bereits beim letzten mal versucht hatte alle Knochen mit LookAt auf den Spieler zeigen zu lassen.

UpperArm.transform.LookAt(target.transform);
UpperArm.transform.rotation= UpperArm.transform.rotation * Quaternion.Euler(offset);
LowerArm.transform.LookAt(target.transform);
LowerArm.transform.rotation = Quaternion.Euler(offset);
Hand.transform.LookAt(target.transform);
Hand.transform.rotation = Quaternion.Euler(offset);

Und? Habt ihr den Fehler gesehen? Richtig ich setzte fehlerhafterweise Hand und Unterarm zuerst auf die Rotation in Richtung Spieler und dann setzte ich die Rotation auf den Offset. Ich sollte mich echt schämen, dass ich an soetwas verzweifle.

Naja egal, Hauptsache ist, dass ich dieses Problem nun lösen konnte. So sieht dieser CodeAbschnitt jetzt aus :

UpperArm.transform.LookAt(target.transform);
UpperArm.transform.rotation erArm.transform.rotation= UpperArm.transform.rotation * Quaternion.Euler(offset);
LowerArm.transform.LookAt(target.transform);
LowerArm.transform.rotation = LowerArm.transform.rotation * Quaternion.Euler(offset);
Hand.transform.LookAt(target.transform);
Hand.transform.rotation = Hand.transform.rotation * Quaternion.Euler(offset);

Was Quaternion.Euler macht? Nun ja, in Unity werden Rotationen in Quaternions gerechnet. Deshalb muss ich mit Quaternion.Euler einen EulerWinkel in einen Quaternion umwandeln. Ein EulerWinkel besteht einfach aus x, y und z Achse einer Rotation. Ein Quaternion ist … ich habe keine Ahnung. Quaternions werden benutzt, weil EulerWinkel scheinbar sich scheinbar irgendwie sebst blockieren können. Wie das funktioniert wüsste ich auch gern.

Nun habe ich noch ein Boot in Blender modelliert, von dem aus der Spieler das Spiel startet und ein paar Textnachrichten, die dem Spieler helfen sollen. Nach alldem ist es Zeit für die Version 0.2! Übrigens habe ich diesmal auch einen WebGL build erstellt, damit ihr mein Spiel nicht downloaden müsst.

Blender Laden

Hallo Leute,
Vor ein paar Tagen habe ich ein Modulares Ladenhaus in Blender modelliert und gerendert.

Mein gerendertes Ladengebäude

Ja, ich weiß man kann sehen, dass nur die sichtbare Fassade modelliert wurde. Ich weiß wirklich nicht, warum ich unbedingt diese offene Tür haben wollte.

Ich hätte das Schanier der Tür natürlich auch auf die andere Seite setzten können aber das wäre auch inkorrekt. Was nun die beste Lösung gewesen wäre ist jetzt aber auch nebensächlich.

Ein weiterer interessanter Bestandteil dieses Renderings sind die Scheiben. Diese sind hellblau und haben eine weiße SubsurfaceColor. Dadurch entsteht dieser interessante Look.

Unnoticed Devlog #3

Hallo Leute,
In den letzten Tagen ist wieder sehr viel passiert.

Zuerst habe ich ein menschliches 3D-Modell in Blender eingefärbt, geriggt und animiert. Dieses Modell habe ich dann in Unity importiert. Dann habe ich dem Gegner die notwendigen Scripte hinzugefügt und geändert, damit sie die Animation korrekt abspielen. Jetzt habe ich einen Gegner, der sich nicht nur auf magische Weise bewegt, sondern einen Menschen, der sich laufend fortbewegt.

Der nächte Schritt war dann das Schießen auf den Spieler. Das war bis jetzt das größte Problem an der Entwicklung von Unnoticed. Zuerst wollte ich es mit Animation Rigging lösen. Animation Rigging ist ein von Unity entwickeltes Package, mit dem man Transformationen von Knochen eines Skeletts, das durch eine Animation angesteuert wird überschreiben kann. Ein guten Beispiel dafür ist ein Gegner der herum läuft, aber immer in die Richtung des Spielers guckt. Noch ein Beispiel ist ein Gegner der mit seiner Runnig Animation den Spieler verfolgt, aber dabei mit einer Hand, in der er eine Waffe hält immer auf den Spieler zielt. Letzteres ist exakt das, was ich erreichen möchte. Leider funktionierte Animation Rigging überhaupt nicht.

Dieses Problem bereitete mir sehr viel Frust und ich arbeitete erstmal für ein paar Tage nicht an Unnoticed. Dar das Schießen des Gegners ein fundamentaler Bestandteil des Spiel war, schien es mir nicht möglich einfach an diesem Problem vorbeizuarbeiten.

Aus diesem Grund begann ich verstärkte Recherche im Internet und fand eine andere Lösung : Fast IK. Fast IK ist ein Asset für Unity, mit dem es möglich ist einfach Inverse Kinematics kurz IK für ein Skelett zu erstellen. Nach ein wenig Recherche fand ich heraus, wie man mit Fast IK umgeht und wie ich es für meine Zwecke nutzen konnte. Mithilfe von diesem Assets habe ich in kürzester Zeit mein Problem gelöst.

Nun arbeite ich an der Umsetzung des Schießen des Gegners und vorallem an dem Design dessen.

Zudem habe ich schon ein kleines Menü erstellt. In der ersten Version von Unnoticed war der Hintergrund des Menüs ja komplett einfarbig. Dies wollte ich für die Nächste Version nicht mehr also öffnete ich die Umgebung meines Spiels in Blender und rendert schnell ein Bild davon. Blender rendert standardmäßig im 16:9 Format, das Standard Unity Format ist allerdings 4:3. Als mir dies auffiel dachte ich mir ging ich wieder in Blender um das Format umzustellen doch da kam mir eine Idee: Ein sich langsam verschiebender Hintergrund. Ich importierte das 16:9 Rendering in Unity und begann zu recherchieren, wie man in Unity animiert. Das war keine sonderlich schwierige Aufgabe. Zudem bräuchte ich keine einzige Zeile Code, denn Unity spielte meine Animation automatisch in dauerschleife ab. Exakt das, was ich wollte.