Sdílet prostřednictvím


Definování hlavního herního objektu

Poznámka:

Toto téma je součástí Vytvoření jednoduché hry pro Univerzální platformu Windows (UPW) pomocí série kurzů DirectX. Téma na tomto odkazu nastaví kontext pro řadu.

Jakmile vytvoříte základní rámec ukázkové hry a implementujete stavový stroj, který zpracovává chování uživatelů a systémů vysoké úrovně, budete chtít prozkoumat pravidla a mechaniku, které ukázkovou hru převedou na hru. Pojďme se podívat na podrobnosti hlavního objektu ukázkové hry a na to, jak přeložit pravidla hry do interakcí s herním světem.

Cíle

  • Naučte se používat základní vývojové techniky k zavedení herních pravidel a mechanik pro hru DirectX UWP.

Hlavní herní objekt

Ve vzorové hře Simple3DGameDX je Simple3DGame hlavní třída herního objektu. Instance Simple3DGame je vytvořena nepřímo prostřednictvím metody App::Load.

Tady jsou některé z funkcí třídy Simple3DGame.

  • Obsahuje implementaci herní logiky.
  • Obsahuje metody, které tyto podrobnosti komunikují.
    • Změny stavu hry pro stavový automat definovaný v rámci aplikace.
    • Změny ve stavu hry z aplikace na samotný herní objekt.
    • Podrobnosti o aktualizaci uživatelského rozhraní hry (overlay a HUD), animací a dynamiky fyziky.

    Poznámka:

    Aktualizaci grafiky zajišťuje třída GameRenderer, která obsahuje metody k získání a používání prostředků grafického zařízení využívané hrou. Další informace najdete v tématu Rendering Framework I: Úvod k vykreslování.

  • Slouží jako kontejner pro data, která definují herní relaci, úroveň nebo životnost hry, podle toho, jak na vysoké úrovni definujete svou hru. V tomto případě jsou data o stavu hry po celou dobu života hry a inicializována jednou, když uživatel spustí hru.

Chcete-li zobrazit metody a data definovaná touto třídou, viz Třída Simple3DGame níže.

Inicializace a spuštění hry

Když hráč hru spustí, musí objekt hry inicializovat svůj stav, vytvořit a přidat překryv, nastavit proměnné, které sledují výkon hráče, a vytvořit instanci objektů, které bude používat k sestavení úrovní. V této ukázce se to provede během vytvoření instance GameMain v App::Load.

Herní objekt typu Simple3DGameje vytvořen v konstruktoru GameMain::GameMain. Pak se inicializuje pomocí metody Simple3DGame::Initialize během GameMain::ConstructInBackground fire-and-forget coroutine, která je volána z GameMain::GameMain.

Metoda Simple3DGame::Initialize

Ukázková hra nastaví tyto komponenty v herním objektu.

  • Vytvoří se nový objekt pro přehrávání zvuku.
  • Pole pro grafická primitiva hry jsou vytvořena, včetně polí pro primitiva úrovní, munici a překážky.
  • Vytvoří se umístění pro ukládání dat o stavu hry s názvem Game, které je umístěno do úložiště nastavení dat aplikace specifikovaného jako ApplicationData::Current.
  • Vytvoří se časovač hry a počáteční rastrový obrázek překrytí v rámci hry.
  • Vytvoří se nová kamera s konkrétní sadou parametrů zobrazení a projekce.
  • Vstupní zařízení (ovladač) je nastaveno na stejnou počáteční výšku a natočení (yaw) jako kamera, takže hráč má přímou souvztažnost mezi počáteční pozicí ovládání a polohou kamery.
  • Objekt přehrávače se vytvoří a nastaví na aktivní. K detekci blízkosti hráče k zdím a překážkám používáme objekt koule a abychom zabránili umístění kamery do pozice, která by mohla narušit zážitek.
  • Primitiv herního světa byl vytvořen.
  • Válcové překážky jsou vytvořeny.
  • Cíle (Face objekty) jsou vytvořeny a očíslovány.
  • Vytvoří se koule ammo.
  • Vytvoří se úrovně.
  • Nejvyšší skóre je načteno.
  • Načte se jakýkoli předchozí uložený stav hry.

Hra má nyní instance všech klíčových komponent – svět, hráče, překážky, cíle a střelné koule. Má také instance úrovní, které představují konfigurace všech výše uvedených komponent a jejich chování pro každou konkrétní úroveň. Teď se podíváme, jak hra staví úrovně.

Sestavování a načítání herních úrovní

