Wann sollte ich Punkte und Größen als Strukturen darstellen?

9

Als Teil meines einfachen Ruby 2D-Spieleentwicklungsframeworks haben meine Spielobjekte Position (x- und y-Werte) und Größe (Breite und Höhe).

class MyGameObject
  attr_accessor :x
  attr_accessor :y
  attr_accessor :width
  attr_accessor :height
  ...

Ein anderer Ansatz, den ich gesehen habe, war, die Position als PointStruktur und die Größe als SizeStruktur zu behandeln:

Point = Struct.new(:x, :y)
Size = Struct.new(:width,:height)

class MyGameObject
  attr_accessor :position   # Point instance
  attr_accessor :size       # Size instance
  ...

Einige Frameworks verwenden das erstere (ich denke GDX, Gosu ...). Andere verwenden letzteres (cocos2d-iphone). Das Problem ist, dass mir die Vor- und Nachteile beider Verhaltensweisen (in der Spieleentwicklung) nicht ganz klar sind - ich weiß nicht, warum einige Frameworks das eine und nicht das andere gewählt haben.

Gibt es signifikante Unterschiede, die ich berücksichtigen sollte?

Oxid
quelle

Antworten:

8

Einige benutzen sogar die RectangleKlasse:

class Rectangle
{
    float x, y, w, h;
}
class GameObject
{
    Rectangle dimensions;
}

Es ist nur eine Wahl des Designs, es spielt keine Rolle. Wenn Sie Ihren eigenen Code erstellen, machen Sie das, was Sie sich wohler fühlen. Wenn Sie eine API, ein Framework oder eine Engine verwenden oder ein Spiel bearbeiten / modifizieren, versuchen Sie, mit dem Rest des Codes konsistent zu sein, und tun Sie genau so, wie es im Code in der Nähe ist.

Ich würde sagen, mit zwei getrennten Vektoren zu gehen, wie folgt:

class Vector2
{
    float x, y;
    //helper functions like operator overload, dot, length, distance, etc.
}

class GameObject
{
    Vector2 position;
    Vector2 size;
    Vector2 direction;
}

Auf diese Weise können Sie beispielsweise den Winkel zwischen Objekten einfacher handhaben:

GameObject foe;
GameObject player;
Vector2 dist = player.position - foe.position;
dist.normalize();
float angleBetween = acos(dist.dot(foe.direction));

anstatt die Vektoren aus Rechtecken zu extrahieren oder sie aus einfachen Floats zu erstellen.

Gustavo Maciel
quelle
2

Verwenden Sie im Allgemeinen immer eine separate Datenstruktur. Dies erleichtert die Verwendung, das Lesen und die Wartung Ihres Codes erheblich. Wie oft müssen Sie xgetrennt von yund wie oft müssen Sie einen Vektorversatz, eine Länge, ein Punktprodukt usw. berechnen? Ziel für den allgemeinen Fall; Erleichtern Sie die Arbeit mit dem Code, den Sie wiederholt schreiben. Bei Punkten und Vektoren handelt es sich in der Regel um Operationen für das gesamte "Objekt" und nicht um Operationen für einzelne Komponenten.

Die einzige Ausnahme, die ich machen würde, ist, dass Sie nach ordnungsgemäßer Profilerstellung feststellen, dass die separate Struktur zu langsam ist. Sprachen wie Ruby ermöglichen keine einfachen benutzerdefinierten "By-Value" -Typen, und meiner Erfahrung nach ist es manchmal schmerzhaft, Punkte und Vektoren als "By-Reference" -Typen zu haben, und kann eine massive Verlangsamung ohne sorgfältige Beachtung der Zeiträume darstellen . Es kann zum Beispiel vorteilhaft sein, zwei Arrays von Ints zu haben, eines für xund eines für y, und dann ein einzelnes Array von PointObjekten zu haben; Es ist jedoch viel schwieriger, damit zu arbeiten. Führen Sie diese Aufteilung nur durch, wenn Sie über gültige Leistungsmetriken verfügen, die darauf hinweisen, dass es sich lohnt!

Sean Middleditch
quelle
+1, aber ich möchte erwähnen, dass die Voroptimierung die Wurzel allen Übels ist.
Gustavo Maciel
3
@GustavoMaciel: in der Tat. Fakt: Cruella de Vil versuchte nur, ihre Garderobe zu optimieren, bevor sie ihre Persönlichkeit aufräumte, und schaute, wohin das sie führte.
Sean Middleditch