Benutze eine Instanz oder eine Klasse für Spielressourcen (Holz, Eisen, Gold)

31

Also mache ich ein Spiel, in dem du Schiffe an Orte schicken kannst, an denen du Rohstoffe wie Holz, Eisen, Gold usw. verkaufen oder kaufen kannst.

Jetzt habe ich mich gefragt, wie die Ressourcen im Spiel erstellt werden sollen. Ich habe mir 2 Optionen ausgedacht

  1. Erstellen Sie eine Klasse für jede Ressource:

    public class ResourceBase {
        private int value;
        // other base properties
    }
    
    public class Gold : ResourceBase {
         public Gold {
             this.value = 40 // or whatever
         }
    }
  2. Erstellen Sie Instanzen einer Ressourcenklasse

    public class Resource {
        string name;
        int value;
    
        public Resource(string name, int value) {
            this.name = name;
            this.value = value;
         }
     }
    
    // later on...
    
    Resource gold = new Resource("Gold",40);

Mit der zweiten Option ist es möglich, die Spielressourcen aus einer resources.json-Datei zu füllen. Diese Struktur gefällt mir irgendwie.

Neue Ideen / Designmuster / Strukturen sind immer willkommen!

EDIT: Es ist ein bisschen wie Assassins Creed Black Flag Companion App. Siehe die Ressourcenleiste auf dem Bild unten

BEARBEITEN 2: Ich habe einige Nachforschungen zum "Laden von Elementen / Ressourcen aus JSON-Datei" angestellt und dieses Blog gefunden: Power of JSON in Game Development - Items . Es zeigt das Beste aus Option 1 und Option 2. Sie können weiterhin jeder Ressource Funktionen hinzufügen :)

Bildbeschreibung hier eingeben

MDijkstra
quelle
1
Minecraft hat separate Klassen für jede Ressource (nicht, dass Sie sollten)
MCMastery
@MCMastery ohh ist Minecraft Open-Source? Ich würde gerne einen Blick auf diese Struktur werfen. Und danke für die Erwähnung
MDijkstra
1
Es ist nicht, aber es ist immer für Server de-verschleiertes wird ... siehe github.com/Wolf-in-a-bukkit/Craftbukkit/tree/master/src/main/...
MCMastery
12
Verwenden Sie keine solche Zeichenfolge. Es ist allzu leicht, "Gold" mit "Glod" oder ähnlichem zu verwechseln, und es ist ein großer Schmerz, Fehler zu beheben. Verwenden Sie enumstattdessen eine.
Nolonar
Minecraft ist technisch gesehen kein Open-Source-Programm, wurde jedoch fast vollständig dekompiliert und deobfusciert. Sie sollten nirgendwo im Internet eine deobfuscierte Quelle finden können, aber Sie können den entsprechenden Vorgang von files.minecraftforge.net herunterladen und ausführen (der MDK-Download ist der von Ihnen gewünschte)
JamEngulfer

Antworten:

51

Entscheiden Sie sich für den zweiten Ansatz, da Sie jederzeit neue Ressourcentypen oder -elemente einführen können, ohne den Code neu schreiben oder aktualisieren zu müssen ( datengesteuerte Entwicklung ).


Bearbeiten:

Um ein bisschen mehr darüber zu sagen, warum dies im Allgemeinen eine gute Praxis ist, auch wenn Sie zu 100% sicher sind, dass sich ein bestimmter Wert niemals ändern wird.

Nehmen wir das in den Kommentaren erwähnte Beispiel für ein Konsolenspiel, denn dies ist ein guter Grund, warum Sie nichts hart codieren sollten, es sei denn, Sie haben wirklich (oder möchten), z. B. Verschlüsselungsschlüssel, Ihren eigenen Namen usw.

Wenn Sie ein Spiel auf Konsolen veröffentlichen, müssen diese in der Regel einen Überprüfungsprozess durchlaufen, bei dem die QS des Konsolenherstellers das Spiel testet, es durchspielt, nach Problemen sucht usw. Dies ist obligatorisch und kostet Geld, viel Geld. Ich habe mal gelesen, dass eine Veröffentlichung allein für die Zertifizierung 30.000-50.000 US-Dollar kosten könnte.

Stellen Sie sich vor, Sie wollen Ihr Spiel veröffentlichen, zahlen die 30.000 US-Dollar und warten. Und plötzlich merkst du, dass einige deiner Spielwerte buchstäblich gebrochen sind. Nehmen wir an, Sie können Eisenbarren für 50 Gold kaufen und für 55 Gold verkaufen.

Wie geht's? Wenn Sie dieses Zeug fest programmiert haben, müssen Sie eine neue Update- / Release-Version erstellen, die noch einmal überprüft werden muss, sodass Sie im schlimmsten Fall erneut für die Neuzertifizierung zahlen müssen! Ich wette, Ubisoft hat nichts dagegen, aber für Ihre eigene kleine Indie-Spiele-Entwicklertasche ... autsch!

