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 > Player Controller oluşturma

Unreal Engine player controller oluşturma

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

PlayerController 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 "Player Controller" seçeneği seçili iken, "Next" butonuna tıklayalım.

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

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


// MyGamePlayerController.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyGamePlayerController.generated.h"

/**
 * 
 */
UCLASS()
class MYGAME_API AMyGamePlayerController : public APlayerController
{
	GENERATED_BODY()
	
};


// MyGamePlayerController.cpp

#include "MyGamePlayerController.h"

4. Kodlar derlendikten sonra "C++ Classes/MyGame" dizini altında "MyGamePlayerController" adlı bir dosya otomatik olarak oluşturulacaktır.

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

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


// MyGamePlayerController.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "InputAction.h"  // UInputAction için
#include "InputMappingContext.h"  // UInputMappingContext için
#include "MyGamePlayerController.generated.h"

// Artık forward declaration'a ihtiyacımız yok çünkü header'ları include ettik

/**
 * MyGame projesi için özel PlayerController sınıfı
 * Girdi işleme, UI yönetimi ve oyuncu kontrol mantığını yönetir
 */
UCLASS()
class MYGAME_API AMyGamePlayerController : public APlayerController
{
    GENERATED_BODY()

public:
    // Constructor
    AMyGamePlayerController();

protected:
    //////////////////////////////////////////////////////////////////////////
    // Temel Yaşam Döngüsü Fonksiyonları

    /**
     * Oyuncu controller'ı başlatıldığında çağrılır
     */
    virtual void BeginPlay() override;

    /**
     * Her frame'de çağrılır
     * @param DeltaTime - Geçen süre
     */
    virtual void Tick(float DeltaTime) override;

    /**
     * Oyuncu pawn'ını kontrol etmeye başladığında çağrılır
     * @param InPawn - Kontrol edilecek pawn
     */
    virtual void OnPossess(APawn* InPawn) override;

    /**
     * Oyuncu pawn'ını kontrol etmeyi bıraktığında çağrılır
     * @param InPawn - Kontrolü bırakılan pawn
     */
    virtual void OnUnPossess() override;

    //////////////////////////////////////////////////////////////////////////
    // Girdi (Input) Sistemi

    /**
     * Enhanced Input sistemini kur
     */
    virtual void SetupInputComponent() override;

    /**
     * Input mapping context'leri ekle
     */
    void SetupInputMapping();

    /**
     * Input action'ları bağla
     */
    void BindInputActions();

    /**
     * Mouse cursor ayarlarını yap
     */
    void SetMouseCursorSettings();

public:
    //////////////////////////////////////////////////////////////////////////
    // Girdi İşleme Fonksiyonları

    /**
     * Hareket girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandleMoveInput(const FInputActionValue& InputActionValue);

    /**
     * Bakış girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandleLookInput(const FInputActionValue& InputActionValue);

    /**
     * Zıplama girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandleJumpInput(const FInputActionValue& InputActionValue);

    /**
     * Koşma girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandleSprintInput(const FInputActionValue& InputActionValue);

    /**
     * Etkileşim girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandleInteractInput(const FInputActionValue& InputActionValue);

    /**
     * Pause/Menü girdisini işle
     * @param InputActionValue - Girdi değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Input")
    void HandlePauseInput(const FInputActionValue& InputActionValue);

    //////////////////////////////////////////////////////////////////////////
    // UI Yönetimi Fonksiyonları

    /**
     * HUD'u oluştur ve göster
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void CreateHUD();

    /**
     * HUD'u kaldır
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void RemoveHUD();

    /**
     * Pause menüsünü göster/gizle
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void TogglePauseMenu();

    /**
     * Skor UI'ını güncelle
     * @param NewScore - Yeni skor değeri
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void UpdateScoreUI(float NewScore);

    /**
     * Sağlık UI'ını güncelle
     * @param HealthPercentage - Sağlık yüzdesi (0-1)
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void UpdateHealthUI(float HealthPercentage);

    /**
     * Mesaj UI'ını göster
     * @param Message - Gösterilecek mesaj
     * @param DisplayTime - Görüntülenme süresi (saniye)
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|UI")
    void ShowMessage(const FString& Message, float DisplayTime = 3.0f);

    //////////////////////////////////////////////////////////////////////////
    // Oyun Mantığı Fonksiyonları

    /**
     * Oyuncu öldüğünde çağrılır
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    void OnPlayerDied();

    /**
     * Oyuncu respawn olduğunda çağrılır
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    void OnPlayerRespawned();

    /**
     * Oyuncu skorunu artır
     * @param ScoreAmount - Eklenecek skor miktarı
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    void AddScore(float ScoreAmount);

    /**
     * Oyuncu hasar aldığında çağrılır
     * @param DamageAmount - Hasar miktarı
     * @param DamageCauser - Hasarı veren aktör
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    void OnPlayerDamaged(float DamageAmount, AActor* DamageCauser);

    //////////////////////////////////////////////////////////////////////////
    // Ağ (Multiplayer) Fonksiyonları

    /**
     * Sunucuda skor güncelle (RPC)
     * @param ScoreAmount - Eklenecek skor
     */
    UFUNCTION(Server, Reliable, WithValidation, Category = "MyGame|Network")
    void Server_AddScore(float ScoreAmount);

