Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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ů.