Quand dois-je représenter des points et des tailles en tant que structures?

9

Dans le cadre de mon cadre de développement de jeu Ruby 2D simple, mes objets de jeu ont une position (valeurs x et y) et une taille (largeur et hauteur).

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

Une autre approche que j'ai vue consistait à traiter la position comme une Pointstructure et la taille comme une Sizestructure:

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

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

Certains frameworks utilisent le premier (je pense que GDX, Gosu ...). D'autres utilisent ce dernier (cocos2d-iphone). Le problème est qu'il n'est pas entièrement clair pour moi les avantages et les inconvénients des deux comportements (dans le développement de jeux) - je ne sais pas pourquoi certains frameworks ont choisi l'un et pas l'autre.

Y a-t-il des différences importantes à considérer?

Oxyde
la source

Réponses:

8

Certains utilisent même la Rectangleclasse:

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

C'est juste un choix de design, ça n'a pas vraiment d'importance. Si vous faites votre propre code, rendez ce que vous ressentez plus confortable. Si vous utilisez une API, un framework ou un moteur, ou éditez / modifiez un jeu, essayez d'être cohérent avec le reste du code et faites comme dans le code à proximité.

Je dirais d'aller avec deux vecteurs distincts, comme ceci:

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

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

De cette façon, vous pouvez gérer des choses comme l'angle entre les objets plus facilement, comme ceci:

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

au lieu d'avoir à extraire les vecteurs de rectangles ou à les créer à partir de flottants simples.

Gustavo Maciel
la source
2

En général, utilisez toujours une structure de données distincte. Il facilite considérablement l'utilisation, la lecture et la maintenance de votre code. À quelle fréquence avez-vous besoin de xséparer de yvs à quelle fréquence devez-vous calculer un décalage vectoriel, une longueur, un produit scalaire, etc.? Visez le cas commun; rendre le code que vous écrivez à plusieurs reprises plus facile à utiliser, qui pour les points et les vecteurs, sera généralement des opérations sur l'ensemble de "l'objet" plutôt que des opérations sur des composants individuels.

La seule exception que je ferais est qu'après un profilage correct , vous trouvez que la structure séparée est trop lente. Des langages comme Ruby ne rendent pas possible la "valeur par valeur" simple définie par l'utilisateur et, selon mon expérience, avoir des points et des vecteurs de type "par référence" est à la fois une douleur parfois et peut être un ralentissement massif sans une attention particulière aux temporaires . Il peut être avantageux, par exemple, d'avoir deux tableaux d'entiers, un pour xet un pour y, puis d'avoir un seul tableau d' Pointobjets; c'est beaucoup plus pénible de travailler avec, alors ne faites cette répartition que si vous avez des mesures de performances valides pour indiquer que cela en vaut la peine!

Sean Middleditch
la source
+1, mais je voudrais mentionner que la préoptimisation est la racine de tout mal.
Gustavo Maciel
3
@GustavoMaciel: en effet. Réalité: Cruella de Vil essayait juste d'optimiser sa garde-robe avant de nettoyer sa personnalité et de voir où cela l'avait amenée.
Sean Middleditch