Comment faire un jeu sans OOP? [fermé]

10

J'étudie actuellement le développement de jeux et je pratique la création de jeux.

J'utilise beaucoup de POO dans mes jeux. Par exemple, chaque missile qui est tiré est une instance d'un Missileobjet et ajouté à une liste d' Missileobjets. Chaque char du jeu est un Tankobjet. Etc.

Toute la conception du programme est basée sur cela. Par exemple, avoir une liste d' Missileobjets me permet à chaque image de déplacer les missiles, les dessiner, etc. Et avoir une instance d'un Tankobjet pour chaque char me permet de vérifier pour chaque char s'il entre en collision avec quelque chose, etc.

Il est difficile pour moi d'imaginer comment un jeu (qui est plus complexe que Pac-Man) pourrait être programmé dans un langage non OO. (Sans manquer de respect aux programmeurs non-OO bien sûr). Non seulement en termes de temps, mais surtout en termes de conception d'un jeu de cette manière.

Je ne peux pas imaginer concevoir un jeu sans utiliser la programmation orientée objet, car toute ma compréhension de la façon de concevoir un programme de jeu est basée sur la POO.

Je voudrais demander: Aujourd'hui, y a-t-il des jeux qui ne sont pas programmés en utilisant la POO, d'une manière similaire à ce que j'ai décrit ci-dessus? Existe-t-il des jeux «professionnels» qui n'utilisent pas la POO comme facteur majeur dans le processus de développement?

Si oui, pourriez-vous me donner une idée de la façon, par exemple, dont la détection de collision entre un char et un nombre N de missiles pourrait être mise en œuvre, sans POO?

user3150201
la source
6
Est-ce une question philosophique? Même si vous n'appelez pas vos tanks "objets", vous voudrez probablement "entités", "acteurs", "agents", "structs" ou juste un autre nom pour la même idée, qui est une collection de les attributs et les comportements qui composent une chose cuboïde en rotation avec une tourelle qui peut tirer des choses, appelé un char. Les langages de programmation auront différentes façons de formaliser cette même idée, mais au final, ce sera un tank.
Anko
De nombreux jeux utilisent un système basé sur les composants comme cette réponse le décrit: gamedev.stackexchange.com/a/31491/9366
John McDonald
C'est à la fois extrêmement large (que vous pourriez éventuellement corriger en rétrécissant la portée) et pas vraiment spécifique au développement de jeux (car la création de logiciels sans techniques OO n'est pas quelque chose qu'un développeur de jeux vous donnerait une meilleure réponse que tout autre développeur de logiciels), ce qui en fait hors sujet ici, je le crains.
Il peut convenir à StackOverflow, ou vous pouvez consulter le centre d'aide pour trouver une sélection de sites spécifiques au développement de jeux (comme GDNet) qui permettraient ce type de sujet large et axé sur la discussion. Bonne chance!

Réponses:

16

Je ne peux pas imaginer concevoir un jeu sans utiliser la programmation orientée objet, car toute ma compréhension de la façon de concevoir un programme de jeu est basée sur la POO.

Ensuite, il sera probablement bon pour vous d'essayer d'écrire certains programmes dans un style non OO. Même si vous découvrez que ce n'est pas pragmatique pour vous, vous en apprendrez probablement beaucoup en cours de route qui vous aideront à l'avenir.

Le style OO est assez bien adapté aux jeux car les jeux concernent presque toujours la manipulation d'objets avec état. Un faisceau laser frappe le robot et l'état du robot change tandis que son identité reste la même.

Cependant, il est possible de programmer des jeux dans un style fonctionnel . Dans un style fonctionnel, l'état ne change pas en soi. Les objets sont immuables. Plutôt que de changer d'objets, vous posez la question de savoir comment l'univers serait différent si je changeais cela? puis produire un tout nouvel univers qui a la propriété modifiée. Bien sûr, vous pouvez réutiliser une grande partie de l'univers déjà existant car il est immuable .

Dans la programmation fonctionnelle, chaque fonction doit calculer sa valeur de retour uniquement à partir des informations transmises; il n'y a pas de lecture de "l'état global".

Si vous faites cela, le problème fondamental que vous devrez surmonter est que chaque mise à jour est non destructive . Lorsque le laser frappe le robot, vous ne changez pas l'état du robot. En fin de compte, vous calculez un univers entièrement nouveau identique à l'ancien univers, sauf que le robot a un état différent; si vous avez besoin de cet ancien univers, il est toujours là, inchangé.

Cette série d'articles de blog a plus de réflexions sur l'écriture de jeux dans un style fonctionnel:

http://prog21.dadgum.com/23.html

Cet article répond spécifiquement à votre question "obus frappe un char":

http://prog21.dadgum.com/189.html

En fait, lisez tout le blog. Il y a de bonnes choses là-bas et les articles sont courts.

Eric Lippert
la source
12

Tout programme orienté objet peut être refactorisé en un programme procédural en remplaçant toutes les classes par des structures et en convertissant toutes les fonctions membres en fonction autonome qui prennent l'objet qui serait thiscomme argument.

Alors

 missile.setVelocity(100);

devient

 setMissileVelocity(missile, 100);

ou quand cette fonction est triviale, vous faites juste

 missile.velocity = 100;

La principale différence entre la programmation orientée objet et la programmation procédurale est la façon dont vous traitez vos données. Dans la POO, les données sont intelligentes . Il se gère et se manipule. Mais dans la programmation procédurale, les données sont stupides . Il ne fait rien par lui-même et doit être manipulé de l'extérieur.

