Création d’un système de relecture dans Unity

Online Coding Courses for Kids

Une caméra vidéo capture un événement en direct

Un système de relecture est un excellent moyen de permettre aux joueurs de revivre leurs meilleurs ou pires moments de jeu, d’améliorer leur stratégie et plus encore!

Vous pouvez choisir d’en utiliser un dans l’Asset Store, mais je suppose que vous lisez ceci avec un bandana rouge et un couteau entre les dents, prêt à relever un défi. :]

Dans ce didacticiel, vous apprendrez:

  • Qu’est-ce qu’un système de relecture.
  • La différence entre les systèmes de relecture basés sur l’état et sur les entrées.
  • Comment mettre en œuvre un système de relecture basé sur l’état à partir de zéro.

Remarque: Il s’agit d’un didacticiel de niveau avancé, qui suppose que vous utilisez Unity depuis un certain temps. Vous devez également avoir une solide compréhension de C #. Bien que l’exemple de projet utilise Unity 2019.3, tout dans ce didacticiel doit fonctionner dans les versions antérieures.

Commencer

Téléchargez les fichiers du projet en cliquant sur le Télécharger les matériaux bouton en haut ou en bas de ce didacticiel.

Capture d'écran du projet de démonstration montrant le personnage principal, Birdy

Aller à RW / Scènes et ouvrez le Principale scène. Il est déjà configuré avec un sprite, un contrôleur de personnage, des boutons pour le système de relecture et de la musique 8 bits accrocheuse.

appuyez sur la Jouer bouton et essayez-le. Contrôle Birdy en utilisant les flèches gauche et droite et la barre d’espace pour sauter.

Clique le Commencer l’enregistrement / Arrête d’enregistrer et Lancer la relecture / Arrêter la relecture boutons. Les boutons sont mis à jour lorsque vous cliquez dessus, mais le système n’enregistre ni ne rejoue pour le moment. C’est là que vous entrerez en jeu.:]

Qu’est-ce qu’un système de relecture?

Un système de relecture est un système en jeu qui permet au joueur record une séquence de jeu et rejouer comme enregistrer une vidéo avec un téléphone et la lire à nouveau.

Pour les besoins de ce didacticiel:

  • Record signifie capturer une séquence d’images rendues par le jeu.
  • Rejouer signifie lire la séquence capturée, dans le même ordre, depuis le début.
  • Le système ne capturera qu’une séquence à la fois. Pressage Record écrasera à nouveau la séquence précédente.
  • Le système enregistrera la séquence dans la RAM. Ce didacticiel ne couvre pas l’enregistrement et le chargement d’une séquence à partir du disque.
  • L’entrée du lecteur sera toujours active pendant la lecture de la relecture. Ce ne sera de toute façon pas un gros problème car vous mettrez à jour les états à chaque image.

Choix de l’approche: basée sur l’état ou sur les entrées

Il existe deux types populaires de systèmes de relecture: Basé sur l’État et basé sur les entrées. Bien qu’ils fassent la même chose, chaque approche a ses avantages et ses inconvénients. Alors, quelle est la différence?

Qu’est-ce que la relecture basée sur l’état?

Lorsque vous enregistrez avec un système basé sur l’état, vous capturez une séquence de États. Un état est un instantané des propriétés d’une entité.

Par exemple, dans un jeu Unity, vous voudrez peut-être enregistrer et rejouer la position du personnage du joueur et de son impressionnante arme boomerang.

À l’image 1, vous enregistrez ces positions:

  • Personnage du joueur: (X: 0, Y: 1, Z: 0)
  • Boomerang: (X: 0, Y: 2, Z: 1)

Lorsque Unity traite l’image suivante du jeu, les deux objets se déplacent. Donc, pour l’image 2, vous stockez ces valeurs:

  • Personnage du joueur: (X: 0, Y: 1, Z: 1)
  • Boomerang: (X: 1, Y: 1, Z: 1)

