Moderne Beutetischkonzepte

7

Erlauben Sie mir zunächst, mich zu entschuldigen, da dies ein ziemlich langer Beitrag sein wird. Ich habe viel über dieses Thema nachgedacht und sogar mit Konzepten und früheren Implementierungen experimentiert .

Wenn ich mir einen Beutetisch vorstelle, denke ich immer an etwas Ähnliches wie World of Warcraft. Hier ist ein Bildbeispiel:

Geben Sie hier die Bildbeschreibung ein

Verwenden Sie jetzt nur visuelle Hinweise, um die Funktionsweise des Beutesystems zu analysieren, aber lassen Sie uns auch einige Wowhead-Statistiken verwenden, um ein besseres Verständnis für das zu erlangen, womit wir es zu tun haben. Hier ist ein kurzes Modell: Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, ist das Beutesystem, das World of Warcraft verwendet, ziemlich dynamisch. Es ist nicht gerade ein Roulette, kann aber auf seiner Basisebene in gewisser Weise einem ähneln. Bei meiner ersten Implementierung eines Beutetisches wurde das Konzept eines Roulettes und sogar eines Wiegemechanikers vermieden (wodurch bestimmte Gegenstände mit größerer Wahrscheinlichkeit vom Tisch genommen werden und andere verlassen werden). Die Idee, Gegenstände dieser Art zu wiegen, gefiel mir nicht. Also habe ich so etwas gemacht:

Geben Sie hier die Bildbeschreibung ein

Diese Beutetabelle war eher eine "Liste". Grundsätzlich würde ich jedes Element durchlaufen, seine Wahrscheinlichkeit testen und wenn es erfolgreich wäre, würde ich es einer Liste von Tropfen hinzufügen. Theoretisch hat das gut funktioniert, aber ich habe schnell einen schwerwiegenden Fehler im System bemerkt.

Betrachten Sie diese Ausgabe:

Ore_Copper x1
Nugget_Copper x2
Gem_Diamond x1
Gem_Emerald x1
Gem_Topaz x1

Das ist übertrieben, aber wenn man sich die Grundstruktur ansieht, ist das durchaus möglich ... aber was ist, wenn wir nicht 4 Arten von Edelsteinen fallen lassen wollten? Aufgrund der kollektiven Wahrscheinlichkeit der Edelsteine ​​erhalten Sie praktisch garantiert mindestens einen, da Sie eine Chance von 10% * 5 haben, einen zu erhalten. Nun, das mag für ein Spiel in Ordnung sein, aber es ist nicht das, was ich beabsichtigt hatte. Ich wollte die Fähigkeit, Edelsteine ​​fallen zu lassen, aber mit nur einer Chance. Ich wollte, dass die Wahrscheinlichkeit, einen Edelstein zu erhalten, genau 10% beträgt. Also ... wie beheben wir das? Folgendes habe ich mir ausgedacht:

Geben Sie hier die Bildbeschreibung ein

Also, was passiert jetzt? Wir haben im Wesentlichen ein Roulette-System gebaut. Anstelle einer direkten Sammlung von LootTableItems verschieben wir jetzt Elemente in unabhängige Sammlungen. LootTableLists. Jede Liste hat ihre eigene Wahrscheinlichkeit, ähnlich wie mein Original LootTable, aber jetzt können wir Elemente erfolgreich in einem eigenen "Pool" in einem einzigen organisieren LootTable. Wenn ein Element weniger selten sein soll als ein anderes, fügen wir dem gewünschten Element einfach zusätzliche Instanzen hinzu LootTableList. Wenn mein LootTableAlgorithmus ausgeführt wird, wählt er ein Element aus dem aus LootTableList. Das ist großartig, hat aber auch ein anderes Problem aufgeworfen. Was wäre, wenn ich wollte, dass alle Gegenstände in einem LootTableListfallen gelassen werden oder eine Chance haben, fallen gelassen zu werden? Nun, ich ging voran und fügte ein zusätzliches Feld hinzu, genanntCollectAll. Wenn dieses Feld aktiviert ist, wird vermieden, dass ein Element ausgewählt wird, und stattdessen werden alle Elemente im Feld ausgewählt LootTableList.

