Unvorhersehbar lustig: Der Wert der Zufallsgenerierung bei der Spieleentwicklung
![Unvorhersehbar lustig: Der Wert der Zufallsgenerierung bei der Spieleentwicklung](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fb4244389c0f3139092a19657b2aadf7c5420c07b-1230x410.jpg&w=3840&q=75)
Lernen Sie, wie Sie Zufallsgeneratoren in Ihr Spiel einbauen können, um die Spieler bei der Stange zu halten und sie auf die nächste Szene neugierig zu machen. Dies ist der zweite Beitrag von Christo Nobbs in seiner Serie über das Entwerfen von Systemen, der auf seine Beiträge zu Das Unity-Spielentwickler-Spielbuch. Lesen Sie das E-Book, um mehr darüber zu erfahren, wie Sie in Unity Prototypen erstellen, entwickeln und testen können.
In einem früheren Blogbeitrag ging Christo darauf ein, wie Designer in ihren Spielen Systeme schaffen können, die zu einem faszinierenden und unerwarteten Gameplay führen. In diesem Beitrag geht er anhand von Beispielen näher darauf ein, wie die Randomisierung eingerichtet werden kann.
![Bild mit hellviolettem Hintergrund mit dem GamePlay & Design Ebook Cover](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fe7a3427474c05b24a7d7358c959e5881ed024953-1200x675.jpg&w=3840&q=75)
Mit visuellen Hinweisen können Sie die Spieler dazu verleiten, die Systeme Ihrer Spielwelt weiter zu erforschen und einzigartige Erfahrungen für sie zu entwickeln. Der vorherige Beitrag, Systeme, die Ökosysteme schaffen: Emergentes Spieldesign, skizzierte einen Sandkastenraum, in dem alles aus Holz hergestellt und konstruiert ist, mit der Möglichkeit einer unvorhersehbaren Feuerausbreitung. Bauen wir auf diesem Beispiel auf und geben wir den Spielern die Möglichkeit, Bäume mit einer Axt zu fällen.
Nehmen wir an, dass die Landschaft, in der die Spieler stehen, flach ist. Sie wissen nicht, in welche Richtung der Baum fallen wird, wenn sie ihn fällen. Was wäre, wenn es sich bei einigen Bäumen um "Baumstümpfe" handeln würde, d. h. um Bäume, die zwar tot sind, aber noch stehen? Sie könnten jeden Moment fallen. Ein unvorhersehbares Element wie dieses im Spiel wird die Spieler aufschrecken und ihre Spielumgebung intensivieren.
Sie können eine beliebige Anzahl von visuellen Hinweisen hinzufügen, um den Spielern zu zeigen, dass umstürzende Bäume in dieser Welt gefährlich sind und sie auf Trab zu halten. Die Hinweise helfen den Spielern, zwischen den gefährlichen und den gesunden, weniger bedrohlichen Bäumen zu unterscheiden. Sie werden entscheiden müssen, wie sie die Risiken abwägen: Wäre es klüger, Brennholz von bereits umgestürzten Bäumen zu sammeln, um sich nicht an den toten Bäumen zu verletzen, die auf den Fall warten?
Als Designer sollten Sie herausfinden, wo diese systemischen Kettenreaktionen auftreten, und sie einplanen, um sie zu unterstützen, wenn die Spieler sie auslösen, z. B. wenn Bäume unvorhersehbar umfallen, Feuer fangen und dadurch ein herrliches Chaos verbreiten.
Die Skriptklasse Random in Unity ist eine statische Klasse, die Ihnen Ansätze zur Erzeugung von Zufallsdaten in einem Spiel bietet. Diese Klasse hat den gleichen Namen wie die .NET Framework-Klasse System.Random und dient einem ähnlichen Zweck, unterscheidet sich aber in einigen wichtigen Punkten - einer davon ist, dass sie 20 bis 40 % schneller ist als System.Random.
Im Folgenden sind die statischen Eigenschaften und Methoden aufgeführt, die in der Klasse Random verfügbar sind:
Statische Eigenschaften
- insideUnitCircle: Liefert einen zufälligen Punkt innerhalb oder auf einem Kreis mit Radius 1,0 (nur Lesezugriff)
- insideUnitSphere: Liefert einen zufälligen Punkt innerhalb oder auf einer Kugel mit dem Radius 1,0 (schreibgeschützt)
- onUnitSphere: Liefert einen zufälligen Punkt auf der Oberfläche einer Kugel mit dem Radius 1,0 (schreibgeschützt)
- Rotation: Gibt eine zufällige Drehung zurück (nur Lesezugriff)
- rotationUniform: Gibt eine zufällige Drehung mit gleichmäßiger Verteilung zurück (nur Lesezugriff)
- Zustand: Liest oder setzt den vollständigen internen Status des Zufallszahlengenerators
- Wert: Liefert einen zufälligen Fließkommawert innerhalb von [0.0..1.0] (Bereich ist inklusive) (nur Lesezugriff)
Statische Methoden
- ColorHSV: Erzeugt eine Zufallsfarbe aus HSV- und Alpha-Bereichen
- InitState: Initialisiert den Zustand des Zufallszahlengenerators mit einem Seed
- Reichweite: Liefert einen zufälligen Float innerhalb von [minInclusive..maxInclusive] (Bereich ist inklusive)
Im vorigen Blogbeitrag wurde die Rolle von Entwurfshebeln und die Verwendung von ScriptableObjects zum Speichern dieser Werte untersucht. Sie können diese Werte durch entsprechend gestaltete Bereiche ersetzen, indem Sie Unity's Random.Range verwenden, das einen zufälligen Fließkommawert innerhalb von [minInclusive..maxInclusive] zurückgibt (der Bereich ist inklusive). Jeder beliebige Float-Wert zwischen diesen beiden Werten, einschließlich minInclusive und maxInclusive, erscheint etwa einmal pro 10 Millionen Stichproben.
Mit diesem Ansatz können Sie einen Wert aus dem festgelegten Bereich für Ihr Ergebnis ziehen. Sie werden mehrere Bereiche testen müssen, um den für Ihre Spielziele geeigneten zu finden, aber auch hier sollten Sie sicherstellen, dass Sie für den von Ihnen festgelegten Bereich entwerfen.
![Bild eines weißen Roboters, der die Brände von Baumzweigen in einem Wald beobachtet](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Ffa1ec8da73916a9e357cad89a52637e1c95c9178-1020x574.jpg&w=3840&q=75)
Der Zufall sorgt für eine bessere Immersion. Nehmen wir zum Beispiel an, dass jeder Baum eine feste Lebenspunktzahl von 100 hat und jeder Axtschlag 25 Punkte von der Lebenspunktzahl eines Baumes abzieht. Diese Aufgabe wird bald vorhersehbar und damit langweilig. Selbst wenn man den Bäumen eine Gesundheitsspanne von 76 bis 100 gibt, ist jeder Baum vier Schläge vom Umfallen entfernt. Aber ein kleinerer Bereich von, sagen wir, 75 bis 76 bietet eine größere Vielfalt an Spielergebnissen, da ein Baum zwischen drei und vier Treffer braucht, um zu fallen.
Eine weitere Möglichkeit, dieses Szenario interessant zu gestalten, ist die Anzeige von Gesundheitsveränderungen durch klare visuelle Hinweise anstelle von Gesundheitsbalken. Auf diese Weise kann der Spieler durch das Spiel lernen, wie viele Axthiebe es ungefähr braucht, um einen Baum zu fällen. Visuelle Hinweise fügen eine begrenzte Unvorhersehbarkeit hinzu, die ausgeglichen und an das angestrebte Gameplay angepasst werden kann. Durch die Verwendung der Klasse Random anstelle eines festen Werts können Sie eine eintönige Aufgabe in eine lustige Aufgabe verwandeln.
Um dieses Beispiel zu erweitern, könnte man einen Zufallswert zwischen 15 und 25 Lebenspunkten für jeden Axtschwung entfernen. Dadurch können die Spieler nicht so leicht vorhersagen, wie viele Schwünge sie brauchen, um einen Baum zu fällen. Sie müssen sich stärker auf visuelle Anhaltspunkte verlassen, um abzuschätzen, wann ein Baum fallen wird; Anhaltspunkte wie die Größe der vom Baum fliegenden Stücke oder Risse, die sich am Stamm bilden, fallende Äste, Geräuscheffekte usw.
Sie werden nicht genau wissen, wann jeder Baum fallen wird, aber mit der Zeit, wenn die Spieler mehr Bäume fällen, können sie fundierte Vermutungen anstellen und so letztlich ihre Überlebenschancen verbessern.
![Screenshot der Einstellungen für zufälligen Trefferschaden im Inspektor](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F01417e5050cabf8f20d67b4485c80a66d5cba228-900x278.png&w=3840&q=75)
Der Zufall soll den Spielern unvorhersehbare Herausforderungen bieten, die sie dazu bringen, Risiken zu kalkulieren und das Ergebnis zu steuern.
Sehen wir uns einige weitere Beispiele für die Verwendung der Klasse Random an.
Stellen Sie sich ein Kartenspiel mit einem KI-Feind vor, der seine Karte ausschließlich auf der Grundlage des Zuges des Spielers spielt. Dieser Vorgang würde ohne Zufall schnell vorhersehbar werden, da er jedes Mal das gleiche Ergebnis liefern würde.
Selbst eine Wahrscheinlichkeit von 50 % ist eine zu einfache Randomisierung und würde für den Spieler schnell offensichtlich werden. Versuchen Sie stattdessen, auf der Grundlage von Spieleraktionen verschiedene Ebenen des Zufalls hinzuzufügen. Auf diese Weise entstehen komplizierte Systeme, die mehr Dynamik bieten als die Wahl zwischen zwei Karten in einem Pool oder die Entscheidung, ob diese Karte einer von vielen aus dem bestehenden Pool vorgezogen wird oder nicht.
Sie können den Schwierigkeitsgrad des Kartentischs erhöhen, indem Sie den Feind dazu bringen, bestimmte Karten anderen vorzuziehen; Entscheidungen, die von einem Wert abhängen, den er erhalten hat, oder davon, wie vollständig seine Zusammensetzung ist, bevor er angreift, zum Beispiel. Eine Bosskarte kann ihren Schaden vervielfachen, wenn sie zusammen mit anderen Machtkarten gespielt wird. Der Gegner wartet also, bis er eine bestimmte Anzahl von Machtkarten auf der Hand hat, um den Schwierigkeitsgrad für den Spieler zu erhöhen. Sie können einer solchen Zusammensetzung "Gewicht" verleihen, indem Sie die Wahrscheinlichkeit erhöhen oder verringern, dass die Bosskarte mit bestimmten anderen Machtkarten gespielt wird.
![Image von Heartstone Kartenspiel](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Febd35b5c479364e40e3f6b564aa67640bbde60a8-1759x894.png&w=3840&q=75)
Sie können den Zufall in verschiedenen Formen für Ihre Spiele nutzen. Perlin-Rauschen beispielsweise hat natürliche Eigenschaften und erzeugt Gradientenrauschen aus einem Saatgut. Versuchen Sie, ihn in Cinemachine zu verwenden, um ein organischeres Kameragefühl für Third-Person-Verfolgungskameras zu erzeugen.
![Screenshot des Perlin-Rauschalgorithmus in Cinemachine](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F558f570390cfc8a3d8b67b3be8374d460c097e81-786x377.png&w=3840&q=75)
Um Perlin Noise auszuprobieren, schaut euch die Starter Assets - Third-person Character Controller oder Gaia Packs im Asset Store an, zusammen mit der Dokumentation zur Verwendung von Mathf.PerlinNoise.
![Bild der mit Perlin-Rauschen abgetasteten Textur (verschwommene schwarze und weiße Punkte)](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F0eef6e566cc66d5f6aaef86b0c48c3895c3b8c1a-270x270.png&w=3840&q=75)
In einem Interview sprach Chris Butcher, einer der leitenden Ingenieure der Bungie Studios bei Halo 2, über die KI des Spiels und sagte: "Das Ziel ist nicht, etwas Unvorhersehbares zu schaffen. Was man will, ist eine künstliche Intelligenz, die so konsistent ist, dass der Spieler ihr bestimmte Vorgaben machen kann. Der Spieler kann Dinge tun und erwarten, dass die KI auf eine bestimmte Weise reagiert."
![Bild eines grünen Roboters, der in der Mitte eines Lowpoly-Geländes außerhalb von würfelförmigen Gebäuden steht](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fde0d8279d41477039d06dbc0c4bf78acef74848b-1929x1123.png&w=3840&q=75)
Wie sollten Sie also KI-Agenten einrichten, damit Ihr Spiel unvorhersehbar und lebendig bleibt?
Eine Möglichkeit, damit zu experimentieren, ist die Kombination von Starter Assets mit KI-Tools aus dem Asset Store, z. B. A* Pathfinding Project Pro, ein Tool, mit dem Sie einen KI-Agenten zu einem bestimmten Punkt bewegen können.
Sobald sich der KI-Agent auf den Spieler zubewegt, erwartet der Spieler, dass er angegriffen wird. Was aber, wenn sie stattdessen ein Gespräch in Gang setzt? Wie wäre es, mehr NSCs hinzuzufügen, die sich bewegen und sich in den Raum einfügen, um ein lebendigeres Gefühl zu erzeugen? Diese NSCs könnten die ihnen zugewiesenen Punkte linear auswählen, einen nach dem anderen, oder noch besser, logische Punkte auf der Grundlage einer Reihe von Regeln mit Hilfe der Zufallsklasse auswählen.
Nehmen wir an, Sie haben einen KI-Agenten, der mit einem schwachen Pfeil und Bogen auf den Spieler schießt. Zum Leidwesen des KI-Agenten muss er sich in einer bestimmten Entfernung von der Spielfigur befinden, da die maximale Schussweite 10 Meter beträgt. Die KI stellt sich 10 Meter vor dem Spieler in Position und schießt. Das ist weder aufregend noch ideal, vor allem, wenn ein zweiter Schütze um dieselbe Position im NavMesh kämpft.
Für ein interessanteres Szenario wählen Sie einen Bereich, in dem sich der Feind dem Spieler nähern soll. Verwenden Sie dazu Random.insideUnitCircle, übergeben Sie das Ergebnis von vector2 an einen vector3 für die X- und Z-Achse, und nutzen Sie RandomRange für beide, um einen Bereich mit einem minimalen und maximalen Radius um den Spieler zu erhalten.
![Screenshot des Skripts für einen KI-Agenten, der einen Angriffspunkt in der Nähe der Hauptfigur auswählt](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F5e93208f3958290fa690159134d0c873ebe517c3-945x1036.png&w=3840&q=75)
![Bildschirmfoto der Gestaltungshebel im Inspektor](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fd58068bf7a3a60e5d35d77c501484c4020019fbb-645x378.png&w=3840&q=75)
Der KI-Agent sollte einen Punkt innerhalb eines akzeptablen Bereichs um den Spieler wählen und schießen, anstatt sich dem Spieler zu nähern. Um das Spiel mit minimalem Programmieraufwand spannender zu gestalten, können Sie diese Funktion auf alle KI-Agenten anwenden, so dass sie den Spieler aus mehreren Blickwinkeln angreifen. Die KI-Aktionen sind bis zu einem gewissen Grad vorhersehbar, weil man weiß, dass die Agenten einen bestimmten Abstand brauchen, um anzugreifen, aber man kann nicht vorhersagen, aus welchen Winkeln sie angreifen werden.
![Bild eines Lowpoly-Raums mit einem weißen Fadenkreuz über einem grauen und grünen Kreis](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F7e7ef1cd1c3e377f9dd2104f562cca635421e134-1519x1294.png&w=3840&q=75)
Sie können dieses Beispiel weiter ausbauen, indem Sie Ihren KI-Agenten die Fähigkeit geben, im Nahkampf anzugreifen. Verwenden Sie denselben zufälligen Punkt des Spielers mit einem engeren Radius. Auf diese Weise können die KI-Agenten aus einem Pool von Angriffen wählen, wenn es Zeit ist, zuzuschlagen.
Der Spieler weiß, dass er von einer Nahkampfformation angegriffen werden kann, aber aus welcher Richtung? Würde es einen Überarmschlag von oben nach unten geben? Oder einen weiten Schwenk von links nach rechts? Sie müssten die Telegrafie auf der Animation lesen, um zu erkennen, was auf sie zukommt.
![Unity-Editor](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fd540644dcad771743bed1b7360a5d19d61bee0dc-1600x949.gif&w=3840&q=75)
Dieses Szenario kann sehr komplex werden, wenn man mehrere feindliche NSCs hat, die den Spieler zur gleichen Zeit angreifen sollen. Aber wenn man sich zum Beispiel Far Cry 2 von Ubisoft ansieht, wird der Spieler nicht von allen Feinden gleichzeitig angegriffen. Verschiedene Feinde greifen zu verschiedenen Zeiten und auf zufällige Art und Weise an. Mehr über dieses Beispiel und andere KI-Szenarien erfahren Sie in diesem Video von Game Maker's Toolkit.
Wenn Sie versuchen würden, die Ergebnisse von Aktionen aus der realen Welt realistisch in Ihr Spiel zu übertragen, wären komplexe, vielschichtige Gleichungen oder ein gut ausgebildeter ML-Agent erforderlich. Am Ende sieht das Ganze vielleicht deplatziert aus, erfordert viel Zeit und technischen Aufwand für die Umsetzung und lässt sich bei einer schlechten Architektur nur schwer ausbalancieren und mit Verständnis kontrollieren.
Durch den Einsatz der Random-Klasse von Unity können Spieldesigner jedoch glaubwürdige Ergebnisse für ihre Szenen erstellen und dabei den Grad der Aufregung genau kontrollieren, und das in kürzerer Zeit als es sonst der Fall wäre. Zufälligkeit ist natürlich nicht immer angebracht. Vorhersehbarkeit ist nach wie vor unerlässlich. Aber wenn Sie sie an den richtigen Stellen und zu den günstigsten Zeitpunkten platzieren, können Sie Ihren Spielern einzigartige Erlebnisse bieten, die sie dazu bringen, immer wieder spielen zu wollen.