Si le joueur est Objet 1 et le boomerang est Objet 2, la mémoire ressemblerait à ceci:

Schéma des emplacements de mémoire stockant la position du joueur et du boomerang GameObjects

Dans un système basé sur l’État, vous rejouer les états en lisant les cadres dans l’ordre et en appliquant les valeurs enregistrées à vos GameObjects.

Par conséquent, si vous relisez les données ci-dessus, à l’image 1, vous attribuez ces positions:

  • Personnage du joueur: (X: 0, Y: 1, Z: 0)
  • Boomerang: (X: 0, Y: 2, Z: 1)

À l’image deux, vous attribuez ces positions:

  • Personnage du joueur: (X: 0, Y: 1, Z: 1)
  • Boomerang: (X: 1, Y: 1, Z: 1)

Et ainsi de suite pour autant d’images que vous avez enregistrées.

Avantages et inconvénients de la relecture basée sur l’état

Les principaux avantages d’un système basé sur l’État sont:

  • Déterminisme: Il donnera toujours le même résultat, même si le moteur sous-jacent n’est pas déterministe.
  • Filtrage de relecture: Vous pouvez sélectionner avec une grande granularité ce que vous souhaitez enregistrer. Par exemple, vous pouvez enregistrer et rejouer uniquement la position du joueur, tandis que des éléments tels que les feuilles des arbres et les nuages ​​se déplacent toujours indépendamment.
  • Simplicité: C’est simple à mettre en œuvre.
  • Aucun contexte nécessaire: Il est facile de rejouer à tout moment. Vous n’avez pas besoin de configurer d’abord un contexte de scène.

La principale faiblesse est:

  • Empreinte mémoire: Si vous commencez à enregistrer un grand nombre d’états, par exemple beaucoup d’images, l’utilisation de la mémoire de la relecture montera en flèche.

Il est temps d’examiner l’approche basée sur les intrants pour comparer.

Qu’est-ce que la relecture basée sur les entrées?

Lorsque vous enregistrez avec un système basé sur les entrées, vous capturez l’état initial de vos objets, puis capturez une séquence de contributions. Les entrées peuvent être des gestes tactiles, des valeurs de joystick ou des pressions sur les touches, selon votre jeu.

Au lieu d’enregistrer l’état des GameObjects individuels à chaque image, vous n’enregistrez leur état qu’une seule fois, dans la toute première image du jeu. Ensuite, vous stockez les entrées susceptibles de modifier leurs états.

Par exemple, au début de votre jeu, vous stockez les positions de tous les GameObjects que vous incluez dans la rediffusion. Si votre joueur se déplace vers la droite puis s’accroupit dans la première image, vous enregistrez les informations comme suit:

Diagramme des emplacements de mémoire stockant l'entrée reçue dans chaque trame

  1. À l’image zéro, qui n’est pas affichée, vous enregistrez l’état de la scène entière.
  2. Vous enregistrez une entrée directionnelle vers la droite et une entrée directionnelle vers le bas à la première image.
  3. À l’image deux, vous enregistrez une entrée directionnelle vers le bas.
  4. Vous enregistrez une entrée directionnelle vers le bas à l’image trois.

Lorsque vous rejouez avec un système basé sur les entrées, vous devez d’abord remettre l’état entier à ce qu’il était au début de la lecture. Ensuite, dans chaque image, vous appliquez les entrées que vous avez enregistrées pour cette image.

Dans cet exemple, à l’image zéro, vous réinitialisez l’état de la scène. Ainsi, vous devez lire tous les états que vous avez enregistrés à l’image zéro et réécrire les informations dans vos GameObjects. À l’image un, vous simulez une entrée directionnelle vers la droite et vers le bas, à l’image deux une entrée directionnelle vers le bas et ainsi de suite pour toutes les images d’entrée que vous avez enregistrées.

Avantages et inconvénients de la relecture basée sur les entrées

