Struttura dell'applicazione Marble Maze

Applies to Windows only

La struttura di un'app di Windows Store DirectX è diversa da quella di un'applicazione desktop tradizionale. Invece di usare tipi di handle come HWND e funzioni come CreateWindow, con Windows Runtime puoi usare interfacce come Windows::UI::Core::ICoreWindow per sviluppare app di Windows Store in modo più moderno e orientato agli oggetti. In questa sezione della documentazione viene descritta la struttura del codice dell'applicazione Marble Maze.

Nota  Il codice di esempio che corrisponde a questo documento è disponibile alla pagina relativa all'esempio di gioco DirectX Marble Maze.

In questo argomento

Ecco alcuni aspetti fondamentali descritti in questo documento e relativi alla struttura del codice del tuo gioco:

  • Nella fase di inizializzazione, impostazione dei componenti della libreria e runtime usati dal gioco. Caricamento delle risorse specifiche del gioco.
  • Le app di Windows Store devono iniziare a elaborare gli eventi entro 5 secondi dall'avvio. Pertanto, è necessario caricare solo le risorse essenziali durante il caricamento dell'app. I giochi devono caricare le risorse di grandi dimensioni in background e visualizzare una schermata sullo stato di avanzamento.
  • Nel ciclo del gioco, risposta agli eventi di Windows, lettura dell'input utente, aggiornamento degli oggetti della scena ed esecuzione del rendering della scena.
  • Uso dei gestori eventi per rispondere agli eventi di finestra, che sostituiscono le finestre di messaggio delle applicazioni desktop di Windows.
  • Uso di una macchina a stati per controllare il flusso e l'ordine della logica del gioco.

Organizzazione dei file

Alcuni dei componenti di Marble Maze possono essere riutilizzati con qualsiasi gioco con modifiche minime o nulle. Per il tuo gioco, puoi adattare l'organizzazione e le idee che questi file forniscono. La tabella seguente descrive brevemente i file del codice sorgente importanti.

FileDescrizione
Audio.h, Audio.cpp Definisce la classe Audio, che gestisce le risorse audio.
BasicLoader.h, BasicLoader.cpp Definisce la classe BasicLoader, che fornisce metodi di utilità che consentono di caricare trame, mesh e shader.
BasicMath.hDefinisce le strutture e le funzioni che consentono di usare i calcoli e i dati di vettori e matrici. Molte di queste funzioni sono compatibili con i tipi di shader HLSL.
BasicReaderWriter.h, BasicReaderWriter.cpp Definisce la classe BasicReaderWriter, che usa Windows Runtime per leggere e scrivere i file di dati in un'app in Windows Store.
BasicShapes.h, BasicShapes.cpp Definisce la classe BasicShapes, che fornisce metodi di utilità per la creazione di forme di base come cubi e sfere. Questi file non vengono usati dall'implementazione di Marble Maze.
BasicTimer.h, BasicTimer.cpp Definisce la classe BasicTimer, che fornisce un modo semplice per ottenere la durata complessiva e il tempo trascorso.
Camera.h, Camera.cpp Definisce la classe Camera, che fornisce la posizione e l'orientamento di una fotocamera.
Collision.h, Collision.cpp Gestisce le informazioni sulla collisione tra la biglia e altri oggetti, ad esempio il labirinto.
DDSTextureLoader.h, DDSTextureLoader.cpp Definisce la funzione CreateDDSTextureFromMemory, che carica le trame nel formato DDS da un buffer di memoria.
DirectXApp.h, DirectXApp.cpp Definisce le classi DirectXApp e DirectXAppSource, che incapsulano la visualizzazione (finestra, thread ed eventi) dell'app.
DirectXBase.h, DirectXBase.cpp Definisce la classe DirectXBase, che fornisce un'infrastruttura comune a molte app in Windows Store DirectX.DirectXSample.h
DirectXSample.hDefinisce le funzioni di utilità che possono essere usate dalle app di Windows Store DirectX.
LoadScreen.h, LoadScreen.cpp Definisce la classe LoadScreen, che visualizza una schermata di caricamento durante l'inizializzazione dell'app.
MarbleMaze.h, MarbleMaze.cpp Definisce la classe MarbleMaze, che gestisce le risorse specifiche del gioco e definisce gran parte della logica del gioco.
MediaStreamer.h, MediaStreamer.cpp Definisce la classe MediaStreamer, che usa Media Foundation per consentire la gestione delle risorse audio.
PersistentState.h, PersistentState.cppDefinisce la classe PersistentState, che legge e scrive i tipi di dati primitivi in un archivio di backup.
Physics.h, Physics.cpp Definisce la classe Physics, che implementa la simulazione della fisica tra la biglia e il labirinto.
Primitives.h Definisce i tipi geometrici usati dal gioco.
SampleOverlay.h, SampleOverlay.cpp Definisce la classe SampleOverlay, che fornisce operazioni e dati comuni dell'interfaccia utente e 2D.
SDKMesh.h, SDKMesh.cpp Definisce la classe SDKMesh, che carica le mesh nel formato SDK-Mesh (.sdkmesh) e ne esegue il rendering.
UserInterface.h, UserInterface.cpp Definisce le funzionalità correlate all'interfaccia utente, ad esempio il sistema di menu e la tabella dei punteggi elevati.

 

