Warning: Undefined array key "HTTP_ACCEPT_LANGUAGE" in /var/www/vhosts/bilgigunlugum.net/httpdocs/index.php on line 43
Unreal Engine

Unreal Engine Oyun Programlama sayfalarımız yayında...

Ana sayfa > Oyun programlama > Unreal Engine > GameMode oluşturma

Unreal Engine gamemode oluşturma

Bu bölümde, proje için yeni bir GameMode sınıfı tanımlayacağız.

Dizin yapısı oluşturma

Projede oluşturacağımız tüm değerleri kaydetmek için,

1. "Content" dizini altında "_Game" adlı bir dizin oluşturalım.

2. "_Game" dizini altında "Blueprints" adlı bir dizin ve bu dizin altında aşağıdaki dizinleri oluşturalım.

  • Actors
  • Characters
  • GameModes
  • Pawns
  • PlayerControllers

GameMode sınıfı oluşturma

1. Unreal Engine Editor'da, ana menüden "Tools - New C++ Class" seçeneği ile açılan "Add C++ Class" penceresinde "Game Mode Base" seçeneği seçili iken, "Next" butonuna tıklayalım.

2. Açılan pencerede "Name" satırına "MyGameGameMode" değerini girdikten sonra "Create Class" butonuna tıklayarak GameMode sınıfını oluşturalım.

3. Dosyaların içerikleri aşağıdaki şekilde olacaktır:


// MyGameGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameGameMode.generated.h"

/**
 * 
 */
UCLASS()
class MYGAME_API AMyGameGameMode : public AGameModeBase
{
	GENERATED_BODY()
	
};


// MyGameGameMode.cpp

#include "MyGameGameMode.h"

4. Kodlar derlendikten sonra "C++ Classes" adlı bir dizin ve bu dizin altında "MyGame" adlı bir dizin ile bu dizin altında "MyGameGameMode" adlı bir dosya otomatik olarak oluşturulacaktır.

GameMode sınıfı kod içeriğini düzenleme

GameMode sınıfı kod dosyalarının içeriğini aşağıdaki şekilde düzenleyelim:


// MyGameGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameGameMode.generated.h"

/* MyGame projesi için özel GameMode sınıfı. Oyun kurallarını ve mantığını yönetir. */
UCLASS()
class MYGAME_API AMyGameGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    // Constructor
    AMyGameGameMode();

protected:
    //////////////////////////////////////////////////////////////////////////
    // Oyuncu Bağlantısı ve Başlangıç Fonksiyonları

    /**
     * Oyuncu bağlandığında çağrılır
     * @param NewPlayer - Yeni bağlanan oyuncunun PlayerController'ı
     */
    virtual void PostLogin(APlayerController* NewPlayer) override;

    /**
     * Oyuncu ayrıldığında çağrılır
     * @param ExitingPlayer - Oyundan ayrılan oyuncunun PlayerController'ı
     */
    virtual void Logout(AController* ExitingPlayer) override;

    /**
     * Oyun başladığında çağrılır
     */
    virtual void BeginPlay() override;

    /**
     * Oyuncu spawn edildiğinde çağrılır (varsayılan spawn işleminden sonra)
     * @param NewPlayer - Oyuncunun PlayerController'ı
     */
    virtual void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) override;

    //////////////////////////////////////////////////////////////////////////
    // Spawn Ayarları

    /**
     * Yeni bir pawn spawn etmek için çağrılır
     * @param NewPlayer - Spawn edilecek pawn'ın PlayerController'ı
     * @return Spawn edilen pawn
     */
    virtual APawn* SpawnDefaultPawnFor_Implementation(AController* NewPlayer, AActor* StartSpot) override;

