Partager via


Structure de l’application Marble Maze

La structure d’une application DirectX plateforme Windows universelle (UWP) diffère de celle d’une application de bureau traditionnelle. Au lieu d’utiliser des types de handle tels que HWND et des fonctions telles que CreateWindow, Windows Runtime fournit des interfaces telles que Windows ::UI ::Core ::ICoreWindow afin de pouvoir développer des applications UWP de manière plus moderne et orientée objet. Cette section de la documentation montre comment le code de l’application Marble Maze est structuré.

Remarque

L’échantillon de code qui correspond à ce document se trouve dans l’échantillon de jeu Marble Maze DirectX.

Voici quelques-uns des points clés que ce document décrit lorsque vous structurez votre code de jeu :

  • Dans la phase d’initialisation, configurez les composants runtime et bibliothèque utilisés par votre jeu et chargez des ressources spécifiques au jeu.
  • Les applications UWP doivent commencer à traiter les événements dans les 5 secondes suivant le lancement. Par conséquent, chargez uniquement les ressources essentielles lorsque vous chargez votre application. Les jeux doivent charger de grandes ressources en arrière-plan et afficher un écran de progression.
  • Dans la boucle de jeu, répondez aux événements Windows, lisez les entrées utilisateur, mettez à jour les objets de scène et affichez la scène.
  • Utilisez des gestionnaires d’événements pour répondre aux événements de fenêtre. (Ces messages remplacent les messages de fenêtre à partir d’applications Windows de bureau.)
  • Utilisez une machine d’état pour contrôler le flux et l’ordre de la logique de jeu.

Organisation de fichiers

Certains des composants de Marble Maze peuvent être réutilisés avec n’importe quel jeu avec peu ou pas de modification. Pour votre propre jeu, vous pouvez adapter l’organisation et les idées que ces fichiers fournissent. Le tableau suivant décrit brièvement les fichiers de code source importants.

Fichiers Description
App.h, App.cpp Définit les classes App et DirectXApplicationSource , qui encapsulent la vue (fenêtre, thread et événements) de l’application.
Audio.h, Audio.cpp Définit la classe Audio , qui gère les ressources audio.
BasicLoader.h, BasicLoader.cpp Définit la classe BasicLoader , qui fournit des méthodes utilitaires qui vous aident à charger des textures, des maillages et des nuanceurs.
BasicMath.h Définit des structures et des fonctions qui vous aident à travailler avec des données et des calculs de vecteur et de matrice. La plupart de ces fonctions sont compatibles avec les types de nuanceurs HLSL.
BasicReaderWriter.h, BasicReaderWriter.cpp Définit la classe BasicReaderWriter , qui utilise Windows Runtime pour lire et écrire des données de fichier dans une application UWP.
BasicShapes.h, BasicShapes.cpp Définit la classe BasicShapes, qui fournit des méthodes utilitaires pour créer des formes de base telles que des cubes et des sphères. (Ces fichiers ne sont pas utilisés par l’implémentation Marble Maze).
Camera.h, Camera.cpp Définit la classe Camera , qui fournit la position et l’orientation d’une caméra.
Collision.h, Collision.cpp Gère les informations de collision entre la bille et d’autres objets, comme le labyrinthe.
DDSTextureLoader.h, DDSTextureLoader.cpp Définit la fonction CreateDDSTextureFromMemory , qui charge les textures au format .dds à partir d’une mémoire tampon mémoire.
DirectXHelper.h Définit les fonctions d’assistance DirectX qui sont utiles pour de nombreuses applications UWP DirectX.
LoadScreen.h, LoadScreen.cpp Définit la classe LoadScreen , qui affiche un écran de chargement pendant l’initialisation de l’application.
MarbleMazeMain.h, MarbleMazeMain.cpp Définit la classe MarbleMazeMain , qui gère les ressources spécifiques au jeu et définit une grande partie de la logique de jeu.
MediaStreamer.h, MediaStreamer.cpp Définit la classe MediaStreamer , qui utilise Media Foundation pour aider le jeu à gérer les ressources audio.
PersistentState.h, PersistentState.cpp Définit la classe PersistentState , qui lit et écrit des types de données primitifs depuis et dans un magasin de stockage.
Physique.h, Physics.cpp Définit la classe Physique , qui implémente la simulation physique entre le marbre et le labyrinthe.
Primitives.h Définit des types géométriques utilisés par le jeu.
SampleOverlay.h, SampleOverlay.cpp Définit la classe SampleOverlay , qui fournit des données et des opérations courantes d’interface utilisateur et 2D.
SDKMesh.h, SDKMesh.cpp Définit la classe SDKMesh , qui charge et affiche les maillages au format SDK Mesh (.sdkmesh).
StepTimer.h Définit la classe StepTimer , qui offre un moyen simple d’obtenir des temps totaux et écoulés.
UserInterface.h, UserInterface.cpp Définit les fonctionnalités associées à l’interface utilisateur, telles que le système de menus et la table de score élevé.

 