Le principal avantage d’un système basé sur les entrées est:

  • Empreinte mémoire: Puisque vous n’enregistrez les entrées qu’après la première image, vous stockez beaucoup moins en mémoire qu’avec un système basé sur l’état.

La principale faiblesse est:

  • Repose sur le déterminisme: Pour utiliser cette approche, vous devez avoir un jeu ou un moteur de jeu déterministe. Cela signifie que lorsque vous rejouez les entrées que vous avez enregistrées, il doit donner exactement la même sortie à chaque fois.

Moteurs de jeu déterministes vs non déterministes

Pour décider du type de système de relecture à créer pour votre jeu, vous devez tenir compte du moteur de jeu avec lequel vous travaillez. Si vous travaillez avec un moteur de jeu non déterministe, un système basé sur les entrées ne produira pas de résultats cohérents.

Il y a de bonnes et de mauvaises nouvelles. Je vais commencer par les mauvaises nouvelles.

La physique dans Unity est non déterministe. Si vous stockez la même scène Unity trois fois, puis appliquez les mêmes entrées aux objets activés pour la physique, vos rediffusions auront un aspect différent. Cela peut être bien pire si vous avez plusieurs entrées d’affilée, car les écarts s’additionnent.

Si vous souhaitez utiliser un système basé sur les entrées pour un jeu basé sur la physique dans Unity, vous devez gérer vous-même la physique.

Quelle est la bonne nouvelle, demandez-vous? Vous n’avez pas besoin d’un diplôme en physique pour ce didacticiel! Par souci de simplicité, vous allez créer un système de relecture basé sur l’état.

Ouf, la théorie est à l’écart! Il est temps d’y arriver.

Configuration pour enregistrer

Jetez un œil au Principale scène à nouveau. Le panneau de l’interface utilisateur a un ReplayPanelController composant, qui écoute les événements déclenchés depuis ReplayManager.cs pour mettre à jour les états des deux boutons.

Les deux boutons déclenchent des événements dans le ReplayManager où, vous l’avez peut-être deviné, réside la logique de relecture.

création d'un gestionnaire de relecture

ReplayManager.cs est le seul fichier à éditer pour transformer Birdy en star de cinéma. Il contient la logique du flux de relecture et les principales méthodes dont le système a besoin.

Trouver les transformations

Tout d’abord, vous devez décider des états que vous allez enregistrer. Restez simple et enregistrez la position de tous les GameObjects dans le Principale scène.

Vous devez rechercher tous les composants de transformation lorsque le script s’exécute pour la première fois et les stocker dans un tableau.

Donc, déclarez un tableau en haut de ReplayManager.cs:

private Transform[] transforms;

Maintenant, ajoutez ceci à Start:

transforms = FindObjectsOfType();

Maintenant que vous avez les transformations, vous devez stocker les états de position lorsque le joueur se déplace.

Enregistrement d’images d’action

Comme mentionné ci-dessus, vous enregistrerez votre relecture uniquement dans la mémoire d’exécution, vous n’avez donc pas à vous soucier de la création de fichiers de sauvegarde. Cependant, vous devez toujours pouvoir écrire et lire des états dans la mémoire d’exécution. Et C # a juste la structure dont vous avez besoin!

Qu’est-ce qu’un MemoryStream?

Un petit oiseau m’a dit que vous ne pouvez pas avoir assez de longues discussions théoriques, alors il est temps d’en couvrir un peu plus. :]

Je dois faire entrer cette théorie

Vous avez probablement entendu une certaine forme de cette perle de sagesse en programmation: Choisir la bonne structure de données, c’est gagner la moitié de la bataille. Cela n’a jamais été aussi vrai qu’ici!

Vous devez enregistrer une position, trois valeurs flottantes, pour chaque GameObject chaque image, puis appliquer ces valeurs aux transformations d’origine lorsque vous rejouez. Existe-t-il une structure de données simple qui stocke des valeurs numériques ordonnées? Je me demande…

Un tableau, bien sûr! Un tableau fonctionnerait bien ici mais C # fournit une structure de données encore plus appropriée appelée MemoryStream.

