Par exemple, le célèbre jeu Flappy Bird, ou quelque chose de ce genre, est le joueur (dans ce cas, l'oiseau, ou la caméra, selon votre préférence) qui avance ou le monde entier qui recule (l'oiseau ne change que la position Y et a une position X constante)?
game-mechanics
platformer
Lendrit Ibrahimi
la source
la source
Réponses:
Je suis légèrement en désaccord avec la réponse de Philipp ; ou du moins avec la façon dont il l'a présenté. Cela donne l'impression que déplacer le monde autour du joueur pourrait être une meilleure idée; quand c'est exactement le contraire. Alors voici ma propre réponse ...
Les deux options peuvent fonctionner, mais c’est généralement une mauvaise idée d’inverser la physique en déplaçant le monde autour du joueur plutôt que le monde entier.
Perte de performance / gaspillage:
Le monde aura généralement beaucoup d'objets; beaucoup sinon la plupart, statique ou en sommeil. Le joueur aura un ou relativement peu d'objets. Déplacer le monde entier autour du lecteur signifie déplacer tout ce qui se trouve dans la scène sauf le joueur. Objets statiques, objets dynamiques en sommeil, objets dynamiques actifs, sources de lumière, sources de son, etc. tous doivent être déplacés.
C'est (évidemment) considérablement plus coûteux que de ne déplacer que ce qui bouge réellement (le joueur et peut-être un peu plus de choses).
Maintenabilité et extensibilité:
En déplaçant le monde autour du joueur, le monde (et tout ce qu'il contient) devient le point où les choses se passent le plus activement. Tout bogue ou changement dans le système signifie que, potentiellement, tout change. Ce n'est pas une bonne façon de faire les choses. vous voulez que les bogues / modifications soient aussi isolés que possible, afin que vous n'ayez pas de comportement inattendu dans un endroit où vous n'avez pas apporté de modifications.
Il y a aussi beaucoup d'autres problèmes avec cette approche. Par exemple, il rompt de nombreuses hypothèses sur la manière dont les choses sont censées fonctionner dans le moteur. Vous ne pouvez pas utiliser dynamique
RigidBody
pour autre chose que le lecteur, par exemple; sous forme d'objet avec uneRigidBody
cinématique non attachée se comportera de manière inattendue lors du réglage de la position / rotation / échelle (comme vous le feriez pour chaque image, pour chaque objet de la scène, à l'exception du lecteur)Donc, la réponse est de ne déplacer que le joueur!
Eh bien ... oui et non . Comme mentionné dans la réponse de Philipp, dans un type de jeu à coureurs infinis (ou tout jeu comportant une grande zone explorable sans soudure), aller trop loin de l'origine introduirait à terme des FPPE ( erreurs de précision en virgule flottante) remarquables , et même plus tard, éventuellement, Débordez le type numérique, soit en provoquant le plantage de votre jeu, soit, en gros, faites en sorte que la fumée du jeu disparaisse ... Sur des stéroïdes! 😵 (car à ce stade, les FPPE rendraient le jeu déjà sur du crack "normal")
La solution actuelle :
Ne faites ni l'un ni l'autre et faites les deux! Vous devriez garder le monde statique et déplacer le joueur autour de lui. Mais « re-root » à la fois , le joueur et le monde, lorsque le joueur commence à devenir trop loin de la racine (position
[0, 0, 0]
) de la scène.Si vous conservez la position relative des éléments (lecteur par rapport au monde qui l'entoure) et effectuez ce processus en une seule mise à jour du cadre, le lecteur (réel) ne le remarquera même pas!
Pour ce faire, vous avez deux options principales:
Voici un exemple de ce processus en action
Mais jusqu'où c'est trop loin?
Si vous regardez le code source de Unity, ils utilisent
1e-5
(0.00001
) comme base pour considérer deux valeurs à virgule flottante "égales", à l'intérieur deVector2
etVector3
(les types de données responsables des positions des objets, des rotations et des échelles [euler-]). Étant donné que la perte de précision en virgule flottante se produit dans les deux sens loin de zéro, il est prudent de supposer que tout élément situé sous1e+5
(100000
) unités éloignées de la racine / origine de la scène peut être utilisé en toute sécurité.Mais! Puisque...
... alors c'est probablement une bonne idée de réintroduire les racines beaucoup plus tôt / plus souvent que la marque des 100 000 unités. L'exemple de vidéo que j'ai fourni semble le faire toutes les 1000 unités environ, par exemple.
la source
Les deux options fonctionnent.
Mais si vous voulez que le coureur sans fin soit vraiment sans fin, vous devrez garder le joueur immobile et bouger le monde. Sinon, vous atteindrez éventuellement les limites des variables que vous utilisez pour stocker la position X. Un entier finirait par déborder et une variable à virgule flottante deviendrait de moins en moins précise, ce qui rendrait le gameplay glitchy au bout d'un moment. Mais vous pouvez éviter ce problème en utilisant un type suffisamment grand pour que personne ne rencontre ces problèmes dans les délais que l'on pourrait éventuellement jouer en une session (lorsqu'un joueur déplace de 1000 pixels par seconde, un entier de 32 bits débordera au bout de 49 jours).
Alors, faites ce qui vous semble conceptuellement plus intuitif.
la source
En vous basant sur la réponse de XenoRo , au lieu de la méthode de ré-enracinement décrite, vous pouvez procéder comme suit:
Créez un tampon circulaire de parties de votre carte infinie générées, que votre personnage déplace avec la position mise à jour avec une arithmétique modulo (afin que vous ne fassiez que contourner le tampon circulaire). Commencez à remplacer des parties de votre tampon dès que votre personnage quitte le bloc. L'équation de mise à jour des joueurs serait quelque chose comme:
player.position = (player.position + player.velocity) % worldBuffer.width;
Voici un exemple pictural de ce dont je parle:
Voici un exemple de ce qui se passe sur l'emballage à la fin.
Avec cette méthode, vous ne rencontrez jamais d'erreurs de précision, mais vous devrez peut-être créer une très grande mémoire tampon si vous avez l'intention de le faire en 3D avec une distance de vision très éloignée (car vous devrez toujours être capable de voir devant vous. ). Si son oiseau flappy, la taille de votre bloc sera probablement aussi grande que nécessaire pour contenir une seule scène pour un seul écran, et votre mémoire tampon peut être très petite.
Notez que vous commencerez à obtenir des résultats répétitifs avec N'IMPORTE QUELLE méthode, et que le nombre maximal de séquences non répétitives d'une génération PRNG est généralement inférieur à la longueur de la puissance (2, nombre de bits utilisés en interne). Avec Merzenne Twister, ce n'est pas Un problème, car il utilise 2,5k d’état interne, mais si vous utilisez la variante minuscule, vous avez 2 ^ 127-1 max d’itérations avant répétition (ou pire), le nombre reste toutefois astronomiquement élevé . Vous pouvez résoudre les problèmes de période répétée même si votre PRNG a une courte période via de bonnes fonctions de mélange par avalanche pour la graine (à mesure que vous ajoutez implicitement un état supplémentaire).
la source
Comme demandé et accepté, cela dépend vraiment de l'étendue et du style de votre jeu, mais comme cela n'a pas été mentionné: FlappyBird déplace l'obstacle sur l'écran plutôt que sur le joueur à travers le monde.
Un géniteur instancie des objets hors de l'écran avec une vitesse fixe dans la
Vector2.left
direction.la source