    /**
     * Client'ta UI güncelle (RPC)
     * @param NewScore - Yeni skor
     */
    UFUNCTION(Client, Reliable, Category = "MyGame|Network")
    void Client_UpdateScore(float NewScore);

    /**
     * Tüm client'lara mesaj gönder (Multicast RPC)
     * @param Message - Gönderilecek mesaj
     */
    UFUNCTION(NetMulticast, Reliable, Category = "MyGame|Network")
    void Multicast_ShowMessage(const FString& Message);

    //////////////////////////////////////////////////////////////////////////
    // Blueprint Implementable Events

    /**
     * HUD oluşturulduğunda çağrılır (Blueprint'te implemente edilecek)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnHUDCreated();

    /**
     * Pause menüsü değiştiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param bIsPausedParam - Oyun durduruldu mu?
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnPauseChanged(bool bIsPausedParam);

    /**
     * Skor güncellendiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param NewScore - Yeni skor
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnScoreUpdated(float NewScore);

    /**
     * Sağlık güncellendiğinde çağrılır (Blueprint'te implemente edilecek)
     * @param HealthPercentage - Sağlık yüzdesi (0-1)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnHealthUpdated(float HealthPercentage);

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

    /**
     * Varsayılan Input Mapping Context
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputMappingContext> DefaultInputMappingContext;

    /**
     * Hareket Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> MoveInputAction;

    /**
     * Bakış Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> LookInputAction;

    /**
     * Zıplama Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> JumpInputAction;

    /**
     * Koşma Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> SprintInputAction;

    /**
     * Etkileşim Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> InteractInputAction;

    /**
     * Pause Input Action
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|Input")
    TSoftObjectPtr<UInputAction> PauseInputAction;

    /**
     * HUD widget sınıfı
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|UI")
    TSubclassOf<class UUserWidget> HUDWidgetClass;

    /**
     * Pause menü widget sınıfı
     */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MyGame|UI")
    TSubclassOf<class UUserWidget> PauseMenuWidgetClass;

    /**
     * Mouse hassasiyeti
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGame|Input", meta = (ClampMin = "0.1", ClampMax = "10.0"))
    float MouseSensitivity = 1.0f;

    /**
     * Gamepad hassasiyeti
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGame|Input", meta = (ClampMin = "0.1", ClampMax = "10.0"))
    float GamepadSensitivity = 1.0f;

    /**
     * Fareyi UI üzerinde kilitle
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGame|Input")
    bool bLockMouseCursor = false;

    /**
     * Oyun şu anda durdurulmuş mu?
     */
    UPROPERTY(BlueprintReadOnly, Category = "MyGame|State")
    bool bIsGamePaused = false;

protected:
    //////////////////////////////////////////////////////////////////////////
    // İç Değişkenler

    /**
     * Aktif HUD widget
     */
    UPROPERTY()
    class UUserWidget* CurrentHUDWidget = nullptr;

    /**
     * Aktif pause menü widget
     */
    UPROPERTY()
    class UUserWidget* CurrentPauseMenuWidget = nullptr;

    /**
     * Enhanced input local player subsystem
     */
    UPROPERTY()
    class UEnhancedInputLocalPlayerSubsystem* InputSubsystem = nullptr;

    /**
     * Mevcut skor
     */
    float CurrentScore = 0.0f;

    /**
     * Mevcut sağlık yüzdesi
     */
    float CurrentHealthPercentage = 1.0f;

    /**
     * Oyun durduruldu mu?
     */
    bool bIsPaused = false;
};