Většina náročné práce na konstrukci úrovně se provádí v Level[N].h/.cpp souborech nalezených v GameLevels složce příkladového řešení. Vzhledem k tomu, že se zaměřuje na velmi konkrétní implementaci, nebudeme je tady zakrývat. Důležité je, že kód pro každou úroveň se spouští jako samostatný Level[N] objektu. Pokud chcete hru rozšířit, můžete vytvořit Úroveň[N] objekt, který jako parametr vezme přiřazené číslo a náhodně umístí překážky a cíle. Nebo můžete mít konfigurační data na úrovni načtení ze souboru prostředků nebo dokonce z internetu.

Definování hry

V tuto chvíli máme všechny komponenty, které potřebujeme k vývoji hry. Úrovně byly sestaveny v paměti z primitivů a jsou připravené, aby hráč mohl začít interagovat.

Nejlepší hry okamžitě reagují na vstup hráče a poskytují okamžitou zpětnou vazbu. To platí pro jakýkoli typ hry, od reflexní akce, stříleček z pohledu první osoby v reálném čase až po promyšlené, tahové strategické hry.

Metoda Simple3DGame::RunGame

Zatímco herní úroveň probíhá, hra je ve stavu Dynamics.

GameMain::Update je hlavní aktualizační smyčka, která aktualizuje stav aplikace jednou za snímek, jak je znázorněno níže. Smyčka aktualizace volá metodu Simple3DGame::RunGame pro vykonání úkolů, pokud je hra ve stavu Dynamics.