public:
    //////////////////////////////////////////////////////////////////////////
    // Oyun Mantığı Fonksiyonları

    /**
     * Oyunu başlat
     * Blueprint'lerden çağrılabilir
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Game Flow")
    void StartGame();

    /**
     * Oyunu bitir
     * @param bPlayerWon - Oyuncu kazandı mı?
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Game Flow")
    void EndGame(bool bPlayerWon);

    /**
     * Oyun durumunu değiştir
     * @param NewState - Yeni oyun durumu
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Game State")
    void ChangeGameState(FName NewState);

    /**
     * Oyuncu skorunu güncelle
     * @param PlayerController - Skoru güncellenecek oyuncu
     * @param ScoreToAdd - Eklenecek skor miktarı
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Score")
    void AddPlayerScore(APlayerController* PlayerController, int32 ScoreToAdd);

    //////////////////////////////////////////////////////////////////////////
    // Blueprint Implementable Events - Blueprint'te override edilebilir

    /**
     * Oyun durumu değiştiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param OldState - Eski durum
     * @param NewState - Yeni durum
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnGameStateChanged(FName OldState, FName NewState);

    /**
     * Oyuncu skoru değiştiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param PlayerController - Skoru değişen oyuncu
     * @param OldScore - Eski skor
     * @param NewScore - Yeni skor
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnPlayerScoreChanged(APlayerController* PlayerController, float OldScore, float NewScore);

    /**
     * Timer güncellendiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param RemainingTimeSeconds - Kalan süre (saniye)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnTimerUpdated(float RemainingTimeSeconds);

    //////////////////////////////////////////////////////////////////////////
    // Blueprint Ayarlanabilir Özellikler

    /**
     * Oyun başlangıç durumu
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Configuration")
    FName InitialGameState;

    /**
     * Oyun süresi (saniye)
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Configuration", meta = (ClampMin = "0"))
    float GameDuration;

    /**
     * Maksimum oyuncu sayısı
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Configuration", meta = (ClampMin = "1", ClampMax = "16"))
    int32 MaxPlayers;

    /**
     * Oyun bitti mi?
     */
    UPROPERTY(BlueprintReadOnly, Category = "MyGame|Game State")
    bool bIsGameOver;

    /**
     * Oyun başladı mı?
     */
    UPROPERTY(BlueprintReadOnly, Category = "MyGame|Game State")
    bool bHasGameStarted;

    /**
     * Kalan süre
     */
    UPROPERTY(BlueprintReadOnly, Category = "MyGame|Game State")
    float RemainingTime;

protected:
    //////////////////////////////////////////////////////////////////////////
    // İç Değişkenler ve Yardımcı Fonksiyonlar

    /**
     * Süre sayacını güncelle
     */
    void UpdateTimer();

    /**
     * Timer handle for game updates
     */
    FTimerHandle GameTimerHandle;

    /**
     * Mevcut oyun durumu
     */
    FName CurrentGameState;

    /**
     * Oyun durumlarının listesi
     */
    TArray<FName> GameStates;

    /**
     * Aktif oyuncu sayısı
     */
    int32 ActivePlayerCount;
};


// MyGameGameMode.cpp

#include "MyGameGameMode.h"
#include "Engine/World.h"
#include "TimerManager.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "Kismet/GameplayStatics.h"

// Log kategorisi tanımlama
DEFINE_LOG_CATEGORY_STATIC(LogMyGame, Log, All);

