Bagikan melalui


Tentukan objek permainan utama

Catatan

Topik ini adalah bagian dari membuat permainan Platform Windows Universal sederhana (UWP) dengan seri tutorial DirectX. Topik di tautan tersebut mengatur konteks untuk seri.

Setelah Anda meletakkan kerangka kerja dasar permainan sampel, dan mengimplementasikan mesin status yang menangani perilaku pengguna dan sistem tingkat tinggi, Anda harus memeriksa aturan dan mekanika yang mengubah permainan sampel menjadi permainan. Mari kita lihat detail objek utama game sampel, dan cara menerjemahkan aturan game ke dalam interaksi dengan dunia game.

Tujuan

  • Pelajari cara menerapkan teknik pengembangan dasar untuk menerapkan aturan dan mekanisme game untuk game UWP DirectX.

Objek permainan utama

Dalam permainan sampel Simple3DGameDX, Simple3DGame adalah kelas objek game utama. Instans Simple3DGame dibangun, secara tidak langsung, melalui metode App::Load .

Berikut adalah beberapa fitur kelas Simple3DGame .

  • Berisi implementasi logika gameplay.
  • Berisi metode yang mengomunikasikan detail ini.
    • Perubahan status game pada komputer status yang ditentukan dalam kerangka kerja aplikasi.
    • Perubahan status game dari aplikasi ke objek game itu sendiri.
    • Detail untuk memperbarui UI game (tampilan overlay dan heads-up), animasi, dan fisika (dinamika).

    Catatan

    Pembaruan grafis ditangani oleh kelas GameRenderer , yang berisi metode untuk mendapatkan dan menggunakan sumber daya perangkat grafis yang digunakan oleh game. Untuk informasi selengkapnya, lihat Merender kerangka kerja I: Pengandalan penyajian.

  • Berfungsi sebagai kontainer untuk data yang mendefinisikan sesi permainan, tingkat, atau masa pakai, tergantung pada bagaimana Anda menentukan permainan Anda pada tingkat tinggi. Dalam hal ini, data status game adalah untuk masa pakai game, dan diinisialisasi satu kali ketika pengguna meluncurkan game.

Untuk melihat metode dan data yang ditentukan oleh kelas ini, lihat Kelas Simple3DGame di bawah ini.

Menginisialisasi dan memulai permainan

Ketika pemain memulai permainan, objek game harus menginisialisasi statusnya, membuat dan menambahkan overlay, mengatur variabel yang melacak performa pemain, dan membuat instans objek yang akan digunakan untuk membangun level. Dalam sampel ini, ini dilakukan ketika instans GameMain dibuat di App::Load.

Objek game, dari jenis Simple3DGame, dibuat di konstruktor GameMain::GameMain . Kemudian diinisialisasi menggunakan metode Simple3DGame::Initialize selama GameMain::ConstructInBackground fire-and-forget coroutine, yang disebut dari GameMain::GameMain.

Metode Simple3DGame::Initialize

Contoh permainan menyiapkan komponen-komponen ini di objek game.

  • Objek pemutaran audio baru dibuat.
  • Array untuk primitif grafis game dibuat, termasuk array untuk tingkat primitif, amunisi, dan rintangan.
  • Lokasi untuk menyimpan data status game dibuat, bernama Game, dan ditempatkan di lokasi penyimpanan pengaturan data aplikasi yang ditentukan oleh ApplicationData::Current.
  • Timer permainan dan bitmap overlay awal dalam game dibuat.
  • Kamera baru dibuat dengan serangkaian parameter tampilan dan proyeksi tertentu.
  • Perangkat input (pengontrol) diatur ke pitch awal dan yaw yang sama dengan kamera, sehingga pemutar memiliki korespondensi 1-ke-1 antara posisi kontrol awal dan posisi kamera.
  • Objek pemutar dibuat dan diatur ke aktif. Kami menggunakan objek bola untuk mendeteksi kedekatan pemutar dengan dinding dan rintangan dan untuk menjaga kamera agar tidak ditempatkan dalam posisi yang mungkin merusak perendaman.
  • Primitif dunia game dibuat.
  • Rintangan silinder dibuat.
  • Target (Objek wajah ) dibuat dan diberi nomor.
  • Bola amunisi dibuat.
  • Tingkat dibuat.
  • Skor tinggi dimuat.
  • Status permainan tersimpan sebelumnya dimuat.

