Marble Maze application structure

1 out of 2 rated this helpful - Rate this topic

The structure of a DirectX Windows Store app differs from that of a traditional desktop application. Instead of working with handle types such as HWND and functions such as CreateWindow, the Windows Runtime provides interfaces such as Windows::UI::Core::ICoreWindow so that you can develop Windows Store apps in a more modern, object-oriented manner. This section of the documentation shows how the Marble Maze application code is structured.

Note Note

The sample code that corresponds to this document is found in the DirectX marble maze game sample.

Here are some of the key points that this document discusses for when you structure your game code:

  • In the initialization phase, set up runtime and library components that your game uses. Also load game-specific resources.

  • Windows Store apps must start processing events within 5 seconds of launch. Therefore, load only essential resources when you load your app. Games should load large resources in the background and display a progress screen.

  • In the game loop, respond to Windows events, read user input, update scene objects, and render the scene.

  • Use event handlers to respond to window events. (These replace the window messages from desktop Windows applications.)

  • Use a state machine to control the flow and order of the game logic.

The following sections describe these points in greater detail.

Some of the components in Marble Maze can be reused with any game with little or no modification. For your own game, you can adapt the organization and ideas that these files provide.

The following table briefly describes the important source code files.

Audio.h, Audio.cpp

Defines the Audio class, which manages audio resources.

BasicLoader.h, BasicLoader.cpp

Defines the BasicLoader class, which provides utility methods that help you load textures, meshes, and shaders.

BasicMath.h

Defines structures and functions that help you work with vector and matrix data and computations. Many of these functions are compatible with HLSL shader types.

BasicReaderWriter.h, BasicReaderWriter.cpp

Defines the BasicReaderWriter class, which uses the Windows Runtime to read and write file data in a Windows Store app.

BasicShapes.h, BasicShapes.cpp

Defines the BasicShapes class, which provides utility methods for creating basic shapes such as cubes and spheres. (These files are not used by the Marble Maze implementation).

BasicTimer.h, BasicTimer.cpp

Defines the BasicTimer class, which provides an easy way to get total and elapsed times.

Camera.h, Camera.cpp

Defines the Camera class, which provides the position and orientation of a camera.

Collision.h, Collision.cpp

Manages collision info between the marble and other objects, such as the maze.

DDSTextureLoader.h, DDSTextureLoader.cpp

Defines the CreateDDSTextureFromMemory function, which loads textures that are in .dds format from a memory buffer.

DirectXApp.h, DirectXApp.cpp

Defines the DirectXApp and DirectXAppSource classes, which encapsulate the view (window, thread, and events) of the app.

DirectXBase.h, DirectXBase.cpp

Defines the DirectXBase class, which provides infrastructure that is common to many DirectX Windows Store apps.

DirectXSample.h

Defines utility functions that can be used by DirectX Windows Store apps.

LoadScreen.h, LoadScreen.cpp

Defines the LoadScreen class, which displays a loading screen during app initialization.

MarbleMaze.h, MarbleMaze.cpp

Defines the MarbleMaze class, which manages game-specific resources and defines much of the game logic.

MediaStreamer.h, MediaStreamer.cpp

Defines the MediaStreamer class, which uses Media Foundation to help the game manage audio resources.

PersistentState.h, PersistentState.cpp

Defines the PersistentState class, which reads and writes primitive data types from and to a backing store.

Physics.h, Physics.cpp

Defines the Physics class, which implements the physics simulation between the marble and the maze.

Primitives.h

Defines geometric types that are used by the game.

SampleOverlay.h, SampleOverlay.cpp

Defines the SampleOverlay class, which provides common 2-D and user-interface data and operations.

SDKMesh.h, SDKMesh.cpp

Defines the SDKMesh class, which loads and renders meshes that are in SDK Mesh (.sdkmesh) format.

UserInterface.h, UserInterface.cpp

Defines functionality that's related to the user interface, such as the menu system and the high score table.

When you can, use run-time formats instead of design-time formats to more efficiently load game resources.

A design-time format is the format you use when you design your resource. Typically, 3-D designers work with design-time formats. Some design-time formats are also text-based so that you can modify them in any text-based editor. Design-time formats can be verbose and contain more information than your game requires. A run-time format is the binary format that is read by your game. Run-time formats are typically more compact and more efficient to load than the corresponding design-time formats. This is why the majority of games use run-time assets at run time.