// MyGamePlayerController.cpp

#include "MyGamePlayerController.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Blueprint/UserWidget.h"
#include "Engine/LocalPlayer.h"
#include "Kismet/GameplayStatics.h"
#include "MyGameGameMode.h"

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

AMyGamePlayerController::AMyGamePlayerController()
{
    // Mouse ve input ayarları
    bShowMouseCursor = false;
    bEnableClickEvents = false;
    bEnableTouchEvents = false;
    bEnableMouseOverEvents = false;

    // Asset path'leri yorum satırı yapıldı
    // Bunları Unreal Editor'de asset'leri oluşturduktan sonra aktifleştirin
    DefaultInputMappingContext = TSoftObjectPtr<UInputMappingContext>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/IMC_Default.IMC_Default"))
    );

    MoveInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Move.IA_Move"))
    );

    LookInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Look.IA_Look"))
    );

    JumpInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Jump.IA_Jump"))
    );

    SprintInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Sprint.IA_Sprint"))
    );

    InteractInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Interact.IA_Interact"))
    );

    PauseInputAction = TSoftObjectPtr<UInputAction>(
        FSoftObjectPath(TEXT("/Game/_Game/Input/Actions/IA_Pause.IA_Pause"))
    );

    UE_LOG(LogMyGamePlayerController, Log, TEXT("MyGamePlayerController initialized"));
}

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

    UE_LOG(LogMyGamePlayerController, Log, TEXT("PlayerController BeginPlay"));

    // Enhanced Input subsystem'ini al
    if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
    {
        InputSubsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
        if (InputSubsystem)
        {
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Enhanced Input subsystem found"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Warning, TEXT("Enhanced Input subsystem NOT found"));
        }
    }

    // HUD'u oluştur
    CreateHUD();

    // Mouse cursor ayarı
    SetMouseCursorSettings();

    // Input mapping context'ini ekle (BeginPlay'den sonra)
    // Eğer pawn zaten sahipsek, input'u ayarla
    if (GetPawn())
    {
        SetupInputMapping();
    }
}

void AMyGamePlayerController::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    // Debug için: Input sisteminin durumunu kontrol et
    static bool bHasLoggedInputStatus = false;
    if (!bHasLoggedInputStatus && GetWorld()->GetTimeSeconds() > 2.0f)
    {
        FString InputStatus = FString::Printf(TEXT(
            "Input Status: Subsystem=%s, Pawn=%s, IMC=%s, MoveAction=%s"
        ),
            InputSubsystem ? TEXT("Valid") : TEXT("Null"),
            GetPawn() ? *GetPawn()->GetName() : TEXT("Null"),
            DefaultInputMappingContext.IsNull() ? TEXT("Null") : TEXT("Set"),
            MoveInputAction.IsNull() ? TEXT("Null") : TEXT("Set")
        );

        UE_LOG(LogMyGamePlayerController, Log, TEXT("%s"), *InputStatus);
        ShowMessage(InputStatus, 5.0f);

        bHasLoggedInputStatus = true;
    }
}

void AMyGamePlayerController::OnPossess(APawn* InPawn)
{
    Super::OnPossess(InPawn);

    UE_LOG(LogMyGamePlayerController, Log, TEXT("PlayerController possessing pawn: %s"),
        *GetNameSafe(InPawn));

    // Eğer bir karaktere sahipsek, bazı ayarları yap
    if (ACharacter* ControlledCharacter = Cast<ACharacter>(InPawn))
    {
        // Character'in input yönünü controller'a göre ayarla
        ControlledCharacter->bUseControllerRotationYaw = false;
        ControlledCharacter->bUseControllerRotationPitch = false;
        ControlledCharacter->bUseControllerRotationRoll = false;

        if (UCharacterMovementComponent* MovementComp = ControlledCharacter->GetCharacterMovement())
        {
            MovementComp->bOrientRotationToMovement = true;
            MovementComp->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
        }
    }
}

void AMyGamePlayerController::OnUnPossess()
{
    UE_LOG(LogMyGamePlayerController, Log, TEXT("PlayerController unpossessing pawn"));

    // Input mapping context'ini kaldır
    if (InputSubsystem)
    {
        // Null check yapmadan önce asset'in yüklü olup olmadığını kontrol et
        if (DefaultInputMappingContext.IsNull())
        {
            UE_LOG(LogMyGamePlayerController, Warning, TEXT("DefaultInputMappingContext is null"));
        }
        else if (UInputMappingContext* IMC = DefaultInputMappingContext.LoadSynchronous())
        {
            InputSubsystem->RemoveMappingContext(IMC);
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load DefaultInputMappingContext"));
        }
    }

    Super::OnUnPossess();
}

void AMyGamePlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    UE_LOG(LogMyGamePlayerController, Log, TEXT("Setting up input component"));

    // Enhanced Input Component oluştur
    UEnhancedInputComponent* EnhancedInputComp = Cast<UEnhancedInputComponent>(InputComponent);
    if (EnhancedInputComp)
    {
        UE_LOG(LogMyGamePlayerController, Log, TEXT("Enhanced Input Component found"));

        // Input subsystem'i al
        if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
        {
            InputSubsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
            if (InputSubsystem)
            {
                UE_LOG(LogMyGamePlayerController, Log, TEXT("Enhanced Input subsystem found in SetupInputComponent"));

                // Input mapping context'ini ekle
                SetupInputMapping();

                // Input action'ları bağla
                BindInputActions();
            }
            else
            {
                UE_LOG(LogMyGamePlayerController, Error, TEXT("Enhanced Input subsystem NOT found"));
            }
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("LocalPlayer is null"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("Enhanced Input Component NOT found"));
    }
}

void AMyGamePlayerController::SetupInputMapping()
{
    // InputSubsystem null mu kontrol et
    if (!InputSubsystem)
    {
        // Henüz başlatılmamışsa, başlatmaya çalış
        if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
        {
            InputSubsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
            if (InputSubsystem)
            {
                UE_LOG(LogMyGamePlayerController, Log, TEXT("InputSubsystem initialized in SetupInputMapping"));
            }
            else
            {
                UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to initialize InputSubsystem"));
                return;
            }
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("LocalPlayer is null"));
            return;
        }
    }

    // Default input mapping context'i yükle ve ekle
    if (DefaultInputMappingContext.IsNull())
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("DefaultInputMappingContext is not set"));
        return;
    }

    if (UInputMappingContext* IMC = DefaultInputMappingContext.LoadSynchronous())
    {
        // Önce aynı context'i kaldır (tekrar eklememek için)
        InputSubsystem->RemoveMappingContext(IMC);

        // Sonra yeni olarak ekle
        InputSubsystem->AddMappingContext(IMC, 0);
        UE_LOG(LogMyGamePlayerController, Log, TEXT("Default Input Mapping Context added"));
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load Default Input Mapping Context"));
    }
}

void AMyGamePlayerController::BindInputActions()
{
    UEnhancedInputComponent* EnhancedInputComp = Cast<UEnhancedInputComponent>(InputComponent);
    if (!EnhancedInputComp)
    {
        UE_LOG(LogMyGamePlayerController, Error, TEXT("EnhancedInputComponent is null"));
        return;
    }

    // Move input binding
    if (!MoveInputAction.IsNull())
    {
        if (UInputAction* MoveAction = MoveInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                MoveAction,
                ETriggerEvent::Triggered,
                this,
                &AMyGamePlayerController::HandleMoveInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Move input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load MoveInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("MoveInputAction is not set"));
    }

    // Look input binding
    if (!LookInputAction.IsNull())
    {
        if (UInputAction* LookAction = LookInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                LookAction,
                ETriggerEvent::Triggered,
                this,
                &AMyGamePlayerController::HandleLookInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Look input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load LookInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("LookInputAction is not set"));
    }

    // Jump input binding
    if (!JumpInputAction.IsNull())
    {
        if (UInputAction* JumpAction = JumpInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                JumpAction,
                ETriggerEvent::Started,
                this,
                &AMyGamePlayerController::HandleJumpInput
            );

            EnhancedInputComp->BindAction(
                JumpAction,
                ETriggerEvent::Completed,
                this,
                &AMyGamePlayerController::HandleJumpInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Jump input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load JumpInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("JumpInputAction is not set"));
    }

    // Sprint input binding
    if (!SprintInputAction.IsNull())
    {
        if (UInputAction* SprintAction = SprintInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                SprintAction,
                ETriggerEvent::Triggered,
                this,
                &AMyGamePlayerController::HandleSprintInput
            );

            EnhancedInputComp->BindAction(
                SprintAction,
                ETriggerEvent::Canceled,
                this,
                &AMyGamePlayerController::HandleSprintInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Sprint input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load SprintInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("SprintInputAction is not set"));
    }

    // Interact input binding
    if (!InteractInputAction.IsNull())
    {
        if (UInputAction* InteractAction = InteractInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                InteractAction,
                ETriggerEvent::Started,
                this,
                &AMyGamePlayerController::HandleInteractInput
            );

            EnhancedInputComp->BindAction(
                InteractAction,
                ETriggerEvent::Completed,
                this,
                &AMyGamePlayerController::HandleInteractInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Interact input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load InteractInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("InteractInputAction is not set"));
    }

    // Pause input binding
    if (!PauseInputAction.IsNull())
    {
        if (UInputAction* PauseAction = PauseInputAction.LoadSynchronous())
        {
            EnhancedInputComp->BindAction(
                PauseAction,
                ETriggerEvent::Started,
                this,
                &AMyGamePlayerController::HandlePauseInput
            );
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Pause input action bound"));
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to load PauseInputAction"));
        }
    }
    else
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("PauseInputAction is not set"));
    }
}

