Aracılığıyla paylaş


Oyun akışı yönetimi

Uyarı

Bu konu, DirectX öğretici serisi ile basit bir Evrensel Windows Platformu (UWP) oluşturma oyunun bir parçasıdır. Bu bağlantıdaki konu başlığı, serinin bağlamını ayarlar.

Oyunun artık bir penceresi var, bazı olay işleyicilerini kaydetti ve varlıkları zaman uyumsuz olarak yükledi. Bu konu başlığı altında oyun durumlarının kullanımı, belirli önemli oyun durumlarının nasıl yönetileceğini ve oyun altyapısı için bir güncelleştirme döngüsünün nasıl oluşturulacağı açıklanmaktadır. Ardından kullanıcı arabirimi akışı hakkında bilgi edineceğiz ve son olarak UWP oyunu için gereken olay işleyicileri hakkında daha fazla bilgi edineceğiz.

Oyun akışını yönetmek için kullanılan oyun durumları

Oyunun akışını yönetmek için oyun durumlarını kullanırız.

Simple3DGameDX örnek oyun bir makinede ilk kez çalıştırıldığında, hiçbir oyunun başlatılmadığı bir durumda olur. Oyun daha sonra çalıştırıldığında, bu durumların herhangi birinde olabilir.

  • Oyun başlatılmadı ya da oyun, düzeyler arasında (yüksek puan sıfır olduğu için) duruyor.
  • Oyun döngüsü çalışıyor ve bir seviyenin ortasında sürdürülüyor.
  • Bir oyun tamamlandığından oyun döngüsü çalışmıyor (yüksek puan sıfır olmayan bir değere sahip).

Oyununuz ihtiyaç duyduğu kadar eyalete sahip olabilir. Ancak istediğiniz zaman sonlandırılabildiğini unutmayın. Ve devam ettiğinde, kullanıcı sonlandırıldığı andaki durumunda devam etmesini bekler.

Oyun durumu yönetimi

Bu nedenle, oyun başlatma sırasında, oyunu soğuk başlatmayı desteklemeniz ve oyunu durdurduktan sonra kaldığınız yerden devam ettirmeniz gerekecek. Simple3DGameDX örneği, hiç durmadığını izlenimi vermek için her zaman oyun durumunu kaydeder.

Askıya alma olayına yanıt olarak oyun askıya alınır, ancak oyunun kaynakları hala bellektedir. Benzer şekilde, örnek oyunun askıya alındığı veya sonlandırıldığı durumdan devam etmesini sağlamak için devam etme olayı işlenir. Duruma bağlı olarak, oyuncuya farklı seçenekler sunulur.

  • Oyun bir düzeyin ortasından devam ederse, duraklatılmış gibi görünür ve ekran devam etme seçeneği sunar.
  • Oyun, oyunun tamamlandığı bir durumda devam ederse, yüksek puanları ve yeni bir oyun oynama seçeneğini görüntüler.
  • Son olarak, oyun bir düzey başlamadan önce devam ederse katman kullanıcıya bir başlangıç seçeneği sunar.

Örnek oyun, oyunun soğuk başlangıç, askıya alma olayı olmadan ilk kez başlatma veya askıya alma durumundan devam etme durumunu ayırt etmez. Bu, herhangi bir UWP uygulaması için uygun tasarımdır.

Bu örnekte, oyun durumlarının başlatılması GameMain::InitializeGameState gerçekleşir (sonraki bölümde bu yöntemin ana hattı gösterilir).

Akışı görselleştirmenize yardımcı olacak bir akış çizelgesi aşağıdadır. Hem başlatma hem de güncelleştirme döngüsünü kapsar.

  • Başlatma, geçerli oyun durumunu denetlediğinizde Başlangıç düğümünde başlar. Oyun kodu için sonraki bölümdeki GameMain::InitializeGameState bölümüne bakın.

oyunumuz için ana durum makinesi

GameMain::InitializeGameState yöntemi

GameMain::InitializeGameState yöntemi, App::Loadiçinde bir GameMain örneği oluşturmanın sonucu olan GameMain sınıfı oluşturucu aracılığıyla dolaylı olarak çağrılır.

GameMain::GameMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
{
    m_deviceResources->RegisterDeviceNotify(this);
    ...
    ConstructInBackground();
}