Nachdem ich das alles geplant und meine Hausaufgaben zu diesem Thema gemacht hatte, kam ich dazu, diese Art von LootTable zu erstellen. Ich bin mir auch ziemlich sicher, dass dies auch die Art von LootTable ist, die World of Warcraft verwendet, oder zumindest konzeptionell sind sie sehr nahe beieinander. Hier ist der Code:

using System;
using System.Collections.Generic;
using System.Linq;
using GrimoireDevelopmentKit.Framework.Interfaces;
using GrimoireDevelopmentKit.Framework.Maths;
using GrimoireDevelopmentKit.Framework.Utilities;

namespace GrimoireDevelopmentKit.Framework.Collections
{
    [Serializable]
    public class LootTable : Dictionary<string, LootTableList>, ICopy<LootTable>
    {
        public string Identifier { get; set; }
        public AdvancedRandom Random { get; set; }

        public int TotalItems
        {
            get
            {
                int total = 0;
                foreach (LootTableList list in this.Values)
                {
                    total += list.Values.Count;
                }
                return total;
            }
        }

        public LootTable(string identifier = null)
            : this(identifier, new AdvancedRandom())
        {
        }

        public LootTable(string identifier, AdvancedRandom random)
        {
            Identifier = identifier;
            Random = random;
        }

        public void Add(LootTableList list)
        {
            if (!Contains(list))
            {
                Add(list.Identifier, list);
            }
            else
            {
                throw new ArgumentException("Key: '" + list.Identifier + "' already exists!");
            }
        }

        public bool Remove(LootTableList list)
        {
            return Remove(list.Identifier);
        }

        public bool Contains(string listIdentifier)
        {
            return ContainsKey(listIdentifier);
        }

        public bool Contains(LootTableList list)
        {
            return Contains(list.Identifier);
        }

        public List<LootTableResult> Next()
        {
            Range probabilityScope = new Range(0, 100);
            List<LootTableResult> drops = new List<LootTableResult>(TotalItems);
            foreach (LootTableList list in Values)
            {
                bool success = Random.NextRange(probabilityScope) <= list.Probability;
                if (success)
                {
                    if (list.CollectAll)
                    {
                        foreach (LootTableItem item in list.Values)
                        {
                            int quantity = Random.NextRange(item.Quantity);
                            if(quantity > 0)
                            {
                                drops.Add(new LootTableResult(item.Identifier, quantity));
                            }
                        }
                    }
                    else
                    {
                        LootTableItem item = list.ElementAt(Random.NextInteger(0, list.Count)).Value;
                        int quantity = Random.NextRange(item.Quantity);
                        if(quantity > 0)
                        {
                            drops.Add(new LootTableResult(item.Identifier,quantity));
                        }
                    }
                }
            }
            return drops;
        }

        public void Copy(LootTable other)
        {
            this.Clear();
            this.Identifier = other.Identifier;
            this.Random = other.Random;
            foreach (LootTableList list in other.Values)
            {
                this.Add(list.MakeCopy());
            }
        }

        public LootTable MakeCopy()
        {
            LootTable copy = new LootTable();
            copy.Copy(this);
            return copy;
        }
    }

}

Meine Frage:

Gibt es eine bessere und modernere konzeptionelle Grundlage, auf der ich diese Art von Beutetisch erreichen könnte?

Gibt es auf dieser Website Spieleentwickler, die Beutetabellen dieser Art erstellt haben? Ich möchte nur wissen, ob ich mein Konzept verbessern kann, nicht nur den Code , sondern das Konzept selbst. Ich bin sicher, dass noch mehr getan werden kann, um meine Arbeit zu verbessern. Ich suche nur Rat von Leuten, die Erfahrung in diesem Thema haben.

Ich kann meine Implementierung auch in einem separaten Projekt veröffentlichen, wenn dies den Leuten helfen würde, mir zu helfen . Tatsächlich denke ich, dass ich jetzt tatsächlich daran arbeiten werde.

BEARBEITEN

Ich habe gerade festgestellt, dass ich vergessen habe, die Anzahl der Mengen in den LootTableLists zu codieren. Wenn diese codiert werden, kann eine LootTableList während desselben Ablegens mehrmals erfasst werden.