UNE MemoryStream te laisse Écrire, ou enregistrez-y, Lis, ou charger, à partir de celui-ci, et Chercher à une position différente, ce qui facilite le passage au début. Sonne assez similaire aux commandes que vous attendez d’une rediffusion, n’est-ce pas?

Utilisez-le comme structure de données principale. Vous utiliserez un BinaryWriter pour écrire dans le MemoryStream et un BinaryReader pour lire à partir du flux.

Configuration d’un MemoryStream

Tout d’abord, ajoutez cette exigence en haut de ReplayManager.cs:

using System.IO;

Déclarez maintenant ces trois variables en haut de la classe:

private MemoryStream memoryStream = null;
private BinaryWriter binaryWriter = null;
private BinaryReader binaryReader = null;

Vous devez initialiser les trois, c’est-à-dire créer des instances vides, avant de les utiliser. Vous pouvez le faire en Start, mais ce serait un gaspillage si le joueur ne clique jamais réellement Commencer l’enregistrement. Au lieu de cela, configurez-les la première fois que le lecteur commence à enregistrer.

Déclarez une autre variable en haut de ReplayManager:

private bool recordingInitialized;

Maintenant, ajoutez la nouvelle méthode suivante avant StartRecording:

private void InitializeRecording()
{
    memoryStream = new MemoryStream();
    binaryWriter = new BinaryWriter(memoryStream);
    binaryReader = new BinaryReader(memoryStream);
    recordingInitialized = true;
}

Notez que vous passez un MemoryStream comme argument à BinaryWriter et BinaryReader. Vous faites cela parce que les assistants binaires ont besoin de savoir dans quoi vous écrivez ou lisez.

Appelez maintenant votre nouvelle méthode au début de StartRecording:

if (!recordingInitialized)
{
    InitializeRecording();
}

Ce code initialise le flux afin que vous puissiez lire et écrire des informations, mais il ne définit pas de position de flux spécifique pour l’écriture. Cela signifie que vous continuerez à écrire ou à lire à partir de la fin du flux pour toujours! Pas bon.

À partir du début

Chaque fois que le lecteur commence un nouvel enregistrement, trois choses doivent se produire:

  1. Vous devez réinitialiser la taille de memoryStream à zéro, pour effacer l’enregistrement précédent.
  2. La position dans memoryStream doit être réglé au début.
  3. La position dans binaryWriter doit être réglé au début.

Pour prendre soin du premier élément, ajoutez-le immédiatement après le if déclaration ci-dessus pour réinitialiser memoryStreamTaille de:

else
{
    memoryStream.SetLength(0);
}

Il ne vous reste plus qu’à réinitialiser la position. Trouver StopReplaying et ajoutez cette méthode ci-dessous:

private void ResetReplayFrame()
{
    memoryStream.Seek(0, SeekOrigin.Begin);
    binaryWriter.Seek(0, SeekOrigin.Begin);
}

Cela définit la position interne à zéro octet depuis le début, ce qui est exactement ce que vous voulez.

Enfin, appelez votre nouvelle méthode dans StartRecording, juste en dessous du code que vous avez ajouté ci-dessus et ci-dessus recording = true;.

ResetReplayFrame();

Ecrire des positions de transformation dans le flux

Maintenant, le plus amusant: enregistrer les positions de transformation.

Vous avez peut-être remarqué que ReplayManager a un FixedUpdate méthode mais non Update. FixedUpdate vous permet de contrôler la longueur d’une image d’enregistrement, de sorte que vous pouvez reproduire l’état du jeu de manière cohérente. Déterminisme, tu te souviens?

Chaque image, FixedUpdate appellera UpdateRecording si le jeu est en cours d’enregistrement, ou UpdateReplaying si le jeu lit l’enregistrement.

Tout ce que vous avez à faire est de parcourir la liste des composants Transform stockés et d’utiliser binaryWriter pour écrire les valeurs x, y et z pour la position de chacun.