Although your game can directly read a design-time format, there are several benefits to using a separate run-time format. Because run-time formats are often more compact, they require less disk space and require less time to transfer over a network. Also, run-time formats are often represented as memory-mapped data structures. Therefore, they can be loaded into memory much faster than, for example, an XML-based text file. Finally, because separate run-time formats are typically binary-encoded, they are more difficult for the end-user to modify.

HLSL shaders are one example of resources that use different design-time and run-time formats. Marble Maze uses .hlsl as the design-time format, and .cso as the run-time format. A .hlsl file holds source code for the shader; a .cso file holds the corresponding shader byte code. When you convert .hlsl files offline and provide .cso files with your game, you avoid the need to convert HLSL source files to byte code when your game loads.

For instructional reasons, the Marble Maze project includes both the design-time format and the run-time format for many resources, but you only have to maintain the design-time formats in the source project for your own game because you can convert them to run-time formats when you need them. This documentation shows how to convert the design-time formats to the run-time formats.

[Top]

Marble Maze follows the life cycle of a typical Windows Store app. For more info about the life cycle of a Windows Store app, see Application lifecycle.

When a Windows Store game initializes, it typically initializes runtime components such as Direct3D, Direct2D, and any input, audio, or physics libraries that it uses. It also loads game-specific resources that are required before the game begins. This initialization occurs one time during a game session.

After initialization, games typically run the game loop. In this loop, games typically perform four actions: process Windows events, collect input, update scene objects, and render the scene. When the game updates the scene, it can apply the current input state to the scene objects and simulate physical events, such as object collisions. The game can also perform other activities such as playing sound effects or sending data over the network. When the game renders the scene, it captures the current state of the scene and draws it to the display device. The following sections describe these activities in greater detail.

To add DirectX support to a Windows Store app, you create a view provider for DirectX resources by implementing the Windows::ApplicationModel::Core::IFrameworkViewSource and Windows::ApplicationModel::Core::IFrameworkView interfaces, which provide a factory for your view provider type and the implementation of your DirectX view provider, respectively. The Windows::ApplicationModel::Core::CoreApplication class runs this implementation. During the life cycle of the application, the Windows Runtime calls methods on the IFrameworkView object that was created by the factory to initialize the view and perform application logic.

Marble Maze implements IFrameworkViewSource by defining the DirectXAppSource class, and IFrameworkView by defining the DirectXApp class. These classes are declared in DirectXApp.h. The DirectXAppSource::CreateView method creates an instance of DirectXApp, as shown in the following example.

DirectXApp.h


ref class DirectXAppSource : Windows::ApplicationModel::Core::IFrameworkViewSource
{
public:
    virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
};


DirectXApp.cpp


IFrameworkView^ DirectXAppSource::CreateView()
{
    return ref new DirectXApp();
}


The IFrameworkView interface defines 5 methods that the derived class, DirectXApp, must implement: Initialize, SetWindow, Load, Run, and Uninitialize.

The DirectXApp::Initialize method, which is called when the app is launched, sets handlers for events that deal with the view, such as when the app is activated, suspended, and resumed. (Suspend and resume are described in greater detail in the section Supporting suspend, resume, and restart in this document). It also initializes the MarbleMaze member variable m_renderer, which manages game-specific resources and defines the majority of the game logic.


void DirectXApp::Initialize(
    _In_ CoreApplicationView^ applicationView
    )
{
    applicationView->Activated +=
        ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &DirectXApp::OnActivated);

    CoreApplication::Suspending +=
        ref new EventHandler<SuspendingEventArgs^>(this, &DirectXApp::OnSuspending);

    CoreApplication::Resuming +=
        ref new EventHandler<Platform::Object^>(this, &DirectXApp::OnResuming);

    m_renderer = ref new MarbleMaze();
}


Because the runtime calls the DirectXApp::Initialize method before any others, it's crucial that this method define the most fundamental behaviors of a Windows Store game—this includes the activation of the main window and making sure that game can handle a sudden suspend event (and a later resume event).

The DirectXApp::SetWindow method registers for window events, such as when the window is resized or closed, and when input is entered. (Handling window size changes is described in the section Handling window size changes in this document). It also calls the MarbleMaze::Initialize method to allow the MarbleMaze object to create resources that depend on the provided window. Input handling is described in the document Adding input and interactivity to the Marble Maze sample.