Formats de ressources au moment du design et au moment de l’exécution

Lorsque vous pouvez, utilisez des formats d’exécution plutôt que des formats au moment du design pour charger plus efficacement les ressources de jeu.

Un format au moment du design est le format que vous utilisez lorsque vous concevez votre ressource. En règle générale, les concepteurs 3D fonctionnent avec des formats au moment du design. Certains formats au moment du design sont également basés sur du texte afin de pouvoir les modifier dans n’importe quel éditeur de texte. Les formats au moment du design peuvent être détaillés et contenir plus d’informations que votre jeu n’en a besoin. Un format d’exécution est le format binaire lu par votre jeu. Les formats au moment de l’exécution sont généralement plus compacts et plus efficaces à charger que les formats au moment du design correspondants. C’est pourquoi la majorité des jeux utilisent des ressources au moment de l’exécution.

Bien que votre jeu puisse lire directement un format au moment du design, il existe plusieurs avantages à l’utilisation d’un format d’exécution distinct. Étant donné que les formats d’exécution sont souvent plus compacts, ils nécessitent moins d’espace disque et nécessitent moins de temps pour transférer sur un réseau. En outre, les formats d’exécution sont souvent représentés sous forme de structures de données mappées en mémoire. Par conséquent, ils peuvent être chargés en mémoire beaucoup plus rapidement que, par exemple, un fichier texte XML. Enfin, étant donné que les formats d’exécution distincts sont généralement codés en binaires, ils sont plus difficiles pour l’utilisateur final de modifier.

Les nuanceurs HLSL sont un exemple de ressources qui utilisent différents formats au moment du design et au moment de l’exécution. Marble Maze utilise .hlsl comme format au moment du design et .cso comme format d’exécution. Un fichier .hlsl contient le code source du nuanceur ; un fichier .cso contient le code d’octet du nuanceur correspondant. Lorsque vous convertissez des fichiers .hlsl hors connexion et fournissez des fichiers .cso à votre jeu, vous évitez de devoir convertir des fichiers sources HLSL en code octet lorsque votre jeu se charge.

Pour des raisons d’instruction, le projet Marble Maze inclut à la fois le format au moment du design et le format d’exécution pour de nombreuses ressources, mais vous devez uniquement conserver les formats au moment de la conception dans le projet source pour votre propre jeu, car vous pouvez les convertir en formats d’exécution lorsque vous en avez besoin. Cette documentation montre comment convertir les formats au moment du design en formats d’exécution.

Cycle de vie de l’application

Marble Maze suit le cycle de vie d’une application UWP classique. Pour plus d’informations sur le cycle de vie d’une application UWP, consultez cycle de vie de l’application.

Lorsqu’un jeu UWP initialise, il initialise généralement les composants d’exécution tels que Direct3D, Direct2D et toutes les bibliothèques d’entrée, audio ou physique qu’il utilise. Il charge également des ressources spécifiques au jeu qui sont requises avant le début du jeu. Cette initialisation se produit une fois pendant une session de jeu.

Après l’initialisation, les jeux exécutent généralement la boucle de jeu. Dans cette boucle, les jeux effectuent généralement quatre actions : traiter les événements Windows, collecter des entrées, mettre à jour des objets de scène et afficher la scène. Lorsque le jeu met à jour la scène, il peut appliquer l’état d’entrée actuel aux objets de scène et simuler des événements physiques, tels que des collisions d’objets. Le jeu peut également effectuer d’autres activités telles que la lecture d’effets sonores ou l’envoi de données sur le réseau. Lorsque le jeu affiche la scène, il capture l’état actuel de la scène et le dessine sur l’appareil d’affichage. Les sections suivantes décrivent ces activités plus en détail.

Ajout au modèle