void AMyGamePlayerController::HandleMoveInput(const FInputActionValue& InputActionValue)
{
    if (!GetPawn() || bIsPaused)
        return;

    FVector2D MovementVector = InputActionValue.Get<FVector2D>();

    // X ekseni: Sağ/Sol (Yaw)
    // Y ekseni: İleri/Geri (Pitch)
    if (MovementVector.SizeSquared() > 0.0f)
    {
        // Controller'ın rotasyonuna göre hareket vektörünü dönüştür
        const FRotator Rotation = GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // İleri vektör
        const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

        // Sağ vektör
        const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

        // Son hareket vektörü
        FVector FinalMovement = (ForwardDirection * MovementVector.Y) + (RightDirection * MovementVector.X);
        FinalMovement.Normalize();

        // Pawn'a hareket emri ver
        GetPawn()->AddMovementInput(FinalMovement, 1.0f);

        UE_LOG(LogMyGamePlayerController, Verbose, TEXT("Move Input: X=%.2f, Y=%.2f"),
            MovementVector.X, MovementVector.Y);
    }
}

void AMyGamePlayerController::HandleLookInput(const FInputActionValue& InputActionValue)
{
    if (bIsPaused)
        return;

    FVector2D LookAxisVector = InputActionValue.Get<FVector2D>();

    // Mouse/Gamepad hassasiyeti ile çarp
    LookAxisVector.X *= MouseSensitivity;
    LookAxisVector.Y *= MouseSensitivity;

    // Yatay bakış (sağ/sol)
    if (LookAxisVector.X != 0.0f)
    {
        AddYawInput(LookAxisVector.X);
    }

    // Dikey bakış (yukarı/aşağı)
    if (LookAxisVector.Y != 0.0f)
    {
        AddPitchInput(LookAxisVector.Y);
    }

    UE_LOG(LogMyGamePlayerController, Verbose, TEXT("Look Input: X=%.2f, Y=%.2f"),
        LookAxisVector.X, LookAxisVector.Y);
}

void AMyGamePlayerController::HandleJumpInput(const FInputActionValue& InputActionValue)
{
    if (!GetPawn() || bIsPaused)
        return;

    bool bIsPressed = InputActionValue.Get<bool>();

    if (ACharacter* ControlledCharacter = Cast<ACharacter>(GetPawn())) // Değişken adını ControlledCharacter yaptık
    {
        if (bIsPressed)
        {
            ControlledCharacter->Jump();
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Jump started"));
        }
        else
        {
            ControlledCharacter->StopJumping();
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Jump stopped"));
        }
    }
}

void AMyGamePlayerController::HandleSprintInput(const FInputActionValue& InputActionValue)
{
    if (!GetPawn() || bIsPaused)
        return;

    bool bIsSprinting = InputActionValue.Get<bool>();

    if (ACharacter* ControlledCharacter = Cast<ACharacter>(GetPawn()))
    {
        if (UCharacterMovementComponent* MovementComp = ControlledCharacter->GetCharacterMovement())
        {
            if (bIsSprinting)
            {
                // Sprint hızını ayarla (normal hızın 1.5 katı)
                MovementComp->MaxWalkSpeed = 600.0f; // Varsayılan 400-500'dür
                UE_LOG(LogMyGamePlayerController, Log, TEXT("Sprinting started"));
            }
            else
            {
                // Normal hıza dön
                MovementComp->MaxWalkSpeed = 450.0f; // Varsayılan yürüme hızı
                UE_LOG(LogMyGamePlayerController, Log, TEXT("Sprinting stopped"));
            }
        }
    }
}