void DirectXApp::SetWindow(
    _In_ CoreWindow^ window
    )
{
    window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);

    window->SizeChanged += 
        ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &DirectXApp::OnWindowSizeChanged);

    window->VisibilityChanged +=
        ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &DirectXApp::OnVisibilityChanged);

    window->Closed += 
        ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &DirectXApp::OnWindowClosed);

    window->PointerPressed += 
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &DirectXApp::OnPointerPressed);

    window->PointerReleased +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &DirectXApp::OnPointerReleased);

    window->PointerMoved +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &DirectXApp::OnPointerMoved);

    window->KeyDown += 
        ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &DirectXApp::OnKeyDown);

    window->KeyUp += 
        ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &DirectXApp::OnKeyUp);

    DisplayProperties::LogicalDpiChanged +=
        ref new DisplayPropertiesEventHandler(this, &DirectXApp::OnLogicalDpiChanged);

    m_renderer->Initialize(window, DisplayProperties::LogicalDpi);
}


The DirectXApp::Run method runs the main game loop, which is explained in greater detail in the section The game loop in this document. The DirectXApp::Load loads game assets in the background, and is explained in greater detail in the section Loading game assets in the background. The DirectXApp::Uninitialize method does nothing in this implementation.

[Top]

DirectX Windows Store apps use main as the entry-point function. To implement main, create an instance of your view provider and pass it to the Windows::ApplicationModel::Core::CoreApplication::Run method.

The following example shows the application entry point, main, which is defined in DirectXApp.cpp. The entry point creates a DirectXAppSource object and passes that object to the CoreApplication::Run method.


[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)
{
    auto directXAppSource = ref new DirectXAppSource();
    CoreApplication::Run(directXAppSource);
    return 0;
}


[Top]

To ensure that your game can respond to window events within 5 seconds after it is launched, we recommend that you load your game assets asynchronously, or in the background. As assets load in the background, your game can respond to window events.

Note Note

You can also display the main menu when it is ready, and allow the remaining assets to continue loading in the background. If the user selects an option from the menu before all resources are loaded, you can indicate that scene resources are continuing to load by displaying a progress bar, for example.

Even if your game contains relatively few game assets, it is good practice to load them asynchronously for two reasons. One reason is that it is difficult to guarantee that all of your resources will load quickly on all devices and all configurations. Also, by incorporating asynchronous loading early, your code is ready to scale as you add functionality.

Asynchronous asset loading begins with the DirectXApp::Load method. This method uses the concurrency::task class to load game assets in the background.


void DirectXApp::Load(
    _In_ Platform::String^ entryPoint
    )
{
    task<void>([=]()
    {
        m_renderer->LoadDeferredResources();
    });
}


The MarbleMaze class defines the m_deferredResourcesReady flag to indicate that asynchronous loading is complete. The MarbleMaze::LoadDeferredResources method loads the game resources and then sets this flag. The update (MarbleMaze::Update) and render (MarbleMaze::Render) phases of the app check this flag. When this flag is set, the game continues as normal. If the flag is not yet set, the game shows the loading screen.

For more information about asynchronous programming for Windows Store apps, see Asynchronous programming in C++.

Tip Tip

If you’re writing game code that is part of a Windows Runtime C++ Library (in other words, a DLL), consider whether to read Creating Asynchronous Operations in C++ for Windows Store Apps to learn how to create asynchronous operations that can be consumed by apps and other libraries.

[Top]

The DirectXApp::Run method runs the main game loop. This game loop resembles a game loop that you might create for a classic desktop game. However, the Run method uses the Windows::UI::Core::CoreDispatcher::ProcessEvents method to process Windows messages and event handlers to react to certain events, such as window resizing.

To help separate view and window code from game-specific code, we implemented the DirectXApp::Run method to forward update and render calls to the MarbleMaze object. The DirectXApp::Run method also defines the game timer, which is used for animation and physics simulation.

The following example shows the DirectXApp::Run method, which includes the main game loop. The game loop updates the total time and frame time variables, processes any pending Windows events, and then updates and renders the scene.