Le modèle Application DirectX 11 (Windows universel) crée une fenêtre principale dans laquelle vous pouvez effectuer un rendu avec Direct3D. Le modèle inclut également la classe DeviceResources qui crée toutes les ressources d’appareil Direct3D nécessaires au rendu du contenu 3D dans une application UWP.

La classe App crée l’objet de classe MarbleMazeMain , démarre le chargement des ressources, des boucles pour mettre à jour le minuteur et appelle la méthode MarbleMazeMain ::Render chaque image. Les méthodes App ::OnWindowSizeChanged, App ::OnDpiChanged et App ::OnOrientationChanged appellent chacune la méthode MarbleMazeMain ::CreateWindowSizeDependentResources , et la méthode App ::Run appelle les méthodes MarbleMazeMain ::Update et MarbleMazeMain ::Render .

L’exemple suivant montre où la méthode App ::SetWindow crée l’objet de classe MarbleMazeMain . La classe DeviceResources est passée à la méthode afin qu’elle puisse utiliser les objets Direct3D pour le rendu.

    m_main = std::unique_ptr<MarbleMazeMain>(new MarbleMazeMain(m_deviceResources));

La classe App démarre également le chargement des ressources différées pour le jeu. Pour plus d’informations, consultez la section suivante.

En outre, la classe App configure les gestionnaires d’événements pour les événements CoreWindow . Lorsque les gestionnaires de ces événements sont appelés, ils passent l’entrée à la classe MarbleMazeMain .

Chargement des ressources de jeu en arrière-plan

Pour vous assurer que votre jeu peut répondre aux événements de fenêtre dans un délai de 5 secondes après son lancement, nous vous recommandons de charger vos ressources de jeu de manière asynchrone ou en arrière-plan. À mesure que des éléments multimédias se chargent en arrière-plan, votre jeu peut répondre aux événements de fenêtre.

Remarque

Vous pouvez également afficher le menu principal lorsqu’il est prêt et autoriser le chargement des ressources restantes en arrière-plan. Si l’utilisateur sélectionne une option dans le menu avant le chargement de toutes les ressources, vous pouvez indiquer que les ressources de scène continuent de se charger en affichant une barre de progression, par exemple.

 

Même si votre jeu contient relativement peu de ressources de jeu, il est recommandé de les charger de manière asynchrone pour deux raisons. Une des raisons est qu’il est difficile de garantir que toutes vos ressources se chargeront rapidement sur tous les appareils et toutes les configurations. En outre, en intégrant le chargement asynchrone tôt, votre code est prêt à être mis à l’échelle à mesure que vous ajoutez des fonctionnalités.

Le chargement asynchrone des ressources commence par la méthode App ::Load . Cette méthode utilise la classe de tâches pour charger des ressources de jeu en arrière-plan.

    task<void>([=]()
    {
        m_main->LoadDeferredResources(true, false);
    });

La classe MarbleMazeMain définit l’indicateur m_deferredResourcesReady pour indiquer que le chargement asynchrone est terminé. La méthode MarbleMazeMain ::LoadDeferredResources charge les ressources de jeu, puis définit cet indicateur. Les phases de mise à jour (MarbleMazeMain ::Update) et de rendu (MarbleMazeMain ::Render) de l’application vérifient cet indicateur. Lorsque cet indicateur est défini, le jeu se poursuit normalement. Si l’indicateur n’est pas encore défini, le jeu affiche l’écran de chargement.

Pour plus d’informations sur la programmation asynchrone pour les applications UWP, consultez Programmation asynchrone en C++.

Conseil

Si vous écrivez du code de jeu qui fait partie d’une bibliothèque C++ Windows Runtime (en d’autres termes, une DLL), déterminez s’il faut lire La création d’opérations asynchrones en C++ pour les applications UWP pour apprendre à créer des opérations asynchrones qui peuvent être consommées par des applications et d’autres bibliothèques.

 

Boucle de jeu

La méthode App ::Run exécute la boucle de jeu principale (MarbleMazeMain ::Update). Cette méthode est appelée chaque frame.

Pour aider à séparer l’affichage et le code de fenêtre du code spécifique au jeu, nous avons implémenté la méthode App ::Run pour transférer les appels de mise à jour et de rendu de l’objet MarbleMazeMain .

L’exemple suivant montre la méthode App ::Run , qui inclut la boucle de jeu principale. La boucle de jeu met à jour le temps total et les variables de temps d’intervalle, puis met à jour et affiche la scène. Cela permet également de s’assurer que le contenu est affiché uniquement lorsque la fenêtre est visible.