Gim ini sekarang memiliki contoh semua komponen utama—dunia, pemain, rintangan, target, dan bola amunisi. Ini juga memiliki instans tingkat, yang mewakili konfigurasi semua komponen di atas dan perilakunya untuk setiap tingkat tertentu. Sekarang mari kita lihat bagaimana permainan membangun level.

Membangun dan memuat tingkat permainan

Sebagian besar pengangkatan berat untuk konstruksi tingkat dilakukan dalam file yang Level[N].h/.cpp ditemukan di folder GameLevels dari solusi sampel. Karena berfokus pada implementasi yang sangat spesifik, kami tidak akan membahasnya di sini. Yang penting adalah bahwa kode untuk setiap tingkat dijalankan sebagai objek Level[N] terpisah. Jika Anda ingin memperluas permainan, Anda dapat membuat objek Level[N] yang mengambil angka yang ditetapkan sebagai parameter dan secara acak menempatkan rintangan dan target. Atau, Anda dapat memilikinya memuat data konfigurasi tingkat dari file sumber daya, atau bahkan internet.

Tentukan gameplay

Pada titik ini, kita memiliki semua komponen yang kita butuhkan untuk mengembangkan permainan. Level telah dibangun dalam memori dari primitif, dan siap bagi pemutar untuk mulai berinteraksi.

Game terbaik bereaksi langsung terhadap input pemain, dan memberikan umpan balik langsung. Ini berlaku untuk semua jenis permainan, mulai dari penembak orang pertama aksi kedokteran real time hingga game strategi berbasis giliran yang bijaksana.

Metode Simple3DGame::RunGame

Saat level game sedang berlangsung, game berada dalam status Dynamics .

GameMain::Update adalah perulangan pembaruan utama yang memperbarui status aplikasi sekali per bingkai, seperti yang ditunjukkan di bawah ini. Perulangan pembaruan memanggil metode Simple3DGame::RunGame untuk menangani pekerjaan jika game berada dalam status 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 menangani kumpulan data yang menentukan status permainan saat ini untuk iterasi perulangan game saat ini.

Berikut adalah logika alur game di Simple3DGame::RunGame.

  • Metode memperbarui timer yang menghitung mundur detik hingga tingkat selesai, dan menguji untuk melihat apakah waktu tingkat telah kedaluwarsa. Ini adalah salah satu aturan permainan —ketika waktu habis, jika tidak semua target telah ditembak, maka permainan berakhir.
  • Jika waktu telah habis, maka metode mengatur status permainan TimeExpired , dan kembali ke metode Perbarui di kode sebelumnya.
  • Jika waktu tetap ada, pengontrol move-look akan dijajaki untuk pembaruan ke posisi kamera; secara khusus, pembaruan pada sudut pandang proyeksi normal dari bidang kamera (di mana pemutar mencari), dan jarak yang telah dipindahkan sudut sejak pengontrol dijajaki terakhir.
  • Kamera diperbarui berdasarkan data baru dari pengontrol tampilan pemindahan.
  • Dinamika, atau animasi dan perilaku objek di dunia game yang independen dari kontrol pemain, diperbarui. Dalam permainan sampel ini, metode Simple3DGame::UpdateDynamics dipanggil untuk memperbarui gerakan bola amunisi yang telah ditembakkan, animasi rintangan pilar dan pergerakan target. Untuk informasi selengkapnya, lihat Memperbarui dunia game.
  • Metode memeriksa untuk melihat apakah kriteria untuk keberhasilan penyelesaian tingkat telah terpenuhi. Jika demikian, ini menyelesaikan skor untuk tingkat tersebut, dan memeriksa untuk melihat apakah ini adalah tingkat terakhir (dari 6). Jika ini adalah level terakhir, maka metode mengembalikan status game GameState::GameComplete ; jika tidak, ia mengembalikan status game GameState::LevelComplete .
  • Jika level belum selesai, maka metode mengatur status game ke GameState::Active, dan mengembalikan.

