Conception d'un moteur flexible basé sur des tuiles

8

J'essaie de créer un moteur de jeu flexible basé sur des tuiles pour créer toutes sortes de jeux de puzzle non en temps réel, tout comme Bejeweled, Civilization, Sokoban, etc.

La première approche que j'ai eue était d'avoir un tableau 2D d'objets Tile, puis d'avoir des classes héritées de Tile qui représentaient les objets du jeu. Malheureusement, de cette façon, je ne pouvais pas empiler plus d'éléments de jeu sur la même tuile sans avoir un tableau 3D.

Ensuite, j'ai fait quelque chose de différent: j'avais toujours le tableau 2D d'objets Tile, mais chaque objet Tile contenait une liste où je mettais et différentes entités. Cela a bien fonctionné jusqu'à il y a 20 minutes, quand je me suis rendu compte que c'était trop cher de faire beaucoup de choses, regardez cet exemple:

J'ai une entité Wall. À chaque mise à jour, je dois vérifier les 8 tuiles adjacentes, puis vérifier toutes les entités dans la liste des tuiles, vérifier si l'une de ces entités est un mur, puis enfin dessiner le sprite correct. (Ceci est fait pour dessiner les murs les uns à côté des autres de façon transparente)

La seule solution que je vois maintenant est d'avoir un réseau 3D, avec de nombreuses couches, qui pourrait convenir à toutes les situations. Mais de cette façon, je ne peux pas empiler deux entités qui partagent la même couche sur la même tuile. Chaque fois que je veux le faire, je dois créer un nouveau calque.

Y a-t-il une meilleure solution? Qu'est-ce que tu ferais?

Vee
la source
Comment l'utilisation d'une matrice 3D résoudrait-elle votre problème avec cette situation de vérification murale. Ne serait-ce pas toujours pareil?
Michael Coleman
Je sais que les murs ne restent que sur le calque numéro 1. Je peux donc simplement faire: les carreaux [Wall.X - 1, Wall.Y, 1] sont Wall?
Vee
Que ne pouvez-vous pas simplement vérifier le premier élément de la liste? Je ne vois pas comment une liste pose un problème.
Michael Coleman
Le mur peut être n'importe où dans la liste avec la deuxième approche. Je dois parcourir chaque entité et vérifier s'il s'agit d'un mur.
Vee
4
Avez-vous réellement eu un problème de performances ou avez-vous simplement peur de le faire? L'avez-vous profilé?
munificent

Réponses:

2

Avez-vous réellement vu ce problème ou venez-vous de le penser? Parce que je ne vois pas comment l'itération sur la liste d'objets a un effet notable sur les performances. Il faudrait avoir des centaines d'objets par tuile pour que cela compte. La plupart des jeux auxquels je pense ont 10 tops.

Si vous avez vraiment un problème, la meilleure idée est de mettre en cache les données, pas de changer la représentation. Dans votre exemple, la configuration du mur ne change probablement pas chaque cadre. Il suffit de mettre en cache le type de sprite correct, ne le recalculez pas constamment.

Si vous voulez faire un jeu fou qui a beaucoup d'objets, et ils changent tout le temps, vous pouvez créer différentes couches avec une sémantique différente. Par exemple, la couche de mur ne contient que des murs, pas plus de 1 par tuile et, par exemple, la couche de décoration contient des listes d'objets qui ne changent jamais.

Enfin, si vous voulez avoir une architecture ultra-flexible qui supporte idéalement tous les jeux possibles - pas de chance, il n'y a rien de tel.

Ça ne fait rien
la source
2

Deux suggestions. Tout d'abord, vous devez résoudre le sprite avec lequel chaque tuile sera dessinée pendant le chargement de votre carte / niveau de tuile. Il devrait être assez facile de parcourir le tableau 2D, celui de votre niveau est chargé et de décider qu'un mur certian doit être une forme en L et un autre doit être un | forme.

Deuxièmement, je stockerais des données statiques liées à une tuile dans un endroit différent des données actives. Ainsi, un objet tuile peut avoir une liste d'objets qui vont et viennent à partir de la tuile mais il n'est pas nécessaire de la parcourir pour voir si, par exemple, nous voulons simplement savoir si la tuile est accessible à pied ou non.

Ce code psudo est plus ou moins la façon dont j'ai approché un système basé sur des tuiles.

Tile {
  Texture tex;
  //...other static data...
  List currentObjects;
}

Tile t = Tiles[x][y];