void App::Run()
{
    while (!m_windowClosed)
    {
        if (m_windowVisible)
        {
            CoreWindow::GetForCurrentThread()->Dispatcher->
                ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            m_main->Update();

            if (m_main->Render())
            {
                m_deviceResources->Present();
            }
        }
        else
        {
            CoreWindow::GetForCurrentThread()->Dispatcher->
                ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }

    // The app is exiting so do the same thing as if the app were being suspended.
    m_main->OnSuspending();

#ifdef _DEBUG
    // Dump debug info when exiting.
    DumpD3DDebug();
#endif //_DEGBUG
}

Ordinateur d’état

Les jeux contiennent généralement une machine d’état (également appelée machine à état fini, ou FSM) pour contrôler le flux et l’ordre de la logique de jeu. Un ordinateur d’état contient un nombre donné d’états et la possibilité de passer entre eux. Une machine d’état commence généralement à partir d’un état initial , passe à un ou plusieurs états intermédiaires , et se termine éventuellement à un état terminal .

Une boucle de jeu utilise souvent une machine d’état afin qu’elle puisse effectuer la logique spécifique à l’état actuel du jeu. Marble Maze définit l’énumération GameState , qui définit chaque état possible du jeu.

enum class GameState
{
    Initial,
    MainMenu,
    HighScoreDisplay,
    PreGameCountdown,
    InGameActive,
    InGamePaused,
    PostGameResults,
};

L’état MainMenu , par exemple, définit que le menu principal s’affiche et que le jeu n’est pas actif. À l’inverse, l’état InGameActive définit que le jeu est actif et que le menu n’apparaît pas. La classe MarbleMazeMain définit la variable membre m_gameState pour contenir l’état du jeu actif.

Les méthodes MarbleMazeMain ::Update et MarbleMazeMain ::Render utilisent des instructions switch pour effectuer une logique pour l’état actuel. L’exemple suivant montre à quoi ressemble une instruction switch pour la méthode MarbleMazeMain ::Update (les détails sont supprimés pour illustrer la structure).

switch (m_gameState)
{
case GameState::MainMenu:
    // Do something with the main menu. 
    break;

case GameState::HighScoreDisplay:
    // Do something with the high-score table. 
    break;

case GameState::PostGameResults:
    // Do something with the game results. 
    break;

case GameState::InGamePaused:
    // Handle the paused state. 
    break;
}

Lorsque la logique de jeu ou le rendu dépend d’un état de jeu spécifique, nous le mettons en évidence dans cette documentation.

Gestion des événements d’application et de fenêtre

Windows Runtime fournit un système de gestion des événements orienté objet afin que vous puissiez gérer plus facilement les messages Windows. Pour consommer un événement dans une application, vous devez fournir un gestionnaire d’événements ou une méthode de gestion des événements qui répond à l’événement. Vous devez également inscrire le gestionnaire d’événements auprès de la source d’événement. Ce processus est souvent appelé câblage d’événements.

Prise en charge de la suspension, de la reprise et du redémarrage

Marble Maze est suspendu lorsque l’utilisateur s’éloigne de celui-ci ou lorsque Windows entre dans un état d’alimentation faible. Le jeu est repris lorsque l’utilisateur le déplace au premier plan ou lorsque Windows sort d’un état d’alimentation faible. En règle générale, vous ne fermez pas les applications. Windows peut arrêter l’application lorsqu’elle est dans l’état suspendu et Windows nécessite les ressources, telles que la mémoire, que l’application utilise. Windows avertit une application lorsqu’elle est sur le point d’être suspendue ou reprise, mais elle n’avertit pas l’application lorsqu’elle est sur le point d’être arrêtée. Par conséquent, votre application doit être en mesure d’enregistrer , à ce stade, lorsque Windows informe votre application qu’elle est sur le point d’être suspendue, toutes les données qui seraient nécessaires pour restaurer l’état utilisateur actuel lorsque l’application est redémarrée. Si votre application a un état utilisateur important qui est coûteux à économiser, vous devrez peut-être également enregistrer régulièrement l’état, même avant que votre application ne reçoive la notification de suspension. Marble Maze répond à l’interruption et à la reprise des notifications pour deux raisons :

  1. Lorsque l’application est suspendue, le jeu enregistre l’état actuel du jeu et suspend la lecture audio. Lorsque l’application est reprise, le jeu reprend la lecture audio.
  2. Lorsque l’application est fermée et redémarrée ultérieurement, le jeu reprend à partir de son état précédent.

Marble Maze effectue les tâches suivantes pour prendre en charge la suspension et la reprise :

  • Il enregistre son état dans le stockage persistant aux points clés du jeu, par exemple lorsque l’utilisateur atteint un point de contrôle.
  • Elle répond à la suspension des notifications en enregistrant son état dans le stockage persistant.
  • Elle répond aux notifications de reprise en chargeant son état à partir du stockage persistant. Il charge également l’état précédent au démarrage.

Pour prendre en charge la suspension et la reprise, Marble Maze définit la classe PersistentState . (Voir PersistentState.h et PersistentState.cpp). Cette classe utilise l’interface Windows ::Foundation ::Collections ::IPropertySet pour lire et écrire des propriétés. La classe PersistentState fournit des méthodes qui lisent et écrivent des types de données primitifs (tels que bool, int, float, XMFLOAT3 et Platform ::String), depuis et vers un magasin de stockage.

ref class PersistentState
{
internal:
    void Initialize(
        _In_ Windows::Foundation::Collections::IPropertySet^ settingsValues,
        _In_ Platform::String^ key
        );

    void SaveBool(Platform::String^ key, bool value);
    void SaveInt32(Platform::String^ key, int value);
    void SaveSingle(Platform::String^ key, float value);
    void SaveXMFLOAT3(Platform::String^ key, DirectX::XMFLOAT3 value);
    void SaveString(Platform::String^ key, Platform::String^ string);

    bool LoadBool(Platform::String^ key, bool defaultValue);
    int  LoadInt32(Platform::String^ key, int defaultValue);
    float LoadSingle(Platform::String^ key, float defaultValue);

    DirectX::XMFLOAT3 LoadXMFLOAT3(
        Platform::String^ key, 
        DirectX::XMFLOAT3 defaultValue);

    Platform::String^ LoadString(
        Platform::String^ key, 
        Platform::String^ defaultValue);

private:
    Platform::String^ m_keyName;
    Windows::Foundation::Collections::IPropertySet^ m_settingsValues;
};

La classe MarbleMazeMain contient un objet PersistentState . Le constructeur MarbleMazeMain initialise cet objet et fournit le magasin de données d’application local comme magasin de données de stockage.

m_persistentState = ref new PersistentState();

m_persistentState->Initialize(
    Windows::Storage::ApplicationData::Current->LocalSettings->Values,
    "MarbleMaze");

Marble Maze enregistre son état lorsque la bille passe sur un point de contrôle ou l’objectif (dans la méthode MarbleMazeMain ::Update ) et lorsque la fenêtre perd le focus (dans la méthode MarbleMazeMain ::OnFocusChange ). Si votre jeu contient une grande quantité de données d’état, nous vous recommandons d’enregistrer occasionnellement l’état dans un stockage persistant de manière similaire, car vous n’avez que quelques secondes pour répondre à la notification de suspension. Par conséquent, lorsque votre application reçoit une notification de suspension, elle doit uniquement enregistrer les données d’état qui ont changé.

Pour répondre aux notifications de suspension et de reprise, la classe MarbleMazeMain définit les méthodes SaveState et LoadState appelées sur suspend et resume. La méthode MarbleMazeMain ::OnSuspending gère l’événement suspend et la méthode MarbleMazeMain ::OnResuming gère l’événement de reprise.

La méthode MarbleMazeMain ::OnSuspending enregistre l’état du jeu et suspend l’audio.

void MarbleMazeMain::OnSuspending()
{
    SaveState();
    m_audio.SuspendAudio();
}

La méthode MarbleMazeMain ::SaveState enregistre les valeurs d’état du jeu telles que la position actuelle et la vitesse du marbre, le point de contrôle le plus récent et la table de score élevé.

void MarbleMazeMain::SaveState()
{
    m_persistentState->SaveXMFLOAT3(":Position", m_physics.GetPosition());
    m_persistentState->SaveXMFLOAT3(":Velocity", m_physics.GetVelocity());

    m_persistentState->SaveSingle(
        ":ElapsedTime", 
        m_inGameStopwatchTimer.GetElapsedTime());

    m_persistentState->SaveInt32(":GameState", static_cast<int>(m_gameState));
    m_persistentState->SaveInt32(":Checkpoint", static_cast<int>(m_currentCheckpoint));

    int i = 0;
    HighScoreEntries entries = m_highScoreTable.GetEntries();
    const int bufferLength = 16;
    char16 str[bufferLength];

    m_persistentState->SaveInt32(":ScoreCount", static_cast<int>(entries.size()));

    for (auto iter = entries.begin(); iter != entries.end(); ++iter)
    {
        int len = swprintf_s(str, bufferLength, L"%d", i++);
        Platform::String^ string = ref new Platform::String(str, len);

        m_persistentState->SaveSingle(
            Platform::String::Concat(":ScoreTime", string), 
            iter->elapsedTime);

        m_persistentState->SaveString(
            Platform::String::Concat(":ScoreTag", string), 
            iter->tag);
    }
}

Lorsque le jeu reprend, il doit uniquement reprendre l’audio. Il n’est pas obligé de charger l’état à partir du stockage persistant, car l’état est déjà chargé en mémoire.

Comment le jeu suspend et reprend l’audio est expliqué dans le document Ajout d’audio à l’exemple Marble Maze.

Pour prendre en charge le redémarrage, le constructeur MarbleMazeMain , appelé au démarrage, appelle la méthode MarbleMazeMain ::LoadState . La méthode MarbleMazeMain ::LoadState lit et applique l’état aux objets de jeu. Cette méthode définit également l’état actuel du jeu à suspendre si le jeu a été suspendu ou actif lorsqu’il a été suspendu. Nous suspendons le jeu afin que l’utilisateur ne soit pas surpris par une activité inattendue. Il passe également au menu principal si le jeu n’était pas dans un état de jeu lorsqu’il a été suspendu.

void MarbleMazeMain::LoadState()
{
    XMFLOAT3 position = m_persistentState->LoadXMFLOAT3(
        ":Position", 
        m_physics.GetPosition());

    XMFLOAT3 velocity = m_persistentState->LoadXMFLOAT3(
        ":Velocity", 
        m_physics.GetVelocity());

    float elapsedTime = m_persistentState->LoadSingle(":ElapsedTime", 0.0f);

    int gameState = m_persistentState->LoadInt32(
        ":GameState", 
        static_cast<int>(m_gameState));

    int currentCheckpoint = m_persistentState->LoadInt32(
        ":Checkpoint", 
        static_cast<int>(m_currentCheckpoint));

    switch (static_cast<GameState>(gameState))
    {
    case GameState::Initial:
        break;

    case GameState::MainMenu:
    case GameState::HighScoreDisplay:
    case GameState::PreGameCountdown:
    case GameState::PostGameResults:
        SetGameState(GameState::MainMenu);
        break;

    case GameState::InGameActive:
    case GameState::InGamePaused:
        m_inGameStopwatchTimer.SetVisible(true);
        m_inGameStopwatchTimer.SetElapsedTime(elapsedTime);
        m_physics.SetPosition(position);
        m_physics.SetVelocity(velocity);
        m_currentCheckpoint = currentCheckpoint;
        SetGameState(GameState::InGamePaused);
        break;
    }

    int count = m_persistentState->LoadInt32(":ScoreCount", 0);

    const int bufferLength = 16;
    char16 str[bufferLength];

    for (int i = 0; i < count; i++)
    {
        HighScoreEntry entry;
        int len = swprintf_s(str, bufferLength, L"%d", i);
        Platform::String^ string = ref new Platform::String(str, len);

        entry.elapsedTime = m_persistentState->LoadSingle(
            Platform::String::Concat(":ScoreTime", string), 
            0.0f);

        entry.tag = m_persistentState->LoadString(
            Platform::String::Concat(":ScoreTag", string), 
            L"");

        m_highScoreTable.AddScoreToTable(entry);
    }
}

Important

Marble Maze ne fait pas la distinction entre le démarrage à froid, c’est-à-dire le démarrage pour la première fois sans événement de suspension préalable, et la reprise d’un état suspendu. Il est recommandé de concevoir toutes les applications UWP.

Pour plus d’informations sur les données d’application, consultez Store et récupérer des paramètres et d’autres données d’application.

Étapes suivantes

Lisez l’ajout de contenu visuel à l’exemple Marble Maze pour plus d’informations sur certaines des pratiques clés à garder à l’esprit lorsque vous travaillez avec des ressources visuelles.