winrt::fire_and_forget GameMain::ConstructInBackground()
{
    ...
    m_renderer->FinalizeCreateGameDeviceResources();

    InitializeGameState();
    ...
}

void GameMain::InitializeGameState()
{
    // Set up the initial state machine for handling Game playing state.
    if (m_game->GameActive() && m_game->LevelActive())
    {
        // The last time the game terminated it was in the middle
        // of a level.
        // We are waiting for the user to continue the game.
        ...
    }
    else if (!m_game->GameActive() && (m_game->HighScore().totalHits > 0))
    {
        // The last time the game terminated the game had been completed.
        // Show the high score.
        // We are waiting for the user to acknowledge the high score and start a new game.
        // The level resources for the first level will be loaded later.
        ...
    }
    else
    {
        // This is either the first time the game has run or
        // the last time the game terminated the level was completed.
        // We are waiting for the user to begin the next level.
        ...
    }
    m_uiControl->ShowGameInfoOverlay();
}

Oyun motorunu güncelleştirme

App::Run yöntemi GameMain::Runçağırır. GameMain::Run, kullanıcının gerçekleştirebileceği tüm önemli eylemleri işlemeye yönelik temel bir durum makinesidir. Bu durum makinesinin en yüksek düzeyi, bir oyunu yükleme, belirli bir seviyeyi oynama veya oyun duraklatıldıktan sonra (sistem veya kullanıcı tarafından) bir düzeye devam etme ile ilgilenir.

Örnek oyunda, oyunun içinde bulunabileceği 3 ana durum (UpdateEngineState numaralandırması ile temsil edilir) vardır.

  1. UpdateEngineState::WaitingForResources. Oyun döngüsü döngüdedir, kaynaklar (özellikle grafik kaynakları) kullanılabilir olana kadar geçiş yapamaz. Zaman uyumsuz kaynak yükleme görevleri tamamlandığında, durumu UpdateEngineState::ResourcesLoadedolarak güncelleriz. Bu durum genellikle düzeyler arasında, düzey diskten, oyun sunucusundan veya bulut arka plandan yeni kaynaklar yüklerken gerçekleşir. Örnek oyunda bu davranışın benzetimini yapıyoruz çünkü örnekte o sırada ek düzey başına kaynak gerekmez.
  2. updateEngineState::WaitingForPress. Oyun döngüsü dönüyor ve belirli bir kullanıcı girdisi bekliyor. Bu giriş, bir oyunu yüklemek, bir düzey başlatmak veya bir düzeye devam etmek için bir oyuncu eylemidir. Örnek kod, PressResultState numaralandırması aracılığıyla bu alt durumlara başvurur.
  3. UpdateEngineState::D ynamics. Oyun döngüsü, kullanıcı oynarken çalışıyor. Kullanıcı oynarken, oyun geçiş için 3 koşul olup olmadığını denetler:
  • GameState::TimeExpired. Bir düzey için süre sınırının sona ermesi.
  • GameState::LevelComplete. Oyuncu tarafından bir seviyenin tamamlanması.
  • GameState::GameComplete. Tüm düzeylerin oyuncu tarafından tamamlanması.

Bir oyun, birden fazla küçük durum makinesi içeren bir durum makinesidir. Her belirli durum çok belirli ölçütler tarafından tanımlanmalıdır. Bir durumdan diğerine geçişler, ayrık kullanıcı girişlerini veya sistem eylemlerini (grafik kaynağı yükleme gibi) temel almalıdır.

Oyununuzu planlarken, kullanıcının veya sistemin gerçekleştirebileceği tüm olası eylemleri ele alındığından emin olmak için oyun akışının tamamını çizmeyi göz önünde bulundurun. Bir oyun çok karmaşık olabilir, bu nedenle durum makinesi bu karmaşıklığı görselleştirmenize ve daha yönetilebilir hale getirmenize yardımcı olan güçlü bir araçtır.

Şimdi güncelleştirme döngüsünün koduna göz atalım.

GameMain::Update yöntemi

Bu, oyun motorunu güncelleştirmek için kullanılan durum makinesinin yapısıdır.

