Jump to content
Unity Insider Forum

Spawnintervalle unvorhersehbar aber fair


Shaitan1805

Recommended Posts

Hey Leute,

 

ich zerbreche mir seit einiger Zeit den Kopf über ein Problem für ein kleines Handygame.

Grundlegend zum Spiel ist zu sagen, es ist ein Reaktionsspiel, bei dem immer wieder Ziele erscheinen die man treffen muss.

 

Da ein Reaktionsspiel ja durch die Reaktion und nicht durch das richtige Abzählen von Sekunden gespielt werden soll, müssen die Intervalle für das nächste Ziel dynamisch sein.

 

Aktuell arbeite ich einfach mit einem Random-Wert in einem bestimmten Bereich. Problem dabei ist allerdings, dass mir das Spiel dann zu stark abhängig vom Glück ist. Sprich, wenn man Pech hat, sind die Intervalle oft im höheren Bereich und dadurch bekommt man weniger Punkte (Zeit läuft ab).

Wenn ich die Intervalle kleiner mache, oder einen statischen Wert nutze wird das Spiel vorhersehbar und dadurch zu leicht.

 

Mir ist klar, dass ich nicht beides haben kann: unvorhersehbar und fair, aber ich möchte mich dem annähern, also einen guten Kompromiss finden. (Da merkt man mal, dass auch bei kleinen Spielen, das Balancing ganz schön nerven aufreibend werden kann :wacko: )

 

Ein Ansatz dafür wäre zu sagen, wenn hohe Intervalle kamen, müssen auch kleine Intervalle folgen um es auszugleichen.

 

Da wäre eine Umsetzung z.B. Eine Zeit t vorgeben, in der 2 Ziele erscheinen müssen. Das erste wird per Random bestimmt und das zweite dann anhand der vorgegeben Zeit t berechnen.

Allerdings befürchte ich dabei, dass auch hier Spieler nach relativ kurzer Zeit diesen Trick erkennen und das Spiel schon wieder zu vorhersehbar wird.

 

Eine andere Idee war nicht den Wert direkt zu berechnen sondern eine Wahrscheinlichkeit anzupassen. Heißt, wenn ein hoher Intervall kam, ist die Wahrscheinlichkeit höher einen niedrigen Intervall zu generieren, aber dennoch möglich einen mittleren bzw hohen zu bekommen.

Dabei habe ich aber das Gefühl mit einer Panzerfaust auf einen Spatzen zu schießen :D

 

Nun kennt ihr mein Problem. Meine Frage an Euch alte Hasen oder vlt auch Neulinge mit der super Idee: Welche Methode würdet ihr bevorzugen? Habt ihr vlt andere Ansätze oder standet ihr schon vor dem selben Problem und habt schon eine Lösung ausgearbeitet, die ihr gerne teilt?

 

Gruß

Shaitan

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich habe keine aktuelle Lösung dafür, weil ich das noch nie probiert habe. Aber ich würde so vorghehen:

 

Ich erstelle ein Array mit Wahrscheinlichkeiten. Die Wahrscheinlichkeit eines Spawns steigt mit steigender Wartezeit. Das Array würde z.B. so aussehen:

 

int[] spawnChances = new[] { 10, 40, 80, 99 };

In der ersten Sekunde 10%, in der zweiten 40% usw.

 

Deine Spawn-Routine schaut dann, wie viel Zeit vergangen ist. Ist eine Sekunde vergangen, wird eine Random-Zahl zwischen 0 und 100 geholt, Wenn die kleiner als 10 ist -> Spawn, ansonsten warten. Wenn bereits zwei Sekunden vergangen sind, steigt die Wahrscheinlichkeit auf 40% usw.

 

Hat auch den Vorteil, dass das sehr einfach zu balancen ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zusammenfassung:

Das zeitliche Erscheinen der Ziele ist grundsätzlich zufällig.

Innerhalb eines gewissen Zeitraumes soll eine Mindestmenge an Zielen erscheinen.

Wenn ein langer Intervall da war, soll die Wahrscheinlichkeit für einen kurzen Intervall steigen.

 

Ich würde jetzt ein Variable für die Minimalzeit und die Maximalzeit erzeugen.

 

float minimum=0.3f; // Diese Zeit verstreicht mindestens.
float maximum=1.0f; // spätestens nach dieser Zeit entsteht ein Ziel.

 

Es gäbe eine Variable, die mir die exakte Zeit zum erstellen des Ziels gibt.

 

float spawnZeit; // nach dieser Zeit wird das Ziel erzeugt

 