AMyGameGameMode::AMyGameGameMode()
{
    // Varsayılan değerleri ayarla
    InitialGameState = FName("Waiting");
    GameDuration = 300.0f; // 5 dakika
    MaxPlayers = 4;

    // Başlangıç durumları
    bIsGameOver = false;
    bHasGameStarted = false;
    RemainingTime = GameDuration;
    ActivePlayerCount = 0;

    // Oyun durumlarını başlat
    GameStates.Add(FName("Waiting"));
    GameStates.Add(FName("Playing"));
    GameStates.Add(FName("Paused"));
    GameStates.Add(FName("Finished"));

    CurrentGameState = InitialGameState;

    // Default sınıfları ayarla (Blueprint'ten override edilebilir)
    // NOT: Bu yolları kendi projenize göre değiştirin veya tamamen kaldırın
    // Eğer bu asset'ler yoksa, constructor'ı basitleştirebilirsiniz

    // Eğer BP_MyGameCharacter blueprint'iniz varsa bu kodu kullanın
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnClassFinder(TEXT("/Game/_Game/Blueprints/Pawns/BP_MyGameCharacter"));
    if (PlayerPawnClassFinder.Class != nullptr)
    {
        DefaultPawnClass = PlayerPawnClassFinder.Class;
    }

    // Eğer BP_MyGamePlayerController blueprint'iniz varsa bu kodu kullanın
    static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerClassFinder(TEXT("/Game/Blueprints/PlayerControllers/BP_MyGamePlayerController"));
    if (PlayerControllerClassFinder.Class != nullptr)
    {
        PlayerControllerClass = PlayerControllerClassFinder.Class;
    }

    // Alternatif: Varsayılan Unreal sınıflarını kullan
    // DefaultPawnClass = ADefaultPawn::StaticClass();
    // PlayerControllerClass = APlayerController::StaticClass();

    UE_LOG(LogMyGame, Log, TEXT("MyGameGameMode initialized"));
}

void AMyGameGameMode::BeginPlay()
{
    Super::BeginPlay();

    UE_LOG(LogMyGame, Log, TEXT("GameMode BeginPlay - Current State: %s"), *CurrentGameState.ToString());

    // Başlangıçta timer'ı başlat
    GetWorld()->GetTimerManager().SetTimer(
        GameTimerHandle,
        this,
        &AMyGameGameMode::UpdateTimer,
        1.0f, // Her saniye
        true  // Tekrarlı
    );
}

void AMyGameGameMode::PostLogin(APlayerController* NewPlayer)
{
    Super::PostLogin(NewPlayer);

    ActivePlayerCount++;

    if (NewPlayer && NewPlayer->PlayerState)
    {
        UE_LOG(LogMyGame, Log, TEXT("Player %s joined the game. Total players: %d"),
            *NewPlayer->PlayerState->GetPlayerName(),
            ActivePlayerCount);

        // Yeni oyuncuya hoşgeldin mesajı gönder
        if (GEngine)
        {
            FString WelcomeMessage = FString::Printf(TEXT("Welcome %s to MyGame!"),
                *NewPlayer->PlayerState->GetPlayerName());
            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, WelcomeMessage);
        }

        // Eğer yeterli oyuncu varsa ve oyun başlamadıysa, oyunu başlat
        if (!bHasGameStarted && ActivePlayerCount >= 1) // Örnek: 1 oyuncuyla başlat
        {
            // 3 saniye bekle sonra başlat (oyuncunun hazırlanması için)
            FTimerHandle StartTimerHandle;
            GetWorld()->GetTimerManager().SetTimer(
                StartTimerHandle,
                [this]()
                {
                    StartGame();
                },
                3.0f,
                false
            );
        }
    }
}

void AMyGameGameMode::Logout(AController* ExitingPlayer)
{
    if (ExitingPlayer && ExitingPlayer->PlayerState)
    {
        UE_LOG(LogMyGame, Log, TEXT("Player %s left the game"),
            *ExitingPlayer->PlayerState->GetPlayerName());
    }

    ActivePlayerCount = FMath::Max(0, ActivePlayerCount - 1);

    Super::Logout(ExitingPlayer);

    // Eğer hiç oyuncu kalmadıysa veya oyun bittiyse
    if (ActivePlayerCount == 0 && bHasGameStarted)
    {
        EndGame(false); // Hiç kimse kazanmadı
    }
}

void AMyGameGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
{
    Super::HandleStartingNewPlayer_Implementation(NewPlayer);

    UE_LOG(LogMyGame, Log, TEXT("Handling new player: %s"),
        NewPlayer ? *NewPlayer->GetName() : TEXT("NULL"));

    // Oyuncu başlangıç özelliklerini ayarla
    if (NewPlayer && NewPlayer->PlayerState)
    {
        // Başlangıç skoru
        NewPlayer->PlayerState->SetScore(0);

        // Varsayılan isim ayarla
        if (NewPlayer->PlayerState->GetPlayerName().IsEmpty())
        {
            NewPlayer->PlayerState->SetPlayerName(FString::Printf(TEXT("Player_%d"), ActivePlayerCount));
        }
    }
}

APawn* AMyGameGameMode::SpawnDefaultPawnFor_Implementation(AController* NewPlayer, AActor* StartSpot)
{
    APawn* SpawnedPawn = Super::SpawnDefaultPawnFor_Implementation(NewPlayer, StartSpot);

    if (SpawnedPawn)
    {
        UE_LOG(LogMyGame, Log, TEXT("Pawn spawned for player: %s"),
            *SpawnedPawn->GetName());

        // Pawn spawn edildiğinde yapılacak ek işlemler
        // Örneğin: başlangıç ekipmanı verme, spawn efektleri, vb.
    }

    return SpawnedPawn;
}

void AMyGameGameMode::StartGame()
{
    if (bHasGameStarted)
    {
        UE_LOG(LogMyGame, Warning, TEXT("Game already started!"));
        return;
    }

    bHasGameStarted = true;
    bIsGameOver = false;
    RemainingTime = GameDuration;
    ChangeGameState(FName("Playing"));

    UE_LOG(LogMyGame, Log, TEXT("Game Started! Duration: %.0f seconds"), GameDuration);

    // Tüm oyunculara mesaj gönder
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("GAME STARTED!"));
    }

    // Tüm player controller'lara oyunun başladığını bildir
    for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
    {
        APlayerController* PC = It->Get();
        if (PC)
        {
            // Burada oyuncuya özel başlangıç işlemleri yapılabilir
            // Örneğin: UI güncellemesi, ses çalma vb.
        }
    }
}

void AMyGameGameMode::EndGame(bool bPlayerWon)
{
    if (bIsGameOver)
        return;

    bIsGameOver = true;
    bHasGameStarted = false;
    ChangeGameState(FName("Finished"));

    FString EndMessage;
    if (bPlayerWon)
    {
        EndMessage = TEXT("VICTORY! You won the game!");
        UE_LOG(LogMyGame, Log, TEXT("Game Ended: Player Won"));
    }
    else
    {
        EndMessage = TEXT("GAME OVER");
        UE_LOG(LogMyGame, Log, TEXT("Game Ended: Player Lost"));
    }

    // Ekran mesajı göster
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 10.0f, bPlayerWon ? FColor::Green : FColor::Red, EndMessage);
    }

    // Timer'ı durdur
    GetWorld()->GetTimerManager().ClearTimer(GameTimerHandle);

    // 5 saniye sonra oyunu yeniden başlat
    FTimerHandle RestartTimer;
    GetWorld()->GetTimerManager().SetTimer(
        RestartTimer,
        [this]()
        {
            // Restart the level
            UGameplayStatics::OpenLevel(this, FName(*GetWorld()->GetName()), false);
        },
        5.0f,
        false
    );
}

void AMyGameGameMode::ChangeGameState(FName NewState)
{
    if (CurrentGameState == NewState)
        return;

    FName PreviousState = CurrentGameState;
    CurrentGameState = NewState;

    UE_LOG(LogMyGame, Log, TEXT("Game State Changed: %s -> %s"),
        *PreviousState.ToString(),
        *CurrentGameState.ToString());

    // Blueprint implementable event'i çağır
    // Bu fonksiyon sadece Blueprint'te override edilirse çalışır
    // C++ tarafında otomatik olarak oluşturulur
    OnGameStateChanged(PreviousState, CurrentGameState);
}

