Wenn Sie sich mit einem RPG-Fan unterhalten, wird es nicht lange dauern, bis Sie von randomisierten Ergebnissen und Beute erfahren und wie frustrierend sie sein können. Viele Spieler haben diese Irritation bekannt gemacht, und während einige Entwickler innovative Lösungen entwickelt haben, zwingen uns viele immer noch zu nervenaufreibenden Beharrlichkeitstests.
Es gibt einen besseren Weg. Durch die Änderung der Art und Weise, wie wir als Entwickler Zufallszahlen und deren Generatoren verwenden, sind wir in der Lage, einnehmende Erfahrungen zu kreieren, die auf diese „perfekte“ Schwierigkeit drängen, ohne die Spieler über den Rand zu drängen. Bevor wir uns dem widmen, gehen wir einige Grundlagen von Zufallszahlengeneratoren (kurz RNGs) an..
Zufallszahlen sind überall um uns herum und werden verwendet, um unserer Software Abwechslung zu verleihen. Im Allgemeinen dienen RNGs hauptsächlich dazu, chaotische Ereignisse darzustellen, Volatilität zu zeigen oder sich als künstlicher Begrenzer zu verhalten.
Sie interagieren wahrscheinlich täglich mit Zufallszahlen oder den Ergebnissen ihrer Aktionen. Sie werden in wissenschaftlichen Versuchen, Videospielen, Animationen, Grafiken und nahezu jeder Anwendung auf Ihrem Computer verwendet. Ein RNG ist beispielsweise wahrscheinlich in den Basisanimationen Ihres Telefons implementiert.
Nun, da wir darüber gesprochen haben, was ein RNG ist, werfen wir einen Blick auf seine Implementierung und wie sie unsere Spiele verbessern kann.
In fast jeder Programmiersprache wird in den Grundfunktionen ein Standard-RNG verwendet. Es funktioniert, indem ein zufälliger Wert zwischen zwei Zahlen zurückgegeben wird. Standard-RNGs können auf unterschiedliche Weise in verschiedenen Systemen implementiert werden, sie haben jedoch im Allgemeinen den gleichen Effekt: Geben Sie eine zufällige Zahl zurück, bei der jeder Wert im Bereich die gleiche Wahrscheinlichkeit hat, dass er zurückgegeben wird.
Bei Spielen werden diese häufig zum Simulieren von Würfeln verwendet. Idealerweise sollten sie nur in Situationen verwendet werden, in denen jedes Ergebnis gleich oft vorkommen soll.
Wenn Sie mit Seltenheit oder unterschiedlichen Randomisierungsraten experimentieren möchten, ist diese nächste Methode für Sie besser geeignet.
Diese Art von RNG ist die Basis für jedes RPG mit Artikel-Rarität. Insbesondere, wenn Sie ein randomisiertes Ergebnis benötigen, einige jedoch weniger häufig auftreten sollen als andere. In den meisten Wahrscheinlichkeitsklassen wird dies gewöhnlich mit einem Sack Murmeln dargestellt. Bei gewichteten RNGs kann Ihre Tasche drei blaue und eine rote Murmel haben. Da wir nur einen Marmor wollen, erhalten wir entweder einen roten oder einen blauen, aber es ist viel wahrscheinlicher, dass er blau ist.
Warum ist gewichtete Randomisierung wichtig? Lassen Sie uns die In-Game-Events von SimCity als Beispiel verwenden. Wenn jedes Ereignis mit nicht gewichteten Methoden ausgewählt wurde, ist das Potenzial für das Auftreten jedes Ereignisses statistisch gleich. Daher ist es für Sie genauso wahrscheinlich, dass Sie einen Vorschlag für ein neues Casino erhalten wie ein Erdbeben im Spiel. Durch das Hinzufügen einer Gewichtung können wir sicherstellen, dass diese Ereignisse in einem proportionalen Umfang stattfinden, der das Spiel bewahrt.
In vielen Informatikkursen oder -büchern wird diese Methode häufig als "Tasche" bezeichnet. Der Name ist ziemlich auf der Nase und verwendet Klassen oder Objekte, um eine virtuelle Repräsentation einer wörtlichen Tasche zu erstellen.
Es funktioniert im Wesentlichen wie folgt: Es gibt einen Container, in den Objekte platziert werden können, wo sie abgelegt werden, eine Funktion zum Einfügen eines Objekts in den 'Sack' und eine Funktion zum zufälligen Auswählen eines Artikels aus dem 'Sack'. Um auf unser Marmorbeispiel Bezug zu nehmen, bedeutet dies, dass Sie Ihre Tasche mit einem blauen Marmor, einem blauen Marmor, einem blauen Marmor und einem roten Marmor behandeln würden.
Mit dieser Methode der Randomisierung können wir grob die Rate bestimmen, mit der ein Ergebnis auftritt, um die Erfahrung der einzelnen Spieler zu homogenisieren. Wenn wir die Ergebnisse auf einer Skala von "Sehr schlecht" bis "Sehr gut" vereinfachen würden, haben wir es jetzt wesentlich praktikabler gemacht, dass ein Spieler eine unnötige Reihe unerwünschter Ergebnisse erhält (z. B. das Ergebnis "Sehr schlecht") 20 mal hintereinander).
Es ist jedoch statistisch immer noch möglich, eine Reihe von schlechten Ergebnissen zu erhalten, nur immer weniger. Wir werden einen Blick auf eine Methode werfen, die etwas weiter geht, um unerwünschte Ergebnisse in Kürze zu reduzieren.
Hier ist ein kurzes Pseudocode-Beispiel, wie eine Bag-Klasse aussehen könnte:
Class Bag // Bewahre ein Array aller Elemente auf, die sich im Bag befinden. Array itemsInBag; // Fülle den Beutel mit Elementen, wenn sein Konstruktor erstellt wurde (Array startingItems) itemsInBag = startingItems; // Fügen Sie der Tasche ein Element hinzu, indem Sie das Objekt übergeben (und drücken Sie es einfach in das Array). Funktion addItem (Object item) itemsInBag.push (item); // Um eine zufällige Elementrückgabe zu erhalten, verwenden Sie eine eingebaute Zufallsfunktion, um ein Element aus dem Array zu holen.
Ähnlich wie bei der Gruppierungsimplementierung ist Rarity Slotting eine Standardisierungsmethode, um Raten zu bestimmen (normalerweise, um den Prozess des Spieledesigns und die Belohnung der Spieler einfacher zu verwalten)..
Anstatt die Rate jedes Gegenstandes in einem Spiel einzeln zu bestimmen, erstellen Sie eine repräsentative Rarität, bei der die Rate eines 'Common' eine Chance von 20 in X für ein bestimmtes Ergebnis darstellt, während 'Rare' eine 1 in ist X Chance.
Diese Methode ändert nichts an der eigentlichen Funktion des Beutels selbst, sondern kann eher zur Steigerung der Effizienz am Ende des Entwicklers verwendet werden, wodurch eine exponentiell große Anzahl von Elementen schnell einer statistischen Chance zugeordnet werden kann.
Darüber hinaus ist Raritäten-Slotting hilfreich, um die Wahrnehmung eines Spielers zu formen, indem er leicht erkennen lässt, wie oft ein Ereignis eintritt, ohne dass sein Eintauchen durch Zahlen-Crunching beseitigt wird.
Hier ein einfaches Beispiel, wie wir unserer Tasche Raritäten hinzufügen könnten:
Class Bag // Bewahre ein Array aller Elemente auf, die sich im Bag befinden. Array itemsInBag; // füge der Tasche ein Element hinzu, indem das Objekt übergeben wird Function addItem (Object item) // Verfolgung der mit Raritätenschlitzen verbundenen Schleife Int timesToAdd; // Überprüfen Sie die Raritätsvariable für das Element // (erstellen Sie jedoch zuerst diese Raritätsvariable in der Elementklasse, // vorzugsweise mit einem aufgezählten Typ). Switch (item.rarity) Fall 'common': timesToAdd = 5; Fall 'ungewöhnlich': timesToAdd = 3; Fall 'rare': timesToAdd = 1; // Füge Instanzen des Artikels der Tasche entsprechend der Rarität hinzu. While (timesToAdd> 0) itemsInBag.push (item); timesToAdd--;
Wir haben jetzt über einige der häufigsten Möglichkeiten gesprochen, um mit der Randomisierung in Spielen umzugehen. Das Konzept der Verwendung variabler Zinssätze beginnt ähnlich wie früher: Wir haben eine bestimmte Anzahl von Ergebnissen und wissen, wie oft wir möchten, dass sie auftreten. Der Unterschied zu dieser Implementierung besteht darin, dass wir das Potenzial für Ergebnisse sofort anpassen möchten.
Warum wollen wir das tun? Nehmen Sie zum Beispiel Spiele mit einem sammelbaren Aspekt. Wenn Sie für den Gegenstand, den Sie erhalten, zehn mögliche Ergebnisse haben, wobei neun "gewöhnlich" und eines "selten" sind, sind Ihre Chancen ziemlich einfach: 90% der Fälle, ein Spieler erhält das gemeinsame und 10% der Ergebnisse Mal bekommen sie das Seltene. Das Problem tritt auf, wenn wir mehrere Ziehungen berücksichtigen.
Sehen wir uns Ihre Chancen an, eine Reihe allgemeiner Ergebnisse zu zeichnen:
Das anfängliche Verhältnis von 9: 1 schien anfangs eine ideale Rate zu sein, stellte aber nur das durchschnittliche Ergebnis dar und 1 von 10 Spielern gaben doppelt so viel Geld aus, wie beabsichtigt war, um so selten zu werden. Darüber hinaus würden 4% der Spieler dreimal so lange ausgeben, um selten zu werden, und unglückliche 1,5% würden viermal so lange ausgeben.
Die Lösung besteht darin, einen Seltenheitsbereich bei unseren Objekten zu implementieren. Dazu definieren Sie sowohl eine maximale als auch eine minimale Rarität für jedes Objekt (oder einen Raritäts-Slot, wenn Sie es mit dem vorherigen Beispiel kombinieren möchten). Geben Sie unserem gemeinsamen Gegenstand beispielsweise einen Mindestwert für die Rarität von 1 und einen Höchstwert von 9. Die seltenen Werte haben einen Mindest- und Höchstwert von 1.
Mit dem Szenario von vorhin haben wir zehn Elemente, und neun davon sind ein Beispiel für ein gemeinsames, während eines davon selten ist. Beim ersten Unentschieden besteht eine Chance von 90%, das Common zu erreichen. Mit den variablen Zinssätzen, nachdem dieses Common gezogen wurde, werden wir seinen Seltenheitswert um 1 senken.
Bei der nächsten Ziehung stehen Ihnen insgesamt neun Elemente zur Verfügung, von denen acht üblich sind. Dies ermöglicht eine Chance von 89%, ein gemeinsames zu zeichnen. Nach jedem gemeinsamen Ergebnis sinkt die Seltenheit dieses Gegenstands, so dass es wahrscheinlicher ist, die Seltenen zu ziehen, bis wir mit zwei Gegenständen in der Tasche aufdeckt, einem gemeinsamen und einem seltenen.
Während zuvor eine Chance von 35% bestand, 10 Gemeinschaftsgüter hintereinander zu ziehen, besteht jetzt nur noch eine Chance von 5%. Für die Ergebnisse der Ausreißer, beispielsweise das Ziehen von 20 Commons in einer Reihe, sind die Chancen nun auf 0,5% und sogar noch weiter reduziert. Dies führt zu einem konsistenteren Ergebnis für unsere Spieler und verhindert, dass ein Spieler immer wieder ein schlechtes Ergebnis erzielt.
Die grundlegendste Implementierung des variablen Satzes wäre, einen Artikel aus der Tasche zu nehmen, anstatt ihn einfach zurückzuschicken.
Class Bag // Bewahre ein Array aller Elemente auf, die sich im Bag befinden. Array itemsInBag; // Fülle den Beutel mit Elementen, wenn sein Konstruktor erstellt wurde (Array startingItems) itemsInBag = startingItems; // Fügen Sie der Tasche ein Element hinzu, indem Sie das Objekt übergeben (und drücken Sie es einfach in das Array). Funktion addItem (Object item) itemsInBag.push (item); Funktion getRandomItem () // Wähle ein zufälliges Element aus der Tüte Var currentItem = itemsInBag [random (0, itemsInBag.length-1)]; // Reduziere die Anzahl der Instanzen dieses Elements, wenn es über dem Mindestwert liegt. If (instanceOf (currentItem, itemsInBag)> currentItem.minimumRarity) itemsInBag.remove (currentItem); return (currentItem);
Während eine solche einfache Version einige Probleme mit sich bringt (z. B. wenn die Tasche schließlich den Status der Standard-Randomisierung erreicht), stellt sie die geringfügigen Änderungen dar, die zur Stabilisierung der Ergebnisse der Randomisierung beitragen können.
Während dies die Grundidee der variablen Sätze abdeckt, gibt es für Ihre eigenen Implementierungen noch einiges zu beachten:
Das Entfernen von Elementen aus dem Beutel hilft dabei, konsistente Ergebnisse zu erzielen, kehrt jedoch zu den Problemen der Standard-Randomisierung zurück. Wie könnten wir Funktionen so gestalten, dass sowohl Zu- als auch Abnahmen von Elementen zugelassen werden, um dies zu verhindern?
Was passiert, wenn es sich um Tausende oder Millionen von Gegenständen handelt? Die Verwendung eines mit Taschen gefüllten Beutels könnte eine Lösung dafür sein. Das Erstellen einer Tasche für jede Rarität (alle gängigen Gegenstände in einer Tasche, selten in einer anderen) und das Einsetzen jedes dieser Gegenstände in Schlitze in einer großen Tasche kann beispielsweise eine Vielzahl neuer Manipulationsmöglichkeiten bieten.
Viele Spiele verwenden immer noch die Standard-Zufallszahlengenerierung, um Schwierigkeiten zu erzeugen. Auf diese Weise entsteht ein System, bei dem die Hälfte der Spielerfahrung auf jede Seite des beabsichtigten fällt. Wird das Kontrollkästchen nicht aktiviert, besteht die Möglichkeit, dass unbeabsichtigte Wiederholungen zu unbeabsichtigten Wiederholungen führen.
Durch die Begrenzung des Bereichs, in dem sich die Ergebnisse ändern können, wird eine kohärentere Benutzererfahrung sichergestellt, sodass eine größere Anzahl von Spielern Ihr Spiel ohne anhaltende Langeweile genießen kann.
Die Erzeugung von Zufallszahlen ist eine Grundvoraussetzung für gutes Spieldesign. Stellen Sie sicher, dass Sie Ihre Statistiken überprüfen und die beste Generation für jedes Szenario implementieren, um das Spielererlebnis zu verbessern.
Liebst du eine andere Methode, die ich nicht behandelt habe? Haben Sie Fragen zur Zufallszahlengenerierung im Design Ihres eigenen Spiels? Hinterlasse mir einen Kommentar und ich werde mein Bestes tun, um mich bei dir zu melden.