"Super Meat Boy" est un jeu de plateforme difficile récemment sorti sur PC, nécessitant un contrôle exceptionnel et un saut parfait au pixel près. Le code physique du jeu dépend du taux de rafraîchissement, qui est verrouillé à 60 images par seconde; cela signifie que si votre ordinateur ne peut pas exécuter le jeu à pleine vitesse, la physique deviendra folle, entraînant (entre autres) votre personnage à courir plus lentement et à tomber à travers le sol. De plus, si vsync est désactivé, le jeu tourne extrêmement vite.
Les personnes expérimentées dans la programmation de jeux 2D pourraient-elles expliquer pourquoi le jeu a été codé de cette façon? Une boucle physique fonctionnant à vitesse constante ne serait-elle pas une meilleure solution? (En fait, je pense qu'une boucle physique est utilisée pour certaines parties du jeu, car certaines des entités continuent de se déplacer normalement quel que soit le nombre d'images par seconde. Votre personnage, en revanche, fonctionne exactement [fps / 60] aussi vite.)
Ce qui me dérange dans cette implémentation, c'est la perte d'abstraction entre le moteur de jeu et le rendu graphique, qui dépend de choses spécifiques au système comme le moniteur, la carte graphique et le CPU. Si, pour une raison quelconque, votre ordinateur ne peut pas gérer vsync, ou ne peut pas exécuter le jeu à exactement 60 images par seconde, il se cassera de façon spectaculaire. Pourquoi l'étape de rendu devrait-elle influencer de quelque manière que ce soit les calculs physiques? (La plupart des jeux ralentissent le jeu ou sautent les images de nos jours.) D'un autre côté, je comprends que les plates-formes old-school sur la NES et la SNES dépendent d'un framerate fixe pour une grande partie de leur contrôle et de leur physique. Pourquoi est-ce, et serait-il possible de créer un patformer dans cette veine sans avoir la dépendance de framerate? Y a-t-il nécessairement une perte de précision si vous séparez le rendu graphique du reste du moteur?
Merci et désolé si la question était déroutante.
Réponses:
Quelques raisons, faites votre choix: ils ne savaient pas mieux. C'est plus rapide et plus facile à mettre en œuvre. Ils se concentraient davantage sur le gameplay et moins sur les cas marginaux qui pourraient ne pas apparaître dans la plupart des cas.
Vous avez très bien expliqué pourquoi. Je suis sûr que vous avez remarqué qu'il y a beaucoup de sujets qui couvrent le sujet . Je ne suis pas sûr que vous trouverez une réponse satisfaisante au-delà de celles que j'ai énumérées.
la source
SMB était à l'origine un jeu sur console où il est sûr de supposer qu'il peut fonctionner à 60 images par seconde sur toutes les Xbox360 (enfin, peut-être 50 pour certains joueurs PAL). En supposant un pas de temps fixe simplifie un peu le code.
Bien qu'il soit facile de mettre à l'échelle beaucoup de choses par un pas de temps variable - `` pos + = velocity * timestep '', il est assez difficile de le faire correctement lorsque vous avez affaire à des accélérations, à des taux de changement d'accélération, etc.
Le découplage du gameplay et du rendu est une bonne solution en théorie , mais sa bonne mise en œuvre (avec une bonne interpolation) est assez délicate, et les choses peuvent facilement devenir compliquées. Il est assez rare que cette technique soit utilisée dans de vrais jeux (bien que certains gros jeux le fassent, en particulier les jeux RTS, mais plus pour la synchronisation de jeux en réseau).
Lorsque vous concevez également pour une résolution d'écran fixe ainsi qu'une fréquence d'images fixe, il y a une autre chose que vous pouvez faire pour rendre le défilement encore plus fluide. Vous pouvez vous assurer que le jeu défile sur un nombre entier de pixels par image - en évitant toute oscillation sous-pixel que vous pourriez obtenir en faisant défiler un nombre fractionnaire de pixels par image.
la source
La solution évidente est d'avoir 2 boucles fonctionnant en parallèle - le rendu tous les 1/60 de seconde et la boucle de jeu tous les 1/60 de seconde.
Mais avec mon expérience dans Flash (AS3, dans lequel je suis presque sûr que Super Meat Boy a été créé), le planificateur n'est pas toujours très précis. La précision dépend également fortement de l'environnement. Dans le lecteur flash autonome, il peut avoir une résolution inférieure à la milliseconde. Mais lorsqu'il est exécuté dans certains navigateurs Web, sa précision devient celle de la fréquence d'images.
Ainsi, le moyen le plus proche de découpler les boucles de rendu et de logique de jeu est de faire en sorte que tous les mouvements soient basés sur le temps (et d'exécuter chaque image en fonction du temps écoulé depuis la dernière image). Cela peut introduire des mathématiques plus compliquées (comme appliquer la gravité en continu, plutôt que d'augmenter la vitesse d'un objet à des intervalles définis). Parce que le jeu peut prendre du retard pendant une seconde, puis que votre joueur se déplace de 200 pixels en une seule étape, la détection et la réponse aux collisions peuvent devenir encore plus compliquées. Si le programmeur effectuait une détection de collision basée sur les trames (recherche d'une collision à chaque pas de temps), il devrait également passer à la détection de collision basée sur le temps. Et s'ils voulaient que cela semble naturel, ils devraient utiliser la méthode de gravité décrite ci-dessus, qui fait que le mouvement de l'objet soit une courbe (par opposition à une ligne),
la source
Je ne pense pas que ce soit trop demander des jeux PC 2D pour jouer à 60 images par seconde. Même la plupart des jeux 2D sont accélérés par le matériel maintenant, donc personnellement, je ne me soucierais pas de l'exigence de fps.
La vraie question est pourquoi n'utilisez-vous pas un pixel, les jeux regorgent de tricheurs et de raccourcis.
Si vous faites un jeu basé sur la physique (lancer des oiseaux peut-être?), La réponse est évidente, mais un super mario clone? le mouvement basé sur le temps peut être un peu trop.
la source
Pour éviter un comportement étrange dans la physique 2D qu'ils utilisent?
Honnêtement, je peux juste deviner. Je vais essayer une explication:
Au cœur du jeu se trouve la boucle de jeu principale. Qui ressemble fondamentalement à ceci:
updateGame met à jour gameState: vérifie les entrées des joueurs, applique les entrées des joueurs au monde du jeu et exécute la simulation physique, etc.
renderGame dessine et anime le jeu.
Cela associe la mise à jour physique au rendu. Si vous souhaitez le découpler, vous devez utiliser des threads et synchroniser correctement chaque accès aux données du rendu et du thread gameUpdate avec les données partagées, par exemple la position du joueur. Ceci peut être fait.
Un autre problème pourrait être que la simulation physique nécessite un pas de temps constant pour fonctionner de manière stable. Cela dépend de la façon dont supermeatboy calcule le mouvement (Encore une fois, nous pouvons simplement deviner comment ils l'ont fait;)).
Une approche naïve serait (que j'utilise dans mon jeu * soupir *):
C'est ce qu'on appelle l'intégration d'Euler et est généralement considéré comme une mauvaise idée. Si le pas de temps n'est pas constant, des erreurs de calcul se produisent, ce qui rendra la simulation moins stable. L'objet peut se déplacer à des vitesses excessives ou pas du tout ou voler à travers les murs de l'écran. Même si le pas de temps est constant, l'intégration d'Euler provoque des erreurs de calcul mineures. Mieux vaut utiliser une autre méthode d'intégration comme RK4 ou utiliser un moteur physique.
En dehors de cela, il peut y avoir des problèmes lors de la détection de collision si le pas de temps devient trop grand. Comme les collisions ne sont pas vérifiées entre deux mises à jour de jeu, les objets peuvent passer à travers des obstacles.
la source