Kiedy powinienem przedstawiać punkty i rozmiary jako struktury?

9

W ramach mojego prostego środowiska programowania gier Ruby 2D moje obiekty mają pozycję (wartości xiy) oraz rozmiar (szerokość i wysokość).

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

Innym podejściem, które widziałem, było traktowanie pozycji jako Pointstruktury, a wielkości jako Sizestruktury:

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

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

Niektóre frameworki używają tych pierwszych (myślę, że GDX, Gosu ...). Inni używają tego drugiego (cocos2d-iphone). Problem polega na tym, że nie jest dla mnie całkowicie jasne zalety i wady obu zachowań (w rozwoju gier) - nie wiem, dlaczego niektóre frameworki wybrały jedno, a nie drugie.

Czy są jakieś istotne różnice, które powinienem wziąć pod uwagę?

Tlenek
źródło

Odpowiedzi:

8

Niektórzy nawet korzystają z Rectangleklasy:

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

To tylko wybór projektu, to naprawdę nie ma znaczenia. Jeśli tworzysz własny kod, spraw, abyś czuł się bardziej komfortowo. Jeśli używasz interfejsu API, frameworku lub silnika albo edytujesz / modyfikujesz grę, postaraj się zachować spójność z resztą kodu i postępuj tak, jak w pobliskim kodzie.

Powiedziałbym, że używam dwóch oddzielnych wektorów, takich jak to:

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

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

W ten sposób możesz łatwiej obsługiwać takie rzeczy, jak kąt między obiektami, na przykład:

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

zamiast wyciągać wektory z prostokątów lub tworzyć je ze zwykłych pływaków.

Gustavo Maciel
źródło
2

Zasadniczo zawsze używaj oddzielnej struktury danych. Dzięki temu kod jest znacznie łatwiejszy w użyciu, czytaniu i utrzymaniu. Jak często potrzebujesz xoddzielić od yvs, jak często musisz obliczyć przesunięcie wektora, długość, iloczyn iloczynu itp.? Cel wspólnego przypadku; sprawiają, że kod, który piszesz wielokrotnie, jest łatwiejszy w obsłudze, co w przypadku punktów i wektorów zwykle będzie polegało na operacjach na całym „obiekcie”, a nie na poszczególnych komponentach.

Jedynym wyjątkiem, który zrobiłbym, jest to, że po prawidłowym profilowaniu osobna struktura jest zbyt wolna. Języki takie jak Ruby nie pozwalają na zdefiniowanie przez użytkownika prostej „wartości według” i, z mojego doświadczenia, posiadanie punktów i wektorów jako typów „referencyjnych” jest czasem bolesne i może być ogromnym spowolnieniem bez uważnej uwagi na tymczasowe . Korzystne może być na przykład posiadanie dwóch tablic liczb całkowitych, jednej dla xi jednej dla y, a następnie posiadania pojedynczej tablicy Pointobiektów; praca z tym jest jednak znacznie trudniejsza, więc dokonaj podziału tylko wtedy , gdy masz prawidłowe wskaźniki wydajności wskazujące, że warto!

Sean Middleditch
źródło
+1, ale chciałbym wspomnieć, że wstępna optymalizacja jest źródłem wszelkiego zła.
Gustavo Maciel
3
@GustavoMaciel: rzeczywiście. Fakt: Cruella de Vil po prostu próbowała zoptymalizować swoją garderobę, zanim oczyściła swoją osobowość i sprawdzić, gdzie ją to doprowadziło.
Sean Middleditch,