void DirectXApp::Run()
{
    BasicTimer^ timer = ref new BasicTimer();

    while (!m_windowClosed)
    {
        timer->Update();

        if (m_windowVisible)
        {
            // Process windowing events.
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            // Update and render this frame.
            m_renderer->Update(timer->Total, timer->Delta);
            m_renderer->Render();

            // Present the frame. This call is synchronized to the display frame rate.
            m_renderer->Present();
        }
        else
        {
            // The window is not visible, so just wait for next event and respond to it.
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }

    // The app is exiting so do the same thing as would if app was being suspended.
    m_renderer->OnSuspending();  
}


[Top]

Games typically contain a state machine (also known as a finite state machine, or FSM) to control the flow and order of the game logic. A state machine contains a given number of states and the ability to transition among them. A state machine typically starts from an initial state, transitions to one or more intermediate states, and possibly ends at a terminal state.

A game loop often uses a state machine so that it can perform the logic that is specific to the current game state. Marble Maze defines the GameState enumeration, which defines each possible state of the game.


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


The MainMenu state, for example, defines that the main menu appears, and that the game is not active. Conversely, the InGameActive state defines that the game is active, and that the menu does not appear.

The MarbleMaze class defines the m_gameState member variable to hold the active game state. The MarbleMaze::Update and MarbleMaze::Render methods use the switch statement to perform logic for the current state. The following example shows what this switch statement might look like for the MarbleMaze::Update method (details are removed to illustrate the 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;
}


When game logic or rendering depends on a specific game state, we emphasize it in this documentation.

[Top]

The Windows Runtime provides an object-oriented event-handling system so that you can more easily manage Windows messages. To consume an event in an application, you must provide an event handler, or event-handling method, that responds to the event. You must also register the event handler with the event source. This process is often referred to as event wiring.

Hh699854.collapse_all(en-us,VS.110).gifHandling window size changes

One important Windows event that the game must handle is when the window size changes. The window size can change when the game is suspended and your user changes the screen resolution. The following example shows how the DirectXApp::SetWindow method registers for the Windows::UI::Core::CoreWindow::SizeChanged event. In this example window is a CoreWindow object.


window->SizeChanged += 
    ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &DirectXApp::OnWindowSizeChanged);


The DirectXApp::OnWindowSizeChanged method defers handling to the MarbleMaze::UpdateForWindowSizeChange method.


void DirectXApp::OnWindowSizeChanged(
    _In_ CoreWindow^ sender,
    _In_ WindowSizeChangedEventArgs^ args
    )
{
    // Query for the current ApplicationView state to make sure the most current
    // ApplicationViewState is known before resizing all the resources.

    m_renderer->OnViewChange(ApplicationView::Value);
    m_renderer->UpdateForWindowSizeChange();
}


The following example shows the DirectXBase::UpdateForWindowSizeChange method. The MarbleMaze class derives from DirectXBase and does not override this method. The DirectXBase::UpdateForWindowSizeChange method invalidates the DirectX objects that depend on the window size, for example, the render target, and then calls the DirectXBase::CreateWindowSizeDependentResources method. The DirectXBase::CreateWindowSizeDependentResources method recreates these invalidated resources.


// This routine is called in the event handler for the view SizeChanged event.
void DirectXBase::UpdateForWindowSizeChange()
{
    if (m_window->Bounds.Width  != m_windowBounds.Width ||
        m_window->Bounds.Height != m_windowBounds.Height)
    {
        m_d2dContext->SetTarget(nullptr);
        m_d2dTargetBitmap = nullptr;
        m_renderTargetView = nullptr;
        m_depthStencilView = nullptr;
        CreateWindowSizeDependentResources();
    }
}


Hh699854.collapse_all(en-us,VS.110).gifHandling snapped and fill views

Users can work with one or two apps at a time. They can also define the size relationship between two apps. A view state refers to the three ways a user can choose to display Windows Store apps: full-screen, snap, and fill. In the full-screen view state, the app fills entire screen. In the snap view state, the app is snapped to a narrow region of the screen. In the fill view state, the app fills the remaining screen area that's not occupied by the app that's in the snapped state.

The previous section describes how the DirectXApp::OnWindowSizeChanged method is called when the window size changes. This method calls the MarbleMaze::OnViewChange method, which saves the current view state.


void DirectXApp::OnWindowSizeChanged(
    _In_ CoreWindow^ sender,
    _In_ WindowSizeChangedEventArgs^ args
    )
{
    // Query for the current ApplicationView state to make sure the most current
    // ApplicationViewState is known before resizing all the resources.

    m_renderer->OnViewChange(ApplicationView::Value);
    m_renderer->UpdateForWindowSizeChange();
}