Formati delle risorse della fase di progettazione e della fase di esecuzione

Quando puoi, usa i formati della fase di esecuzione anziché quelli della fase di progettazione per caricare in modo più efficiente le risorse del gioco.

I formati della fase di progettazione vengono usati durante la progettazione delle risorse e sono quelli a cui ricorrono generalmente gli sviluppatori 3D. Alcuni di questi formati sono anche basati su testo e quindi modificabili in qualsiasi editor di testo. I formati della fase di progettazione possono essere dettagliati e contenere più informazioni di quelle richieste dal gioco. Un formato della fase di esecuzione è il formato binario letto dal tuo gioco. I formati di questo tipo sono in genere più compatti e più facili da caricare rispetto ai corrispondenti formati della fase di progettazione. Per questo motivo, la maggior parte dei giochi usa asset di runtime in fase di esecuzione.

Anche se il tuo gioco è in grado di leggere direttamente un formato della fase di progettazione, l'uso di un formato della fase di esecuzione distinto offre svariati vantaggi. Trattandosi di un formato più compatto, richiede meno spazio su disco e meno tempo per il trasferimento in rete. Inoltre, spesso viene rappresentato come struttura di dati mappata alla memoria e può essere pertanto caricato in memoria molto più velocemente rispetto, ad esempio, a un file di testo XML. Infine, i formati della fase di esecuzione distinti sono più difficilmente modificabili da parte dell'utente finale perché usano in genere la codifica binaria.

Gli shader HLSL sono un esempio di risorse che usano formati della fase di progettazione e della fase di esecuzione differenti. Marble Maze usa HLSL come formato della fase di progettazione e CSO come formato della fase di esecuzione. Un file con HLSL contiene il codice sorgente per lo shader, mentre un file CSO contiene il bytecode di shader corrispondente. Quando converti i file HLSL offline e fornisci il tuo gioco ai file CSO, eviti di dover convertire i file di origine HLSL in bytecode durante il caricamento del gioco.

Per scopi di formazione, il progetto di Marble Maze include per molte risorse sia i formati della fase di progettazione sia quelli della fase di esecuzione, ma devi mantenere solo i primi nel progetto originale del gioco perché puoi convertirli nei secondi in caso di necessità. Questa documentazione descrive come convertire i formati della fase di progettazione in formati della fase di esecuzione.

Ciclo di vita dell'applicazione

Marble Maze segue il ciclo di vita di un'app tipica di Windows Store. Per altre info sul ciclo di vita di un'app di Windows Store, vedi Ciclo di vita dell'app.

Quando viene inizializzato, un gioco di Windows Store inizializza in genere i componenti di runtime come Direct3D, Direct2D e tutte le librerie per l'input, l'audio o la fisica che usa, oltre a caricare le risorse specifiche del gioco richieste prima dell'inizio dello stesso. Questa inizializzazione si verifica una sola volta durante la sessione di gioco.