Tout d’abord, ajoutez cette nouvelle méthode à la fin de ReplayManager:

private void SaveTransform(Transform transform)
{
    binaryWriter.Write(transform.localPosition.x);
    binaryWriter.Write(transform.localPosition.y);
    binaryWriter.Write(transform.localPosition.z);
}

Juste au-dessus, ajoutez cette méthode pour boucler sur les transformations et appeler SaveTransform pour chacun:

private void SaveTransforms(Transform[] transforms)
{
    foreach (Transform transform in transforms)
    {
        SaveTransform(transform);
    }
}

Enfin, ajoutez ceci à UpdateRecording:

SaveTransforms(transforms);

C’est tout ce dont vous avez besoin pour enregistrer les états de position de transformation! Prenez un moment pour exécuter la scène et assurez-vous que votre code ne génère aucune erreur.

créer une rediffusion encore un oiseau

Encore un oiseau qui saute, mais nous n’avons encore rien cassé!

Rejouer des cadres d’action

Votre jeu enregistre déjà tout correctement lorsque vous appuyez sur Démarrer l’enregistrement. Mais vous ne pouvez pas encore voir votre système fonctionner! Il est temps de rejouer.

À partir du début

Lorsque la lecture commence, vous devez réinitialiser la position à partir de laquelle vous lisez memoryStream. Sinon, la position interne sera toujours à la fin du flux, là où il n’y a rien à lire!

Vous avez déjà créé une méthode appelée ResetReplayFrame cela fait cela, il vous suffit donc de l’appeler. Ajoutez ceci au début de StartReplaying:

ResetReplayFrame();

Lecture des positions à partir du flux

Chaque image, vous bouclez sur toute la liste des composants de transformation. Pour chacun, vous regardez memoryStream et lisez les trois flotteurs représentant sa position. Ensuite, vous utilisez ces valeurs pour mettre à jour la transformation localPosition.

Ajoutez d’abord cette méthode à la fin de la classe:

private void LoadTransform(Transform transform)
{
    float x = binaryReader.ReadSingle();
    float y = binaryReader.ReadSingle();
    float z = binaryReader.ReadSingle();
    transform.localPosition = new Vector3(x, y, z);
}

Cette méthode définit la position d’une transformation. Tu appelles binaryReader.ReadSingle() trois fois pour lire les valeurs de x, y et z à partir de memoryStream. Ensuite, vous définissez la position locale de la transformation à l’aide de ces valeurs.

Vous devez maintenant effectuer une boucle sur les transformations et les charger dans le même ordre dans lequel elles ont été ajoutées au tableau. Ajoutez cette méthode ci-dessus LoadTransform:

private void LoadTransforms(Transform[] transforms)
{
    foreach (Transform transform in transforms)
    {
        LoadTransform(transform);
    }
}

Enfin, appelez votre logique de chargement dans UpdateReplaying:

LoadTransforms(transforms);

Ce code commence au début du flux mémoire et charge chaque image dans l’ordre, reproduisant exactement ce que le jeu faisait pendant l’enregistrement. Impressionnant!

Mais quand il atteint la fin du flux de mémoire, il continue, car vous ne lui avez pas dit de s’arrêter. Vous pourriez dire que c’est un peu… un cerveau d’oiseau. :]

Si vous jouiez une rediffusion maintenant, vous commenceriez à EndOfStreamException les erreurs:
création d'un replay bird-brained-memoryStream

Corrigez-le en ajoutant ce code au début de UpdateReplaying:

if (memoryStream.Position >= memoryStream.Length)
{
    StopReplaying();
    return;
}

Maintenant, exécutez la scène. Démarrez l’enregistrement et déplacez Birdy autour du niveau. Arrêtez l’enregistrement et cliquez sur Lancer la relecture pour voir tous les mouvements de Birdy jouer comme vous les avez enregistrés.

création d'un enregistrement de relecture et relecture

Donnez-vous un cinq!