The DirectXApp::OnWindowSizeChanged method then calls MarbleMaze::UpdateForWindowSizeChange, which calls MarbleMaze::CreateWindowSizeDependentResources. The MarbleMaze::CreateWindowSizeDependentResources method creates the graphics resources that depend on a given window size. (This process is explained in greater detail in the document Adding visual content to the Marble Maze sample). This method arranges elements in one way for the snap view state and a different way for the other view states (full-screen and fill). The following example shows how the MarbleMaze::CreateWindowSizeDependentResources method defines the layout of the Start Game button for the snap view state versus the other view states:


m_startGameButton.Initialize();
m_startGameButton.SetAlignment(AlignCenter, AlignFar);
m_startGameButton.SetContainer(topHalfRect);
m_startGameButton.SetText(L"Start Game");
m_startGameButton.SetTextColor(D2D1::ColorF(D2D1::ColorF::White));
m_startGameButton.GetTextStyle().SetFontWeight(DWRITE_FONT_WEIGHT_BLACK);
if (snapped)
{
    m_startGameButton.SetPadding(D2D1::SizeF(8.0f, 4.0f));
    m_startGameButton.GetTextStyle().SetFontSize(36.0f);
}
else
{
    m_startGameButton.SetPadding(D2D1::SizeF(32.0f, 16.0f));
    m_startGameButton.GetTextStyle().SetFontSize(72.0f);
}
UserInterface::GetInstance().RegisterElement(&m_startGameButton);


The snapped local variable is set to true if the current view state is snapped:


bool snapped = m_viewState == ApplicationViewState::Snapped;


The game does not need to handle 3-D elements in any special way because the 3-D viewport is already dependent on the window size and is therefore handled when the window size changes.

For more info about snapped and fill views, see and Guidelines for snapped and fill views.

[Top]

Hh699854.collapse_all(en-us,VS.110).gifSupporting suspend, resume, and restart

Marble Maze is suspended when the user switches away from it or when Windows enters a low power state. The game is resumed when the user moves it to the foreground or when Windows comes out of a low power state. Generally, you don't close apps. Windows can terminate the app when it's in the suspended state and Windows requires the resources, such as memory, that the app is using. Windows notifies an app when it is about to be suspended or resumed, but it doesn't notify the app when it's about to be terminated. Therefore, your app must be able to save—at the point when Windows notifies your app that it is about to be suspended—any data that would be required to restore the current user state when the app is restarted. If your app has significant user state that is expensive to save, you may also need to save state regularly, even before your app receives the suspend notification. Marble Maze responds to suspend and resume notifications for two reasons:

  1. When the app is suspended, the game saves the current game state and pauses audio playback. When the app is resumed, the game resumes audio playback.

  2. When the app is closed and later restarted, the game resumes from its previous state.

Marble Maze performs the following tasks to support suspend and resume:

  • It saves its state to persistent storage at key points in the game, such as when the user reaches a checkpoint.

  • It responds to suspend notifications by saving its state to persistent storage.

  • It responds to resume notifications by loading its state from persistent storage. It also loads the previous state during startup.

To support suspend and resume, Marble Maze defines the PersistentState class. (See PersistentState.h and PersistentState.cpp). This class uses the Windows::Foundation::Collections::IPropertySet interface to read and write properties. The PersistentState class provides methods that read and write primitive data types (such as bool, int, float, XMFLOAT3, and Platform::String), from and to a backing store.