Memperbarui dunia game

Dalam sampel ini, ketika permainan berjalan, metode Simple3DGame::UpdateDynamics dipanggil dari metode Simple3DGame::RunGame (yang dipanggil dari GameMain::Update) untuk memperbarui objek yang dirender dalam adegan game.

Perulangan seperti UpdateDynamics memanggil metode apa pun yang digunakan untuk mengatur dunia permainan bergerak, independen dari input pemain, untuk menciptakan pengalaman permainan yang imersif dan membuat level menjadi hidup. Ini termasuk grafik yang perlu dirender, dan menjalankan perulangan animasi untuk menghadirkan dunia dinamis bahkan ketika tidak ada input pemutar. Dalam permainan Anda, itu bisa termasuk pohon yang bergoyang di angin, ombak berderit di sepanjang garis pantai, merokok mesin, dan monster alien yang membentang dan bergerak. Ini juga mencakup interaksi antara objek, termasuk tabrakan antara bola pemain dan dunia, atau antara amunisi dan rintangan dan target.

Kecuali ketika permainan dijeda secara khusus, perulangan permainan harus terus memperbarui dunia game; apakah itu didasarkan pada logika permainan, algoritma fisik, atau apakah itu hanya acak biasa.

Dalam permainan sampel, prinsip ini disebut dinamika, dan mencakup kebangkitan dan jatuhnya rintangan pilar, dan gerakan dan perilaku fisik bola amunisi saat mereka ditembakkan dan bergerak.

Metode Simple3DGame::UpdateDynamics

Metode ini berkaitan dengan empat set komputasi ini.

  • Posisi bola amunisi yang ditembakkan di dunia.
  • Animasi rintangan pilar.
  • Persimpangan pemain dan batas dunia.
  • Tabrakan bola amunisi dengan rintangan, target, bola amunisi lainnya, dan dunia.

Animasi rintangan terjadi dalam perulangan yang ditentukan dalam file kode sumber Animate.h/.cpp . Perilaku amunisi dan tabrakan didefinisikan oleh algoritma fisika yang disederhanakan, disediakan dalam kode dan diparameterkan oleh serangkaian konstanta global untuk dunia game, termasuk gravitasi dan properti material. Ini semua dihitung dalam ruang koordinat dunia game.

Meninjau alur

Sekarang setelah kita memperbarui semua objek dalam adegan, dan menghitung tabrakan apa pun, kita perlu menggunakan info ini untuk menggambar perubahan visual yang sesuai.

Setelah GameMain::Update menyelesaikan iterasi perulangan game saat ini, sampel segera memanggil GameRenderer::Render untuk mengambil data objek yang diperbarui dan menghasilkan adegan baru untuk disajikan kepada pemain, seperti yang ditunjukkan di bawah ini.

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

Merender grafik dunia game

Kami menyarankan agar grafik dalam pembaruan game sering, idealnya persis sesering perulangan game utama berulang. Saat perulangan berulang, status dunia game diperbarui, dengan atau tanpa input pemain. Ini memungkinkan animasi dan perilaku terhitung ditampilkan dengan lancar. Bayangkan jika kita memiliki adegan air sederhana yang bergerak hanya ketika pemain menekan tombol. Itu tidak akan realistis; Permainan yang bagus terlihat halus dan lancar sepanjang waktu.

Ingat perulangan permainan sampel seperti yang ditunjukkan di atas di GameMain::Run. Jika jendela utama game terlihat, dan tidak di-snap atau dinonaktifkan, maka game terus memperbarui dan merender hasil pembaruan tersebut. Metode GameRenderer::Render yang kami periksa selanjutnya merender representasi status tersebut. Ini dilakukan segera setelah panggilan ke GameMain::Update, yang mencakup Simple3DGame::RunGame untuk memperbarui status, seperti yang dibahas di bagian sebelumnya.

GameRenderer::Render menarik proyeksi dunia 3D, dan kemudian menarik overlay Direct2D di atasnya. Setelah selesai, ini menyajikan rantai pertukaran akhir dengan buffer gabungan untuk ditampilkan.

Catatan