Al termine dell'inizializzazione, in genere viene eseguito il ciclo del gioco, che solitamente prevede l'esecuzione di quattro azioni: elaborazione degli eventi di Windows, raccolta dell'input, aggiornamento degli oggetti della scena ed esecuzione del rendering della scena. Quando aggiorna la scena, il gioco può applicare lo stato di input corrente agli oggetti della scena e simulare eventi fisici, ad esempio gli urti. Può eseguire anche altre attività, come riprodurre effetti sonori o inviare dati in rete. Quando esegue il rendering della scena, ne acquisisce lo stato corrente e la disegna sul dispositivo di visualizzazione. Queste attività sono descritte in modo più dettagliato nelle sezioni seguenti.

Aggiunta al modello

Il modello di app DirectX crea una finestra principale in cui puoi eseguire il rendering con Direct3D. Il modello include anche la classe DeviceResources che crea tutte le risorse del dispositivo Direct3D necessarie per il rendering del contenuto 3D in un'app di Windows Store. La classe AppMain crea l'oggetto classe MarbleMaze, avvia il caricamento delle risorse, esegue il ciclo per aggiornare il timer e chiama il metodo Render MarbleMaze a ogni fotogramma. I metodi CreateWindowSizeDependentResources, Update e Render di questa classe chiamano i corrispondenti metodi della classe MarbleMaze. L'esempio seguente mostra dove il costruttore AppMain crea l'oggetto classe MarbleMaze. La classe delle risorse del dispositivo viene passata alla classe in modo che possa usare gli oggetti Direct3D per il rendering.


    m_marbleMaze = std::unique_ptr<MarbleMaze>(new MarbleMaze(m_deviceResources));
    m_marbleMaze->CreateWindowSizeDependentResources();


La classe AppMain avvia anche il caricamento delle risorse differite per il gioco. Per altri dettagli, vedi la sezione successiva. Il costruttore DirectXPage imposta il gestore di eventi e crea le classi DeviceResources e AppMain.

Quando i gestori di questi eventi vengono chiamati, passano l'input alla classe MarbleMaze.

Caricamento degli asset del gioco in background

Per avere la certezza che il gioco sia in grado di rispondere agli eventi di finestra entro 5 secondi dall'avvio, consigliamo di caricare gli asset del gioco in modo asincrono o in background. Mentre gli asset vengono caricati in background, il gioco può rispondere agli eventi di finestra.

Nota  Puoi anche visualizzare il menu principale quando è pronto e consentire il caricamento degli asset rimanenti in background. Se l'utente seleziona un'opzione dal menu prima che tutte le risorse vengano caricate, puoi indicare che è ancora in corso il caricamento delle risorse della scena mostrando, ad esempio, una barra di stato.

Anche se il gioco contiene un numero relativamente ridotto di asset, è consigliabile caricarli in modo asincrono per due motivi. Innanzitutto, è difficile garantire che tutte le risorse vengano caricate rapidamente in tutti i dispositivi e tutte le configurazioni. Inoltre, incorporando il caricamento asincrono in una fase iniziale, il codice potrà essere facilmente scalato con l'aggiunta di nuove funzionalità.

Il caricamento asincrono degli asset inizia con il metodo AppMain::Load. Questo metodo usa la classe task Class (Concurrency Runtime) per caricare gli asset del gioco in background.



    task<void>([=]()
    {
        m_marbleMaze->LoadDeferredResources();
    });



La classe MarbleMaze definisce il flag m_deferredResourcesReady per indicare il completamento del caricamento asincrono. Il metodo MarbleMaze::LoadDeferredResources carica le risorse del gioco e poi imposta questo flag. Le fasi di aggiornamento (MarbleMaze::Update) e rendering (MarbleMaze::Render) dell'app controllano questo flag. Quando il flag è impostato, il gioco continua normalmente. Se invece non è ancora impostato, viene mostrata la schermata di caricamento.

Per altre informazioni sulla programmazione asincrona per le app di Windows Store, vedi Programmazione asincrona in C++.

Suggerimento  Se scrivi codice del gioco che fa parte di una libreria C++ per Windows Runtime (ossia una DLL), puoi leggere il documento Creazione di operazioni asincrone in C++ per app di Windows Store per informazioni su come creare operazioni asincrone che possano essere usate dalle app e da altre librerie.

Ciclo del gioco