ref class PersistentState
{
public:
    void Initialize(
        _In_ Windows::Foundation::Collections::IPropertySet^ m_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;
};


The MarbleMaze class holds a PersistentState object. The MarbleMaze constructor initializes this object and provides the local application data store as the backing data store.


m_persistentState = ref new PersistentState();
m_persistentState->Initialize(ApplicationData::Current->LocalSettings->Values, "MarbleMaze");


Marble Maze saves its state when the marble passes over a checkpoint or the goal (in the MarbleMaze::Update method), and when the window loses focus (in the MarbleMaze::OnFocusChange method). If your game holds a large amount of state data, we recommend that you occasionally save state to persistent storage in a similar manner because you only have a few seconds to respond to the suspend notification. Therefore, when your app receives a suspend notification, it only has to save the state data that has changed.

To respond to suspend and resume notifications, the DirectXApp class registers for the Windows::ApplicationModel::Core::CoreApplication::Suspending and Windows::ApplicationModel::Core::CoreApplication::Resuming events. The DirectXApp::OnSuspending method handles the suspend event and the DirectXApp::OnResuming method handles the resume event. These methods forward the notification to the MarbleMaze class.


CoreApplication::Suspending +=
    ref new EventHandler<SuspendingEventArgs^>(this, &DirectXApp::OnSuspending);

CoreApplication::Resuming +=
    ref new EventHandler<Platform::Object^>(this, &DirectXApp::OnResuming);


The DirectXApp::OnSuspending method calls the Windows::ApplicationModel::SuspendingOperation::GetDeferral method to request that the suspension operation be deferred. The DirectXApp::OnSuspending method then calls the MarbleMaze::OnSuspending method to save game-specific data. After the game saves its state, it calls the Windows::ApplicationModel::SuspendingDeferral::Complete method to notify the operating system that the suspend operation is complete.


void DirectXApp::OnSuspending(
    _In_ Platform::Object^ sender,
    _In_ SuspendingEventArgs^ args
    )
{
    // Save application state after requesting a deferral. Holding a deferral
    // indicates that the application is busy performing suspending operations.
    // Be aware that a deferral may not be held indefinitely. After about five
    // seconds, the application will be forced to exit.

    SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
    m_renderer->OnSuspending();
    deferral->Complete();
}


Note Note

Even when you expect your app to save state in less than 5 seconds, it is a good practice to always use a deferral. A deferral is useful when your game must save a large amount of data to persistent storage. We recommend that your game take less than one second to save state. If an app does not return from the suspending event within 5 seconds, Windows assumes that the app has stopped responding and terminates it.

The MarbleMaze::OnSuspending method saves game state and suspends audio.


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


The MarbleMaze::SaveState method saves game state values such as the current position and velocity of the marble, the most recent checkpoint, and the high-score table.


void MarbleMaze::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);
    }
}


When the game resumes, it only has to resume audio. It doesn't have to load state from persistent storage because the state is already loaded in memory. The DirectXApp::OnResuming method calls the MarbleMaze::OnResuming method to resume audio playback.


void DirectXApp::OnResuming(
    _In_ Platform::Object^ sender,
    _In_ Platform::Object^ args
    )
{
    m_renderer->OnResuming();
}


How the game suspends and resumes audio is explained in the document Adding audio to the Marble Maze sample.

To support restart, the MarbleMaze::Initialize method, which is called during startup, calls the MarbleMaze::LoadState method. The MarbleMaze::LoadState method reads and applies the state to the game objects. This method also sets the current game state to paused if the game was paused or active when it was suspended. We pause the game so that the user is not surprised by unexpected activity. It also moves to the main menu if the game was not in a gameplay state when it was suspended.


void MarbleMaze::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 note Important

Marble Maze doesn't distinguish between cold starting—that is, starting for the first time without a prior suspend event—and resuming from a suspended state. This is recommended design for all Windows Store apps.

For more examples that demonstrate how to store and retrieve settings and files from the local application data store, see Quickstart: Local application data. For more info about application data, see Accessing app data with the Windows Runtime.

[Top]

Hh699854.collapse_all(en-us,VS.110).gifHandling window visibility changes

As a best practice, you should not update or render your scene when the window is not visible. This enables other apps to use the CPU and GPU and can also improve battery life.

The DirectXApp class defines the m_windowVisible flag to indicate when the window is visible. It also creates a handler for the Windows::UI::Core::CoreWindow::VisiblityChanged event. This event handler updates the visibility flag.


window->VisibilityChanged +=
    ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &DirectXApp::OnVisibilityChanged);



void DirectXApp::OnVisibilityChanged(
    _In_ CoreWindow^ sender,
    _In_ VisibilityChangedEventArgs^ args
    )
{
    m_windowVisible = args->Visible;
}


In the method that runs the main game loop, DirectXApp::Run, the app processes all window events and updates and renders the scene if the app window is visible. Otherwise, it waits for the next window event.


void DirectXApp::Run()
{
    BasicTimer^ timer = ref new BasicTimer();

    while (!m_windowClosed)
    {
        timer->Update();

        if (m_windowVisible)
        {
            // Process windowing events.
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            // Update and render this frame.
            m_renderer->Update(timer->Total, timer->Delta);
            m_renderer->Render();

            // Present the frame. This call is synchronized to the display frame rate.
            m_renderer->Present();
        }
        else
        {
            // The window is not visible, so just wait for next event and respond to it.
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }

    // The app is exiting so do the same thing as would if app was being suspended.
    m_renderer->OnSuspending();  
}


[Top]

Read Adding visual content to the Marble Maze sample for information about some of the key practices to keep in mind when you work with visual resources.

[Top]

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.