Lorsque vous considérez même des structures trop orientées objet, vous pouvez remplacer un tableau de structures par plusieurs tableaux, un pour tout ce qui serait une variable d'un missile. Alors

struct Missile {
     int x;
     int y;
     int velocity;
}

Missile missiles[256];

devient

int missileX[256];
int missileY[256];
int missileVelocities[256];

Dans cette conception, une fonction qui effectue une opération impliquant plusieurs attributs sur le même missile prendrait désormais un index de tableau au lieu d'une référence à une structure. Son implémentation ressemblerait à ceci:

function updateMissilePosition(int index) {
     missileX[index] += missileVelocity[index];
}
Philipp
la source
1
Mais missileest une instance d'un objet. En non-POO, il n'y a pas d'exemples, ai-je raison? Si oui, comment pourriez-vous faire setMissileVelocity (missile, 100)?
user3150201
1
@ user3150201 Vous n'êtes pas tout à fait correct. La plupart des langages non-OOP (et je dirais à peu près tous ceux qui conviennent au développement de jeux sérieux) prennent en charge les structures. Une structure est un peu comme une classe, juste qu'elle ne contient que des variables publiques. Donc , il vous permettra de créer un type Missile, qui est une structure à plusieurs domaines, comme x, y, angleet velocity.
Philipp
@ user3150201 Réponse mise à jour avec une section sur la façon de le faire sans structures.
Philipp
Belle réponse Philipp, bien que je ne comprenne pas pourquoi on ne voudrait pas programmer orienté objet. Il est vraiment difficile de lire des langues non-OOP et peut être frustrant. Le code peut devenir un gâchis en un rien de temps.
Zhafur
2
@Zhafur Vous savez que votre déclaration est un énorme appât de flamme, n'est-ce pas?
Philipp
6

Je le fais comme suit:

  • Toutes les classes / méthodes OOP ont accès à this. Afin d'utiliser thisdans une approche non-OO, passez simplement dans n'importe quelle instance (voir le point suivant) thisdevrait être, comme premier paramètre.
  • Maintenant, comme pour les instances, vous pouvez passer des structs dans vos fonctions en tant que this, mais je trouve que la meilleure façon d'obtenir de bonnes performances de cache pour des objets qui sont prolifiques, tels que des entités ou des particules, est de simplement passer un seul index dans plusieurs tableaux de primitives ou petit structs. Cet index est donc utilisé pour chaque membre de données individuel de la classe d'origine. Ainsi, par exemple, si vous aviez

...

class Entity //let's say you had 100 instances of this
{
   int a;
   char b;
   function foo() 
   {
      .../*can access 'this' herein*/
   }
}

Vous remplaceriez cela par

int a[100];
char b[100];
function foo(int index);

Alors que vous passez maintenant un index dans la fonction pour obtenir ce qui serait habituellement this.

Gardez à l'esprit que vous souhaiterez peut-être utiliser soit des tableaux de primitives comme ci-dessus, soit des tableaux de structs, selon la meilleure façon d'entrelacer vos données pour une bonne localité de cache (localité de référence). Bien sûr, des performances de cache décentes reposent sur bien plus que cela - en particulier sur la langue / la plate-forme sur laquelle vous écrivez votre code - mais même dans les langages basés sur des machines virtuelles et alloués dynamiquement comme Java, les grands tableaux linéaires de primitives ont tendance à affichent de meilleures caractéristiques de performances que les instances d'objet. La raison principale en est que les objets sont accessibles par référence et cela signifie que vous sautez dans toute la mémoire pour accéder aux données - inefficace par rapport à l'accès primitif à partir d'un grand tableau.

Pour plus d'informations sur la création d'entités, etc. en tant que tableau de structures ou de primitives, voir Evolve your Hierarchy de Mick West .

Ingénieur
la source
0

En plus des réponses existantes, vous voudrez peut-être savoir comment faire du polymorphisme dans un langage procédural.

Il existe deux approches:

Mémorisation du type

Dans ce cas, la structure possède un champ pour l'identificateur de type, probablement une énumération, qui est vérifié à l'aide d'une switchinstruction lorsque l'action spécifique au type doit être effectuée.

L'autre façon est:

Stockage des pointeurs de fonction

Vous n'avez pas mentionné dans quel langage de programmation vous avez de l'expérience, mais dans divers langages, ils sont également appelés rappels, délégués, événements ou fonctions de haut niveau ou simplement des objets de fonction.

Dans ce cas, vous ne stockerez pas de type, mais stockerez un pointeur / référence vers une fonction qui effectue l'action particulière. Lorsqu'une action spécifique au type doit être effectuée, vous appelez simplement cette fonction. Celui-ci ressemble beaucoup aux méthodes virtuelles ordinaires.

En permettant de définir chaque fonction indépendamment, vous obtenez gratuitement le modèle de stratégie .


Concernant votre dernier paragraphe avec la détection de collision. Je pense que vous avez probablement plusieurs types de chars et vous avez plusieurs types de missiles, et chaque combinaison peut potentiellement avoir un résultat différent quand ils entrent en collision. Si c'est ce que vous recherchez, vous avez un problème qui n'est pas encore résolu par même les langages OOP: plusieurs répartitions et plusieurs méthodes qui peuvent avoir plusieurs thisparamètres de différents types. Pour ce problème, il existe encore deux alternatives:

  • Interrupteurs imbriqués pour chaque combinaison de char et de missile.
  • Tableaux de répartition bidimensionnels, qui contiennent des pointeurs vers des fonctions pour chaque combinaison de char et de missile.
Calmarius
la source