Il metodo DirectPage::OnRendering esegue il ciclo del gioco principale, Questo metodo viene chiamato a ogni fotogramma.

Per agevolare la separazione del codice di visualizzazione e delle finestre dal codice specifico del gioco, abbiamo implementato il metodo DirectXApp::Runper inoltrare le chiamate di aggiornamento e rendering all'oggetto MarbleMaze. Il metodo DirectPage::OnRendering definisce anche il timer del gioco, usato per l'animazione e la simulazione della fisica.

L'esempio seguente mostra il metodo DirectPage::OnRendering, che include il ciclo del gioco principale. Il ciclo del gioco aggiorna le variabili della durata complessiva e della durata del fotogramma e quindi aggiorna la scena e ne esegue il rendering. Questo assicura anche che, quando la finestra è visibile, venga eseguito il rendering solo del contenuto.




void DirectXPage::OnRendering(Object^ sender, Object^ args)
{
    if (m_windowVisible)
    {
        m_main->Update();

        if (m_main->Render())
        {
            m_deviceResources->Present();
        }
    }
}


Macchina a stati.

I giochi contengono in genere una macchina a stati, nota anche come macchina a stati finiti o FSM (Finished State Machine), per controllare il flusso e l'ordine della logica del gioco. Una macchina a stati contiene un determinato numero di stati e consente di effettuare la transizione dall'uno all'altro. In genere parte da uno stato iniziale, effettua la transizione a uno o più stati intermedi ed eventualmente termina con uno stato finale.

Il ciclo del gioco spesso usa una macchina a stati per eseguire la logica specifica dello stato corrente del gioco. Marble Maze definisce l'enumerazione GameState, che definisce a sua volta ogni stato possibile del gioco.


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


Lo stato MainMenu, ad esempio, indica che il menu principale viene visualizzato e il gioco non è attivo. Lo stato InGameActive, invece, indica che il gioco è attivo e che il menu non viene visualizzato. La classe MarbleMaze definisce la variabile membro m_gameState che contiene lo stato del gioco attivo.

I metodi MarbleMaze::Update e MarbleMaze::Render usano l'istruzione switch per eseguire la logica per lo stato corrente. L'esempio seguente mostra quale potrebbe essere la struttura dell'istruzione switch per il metodo MarbleMaze::Update (i dettagli sono stati rimossi per mostrare la struttura).


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;
}


Se la logica o il rendering del gioco dipende da uno stato specifico, questo aspetto viene sottolineato nella presente documentazione.

Gestione degli eventi dell'app e di finestra

Windows Runtime fornisce un sistema di gestione degli eventi orientato agli oggetti che consente di gestire più facilmente i messaggi di Windows. Per usare un evento in un'applicazione, devi predisporre un gestore eventi o un metodo di gestione eventi che risponda all'evento. Devi inoltre registrare il gestore eventi con l'origine evento. Questo processo è spesso chiamato collegamento degli eventi.

Supporto di sospensione, ripresa e riavvio

Marble Maze viene sospesa quando l'utente passa ad altro o quando Windows entra in modalità basso consumo. Il gioco riprende quando l'utente lo riporta in primo piano o quando Windows esce dalla modalità basso consumo. In genere, le app non si chiudono. Windows può terminare l'app quando questa è sospesa e sono richieste le risorse, ad esempio la memoria, usate dall'app. Le app ricevono una notifica del sistema operativo quando stanno per essere sospese o riprese, ma non quando stanno per essere terminate. Di conseguenza, devono essere in grado di salvare, nel momento in cui ricevono la notifica, i dati necessari per ripristinare lo stato dell'utente corrente al momento del riavvio. Se il salvataggio dello stato dell'utente richiede molto tempo, potrebbe essere necessario salvarlo regolarmente, anche prima della ricezione della notifica di sospensione. Marble Maze risponde alle notifiche di sospensione e di ripresa per due motivi:

  1. Quando l'app viene sospesa, il gioco salva lo stato corrente e sospende la riproduzione audio. Quando l'app viene ripresa, il gioco riprende la riproduzione audio.
  2. Quando l'app viene chiusa e successivamente riavviata, il gioco riprende dallo stato precedente.