void GameMain::Update()
{
    // The controller object has its own update loop.
    m_controller->Update(); 

    switch (m_updateState)
    {
    case UpdateEngineState::WaitingForResources:
        ...
        break;

    case UpdateEngineState::ResourcesLoaded:
        ...
        break;

    case UpdateEngineState::WaitingForPress:
        if (m_controller->IsPressComplete())
        {
            ...
        }
        break;

    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)
            {
            case GameState::TimeExpired:
                ...
                break;

            case GameState::LevelComplete:
                ...
                break;

            case GameState::GameComplete:
                ...
                break;
            }
        }

        if (m_updateState == UpdateEngineState::WaitingForPress)
        {
            // Transitioning state, so enable waiting for the press event.
            m_controller->WaitForPress(
                m_renderer->GameInfoOverlayUpperLeft(),
                m_renderer->GameInfoOverlayLowerRight());
        }
        if (m_updateState == UpdateEngineState::WaitingForResources)
        {
            // Transitioning state, so shut down the input controller
            // until resources are loaded.
            m_controller->Active(false);
        }
        break;
    }
}

Kullanıcı arabirimini güncelleştirme

Oyuncuyu sistemin durumundan haberdar etmemiz ve oyuncunun eylemlerine ve oyunu tanımlayan kurallara bağlı olarak oyun durumunun değişmesine izin vermemiz gerekir. Bu örnek oyun dahil olmak üzere birçok oyun, bu bilgileri oyuncuya sunmak için yaygın olarak kullanıcı arabirimi (UI) öğelerini kullanır. Kullanıcı arabirimi, oyun durumunun temsillerini ve puan, cephane veya kalan şans sayısı gibi oyuna özgü diğer bilgileri içerir. Kullanıcı arabirimi, ana grafik işlem hattından ayrı olarak işlendiğinden ve 3B projeksiyonun üzerine yerleştirildiğinden katman olarak da adlandırılır.

Bazı kullanıcı arabirimi bilgileri, kullanıcının gözlerini ana oyun alanından tamamen ayırmadan bu bilgileri görmesine olanak sağlayan bir uyarı ekranı (HUD) olarak da sunulur. Örnek oyunda Direct2D API'lerini kullanarak bu katman oluşturulur. Alternatif olarak, Örnek oyunugenişletme bölümünde ele aldığımız XAML kullanarak bu katmanı oluşturabiliriz.

Kullanıcı arabiriminin iki bileşeni vardır.

  • Oyunun geçerli durumu hakkında puanı ve bilgileri içeren HUD.
  • Oyunun duraklatıldığı/askıya alındığı durum sırasında siyah bir dikdörtgen olarak gösterilen ve üzerine metin yerleştirilen duraklatma biti eşlemi. Bu oyun katmanı. Bunukullanıcı arabirimi ekleme bölümünde daha ayrıntılı olarak ele alıyoruz.

Kuşkusuz katmanda da durum makinesi vardır. Ekran kaplaması, bir düzey başlangıcı veya oyun sonu mesajı görüntüleyebilir. Temelde, oyun duraklatılırken veya askıya alınırken oyuncuya göstermek istediğimiz oyun durumu hakkında herhangi bir bilgi verebileceğimiz bir tuvaldir.

Görüntülenen katman, oyunun durumuna bağlı olarak bu altı ekrandan biri olabilir.

  1. Oyunun başlangıcında kaynak yükleme ilerleme ekranı.
  2. Oyun istatistikleri ekranı.
  3. Seviye başlangıç mesajı ekranı.
  4. Tüm seviyeler süre dolmadan tamamlandığında oyun bitti ekranı.
  5. Zaman dolduğunda oyun bitti ekranı.
  6. Duraklat menüsü ekranı.

Kullanıcı arabiriminizi oyununuzun grafik işlem hattından ayırmak, oyunun grafik işleme motorundan bağımsız olarak üzerinde çalışmanıza olanak tanır ve oyununuzun kodunun karmaşıklığını önemli ölçüde azaltır.

Örnek oyun, bindirmenin durum makinesini şu şekilde yapılandırır.

void GameMain::SetGameInfoOverlay(GameInfoOverlayState state)
{
    m_gameInfoOverlayState = state;
    switch (state)
    {
    case GameInfoOverlayState::Loading:
        m_uiControl->SetGameLoading(m_loadingCount);
        break;

    case GameInfoOverlayState::GameStats:
        ...
        break;

    case GameInfoOverlayState::LevelStart:
        ...
        break;

    case GameInfoOverlayState::GameOverCompleted:
        ...
        break;

    case GameInfoOverlayState::GameOverExpired:
        ...
        break;

    case GameInfoOverlayState::Pause:
        ...
        break;
    }
}