Es gäbe eine Variable die meine Maximalzeit verkürzen könnte, wenn ein langer Intervall gewesen ist.

 

float intervallBonus; // dieser Wert wird von dem Maximalwert abgezogen.

 

Es gäbe eine Boolsche Variable, die mir anzeigt, dass ein langer Intervall da gewesen ist.

 

bool warLang =false; // gab es eben einen langen Intervall?

 

Dann würde ich mit zwei Zufallszahlen arbeiten.

Die erste Zufallszahl würde nur ermittelt werden, wenn zuvor ein langer Intervall gewesen ist.

Über sie würde ich ermitteln, ob ich die Maximalzeit verkürzen müsste.

 

if(warLang){
  warLang=false;
  int bonusZufall = Random.Range(1,4); // bei Int einen mehr als gewünscht. Zufall wird aus 1-3 ermittelt
  if(bonusZufall >1 ){ 
  intervallBonus=0.3f; 
  }else{
  intervallBonus=0f;
  }
}

Die zweite Zufallszahl hat die Range von der Minimalzeit bis zur Maximalzeit, wobei die Maximalzeit über den IntervallBonus verkürzt werden könnte.

 

spawZeit= Random.Range(minimum,maximum-intervallBonus);

 

Um zu definieren wann ein Intervall lang war musst du einfach das Ergebnis mit einem festgelegten Wert vergleichen.

 

if(spawnZeit >0.8f){ 
  warLang=true;
}

 

Alles wird nur ausgeführt, wenn das Spiel beginnt bzw. gerade ein Zielgespawnt wurde.

Zum Steuern einfach eine Boolsche Variable nehmen, die zu Beginn auf true ist und immer dann auf true gesetzt wird, wenn ein Ziel entstanden ist.

Innerhalb der Funktion wird die Variable sofort wieder auf false gesetzt.

 

Jetzt hast du eine Zufallzeit ermittelt, die du einfach mit einem Timer vergleichen musst. Der Timer nimmt sich zu Beginn des Spiels und immer beim erzeugen des Ziels die Time.Time.

Und wird in der Update einfach immer mit der momentanen Zeit, abzüglich der spawnZeit verglichen.

Sobald die Spawnzeit <= der Time.Time-startZeit ist ist der Zeitpunkt erreicht und dein Ziel wird erstellt und alles beginnt von neuem.

 

Du hast also eine Zufällige Zeit die zwischen 0.3 und 1.0 Sec liegt. Und jedes Mal wenn ein langer Intervall da war, also der Wert über z.B. 0.8 Sec. war, wird beim nächsten Mal über eine Wahrscheinlichkeit von 2:1 ein Wert zwischen 0.3 und 0.7 ermittelt.

Nach einem langen Intervall würde also häufiger ein kürzerer Intervall kommen.

Natürlich alles rein Statistisch gesehen, denn der Zufall kann das Ganze auch anders ablaufen lassen. :)

 

Um vielleicht auch nach einem kurzen Intervall häufiger einen längeren kommen zu lassen, musst du nur eine weitere Zufallszahl und einen Bonus für den Minimalwert erzeugen.

Das System würde also in beide Richtungen funktionieren.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mir ist klar, dass ich nicht beides haben kann: unvorhersehbar und fair

 

Doch, ich glaube schon. Ich sehe da mindestens zwei relativ einfache Möglichkeiten:

 

Variante a: Du hebst die Zeitbeschränkung deines Spiels auf, und ersetzt sie durch eine Anzahl von Items die dem Spieler angeboten werden. Durch die zufälligen Intervalle zwischen zwei Items dauern zwei Sessions dann nicht mehr gleich lang - aber das stört nicht. Der Spieler hat immer die gleiche Chance die maximale Punktezahl zu erreichen. Der "Nachteil", dass eine Session etwas länger dauert, eine andere eher etwas kürzer, dürfte dem Spieler kaum auffallen. Im Gegenteil: Wenn du die Zeit anzeigst, um Spannung zu erzeugen, werden einige Spieler das Spiel als unfair empfinden, wenn sie gerade am Ende längere Intervalle haben. Das Problem hast du nicht, wenn du stattdessen die noch übrigen Items anzeigst (das erhöht die Spannung mindestens genau so, wenn nicht sogar noch mehr: "mein letztes Item - jetzt darf ich keinen Fehler machen!!!")

 