Marble Maze esegue le operazioni riportate di seguito per supportare la sospensione e la ripresa:

  • Salva lo stato del gioco in un archivio permanente nei momenti salienti, ad esempio quando l'utente raggiunge un checkpoint.
  • Risponde alle notifiche di sospensione salvando lo stato del gioco in un archivio permanente.
  • Risponde alle notifiche di ripresa caricando lo stato del gioco dall'archivio permanente e durante l'avvio carica anche lo stato precedente.

Per supportare la sospensione e la ripresa, Marble Maze definisce la classe PersistentState. (Vedi PersistentState.h e PersistentState.cpp). Questa classe usa l'interfaccia Windows::Foundation::Collections::IPropertySet per leggere e scrivere le proprietà. La classe PersistentState fornisce metodi che leggono e scrivono i tipi di dati primitivi (come bool, int, float, XMFLOAT3e Platform::String), in un archivio di backup.


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;
};


La classe MarbleMaze contiene un oggetto PersistentState. Il costruttore MarbleMaze inizializza tale oggetto e fornisce l'archivio dati dell'applicazione locale come archivio dati di backup.


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

Marble Maze salva lo stato corrente quando la biglia supera un checkpoint o l'obiettivo (nel metodo MarbleMaze::Update) e quando la finestra perde lo stato attivo (nel metodo MarbleMaze::OnFocusChange). Se il gioco contiene una grande quantità di dati sullo stato, consigliamo di salvare periodicamente lo stato in un archivio permanente in modo simile, perché il tempo a disposizione per rispondere alla notifica di sospensione è di pochi secondi. In questo modo, quando l'app riceve una notifica di sospensione, deve salvare solo i dati sullo stato modificati.

Per rispondere alle notifiche di sospensione e ripresa, la classe DirectXPage definisce i metodi SaveInternalState e LoadInternalState che vengono chiamati alla sospensione e alla ripresa. Il metodo MarbleMaze::OnSuspending gestisce l'evento di sospensione, mentre il metodo MarbleMaze::OnResuming gestisce l'evento di ripresa.

Il metodo MarbleMaze::OnSuspending salva lo stato del gioco e sospende l'audio.


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


Il metodo MarbleMaze::SaveState salva i valori dello stato del gioco come la posizione corrente e la velocità della biglia, il checkpoint più recente e la tabella dei punteggi elevati.


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);
    }
}


Alla ripresa del gioco, è necessario riprendere solo l'audio. Non occorre caricare lo stato dall'archivio permanente in quanto è già caricato in memoria.

Le procedure per la sospensione e la ripresa dell'audio sono spiegate nel documento Aggiunta di audio all'esempio di Marble Maze.

Per supportare il riavvio, il metodo MarbleMaze::Initialize, chiamato durante l'avvio, chiama il metodo MarbleMaze::LoadState. Il metodo MarbleMaze::LoadState legge lo stato e lo applica agli oggetti del gioco. Imposta inoltre lo stato corrente del gioco su in sospeso se il gioco è stato messo in pausa o su attivo se è stato sospeso. La sospensione del gioco serve a fare in modo che l'utente non sia sorpreso da attività non previste. Viene inoltre attivato il menu principale se al momento della sospensione non era attiva la modalità di gioco.


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);
    }
}


Importante  Marble Maze non fa distinzione tra avvio a freddo, ossia il primo avvio senza un precedente evento di sospensione, e la ripresa da uno stato di sospensione. Si tratta della progettazione consigliata per tutte le app di Windows Store.

Per altri esempi sull'archiviazione e il recupero delle impostazioni e dei file dall'archivio dati dell'applicazione locale, vedi Guida introduttiva: Dati locali dell'applicazione Per altre info sui dati dell'applicazione, vedi Accesso ai dati dell'app con Windows Runtime.

Passaggi successivi

Per informazioni su alcune delle procedure chiave da tenere presente durante l'uso delle risorse visive, leggi Aggiunta di contenuto visivo all'esempio di Marble Maze.

Argomenti correlati

Aggiunta di contenuto visivo all'esempio di Marble Maze
Nozioni fondamentali su Marble Maze
Sviluppo di Marble Maze, un gioco di Windows Store scritto in C++ e DirectX

 

 

Mostra:
© 2014 Microsoft