void AMyGamePlayerController::HandleInteractInput(const FInputActionValue& InputActionValue)
{
    if (!GetPawn() || bIsPaused)
        return;

    bool bIsPressed = InputActionValue.Get<bool>();

    if (bIsPressed)
    {
        UE_LOG(LogMyGamePlayerController, Log, TEXT("Interact input pressed"));

        // Raycast ile etkileşim
        FVector StartLocation;
        FRotator StartRotation;
        GetPlayerViewPoint(StartLocation, StartRotation);

        FVector EndLocation = StartLocation + (StartRotation.Vector() * 500.0f); // 5 metre ileri

        FHitResult HitResult;
        FCollisionQueryParams CollisionParams;
        CollisionParams.AddIgnoredActor(GetPawn());

        bool bHit = GetWorld()->LineTraceSingleByChannel(
            HitResult,
            StartLocation,
            EndLocation,
            ECC_Visibility,
            CollisionParams
        );

        if (bHit && HitResult.GetActor())
        {
            UE_LOG(LogMyGamePlayerController, Log, TEXT("Interacted with: %s"),
                *HitResult.GetActor()->GetName());

            // Burada etkileşim mantığınızı ekleyin
            // Örneğin: kapı açma, item toplama, düğmeye basma
            ShowMessage(FString::Printf(TEXT("Interacted with: %s"),
                *HitResult.GetActor()->GetName()), 2.0f);
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Log, TEXT("No interactable object found"));
        }
    }
}

void AMyGamePlayerController::HandlePauseInput(const FInputActionValue& InputActionValue)
{
    bool bIsPressed = InputActionValue.Get<bool>();

    if (bIsPressed)
    {
        TogglePauseMenu();
    }
}

void AMyGamePlayerController::CreateHUD()
{
    if (HUDWidgetClass && !CurrentHUDWidget)
    {
        CurrentHUDWidget = CreateWidget<UUserWidget>(this, HUDWidgetClass);
        if (CurrentHUDWidget)
        {
            CurrentHUDWidget->AddToViewport();

            // Mouse cursor ayarlarını güncelle
            SetMouseCursorSettings();

            UE_LOG(LogMyGamePlayerController, Log, TEXT("HUD created and added to viewport"));

            // Blueprint event'ini tetikle
            OnHUDCreated();
        }
        else
        {
            UE_LOG(LogMyGamePlayerController, Error, TEXT("Failed to create HUD widget"));
        }
    }
    else if (!HUDWidgetClass)
    {
        UE_LOG(LogMyGamePlayerController, Warning, TEXT("HUDWidgetClass is not set"));
    }
}

void AMyGamePlayerController::RemoveHUD()
{
    if (CurrentHUDWidget)
    {
        CurrentHUDWidget->RemoveFromParent();
        CurrentHUDWidget = nullptr;
        UE_LOG(LogMyGamePlayerController, Log, TEXT("HUD removed"));
    }
}

void AMyGamePlayerController::TogglePauseMenu()
{
    bIsPaused = !bIsPaused;

    if (bIsPaused)
    {
        // Pause menüsünü göster
        if (PauseMenuWidgetClass && !CurrentPauseMenuWidget)
        {
            CurrentPauseMenuWidget = CreateWidget<UUserWidget>(this, PauseMenuWidgetClass);
            if (CurrentPauseMenuWidget)
            {
                CurrentPauseMenuWidget->AddToViewport(100); // Yüksek Z-order

                // Oyunu duraklat
                SetPause(true);

                // Mouse'u göster ve kilitle
                bShowMouseCursor = true;
                SetInputMode(FInputModeUIOnly());

                UE_LOG(LogMyGamePlayerController, Log, TEXT("Pause menu shown"));
            }
        }
    }
    else
    {
        // Pause menüsünü kaldır
        if (CurrentPauseMenuWidget)
        {
            CurrentPauseMenuWidget->RemoveFromParent();
            CurrentPauseMenuWidget = nullptr;

            // Oyunu devam ettir
            SetPause(false);

            // Mouse'u gizle
            bShowMouseCursor = false;
            SetInputMode(FInputModeGameOnly());

            UE_LOG(LogMyGamePlayerController, Log, TEXT("Pause menu hidden"));
        }
    }

    // Oyunun durumunu güncelle
    bIsGamePaused = bIsPaused;

    // Blueprint event'ini tetikle (parametre ismini değiştirdik)
    OnPauseChanged(bIsPaused);
}