Cela suppose qu'il y a des tuiles statiques dans votre jeu, mais c'est une assez bonne hypothèse dans de nombreux jeux basés sur des tuiles où vous avez affaire à des murs et autres.

Nick Van Brunt
la source
1

Tu as dit:

Je devais avoir un tableau 2D d'objets Tile, puis avoir des classes héritant de Tile qui représentaient les objets du jeu. Malheureusement, de cette façon, je ne pouvais pas empiler plus d'éléments de jeu sur la même tuile sans avoir un tableau 3D.

Ensuite, j'ai fait quelque chose de différent: j'avais toujours le tableau 2D d'objets Tile, mais chaque objet Tile contenait une liste où je mettais et différentes entités. Cela a bien fonctionné jusqu'à il y a 20 minutes, quand j'ai réalisé qu'il était trop cher de faire beaucoup de choses

C'est exactement la situation que vous souhaitez résoudre en utilisant le modèle MVC (modèle de contrôleur de vue) pour séparer votre modèle de votre vue. MVC fait de ce problème deux parties:

  • Comment modéliser une pile de tuiles
  • Comment afficher une pile de tuiles

Au lieu d'avoir un tableau 2D de tuiles, ou un tableau 2D d'une liste de tuiles, votre modèle stockerait un tableau 2D d'objets de jeu. Ensuite, votre tableau 2D de classes de tuiles examinera les objets de jeu correspondants et décidera comment les rendre.

Au lieu d'avoir une seule instance de tuile rendre un seul objet de jeu, vous pouvez avoir une seule instance de tuile rendre quelque chose qui ressemble à une pile d'objets. Cela serait plus efficace et vous offrirait une séparation nette de votre logique de code de base et de son apparence.

Tout comme les murs. La logique est très banale, mais le rendu est plus complexe.

cendres999
la source
3
Je ne vois pas comment MVC (ce qui n'est pas courant dans les jeux d'après mon expérience) pourrait aider ici. Si quoi que ce soit, cela aggraverait: vous vous retrouveriez avec une redondance entre le modèle et la vue qui doit être synchronisée.
munificent
1
Oui, quand vous avez un problème, utilisez simplement <buzzword>. Vous avez maintenant deux problèmes.
Nevermind
1
np, j'ai débusqué plus de détails.
ashes999
OK, j'ai repris mon downvote. Stil, je ne pense pas que MVC soit utile ici.La plupart du temps, les objets diffèrent par la logique, pas (seulement) par la représentation graphique.
Nevermind
Je ne peux pas plaire à tout le monde, je suppose. Pour moi, MVC a immédiatement sauté dans le cadre de la solution à une partie du problème. Mais je conviens que je ne fournis pas une solution complète à tous les problèmes - cela nécessiterait un essai!
ashes999
0

Je suis d'accord avec @Omnion. Utilisez l'approche par liste, mais conservez-la si les performances sont un problème. C'est-à-dire utiliser une approche hybride. Utilisez la liste comme troisième dimension, mais identifiez uniquement les 5 premiers éléments en tant que type spécifique, et tout ce qui suit est un type d'encre propre ou un type qui ne nécessitera pas de vérifier plusieurs fois par image.

Nate
la source
0

Vous ne devez "jamais" créer des classes séparées pour des choses comme les tuiles. Vous devez créer une structure à partir d'octets, de courts métrages et d'ints qui font référence au type de tuile et d'objets qui s'y trouvent. Les entrées performantes sont les meilleures pour aller avec ou même longtemps sur les systèmes 64 bits. Mais si jamais vous souhaitez enregistrer votre carte, vous devez utiliser des octets ou des courts métrages partout où vous le pouvez, en particulier pour les cartes énormes.

Dans mes jeux, j'ai une classe de carte qui n'a que des champs comme: byte tileType; 256 types de terrain différents suffisent pour ce jeu. Prend 1 octet. ushort tileObject; 65 536 objets différents suffisent. Prend 2 octets, etc.

Si je prends l'exemple ci-dessus, chaque tuile ne prend que 3 octets en mémoire. Un 10000x10000 aurait donc 300 Mo de mémoire et ne devrait pas poser de problème. Chaque fois que vous avez besoin de chercher quelque chose, vous déterminez où sur la carte vous voulez regarder et ce que vous recherchez et itérez les tableaux en conséquence.

Si vous avez des éléments dynamiques sur toute la carte, demandez-vous si le joueur les remarque vraiment loin de sa fenêtre d'affichage.

Madmenyo
la source