void AMyGameGameMode::AddPlayerScore(APlayerController* PlayerController, int32 ScoreToAdd)
{
    if (!PlayerController || !PlayerController->PlayerState || ScoreToAdd <= 0)
        return;

    float OldScore = PlayerController->PlayerState->GetScore();
    float NewScore = OldScore + ScoreToAdd;
    PlayerController->PlayerState->SetScore(NewScore);

    UE_LOG(LogMyGame, Log, TEXT("Player %s score: %.0f -> %.0f (+%d)"),
        *PlayerController->PlayerState->GetPlayerName(),
        OldScore,
        NewScore,
        ScoreToAdd);

    // Blueprint implementable event'i çağır
    OnPlayerScoreChanged(PlayerController, OldScore, NewScore);
}

void AMyGameGameMode::UpdateTimer()
{
    if (!bHasGameStarted || bIsGameOver)
        return;

    RemainingTime -= 1.0f;

    // Her 10 saniyede bir log
    if (FMath::Fmod(RemainingTime, 10.0f) < 1.0f)
    {
        UE_LOG(LogMyGame, Log, TEXT("Remaining Time: %.0f seconds"), RemainingTime);
    }

    // Blueprint event'ini çağır
    OnTimerUpdated(RemainingTime);

    // Süre bitti
    if (RemainingTime <= 0.0f)
    {
        RemainingTime = 0.0f;
        EndGame(false); // Süre bitti, oyuncu kaybetti
    }
}

GameMode Blueprint sınıfını oluşturma

1. "MyGameGameMode" sınıfına sağ tıkladığımızda açılan menüden, "Create Blueprint class based on MyGameGameMode" seçeneğine tıklayarak "Add Blueprint Class" penceresini açalım.

2. "Name" satırına "BP_MyGameGameMode" değerini girdikten ve "Path" bölümünde "Content\_Game\Blueprints\GameModes" dizinini seçtikten sonra "Create My Game Game Mode Class" butonuna tıklayarak Blueprint sınıfını oluşturalım.

3. İşlem tamamlandığında, "Content\_Game\Blueprints\GameModes" dizininde "BP_MyGameGameMode" sınıfı oluşturulur.

Ön tanımlı GameMode değerini ayarlama

Unreal Engine Editor'ün "Edit - Project Settings" menü seçeneği ile erişilen "Project Settings" penceresinin "Maps & Modes" sekmesinde, "Default GameMode" değerini "BP_MyGameGameMode" olarak değiştirelim. "Default Pawn Class" ve "Player Controller Class" değerleri şimdilik aynı kalacaktır.

Projeyi çalıştırma

Projeyi çalıştırdığımızda,

1. "BP_MyGameGameMode" sınıfı aktif GameMode olarak devreye girer ve Outliner'a görünür.

⚠️ AMyGameGameMode::AMyGameGameMode() Constructor fonksiyonu içinde tanımlanan BP_MyGamePlayerController ve BP_MyGameCharacter değerleri henüz oluşturulmadığından, "Project Settings" içindeki "Default Pawn Class" ve "Player Controller Class" değerleri şimdilik aynı kalacaktır.

2. Aşağıdaki ifadeler sahne ekranında görünür ve bir süre sonra kaybolur.

Welcome .... to My Game!
GAME STARTED

3. Aşağıdaki ifadeler Output Log ekranına yazılır:

LogLoad: Game class is 'BP_MyGameGameMode_C'
LogMyGame: MyGameGameMode initialized
LogMyGame: Pawn spawned for player: DefaultPawn_0
LogMyGame: Handling new player: PlayerController_0
LogMyGame: Player ... joined the game. Total players: 1
LogMyGame: GameMode BeginPlay - Current State: Waiting
LogMyGame: Game State Changed: Waiting -> Playing
LogMyGame: Game Started! Duration: 300 seconds
LogMyGame: Player ... left the game
LogMyGame: Game State Changed: Playing -> Finished
LogMyGame: Game Ended: Player Lost