void AMyGamePlayerController::UpdateScoreUI(float NewScore)
{
    CurrentScore = NewScore;

    UE_LOG(LogMyGamePlayerController, Log, TEXT("Score updated: %.0f"), CurrentScore);

    // Blueprint event'ini tetikle
    OnScoreUpdated(CurrentScore);
}

void AMyGamePlayerController::UpdateHealthUI(float HealthPercentage)
{
    CurrentHealthPercentage = FMath::Clamp(HealthPercentage, 0.0f, 1.0f);

    UE_LOG(LogMyGamePlayerController, Log, TEXT("Health updated: %.1f%%"), CurrentHealthPercentage * 100.0f);

    // Blueprint event'ini tetikle
    OnHealthUpdated(CurrentHealthPercentage);
}

void AMyGamePlayerController::ShowMessage(const FString& Message, float DisplayTime)
{
    UE_LOG(LogMyGamePlayerController, Log, TEXT("Showing message: %s"), *Message);

    // Ekran mesajı (debug için)
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(
            -1,
            DisplayTime,
            FColor::Yellow,
            Message
        );
    }

    // Multiplayer için multicast RPC
    if (GetNetMode() != NM_Standalone)
    {
        Multicast_ShowMessage(Message);
    }
}

void AMyGamePlayerController::OnPlayerDied()
{
    UE_LOG(LogMyGamePlayerController, Log, TEXT("Player died"));

    // Input'u devre dışı bırak
    SetIgnoreMoveInput(true);
    SetIgnoreLookInput(true);

    // Ölüm ekranı göster
    ShowMessage(TEXT("YOU DIED"), 5.0f);

    // 3 saniye sonra respawn
    FTimerHandle RespawnTimer;
    GetWorld()->GetTimerManager().SetTimer(
        RespawnTimer,
        [this]()
        {
            OnPlayerRespawned();
        },
        3.0f,
        false
    );
}

void AMyGamePlayerController::OnPlayerRespawned()
{
    UE_LOG(LogMyGamePlayerController, Log, TEXT("Player respawned"));

    // Input'u tekrar aktif et
    SetIgnoreMoveInput(false);
    SetIgnoreLookInput(false);

    // Sağlığı resetle
    UpdateHealthUI(1.0f);

    ShowMessage(TEXT("RESPAWNED"), 2.0f);
}

void AMyGamePlayerController::AddScore(float ScoreAmount)
{
    if (ScoreAmount <= 0.0f)
        return;

    // Multiplayer için sunucuya RPC gönder
    if (GetLocalRole() == ROLE_Authority)
    {
        // Sunucudaysak direkt güncelle
        CurrentScore += ScoreAmount;
        UpdateScoreUI(CurrentScore);

        // GameMode'a bildir
        if (AMyGameGameMode* GameMode = Cast<AMyGameGameMode>(GetWorld()->GetAuthGameMode()))
        {
            GameMode->AddPlayerScore(this, ScoreAmount);
        }
    }
    else
    {
        // Client'taysak sunucuya istek gönder
        Server_AddScore(ScoreAmount);
    }
}

void AMyGamePlayerController::OnPlayerDamaged(float DamageAmount, AActor* DamageCauser)
{
    if (DamageAmount <= 0.0f)
        return;

    // Sağlık hesaplaması (basit örnek)
    // Gerçek implementasyonda sağlık değişkeni Pawn veya PlayerState'te olmalı
    CurrentHealthPercentage = FMath::Max(0.0f, CurrentHealthPercentage - (DamageAmount * 0.1f));

    UpdateHealthUI(CurrentHealthPercentage);

    // Hasar feedback'i
    ShowMessage(FString::Printf(TEXT("Damage Taken: %.0f"), DamageAmount), 1.5f);

    // Eğer sağlık sıfıra ulaştıysa
    if (CurrentHealthPercentage <= 0.0f)
    {
        OnPlayerDied();
    }
}

//////////////////////////////////////////////////////////////////////////
// Ağ (Multiplayer) Fonksiyonları