Vous remarquerez peut-être un petit problème: le système n’enregistre pas la direction dans laquelle Birdy fait face! Il est temps de réparer ça.

Enregistrement de la direction

Il est assez simple d’ajouter d’autres éléments à enregistrer et à charger pour que votre relecture devienne plus précise. Gardez cependant à l’esprit que chaque nouvel élément que vous suivez signifie que la relecture utilisera plus de mémoire chaque image.

Si vous ouvrez CharacterController2D.cs, vous pouvez voir que la direction de Birdy est mise à jour en multipliant le localScale.x propriété de la transformation par 1, face à droite ou -1, face à gauche. Ainsi, pour enregistrer la direction dans laquelle Birdy fait face, vous devez enregistrer et charger la balance ainsi que la position.

Pour stocker l’échelle de chaque image, ajoutez ceci au bas de SaveTransform:

binaryWriter.Write(transform.localScale.x);
binaryWriter.Write(transform.localScale.y);
binaryWriter.Write(transform.localScale.z);

Puis ajoutez ceci à la fin de LoadTransform pour lire et appliquer l’échelle lorsque vous rejouez:

x = binaryReader.ReadSingle();
y = binaryReader.ReadSingle();
z = binaryReader.ReadSingle();
transform.localScale = new Vector3(x, y, z);

Remarque: L’ordre compte. Écrivez et lisez toujours les propriétés rejouables dans le même ordre, sinon le résultat sera mélangé lorsque vous rejouez. Dans ce cas, vous écrivez la position avant l’échelle, donc lorsque vous la reproduisez, vous lisez et chargez d’abord la position, puis lisez et chargez l’échelle.

Exécutez à nouveau la scène, puis enregistrez et rejouez vos mouvements. Birdy est une vraie star de cinéma!

créer-une-relecture-face-dans-la-bonne-voie

Optimiser l’utilisation de la mémoire

Vous avez maintenant tout ce dont vous avez besoin pour un système de relecture fonctionnel. Mais c’est un exemple volontairement petit. Si vous avez besoin de suivre même quelques propriétés supplémentaires dans un système basé sur l’état, vous devrez peut-être enregistrer et charger beaucoup plus d’informations.

Il est temps de jeter un œil à quelques optimisations simples.

Limiter la durée de la relecture

Même si vous n’avez pas l’intention d’optimiser en profondeur, limiter la relecture à un nombre spécifique d’images est facile et élimine le risque de manquer de mémoire si votre joueur quitte le jeu.

Une bonne valeur pour la longueur maximale dépend de votre jeu: cela peut prendre dix secondes ou deux minutes. Pour ce didacticiel, limitez les rediffusions à six secondes. En supposant que le jeu tourne à 60 images par seconde, vous devez enregistrer 60 images par seconde pendant six secondes, soit un total de 360 ​​images.

Ajoutez ces deux variables en haut de ReplayManager.cs:

private int currentRecordingFrames = 0;
public int maxRecordingFrames = 360;

Ajoutez ceci au bas de UpdateRecording pour suivre le nombre d’images que vous avez enregistrées:

++currentRecordingFrames;

Ensuite, allez en haut de la même méthode et ajoutez ceci:

if (currentRecordingFrames > maxRecordingFrames)
{
    StopRecording();
    currentRecordingFrames = 0;
    return;
}

Chaque fois que la méthode est appelée, elle vérifie si vous avez déjà enregistré trop d’images. Si tel est le cas, il arrête l’enregistrement et quitte. Sinon, il continue et enregistre l’image suivante.

Exécutez à nouveau la scène et cliquez sur Commencer l’enregistrement. Après quelques secondes, vous verrez que le texte du bouton se réinitialise automatiquement et Lancer la relecture devient actif, indiquant que l’enregistrement s’est arrêté. Cliquez sur Lancer la relecture et vous verrez que la rediffusion ne capture que les six premières secondes de jeu.

création d'une limite de temps de relecture

Défi: sauter des images