Stellen Sie sich jedoch vor, das Spiel sucht nur gelegentlich nach aktualisierten Spieldefinitionen (z. B. in Form einer JSON-Datei). Ihr Spiel kann jederzeit die neueste Version herunterladen und verwenden, ohne dass ein neues Update erforderlich ist. Sie können dieses Ungleichgewicht / Exploit / Bug jederzeit beheben, ohne eine erneute Zertifizierung oder ein anderes bezahltes Geld zu benötigen. Nur eine kleine Designentscheidung, die Sie nicht für lohnenswert hielten. Sie haben nur fünfstellige Beträge gespart! Ist das nicht großartig? :)

Versteh mich einfach nicht falsch. Dies gilt auch für andere Arten von Software und Plattformen. Das Herunterladen aktualisierter Datendateien ist für den Standardbenutzer im Allgemeinen viel einfacher und praktischer, unabhängig davon, ob es sich um ein Spiel oder eine Anwendung oder ein Tool handelt. Stellen Sie sich ein Spiel vor, das unter Programme in Windows installiert ist. Ihr Updater benötigt Administratorrechte, um Änderungen vorzunehmen, und Sie können laufende Programme nicht ändern. Mit DDD lädt Ihr Programm einfach die Daten herunter und verwendet sie. Der Player bemerkt möglicherweise nicht einmal, dass ein Update durchgeführt wurde.

Mario
quelle
6
+1 für DDD. Dies ist sehr passend für solche Dinge wie Ressourcen.
Kromster sagt Unterstützung Monica
@Funnydutchman Wenn Ihnen eine Antwort geholfen hat, ist es bei Stack Exchange üblich, die Antwort zu verbessern, indem Sie auf den Pfeil über der Zahl links klicken. Wenn Ihnen eine Antwort am meisten geholfen hat, klicken Sie auf das Häkchen unter dem unteren Pfeil, um die Antwort zu akzeptieren. Beide Aktionen verleihen dem Antwortenden einen Reputationsbonus und machen verwertbare Antworten für die Community sichtbarer.
Nzall
2
Ich verstehe nicht, wie Sie Code mit dem ersten Weg umschreiben mussten; Alles, was Sie tun würden, ist eine Klasse hinzuzufügen (es sei denn, mir fehlt etwas).
SirPython
4
Und hier ist der Unterschied zwischen objektorientiertem Code und objektbesessenem Code. Nur weil Sie eine Objekthierarchie erstellen können, müssen Sie dies nicht unbedingt tun.
corsiKa
2
@ AgustínLado Wenn ich einen Dollar für jedes Mal hätte, wenn ein Kunde sagte "Das wird sich nie ändern" und es sich ändert, könnte ich uns beide zum Abendessen in ein anständiges Restaurant bringen (obwohl wir ein bisschen Trinkgeld sparen müssten, also vielleicht nur ein mittelmäßiges restaurant ... trotzdem noch genug
zeiten zum abendessen
29

Als Faustregel gilt, dass Sie unterschiedliche Klassen verwenden, wenn Objekte unterschiedlichen Code und Instanzen derselben Klasse erfordern, wenn die Objekte nur unterschiedliche Werte erfordern.

Wenn die Ressourcen unterschiedliche Spielmechanismen haben, die für sie einzigartig sind, kann es sinnvoll sein, sie mit Klassen darzustellen. Zum Beispiel, wenn Sie Plutonium haben , die eine Halbwertszeit und Kaninchen hat , die gemäß der fortzupflanzen Fibonacci - Sequenz , dann könnte es Sinn machen , eine Basis-Klasse zu haben , Resourcemit einem Verfahren Updateals no-op und zwei abgeleiteten Klassen implementiert HalfLifeResourceund SelfGrowingResourcedie Überschreiben Sie diese Methode, um den Wert zu verringern / erhöhen (und fügen Sie möglicherweise auch Logik ein, um zu steuern, was passiert, wenn Sie beide nebeneinander speichern).

Aber wenn Ihre Ressourcen wirklich nur dumme Zahlen sind, macht es keinen Sinn, sie mit etwas Besonderem als einer einfachen Variablen zu implementieren.

Philipp
quelle
Danke @Phillipp für deine Antwort. Das habe ich mir gedacht. Da sich die Ressourcen in Bezug auf Eigenschaften / Methoden nicht wirklich ändern, werde ich zunächst Option 2
wählen
14

Ich möchte hinzufügen, dass es zwei zusätzliche Optionen gibt:
Schnittstelle: Sie können es in Betracht ziehen, wenn die Ressourcenklasse nur ein "Speicher" für 5 Ganzzahlen wäre und jede andere Entität eine andere Logik in Bezug auf Ressourcen hätte (z. B. Spielerausgaben / Beute, Stadtproduktionen) Ressource). In diesem Fall möchten Sie möglicherweise überhaupt keine Klasse - Sie möchten lediglich offenlegen, dass einige Entitäten über eine Variable verfügen, mit der andere interagieren können:

interface IHasResources {
  int Gold { get; set; }
  int Wood { get; set; }
  int Cloth { get; set; }
  // ....
}

class Player : IHasResources { }
class City: IHasResources { }