void AMyGamePlayerController::Server_AddScore_Implementation(float ScoreAmount)
{
    if (ScoreAmount <= 0.0f)
        return;

    CurrentScore += ScoreAmount;

    // Client'a skor güncellemesini gönder
    Client_UpdateScore(CurrentScore);

    // GameMode'a bildir
    if (AMyGameGameMode* GameMode = Cast<AMyGameGameMode>(GetWorld()->GetAuthGameMode()))
    {
        GameMode->AddPlayerScore(this, ScoreAmount);
    }
}

// Validate fonksiyonunu header'da tanımlamamız gerekiyor
bool AMyGamePlayerController::Server_AddScore_Validate(float ScoreAmount)
{
    // Anti-cheat validation
    // Maksimum skor artışını kontrol et
    return ScoreAmount > 0.0f && ScoreAmount <= 1000.0f;
}

void AMyGamePlayerController::Client_UpdateScore_Implementation(float NewScore)
{
    CurrentScore = NewScore;
    UpdateScoreUI(CurrentScore);
}

void AMyGamePlayerController::Multicast_ShowMessage_Implementation(const FString& Message)
{
    // Tüm client'larda mesajı göster
    if (GEngine && GetNetMode() != NM_DedicatedServer)
    {
        GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Yellow, Message);
    }
}

//////////////////////////////////////////////////////////////////////////
// Yardımcı Fonksiyonlar

void AMyGamePlayerController::SetMouseCursorSettings()
{
    if (bLockMouseCursor)
    {
        bShowMouseCursor = false;
        SetInputMode(FInputModeGameOnly());
    }
    else
    {
        bShowMouseCursor = true;
        FInputModeGameAndUI InputMode;
        InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
        InputMode.SetHideCursorDuringCapture(false);
        SetInputMode(InputMode);
    }
}

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

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

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

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

4. "BP_MyGamePlayerController" penceresini açtığımızda, "Details" sekmesinin "Input" satırında aşağıda gösterilen değerlerin otomatik olarak atandığı görülür:

Ön tanımlı PlayerController değerini ayarlama

Unreal Engine Editor'ün "Project Settings" penceresinin "Maps & Modes" sekmesinde, "Player Controller Class" değerini "BP_MyGamePlayerController" olarak değiştirelim. "Default Pawn Class" değeri şimdilik aynı kalacaktır.

⚠️ Yukarıdaki işlemi yaptığımızda, "BP_MyGameGameMode" sınıfının Details sekmesindeki "Player Controller Class" değeri otomatik olarak "BP_MyGamePlayerController" olarak değişir. Aynı şekilde, "BP_MyGameGameMode" sınıfının Details sekmesindeki "Player Controller Class" değerini değiştirdiğimizde, "Project Settings" penceresinin "Maps & Modes" sekmesindeki "Player Controller Class" değeri de otomatik olarak değişir.

Projeyi çalıştırma

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

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

⚠️ AMyGameGameMode::AMyGameGameMode() Constructor fonksiyonu içinde tanımlanan BP_MyGameCharacter değerleri henüz oluşturulmadığından, "Project Settings" içindeki "Default Pawn Class" değeri ş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
LogMyGamePlayerController: MyGamePlayerController initialized
LogMyGamePlayerController: Setting up input component
LogMyGamePlayerController: Enhanced Input Component found
LogMyGamePlayerController: Move input action bound
LogMyGamePlayerController: Look input action bound
LogMyGamePlayerController: Jump input action bound
LogMyGamePlayerController: Sprint input action bound
LogMyGamePlayerController: Interact input action bound
LogMyGamePlayerController: Pause input action bound
LogMyGame: Pawn spawned for player: DefaultPawn_0
LogMyGamePlayerController: PlayerController possessing pawn: DefaultPawn_0
LogMyGamePlayerController: Warning: InputSubsystem is null, cannot setup input mapping
LogMyGame: Handling new player: BP_MyGamePlayerController_C_0
LogMyGame: Player ... joined the game. Total players: 1
LogMyGame: GameMode BeginPlay - Current State: Waiting
LogMyGamePlayerController: PlayerController BeginPlay
LogMyGamePlayerController: Enhanced Input subsystem found
LogMyGamePlayerController: Warning: HUDWidgetClass is not set
LogMyGame: Game State Changed: Waiting -> Playing
LogMyGame: Game Started! Duration: 300 seconds
LogMyGamePlayerController: PlayerController unpossessing pawn
LogMyGame: Player ... left the game
LogMyGame: Game State Changed: Playing -> Finished
LogMyGame: Game Ended: Player Lost