Ada dua status untuk overlay Direct2D permainan sampel—satu di mana permainan menampilkan overlay info game yang berisi bitmap untuk menu jeda, dan satu di mana permainan menampilkan crosshair bersama dengan persegi panjang untuk pengontrol tampilan-pemindah layar sentuh. Teks skor digambar di kedua status. Untuk informasi selengkapnya, lihat Merender kerangka kerja I: Pengandalan penyajian.

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

Kelas Simple3DGame

Ini adalah metode dan anggota data yang ditentukan oleh kelas Simple3DGame .

Fungsi anggota

Fungsi anggota publik yang ditentukan oleh Simple3DGame mencakup fungsi di bawah ini.

  • Inisialisasi. Mengatur nilai awal variabel global, dan menginisialisasi objek game. Ini tercakup dalam bagian Inisialisasi dan mulai permainan .
  • LoadGame. Menginisialisasi tingkat baru, dan mulai memuatnya.
  • LoadLevelAsync. Koroutine yang menginisialisasi tingkat, lalu memanggil koroutine lain pada perender untuk memuat sumber daya tingkat khusus perangkat. Metode ini berjalan dalam utas terpisah; akibatnya, hanya metode ID3D11Device (dibandingkan dengan metode ID3D11DeviceContext ) yang dapat dipanggil dari utas ini. Metode konteks perangkat apa pun dipanggil dalam metode FinalizeLoadLevel . Jika Anda baru menggunakan pemrograman asinkron, lihat Operasi konkurensi dan asinkron dengan C++/WinRT.
  • FinalizeLoadLevel. Menyelesaikan pekerjaan apa pun untuk pemuatan tingkat yang perlu dilakukan pada utas utama. Ini termasuk panggilan apa pun ke metode konteks perangkat Direct3D 11 (ID3D11DeviceContext).
  • StartLevel. Memulai gameplay untuk level baru.
  • JedaGame. Menjeda permainan.
  • RunGame. Menjalankan perulangan permainan. Ini dipanggil dari App::Update satu kali setiap iterasi perulangan game jika status game Aktif.
  • OnSuspending dan OnResuming. Tangguhkan/lanjutkan audio permainan.

Berikut adalah fungsi anggota privat.

  • LoadSavedState dan SaveState. Muat/simpan status permainan saat ini.
  • LoadHighScore dan SaveHighScore. Muat/simpan skor tinggi di seluruh game.
  • InitializeAmmo. Mengatur ulang status setiap objek bola yang digunakan sebagai amunisi kembali ke status aslinya untuk awal setiap putaran.
  • UpdateDynamics. Ini adalah metode penting karena memperbarui semua objek game berdasarkan rutinitas animasi kalengan, fisika, dan input kontrol. Ini adalah inti dari interaktivitas yang mendefinisikan permainan. Ini tercakup dalam bagian Perbarui dunia game.

Metode publik lainnya adalah aksesor properti yang mengembalikan informasi khusus gameplay dan overlay ke kerangka kerja aplikasi untuk ditampilkan.

Anggota data

Objek-objek ini diperbarui saat perulangan permainan berjalan.

  • Objek MoveLookController . Mewakili input pemutar. Untuk informasi selengkapnya, lihat Menambahkan kontrol.
  • Objek GameRenderer . Mewakili perender Direct3D 11, yang menangani semua objek khusus perangkat dan penyajiannya. Untuk informasi selengkapnya, lihat Merender kerangka kerja I.
  • Objek audio . Mengontrol pemutaran audio untuk permainan. Untuk informasi selengkapnya, lihat Menambahkan suara.

Variabel game lainnya berisi daftar primitif, dan jumlah dalam game masing-masing, dan permainan memainkan data dan batasan tertentu.

Langkah berikutnya

Kami belum berbicara tentang mesin penyajian aktual—bagaimana panggilan ke metode Render pada primitif yang diperbarui diubah menjadi piksel di layar Anda. Aspek-aspek tersebut tercakup dalam dua bagian—Rendering framework I: Intro to rendering dan Rendering framework II: Rendering game. Jika Anda lebih tertarik dengan cara pemain mengontrol status permainan, lihat Menambahkan kontrol.