Olay işleme

Oyunun UWP uygulama çerçevesini tanımlama konusunda gördüğümüz gibi, App sınıfının görünüm sağlayıcısı yöntemlerinin çoğu olay işleyicilerini kaydeder. Oyun mekaniği eklemeden veya grafik geliştirmeye başlamadan önce bu yöntemlerin bu önemli olayları doğru şekilde işlemesi gerekir.

Söz konusu olayların düzgün işlenmesi UWP uygulama deneyimi için temeldir. Bir UWP uygulaması herhangi bir anda etkinleştirilebildiği, devre dışı bırakılabildiği, yeniden boyutlandırılabildiği, tutturulabildiği, tutturulmasının geri alındığı, askıya alınabildiği veya devam ettirilebildiği için, oyunun bu olaylara olabildiğince çabuk kaydolması ve bunları oyuncuya sorunsuz ve öngörülebilir bir deneyim sağlamak için işlemesi gerekir.

Bunlar, bu örnekte kullanılan olay işleyicileri ve işledikleri olaylardır.

Olay işleyicisi Açıklama
Etkinleştirildiğinde CoreApplicationView::Activatedyönetir. Oyun uygulaması ön plana getirildiğinden ana pencere etkinleştirilir.
OnDpiChanged Graphics::Display::DisplayInformation::DpiChangedele alır. Ekranın DPI'sı değişti ve oyun kaynaklarını buna göre ayarlıyor.
CoreWindow koordinatlarının Direct2Diçin cihazdan bağımsız piksellerde (DIP) olduğunu unutmayın. Sonuç olarak, herhangi bir 2B varlığı veya temel öğeyi doğru şekilde görüntülemek için DPI'daki değişikliği Direct2D'ye bildirmeniz gerekir.
OnOrientationChanged Graphics::Display::DisplayInformation::OrientationChangedele alır. Görüntü değişikliklerinin ve işlemenin yönünün güncelleştirilmesi gerekir.
OnDisplayContentsGeçersizKılındı Graphics::Display::DisplayInformation::DisplayContentsInvalidatedişler. Ekranın yenilenmesi ve oyununuzun tekrar render edilmesi gerekir.
OnResuming CoreApplication::Resumingele alır. Oyun uygulaması oyunu askıya alınmış durumdan geri yükler.
OnSuspending CoreApplication::Askıya Almayı Ele Alıyor. Oyun uygulaması durumunu diske kaydeder. Durumu depolama alanına kaydetmek için 5 saniye vardır.
OnVisibilityChanged CoreWindow::VisibilityChangedyönetir. Oyun uygulaması görünürlüğü değiştirdi ve ya görünür hale geldi ya da başka bir uygulama tarafından görünmez hale getirildi.
PencereAktivasyonuDeğiştiğinde ele alır CoreWindow::Activated. Oyun uygulamasının ana penceresi devre dışı bırakıldı veya etkinleştirildi, bu nedenle odağı kaldırmalı ve oyunu duraklatmalı veya odağı yeniden kazanmalıdır. Her iki durumda da katman, oyunun duraklatıldığını gösterir.
Pencere Kapatıldığında CoreWindow::Closedişler. Oyun uygulaması ana pencereyi kapatır ve oyunu askıya alır.
PencereBoyutuDeğiştiğinde (OnWindowSizeChanged) CoreWindow::SizeChangedişlemesini sağlar. Oyun uygulaması, boyut değişikliğine uyum sağlamak için grafik kaynaklarını ve katmanlarını yeniden dağıtır ve ardından işleme hedefini güncelleştirir.

Sonraki Adımlar

Bu konu başlığında, genel oyun akışının oyun durumları kullanılarak nasıl yönetildiğini ve bir oyunun birden çok farklı durum makinesinden yapıldığını gördük. Ayrıca kullanıcı arabirimini güncelleştirme ve önemli uygulama olay işleyicilerini yönetme adımlarını da gördük. Artık işleme döngüsünü, oyunu ve mekaniğine dalmaya hazırız.

Bu oyunu herhangi bir sırayla belgeleyen diğer konuları gözden geçirebilirsiniz.