Variante B: Du lässt die Session-Zeit festgelegt und ermittelst die Zeitpunkte des Spawnings bevor die Session beginnt. Sagen wir das Spiel dauert eine Minute. Wirklich fair ist es nur, wenn jede Session immer exakt gleich viele Items bringt. Sagen wir du möchtest in der Minute 10 Items spawnen lassen, also durchschnittlich eines alle 6 Sekunden. Der einfachste Algorithmus, der mir da gerade einfällt: Fülle Liste mit 10 floats: 6, 12, 18 ... (also erstmal nix zufällig, sondern die exakte Durchschnittszeit). Dann iteriere über diese Liste, und füge jedem float einen zufälligen Wert hinzu, beispielsweise zwischen -2 und +2. Damit hast du zwischen zwei Items mindestens 2 Sekunden (das ist der Fall, das Item1 +2 bekommt, und Item2 -2), und maximal 10 Sekunden (Item1 hat -2, Item2 hat +2). Evtl. müsstest du da eine kleineres Intervall nehmen, z.B. -1.5 bis +1.5, aber das musst du halt testen und optimieren.

 

Was dir bei Variante B aber immer noch passieren kann: Genau die letzten paar Items haben alle negative Offsets, d.h. subjektiv erhält der Spieler am Ende "weniger Items" - gerade da, wo es am spannendsten ist. Daher würde ich Variante A bevorzugen. Aber an sich ist beides gleich fair.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Vielen Dank für Eure schnellen Antworten und Ideen.

Interessant zu sehen wie andere an solche Probleme herangehen :)

 

@jashan: Super Ideen, allerdings ist die ablaufende Zeit ein essentieller Spielfaktor, den ich nicht herausnehmen kann, da das ganze Spiel darauf basiert.

 

Ich habe mir noch weiter Gedanken gemacht und bin auf eine Art Hybrid-Lösung gekommen, die ich mal ausprobieren werde.

 

Die Idee ist es gibt 3 Intervall-Bereiche: einen kleinen, einen mittleren und einen großen Bereich.

Realisiert durch ein Vector2 Array area mit 3 Zellen, je Zelle ein Bereich für je einen minimalen und einen maximalen Wert.

 

Diese 3 Bereiche haben auch Wertigkeiten: klein -1, mittel 0 und groß +1.

 

Eine Variable value startet beim Wert 1 (Eigentlich Wertigkeit 0, aber so kann ich diese Variable gleich als Index für ein Array nutzen), dann wird per Zufall entschieden welcher von den dreien genutzt wird. Die entsprechende Wertigkeit wird zu value addiert. Und aus dem Bereich eine Zufallszahl generiert, welche der neue Intervall ist.

 

Nach Ablauf des Intervalls beginnt das ganze von vorn, nur dass nun der Wert von value nicht als Bereich per Random berücksichtigt wird. Also wenn value = -1 wird zwischen mittlerem und großem gewürfelt, bei +1 zwischen klein und mittel und bei 0 zwischen allen dreien.

 

Dadurch ist noch ein kleiner Glückswert dabei, aber es gleicht sich aus. Also wenn ein hoher Intervall war, kommt ein kleiner oder mittlerer und andersherum. Der Ausgleich ist nicht zu 100%, aber ich glaube das macht dann auch ein bisschen den Reiz des Spieles aus :)

 

private Vector2[] areas = new Vector2[] { new Vector2(3, 4), new Vector2(4, 5), new Vector2(5, 6) };
private int value;

float CalcTimer()
{
int zz;
do
{
	zz = Random.Range(0, 3);
} while (zz == value && value != 0);
value += (zz - 1);
return Random.Range(areas[zz].x, areas[zz].y);
}

 

Was haltet ihr von der Idee?

 

Gruß

Shaitan

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, warum nicht! :)

Teste es doch einfach mal um zu sehen, ob es so ausreicht.

 

Ich persönlich würde aber noch das Können des Spielers mit einbauen. Das Treffen eines Ziels sollte das Entstehen eines neuen Ziels beschleunigen. Natürlich nur, wenn nicht schon sehr viele Ziele zu sehen sind.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, warum nicht! :)

Teste es doch einfach mal um zu sehen, ob es so ausreicht.

 

Ich persönlich würde aber noch das Können des Spielers mit einbauen. Das Treffen eines Ziels sollte das Entstehen eines neuen Ziels beschleunigen. Natürlich nur, wenn nicht schon sehr viele Ziele zu sehen sind.

 

Das Können des Spielers werde ich auf jedenfall einbauen. Ich denke in Form davon, dass mit jedem Ziel das erscheint und der Spieler fehlerfrei trifft, wird die Wahrscheinlichkeit erhöht ein Ziel zu spawnen, welches mehr Punkte gibt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...