// Updates the application state once per frame.
void GameMain::Update()
{
    // The controller object has its own update loop.
    m_controller->Update();

    switch (m_updateState)
    {
    ...
    case UpdateEngineState::Dynamics:
        if (m_controller->IsPauseRequested())
        {
            ...
        }
        else
        {
            // When the player is playing, work is done by Simple3DGame::RunGame.
            GameState runState = m_game->RunGame();
            switch (runState)
            {
                ...

Simple3DGame::RunGame zpracovává sadu dat, která definuje aktuální stav hry pro aktuální iteraci herní smyčky.

Tady je logika toku hry v Simple3DGame::RunGame.

  • Metoda aktualizuje časovač, který počítá sekundy, dokud se úroveň nedokončí, a testuje, jestli vypršela platnost času úrovně. Toto je jedno z pravidel hry – když vyprší časový limit, a pokud nebudou všechny cíle zasaženy, hra končí.
  • Pokud vypršel čas, metoda nastaví herní stav TimeExpired a vrátí se do metody Update v předchozím kódu.
  • Pokud zbývá čas, ovladač pohledu a pohybu se dotazuje na aktualizaci polohy kamery; konkrétně aktualizaci úhlu pohledu normální osy promítané z roviny kamery (kam se hráč dívá) a vzdálenost, o kterou se tento úhel posunul od posledního dotazu ovladače.
  • Kamera se aktualizuje na základě nových dat z ovladače pohledu a pohybu.
  • Aktualizuje se dynamika nebo animace a chování objektů ve herním světě nezávisle na ovládacím prvku hráče. V této ukázkové hře je metoda Simple3DGame::UpdateDynamics volána na aktualizaci pohybu municích koulí, které byly vypáleny, animací pilířových překážek a pohybu cílů. Další informace naleznete v tématu Aktualizace herního světa.
  • Metoda zkontroluje, zda byla splněna kritéria pro úspěšné dokončení úrovně. Pokud ano, finalizuje skóre pro úroveň a zkontroluje, jestli se jedná o poslední úroveň (z 6). Pokud je to poslední úroveň, vrátí metoda GameState::GameComplete stav hry; v opačném případě vrátí GameState::LevelComplete stav hry.
  • Pokud úroveň není dokončena, metoda nastaví stav hry na GameState::Activea vrátí.

Aktualizace herního světa

V této ukázce, když je hra spuštěna, metoda Simple3DGame::UpdateDynamics je volána z metody Simple3DGame::RunGame (která je volána z GameMain::Update) k aktualizaci objektů, které jsou vykresleny v herní scéně.

Smyčka, jako je UpdateDynamics, volá veškeré metody používané k tomu, aby herní svět byl uveden do pohybu nezávisle na zásahu hráče, čímž vytváří poutavý herní zážitek a přivádí úroveň k životu. To zahrnuje grafiku, která se musí vykreslit, a spouštění animačních smyček, které by přinesly dynamický svět, i když neexistuje žádný vstup přehrávače. Ve vaší hře by to mohlo zahrnovat stromy kývající se ve větru, vlny čeřící se podél břehů, stroje kouřící a mimozemská monstra protahující se a pohybující se kolem. Zahrnuje také interakci mezi objekty, včetně kolizí mezi sférou hráče a světem nebo mezi municí a překážkami a cíli.

Kromě případů, kdy je hra výslovně pozastavena, by měla herní smyčka pokračovat v aktualizaci herního světa, ať už je to založeno na herní logice, fyzikálních algoritmech, nebo zda je to čistě náhodné.

Ve vzorové hře se tento princip nazývá dynamikaa zahrnuje vzestup a pád pilířových překážek, pohyb a fyzické chování muničních koulí, jak jsou vystřeleny a v pohybu.

Metoda Simple3DGame::UpdateDynamics

Tato metoda se zabývá těmito čtyřmi sadami výpočtů.

  • Pozice vystřelených koulí střeliva ve světě.
  • Animace pilířových překážek.
  • Průnik mezi hráčem a hranicemi světa.
  • Srážky střeliva s překážkami, cíli, dalšími střelivovými koulemi a světem.

Animace překážek probíhá ve smyčce definované v souboru Animate.h/.cpp zdrojový kód. Chování ammo a všech kolizí jsou definovány zjednodušenými fyzikálními algoritmy, které jsou dodávány v kódu a parametrizovány sadou globálních konstant pro herní svět, včetně závažnosti a materiálových vlastností. To vše se počítá v souřadnicovém prostoru herního světa.

Kontrola toku

Teď, když jsme aktualizovali všechny objekty ve scéně a vypočítali případné kolize, potřebujeme tyto informace použít k vykreslení odpovídajících vizuálních změn.

Jakmile GameMain::Update dokončí aktuální iteraci herní smyčky, ukázka okamžitě zavolá GameRenderer::Render, aby převzala aktualizovaná data objektu a vygenerovala novou scénu, kterou představí hráči, jak je uvedeno níže.

void GameMain::Run()
{
    while (!m_windowClosed)
    {
        if (m_visible)
        {
            switch (m_updateState)
            {
            case UpdateEngineState::Deactivated:
            case UpdateEngineState::TooSmall:
                ...
                // Otherwise, fall through and do normal processing to perform rendering.
            default:
                CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(
                    CoreProcessEventsOption::ProcessAllIfPresent);
                // GameMain::Update calls Simple3DGame::RunGame. If game is in Dynamics
                // state, uses Simple3DGame::UpdateDynamics to update game world.
                Update();
                // Render is called immediately after the Update loop.
                m_renderer->Render();
                m_deviceResources->Present();
                m_renderNeeded = false;
            }
        }
        else
        {
            CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(
                CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }
    m_game->OnSuspending();  // Exiting due to window close, so save state.
}

Vykreslení grafiky herního světa

Doporučujeme, aby se grafika ve hře často aktualizovala, ideálně přesně tak často jako hlavní herní smyčka iteruje. Jak smyčka iteruje, stav herního světa se aktualizuje s vstupem hráče nebo bez něj. To umožňuje, aby se počítané animace a chování zobrazovaly hladce. Představte si, že bychom měli jednoduchou scénu vody, která se přesunula, jen když hráč stiskl tlačítko. To by nebylo realistické; dobrá hra vypadá hladce a plynule neustále.

Vzpomeňte si na smyčku ukázkové hry, jak je znázorněno výše v GameMain::Run. Pokud je hlavní okno hry viditelné a není přichyceno nebo deaktivováno, hra se průběžně aktualizuje a vykresluje výsledky této aktualizace. Metodu GameRenderer::Render, kterou budeme dále zkoumat, vykresluje reprezentaci tohoto stavu. To se provádí okamžitě po volání GameMain::Update, které zahrnuje Simple3DGame::RunGame ke aktualizaci stavů, jak je popsáno v předchozí části.

GameRenderer::Render vykresluje projekci 3D světa a pak na něj nakreslí překrytí Direct2D. Po dokončení zobrazí konečný swap chain s kombinovanými vyrovnávacími paměťmi k zobrazení.

Poznámka:

Existují dva stavy pro překrytí Direct2D ukázkové hry – jeden, kde hra zobrazuje překrytí herních informací, které obsahuje rastrovou mapu pro nabídku pozastavení, a jeden, kde hra zobrazuje křížky spolu s obdélníky pro ovladač pro pohyb-vzhled dotykové obrazovky. Text výsledku se zobrazuje v obou stavech. Další informace naleznete v tématu Rendering Framework I: Úvod k vykreslování.

Metoda GameRenderer::Render

void GameRenderer::Render()
{
    bool stereoEnabled{ m_deviceResources->GetStereoState() };

    auto d3dContext{ m_deviceResources->GetD3DDeviceContext() };
    auto d2dContext{ m_deviceResources->GetD2DDeviceContext() };

    ...
        if (m_game != nullptr && m_gameResourcesLoaded && m_levelResourcesLoaded)
        {
            // This section is only used after the game state has been initialized and all device
            // resources needed for the game have been created and associated with the game objects.
            ...
            for (auto&& object : m_game->RenderObjects())
            {
                object->Render(d3dContext, m_constantBufferChangesEveryPrim.get());
            }
        }

        d3dContext->BeginEventInt(L"D2D BeginDraw", 1);
        d2dContext->BeginDraw();

        // To handle the swapchain being pre-rotated, set the D2D transformation to include it.
        d2dContext->SetTransform(m_deviceResources->GetOrientationTransform2D());

        if (m_game != nullptr && m_gameResourcesLoaded)
        {
            // This is only used after the game state has been initialized.
            m_gameHud.Render(m_game);
        }

        if (m_gameInfoOverlay.Visible())
        {
            d2dContext->DrawBitmap(
                m_gameInfoOverlay.Bitmap(),
                m_gameInfoOverlayRect
                );
        }
        ...
    }
}

Třída Simple3DGame

Jedná se o metody a datové členy, které jsou definovány Simple3DGame třídy.

Členské funkce

Veřejné členské funkce definované Simple3DGame zahrnují následující funkce.

  • Inicializovat. Nastaví počáteční hodnoty globálních proměnných a inicializuje herní objekty. To je popsáno v oddílu Inicializace a spuštění hry.
  • LoadGame. Inicializuje novou úroveň a začne ji načítat.
  • LoadLevelAsync. Korutina, která inicializuje úroveň, a potom spustí další korutinu v rendereru pro načtení prostředků specifických pro zařízení. Tato metoda běží v samostatném vlákně; V důsledku toho lze z tohoto vlákna volat pouze metody ID3D11Device (na rozdíl od metod ID3D11DeviceContext). Všechny metody kontextu zařízení jsou volány v metodě FinalizeLoadLevel. Pokud s asynchronním programováním teprve začínáte, podívejte se na Asynchronní operace a souběžnost sC++/WinRT.
  • FinalizeLoadLevel. Dokončí veškerou práci potřebnou pro načítání úrovně, která musí být provedena na hlavním vlákně. To zahrnuje všechna volání do kontextu zařízení Direct3D 11 (ID3D11DeviceContext).
  • StartLevel . Spustí hru na nové úrovni.
  • PauseGame. Pozastaví hru.
  • SpustitHru. Spustí iteraci herní smyčky. Volá se z aplikace App::Update jednou při každé iteraci herní smyčky, pokud je stav hry Aktivní.
  • OnSuspending a OnResuming. Pozastavte/obnovte zvuk hry.

Tady jsou soukromé členské funkce.

  • LoadSavedState a SaveState. Načtěte nebo uložte aktuální stav hry.
  • LoadHighScore a SaveHighScore. Načtěte nebo uložte vysoké skóre napříč hrami, v uvedeném pořadí.
  • InitializeAmmo. Obnoví stav každého kulového objektu použitého jako munice do původního stavu na začátek každého kola.
  • UpdateDynamics. To je důležitá metoda, protože aktualizuje všechny herní objekty na základě naskenovaných animačních rutin, fyziky a řízení vstupu. Toto je jádro interaktivity, která definuje hru. To je popsáno v části Aktualizace herního světa.

Další veřejné metody jsou vlastnostní přístupové metody, které vrací informace specifické pro hraní a překrytí do rámce aplikace pro zobrazení.

Datové členy

Tyto objekty se aktualizují při spuštění herní smyčky.

  • Objekt MoveLookController. Představuje vstup hráče. Další informace naleznete v tématu Přidání ovládacích prvků.
  • GameRenderer objektu. Představuje renderer Direct3D 11, který zpracovává všechny objekty specifické pro zařízení a jejich vykreslování. Další informace naleznete v tématu Rendering Framework I.
  • audio objekt. Řídí přehrávání zvuku pro hru. Další informace viz Přidání zvuku.

Zbytek herních proměnných obsahuje seznamy primitiv, jejich příslušné množství ve hře a specifická herní data a omezení.

Další kroky

Ještě jsme si promluvili o skutečném vykreslovacím modulu – jak se volání metod Render na aktualizovaných primitivách změní na pixely na obrazovce. Tyto aspekty jsou popsány ve dvou částech –rozhraní Rendering I: Úvod do vykreslování a Rendering framework II: Vykreslování her. Pokud vás více zajímá, jak ovládání hráčem aktualizuje stav hry, podívejte se na Přidání ovládacích prvků.