Une autre façon de réduire l’encombrement de la mémoire consiste à sauter certaines images lors de l’enregistrement.

Par exemple, sauter une image sur deux réduira l’empreinte de 50%, ce qui représente une économie considérable lorsque la relecture est longue. C’est un exercice d’équilibre: plus vous sautez d’images, moins vous avez de précision dans la relecture. Mais souvent sauter une ou même deux images ne fera pas beaucoup de différence du point de vue du joueur.

C’est l’heure du défi! Comment implémenteriez-vous le saut d’images? Cliquez sur Solution découvrir.

Astuce: vous devez sauter les images à la fois lors de l’enregistrement et de la relecture, sinon la relecture a l’air d’avoir une avance rapide!

[spoiler title=”Solution”]

Compter les images

Déclarez deux variables dans ReplayManager.cs:

public int replayFrameLength = 2;
private int replayFrameTimer = 0;

Tu commences replayFrameTimer quel que soit le nombre d’images que vous souhaitez compter. À mesure que chaque image Unity passe, vous en soustrayez une. Quand replayFrameTimer atteint zéro, vous enregistrez ou rejouez une image et démarrez replayFrametimer encore. Cela vous permet de sauter des images.

Tout d’abord, ajoutez ces méthodes ci-dessous StopReplaying:

private void ResetReplayFrameTimer()
{
    replayFrameTimer = replayFrameLength;
}

private void StartReplayFrameTimer()
{
    replayFrameTimer = 0;
}

Chaque fois que vous commencez à rejouer ou à enregistrer, vous appelez déjà ResetReplayFrame donc vous commencez au début du flux mémoire. Maintenant, vous devez également réinitialiser le minuteur d’image à zéro afin d’enregistrer ou de rejouer immédiatement la première image.

Utilisation du compteur

Regarder dans tous les deux StartReplaying et StartRecording et ajoutez ceci après l’appel à ResetReplayFrame:

StartReplayFrameTimer();

Ensuite, trouvez UpdateRecording et remplacez l’appel à SaveTransforms avec ça:

if (replayFrameTimer == 0)
{
    SaveTransforms(transforms);
    ResetReplayFrameTimer();
}
--replayFrameTimer;

Ce code vérifie si replayFrameTimer est prêt pour la prochaine sauvegarde, à zéro. Si tel est le cas, vous enregistrez les transformations et réglez la minuterie sur la longueur totale, deux.

Maintenant, vous ferez de même dans UpdateReplaying. Remplacez l’appel à LoadTransforms dans cette méthode avec ceci:

if (replayFrameTimer == 0)
{
    LoadTransforms(transforms);
    ResetReplayFrameTimer();
}
--replayFrameTimer;

Exécutez la scène, enregistrez un gameplay et rejouez-le. Notez que le résultat est toujours assez lisse. Mettre à jour le Replay Frame Length sur le Gestionnaire de relecture composant à cinq et réessayez. Beaucoup moins lisse!
[/spoiler]

Félicitations pour avoir terminé le didacticiel! Vous avez maintenant le pouvoir de contrôler le temps lui-même!

Où aller en partant d’ici?

Vous pouvez télécharger les fichiers de projet terminés en cliquant sur le Télécharger les matériaux bouton en haut ou en bas du didacticiel.

Dans ce didacticiel, vous avez appris à mettre en œuvre un système de relecture simple basé sur l’état avec quelques petites optimisations. Il y a de nombreux endroits intéressants à visiter à partir d’ici. Vous pouvez stocker vos informations de relecture dans un fichier, implémenter un système basé sur les entrées ou essayer d’intégrer d’autres composants comme le Animateur.

C’est à vous!

Voici quelques ressources qui peuvent vous aider sur votre chemin:

N’hésitez pas à commenter ci-dessous si vous avez des commentaires ou des questions! :]

Référence

Replay System avec Unity
Mettre en œuvre un système de relecture dans Unity et comment je le ferais différemment la prochaine fois
Développer votre propre système de relecture

Close Menu