Darüber hinaus hat dies den Vorteil, dass Sie eine Stadt mit genau demselben Code plündern können, wie Sie eine feindliche Flotte plündern würden, wodurch die Wiederverwendung von Codes gefördert wird.
Der Nachteil ist, dass das Implementieren einer solchen Schnittstelle sich als mühsam erweisen kann, wenn viele Klassen sie implementieren. Daher können Sie möglicherweise Schnittstellen verwenden, jedoch nicht für das ursprüngliche Problem, indem Sie sie mit Option 1 oder 2 lösen:

interface IHasResources { 
   IEnumerable<Resource> Resources { get; }
}

Instanzen (mit Aufzählung): In einigen Fällen wäre es wünschenswert, bei der Definition des Ressourcentyps Aufzählung anstelle von Zeichenfolge zu verwenden:

enum Resource {
   Gold, Wood, Cloth, //...
}

class ResourceStorage {
   public Resource ResourceType { get; set; }
   public int Value { get; set; }
}

Dies bietet ein wenig "Sicherheit", da Sie in Ihrer IDE eine Liste aller gültigen Ressourcen erhalten, wenn Sie sie nach dem Schreiben des Punkts ResourceType = Resource.zuweisen. Außerdem gibt es weitere "Tricks" mit Aufzählungen, die Sie möglicherweise benötigen, z. B. den expliziten Typ oder die Komposition / Flags was ich mir beim basteln als nützlich vorstellen kann:

[Flags]
enum Metal {
 Copper = 0x01/*01*/, Tin = 0x02/*10*/, Bronze = 0x03/*11*/
}
Metal alloy = Metal.Copper; //lets forget Value(s) for sake of simplicity
alloy |= Metal.Tin; //now add a bit of tin
Console.WriteLine(alloy); //will print "Bronze"


Was das Beste ist - es gibt keine Silberkugel, aber ich persönlich würde mich zu jeder Form von Instanzen neigen, sie sind vielleicht nicht so schick wie eine Benutzeroberfläche, aber sie sind einfach, überladen keinen Erbschaftsbaum und sind weithin bekannt.

wondra
quelle
2
Vielen Dank für Ihre Antwort @wondra Ich mag die Enum-Option und wie Sie diese Bronze aus Kupfer und Zinn hergestellt haben; p
MDijkstra
2
Ich bin dieser Community speziell beigetreten, um die Enum-Lösung zu verbessern. Sie können auch das Attribut enum Description verwenden, um eine Zuordnung von einer JSON-Darstellung von enum ( my-great-enum) zu einer C # -Darstellung von enum ( MyGreatEnum) vorzunehmen .
Dan Forbes
Anscheinend habe ich die Java-Schleife zu lange verlassen. Seit wann dürfen Felddeklarationen für Interfaces vorhanden sein? Soweit ich weiß, sind diese automatisch statisch und endgültig und werden daher für die in der Frage beschriebenen Zwecke nicht viel verwendet.
M.Herzkamp
2
@ M.Herzkamp es ist kein Feld, es ist eine Eigenschaft - und Java unterstützt keine Eigenschaften. Frage ist markiert mit C #, C # erlaubt Eigenschaften in Interfaces - Eigenschaften sind immerhin Methoden im Verborgenen. Ich fürchte, dasselbe gilt für die Annotation von Flags, die in Java nicht unterstützt wird, obwohl ich nicht ganz sicher bin.
Wondra
Danke, dass du das geklärt hast! Ich habe das C # -Tag verpasst und der Code in der Frage sah Java so verdammt ähnlich: D
M.Herzkamp,
2

Sie sollten Option 1 verwenden, die drei Vorteile bietet:

  1. Es ist einfacher, eine Ressource zu instanziieren - anstatt den Ressourcentyp als Parameter übergeben zu müssen, würden Sie beispielsweise new Gold(50)
  2. Wenn Sie einer Ressource ein besonderes Verhalten verleihen müssen, können Sie ihre Klasse ändern.
  3. Es ist einfacher zu überprüfen, ob eine Ressource ein bestimmter Typ ist - resource instanceof Gold
Zac
quelle
Alle Antworten haben ihre Vor- und Nachteile. Es ist schwer zu entscheiden, welches besser ist. Ich habe die Frage bearbeitet, was halten Sie von dieser Struktur?
MDijkstra
1

Wenn Ihre Ressourcen keine eigene Geschäftslogik haben oder spezielle Szenarien für die Interaktion mit der Umgebung, andere Ressourcen (außer dem von Ihnen abgedeckten Handel) oder der Player, können Sie mit Option zwei ganz einfach neue hinzufügen.

Wenn Sie sich überlegen müssen, was passieren würde, wenn Sie zwei Rohstoffe zum Herstellen mischen, wenn die Rohstoffe sehr unterschiedliche Eigenschaften haben können (ein Eimer Wasser unterscheidet sich stark von einem Klumpen Kohle) oder andere komplexe wirtschaftliche Wechselwirkungen, dann ist Ansatz 1 weitaus flexibler für den Motor, aber nicht die Daten.

Kristian Williams
quelle