Krythic
quelle
2
Ich bin mir nicht sicher, was Sie hier fragen. Es ist unmöglich zu antworten, "wie kann ich das verbessern", wenn Sie keine Kriterien für "besser" angegeben haben. Stattdessen habe ich festgestellt, dass Sie einige Designprobleme hatten - wie kann ich nur einen Artikel oder alle Artikel löschen -, aber Sie haben sie bereits gelöst.
Congusbongus
Ich werde bearbeiten, um es klarer zu machen.
Krythic
1
Ihre Bearbeitung fügt nur einen Kommentar hinzu. es macht nichts, um klarer zu machen, was Sie tatsächlich fragen .
Gnemlock
2
Was ist falsch an dem nicht so modernen Ansatz? Gibt es etwas, das Sie tun möchten, was Sie mit einer einfacheren Lösung nicht können? - Zum Beispiel verwenden Sie keine Gewichte. Gibt es etwas, das Sie tun möchten, was Gewichte Sie daran hindern würden? - Angenommen, Sie möchten keine Gewichte verwenden, die nicht genug aussagen, um zu wissen, in welche Richtung Sie gehen sollen. - Es scheint, als wollten Sie nur den Stand der Technik kennen. Wenn das die Idee ist, bedeutet dies, dass alle Antworten veraltet sind, sobald etwas "Moderneres" hinzukommt.
Theraot
1
Wenn Sie möchten, dass Ihr Beutesystem ein gleichmäßigeres und ausgewogeneres Spielerlebnis bietet, ohne die Überraschung vollständig zu beseitigen, könnte diese Frage interessant sein: Wie kann ich einen „Zufallsgenerator“ erstellen, der durch frühere Ereignisse verzerrt ist?
Philipp

Antworten:

2

Du bist auf dem richtigen Weg. Hier würde ich anfangen. Alle Ihre Komponenten teilen sich die Kennung und Menge. Es ist sinnvoll, eine Zufallseigenschaft hinzuzufügen und ein zusammengesetztes Muster zu verwenden .

public class Droppable
{
    public string Name { get; private set; }
    public uint QTY { get; private set; }
    public float Chance { get; private set; }

    public Droppable(string name, float chance, uint qty)
    {
      this.Name = name;
      this.QTY = qty;
      this.Chance = chance;
    }

    public virtual bool ShouldDrop()
    {
    // random drop check
    }
}

Sie können dann Ihre Erweiterungstabelle für die Beutetabelle erstellen Droppable.

public class DropBundle extends Droppable
{
    public bool CollectAll { get; private set; }
    private List<Droppable> _items;

    public DropBundle(string name, float chance, uint qty, bool collectAll, List<Droppable> items): base(name, chance, qty)
    {
        CollectAll = collectAll
        _items = items;
    }

    public override bool ShouldDrop()
    {
    // random drop check and if yeas run should drop check for other items
    }
}

Sie benötigen auch Ihre Artikelklasse, die sich ebenfalls erweitern würde Droppable

public class DropItem extends Droppable
{

   public override bool ShouldDrop()
   {
      // random drop check
   }
}

Hinweis CompositeItementhält Verweise auf andere DroppableKomponenten. Das heißt, Sie können sie dann so oft weiterleiten, wie Sie möchten, z.

  • 5% der 10 Goldmünzen gefunden (DropItem)
  • 10% der Suche nach einer Münze (CompositeItem), die entweder Gold (20%) oder Silber (80%) sein kann
  • 20% der Suche nach einer Truhe (CompositeItem) mit einer Wahrscheinlichkeit von 10%, eine Münze (CompositeItem) zu finden, die entweder Gold, Silber oder Kupfer usw. sein kann.

Alle diese Szenarien sind dann möglich, ohne den Abhängigkeitsbaum zu stark zu ändern. Das einzige, woran Sie sich erinnern müssen, ist offensichtlich die Randomisierung zu implementieren und zu beachten, dass die Summe der Chancen verschachtelter Droppables der Einfachheit halber 1 (100%) betragen sollte.

Ein weiterer großer Vorteil der Verwendung von zusammengesetzten Mustern ist die Möglichkeit, diese Implementierung später für Daten zu verwenden, die Sie dynamisch aus statischen XML- (oder JSON-) Dateien lesen können.

IndieForger
quelle