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 > Karakter oluşturma

Unreal Engine karakter oluşturma

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

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

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

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


// MyGameCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyGameCharacter.generated.h"

UCLASS()
class MYGAME_API AMyGameCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyGameCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};


// MyGameCharacter.cpp

#include "MyGameCharacter.h"

// Sets default values
AMyGameCharacter::AMyGameCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AMyGameCharacter::BeginPlay()
{
	Super::BeginPlay();	
}

// Called every frame
void AMyGameCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

// Called to bind functionality to input
void AMyGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}

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

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

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


// MyGameCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyGameCharacter.generated.h"

// Forward declarations
class USpringArmComponent;
class UCameraComponent;

UCLASS()
class MYGAME_API AMyGameCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    //////////////////////////////////////////////////////////////////////////
    // Constructor ve Yaşam Döngüsü

    /**
     * Default constructor
     */
    AMyGameCharacter();

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

    /**
     * Called when the game starts or when spawned
     */
    virtual void BeginPlay() override;

    /**
     * Called every frame
     * @param DeltaTime - Time since last frame
     */
    virtual void Tick(float DeltaTime) override;

    //////////////////////////////////////////////////////////////////////////
    // Input Fonksiyonları

    /**
     * Called to bind functionality to input
     * @param PlayerInputComponent - Input component to bind to
     */
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

public:
    //////////////////////////////////////////////////////////////////////////
    // Component'ler

    /**
     * Spring arm component for camera positioning
     */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
    USpringArmComponent* SpringArm;

    /**
     * Camera component
     */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
    UCameraComponent* Camera;

    //////////////////////////////////////////////////////////////////////////
    // Movement ve Camera Özellikleri

    /**
     * Base turn rate (deg/sec)
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.0", ClampMax = "100.0"))
    float BaseTurnRate = 45.0f;

    /**
     * Base look up/down rate (deg/sec)
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.0", ClampMax = "100.0"))
    float BaseLookUpRate = 45.0f;

    /**
     * Sprint speed multiplier
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement", meta = (ClampMin = "1.0", ClampMax = "3.0"))
    float SprintSpeedMultiplier = 1.5f;

    /**
     * Is the character currently sprinting?
     */
    UPROPERTY(BlueprintReadOnly, Category = "Movement")
    bool bIsSprinting = false;

    //////////////////////////////////////////////////////////////////////////
    // Health ve Gameplay Özellikleri

    /**
     * Maximum health
     */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay", meta = (ClampMin = "1.0"))
    float MaxHealth = 100.0f;

    /**
     * Current health
     */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Gameplay", meta = (ClampMin = "0.0"))
    float CurrentHealth = 100.0f;

    //////////////////////////////////////////////////////////////////////////
    // Blueprint Callable Fonksiyonlar

    /**
     * Start sprinting
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Movement")
    void StartSprinting();

    /**
     * Stop sprinting
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Movement")
    void StopSprinting();

    /**
     * Take damage
     * @param DamageAmount - Amount of damage to take
     * @param DamageEvent - Damage event data
     * @param EventInstigator - Controller that caused the damage
     * @param DamageCauser - Actor that caused the damage
     * @return Actual damage taken
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent,
        class AController* EventInstigator, AActor* DamageCauser) override;

    /**
     * Heal the character
     * @param HealAmount - Amount to heal
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    void Heal(float HealAmount);

    /**
     * Get health percentage (0-1)
     * @return Health percentage
     */
    UFUNCTION(BlueprintCallable, Category = "MyGame|Gameplay")
    float GetHealthPercentage() const;

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

    /**
     * Called when character takes damage (Blueprint implementable)
     * @param DamageAmount - Damage amount
     * @param DamageCauser - Actor that caused the damage
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnTakeDamage(float DamageAmount, AActor* DamageCauser);

    /**
     * Called when character dies (Blueprint implementable)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnDeath();

    /**
     * Called when character health changes (Blueprint implementable)
     * @param NewHealth - New health value
     * @param HealthPercentage - Health percentage (0-1)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnHealthChanged(float NewHealth, float HealthPercentage);

    /**
     * Called when character starts sprinting (Blueprint implementable)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnStartSprinting();

    /**
     * Called when character stops sprinting (Blueprint implementable)
     */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyGame|Events")
    void OnStopSprinting();

protected:
    //////////////////////////////////////////////////////////////////////////
    // İç Yardımcı Fonksiyonlar

    /**
     * Update movement speed based on sprint state
     */
    void UpdateMovementSpeed();

    /**
     * Handle death
     */
    void Die();

private:
    /**
     * Cached default walk speed
     */
    float DefaultWalkSpeed = 0.0f;
};


// MyGameCharacter.cpp

#include "MyGameCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Kismet/GameplayStatics.h"
#include "MyGamePlayerController.h"

// Log category
DEFINE_LOG_CATEGORY_STATIC(LogMyGameCharacter, Log, All);

AMyGameCharacter::AMyGameCharacter()
{
    // Set size for collision capsule
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

    // Don't rotate when the controller rotates (let that just affect the camera)
    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw = false;
    bUseControllerRotationRoll = false;

    // Configure character movement
    GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
    GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
    GetCharacterMovement()->JumpZVelocity = 600.f;
    GetCharacterMovement()->AirControl = 0.2f;

    // Create a camera boom (pulls in towards the player if there is a collision)
    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
    SpringArm->bUsePawnControlRotation = true; // Rotate the arm based on the controller

    // Create a follow camera
    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName); // Attach the camera to the end of the boom
    Camera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm

    // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
    // are set in the derived blueprint asset named BP_MyCharacter (to avoid direct content references in C++)

    // Default walk speed
    DefaultWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;

    UE_LOG(LogMyGameCharacter, Log, TEXT("MyGameCharacter initialized"));
}

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

    // Set current health to max health
    CurrentHealth = MaxHealth;

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character BeginPlay: Health = %.1f/%.1f"), CurrentHealth, MaxHealth);
}

void AMyGameCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

void AMyGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character SetupPlayerInputComponent"));

    // NOT: Enhanced Input işlemleri PlayerController'da yapılacak
    // Burada sadece legacy input sistemi için bağlama yapılabilir
    // Ancak Enhanced Input kullanıyorsanız burada bağlama yapmanıza gerek yok
}

void AMyGameCharacter::StartSprinting()
{
    if (bIsSprinting)
        return;

    bIsSprinting = true;
    UpdateMovementSpeed();

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character started sprinting"));

    // Blueprint event
    OnStartSprinting();
}

void AMyGameCharacter::StopSprinting()
{
    if (!bIsSprinting)
        return;

    bIsSprinting = false;
    UpdateMovementSpeed();

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character stopped sprinting"));

    // Blueprint event
    OnStopSprinting();
}

void AMyGameCharacter::UpdateMovementSpeed()
{
    if (UCharacterMovementComponent* MovementComp = GetCharacterMovement())
    {
        if (bIsSprinting)
        {
            MovementComp->MaxWalkSpeed = DefaultWalkSpeed * SprintSpeedMultiplier;
        }
        else
        {
            MovementComp->MaxWalkSpeed = DefaultWalkSpeed;
        }

        UE_LOG(LogMyGameCharacter, Verbose, TEXT("Movement speed updated: %.1f (Sprinting: %s)"),
            MovementComp->MaxWalkSpeed, bIsSprinting ? TEXT("Yes") : TEXT("No"));
    }
}

float AMyGameCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
    AController* EventInstigator, AActor* DamageCauser)
{
    // Calculate actual damage
    float DamageTaken = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

    if (DamageTaken <= 0.0f || CurrentHealth <= 0.0f)
        return DamageTaken;

    // Apply damage
    float OldHealth = CurrentHealth;
    CurrentHealth = FMath::Clamp(CurrentHealth - DamageTaken, 0.0f, MaxHealth);

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character took damage: %.1f (Health: %.1f -> %.1f)"),
        DamageTaken, OldHealth, CurrentHealth);

    // Notify PlayerController
    if (AMyGamePlayerController* PC = Cast<AMyGamePlayerController>(GetController()))
    {
        PC->OnPlayerDamaged(DamageTaken, DamageCauser);
    }

    // Blueprint events
    OnTakeDamage(DamageTaken, DamageCauser);
    OnHealthChanged(CurrentHealth, GetHealthPercentage());

    // Check for death
    if (CurrentHealth <= 0.0f)
    {
        Die();
    }

    return DamageTaken;
}

void AMyGameCharacter::Heal(float HealAmount)
{
    if (HealAmount <= 0.0f || CurrentHealth >= MaxHealth)
        return;

    float OldHealth = CurrentHealth;
    CurrentHealth = FMath::Clamp(CurrentHealth + HealAmount, 0.0f, MaxHealth);

    UE_LOG(LogMyGameCharacter, Log, TEXT("Character healed: %.1f (Health: %.1f -> %.1f)"),
        HealAmount, OldHealth, CurrentHealth);

    // Blueprint events
    OnHealthChanged(CurrentHealth, GetHealthPercentage());

    // Notify PlayerController
    if (AMyGamePlayerController* PC = Cast<AMyGamePlayerController>(GetController()))
    {
        PC->UpdateHealthUI(GetHealthPercentage());
    }
}

float AMyGameCharacter::GetHealthPercentage() const
{
    return MaxHealth > 0.0f ? CurrentHealth / MaxHealth : 0.0f;
}

void AMyGameCharacter::Die()
{
    UE_LOG(LogMyGameCharacter, Log, TEXT("Character died"));

    // Disable movement
    GetCharacterMovement()->DisableMovement();

    // Disable collision
    GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

    // Notify PlayerController
    if (AMyGamePlayerController* PC = Cast<AMyGamePlayerController>(GetController()))
    {
        PC->OnPlayerDied();
    }

    // Blueprint event
    OnDeath();

    // Schedule destruction
    SetLifeSpan(5.0f); // 5 seconds destroy
}

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

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

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

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

Ön tanımlı Character değerini ayarlama

Unreal Engine Editor'ün "Project Settings" penceresinin "Maps & Modes" sekmesinde, "Default Pawn Class" değerini "BP_MyGameCharacter" olarak değiştirelim.

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

Projeyi çalıştırma

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

1. "BP_MyGameCharacter" sınıfı aktif Character olarak devreye girer ve Outliner'a görünü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: Enhanced Input subsystem found in SetupInputComponent
LogMyGamePlayerController: Default Input Mapping Context added
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
LogMyGameCharacter: MyGameCharacter initialized
LogMyGame: Pawn spawned for player: BP_MyGameCharacter_C_0
LogMyGameCharacter: Character SetupPlayerInputComponent
LogMyGamePlayerController: PlayerController possessing pawn: BP_MyGameCharacter_C_0
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
LogMyGamePlayerController: Default Input Mapping Context added
LogMyGameCharacter: Character BeginPlay: Health = 100.0/100.0
LogMyGame: Game State Changed: Waiting -> Playing
LogMyGame: Game Started! Duration: 300 seconds
LogMyGame: Remaining Time: 290 seconds
LogMyGamePlayerController: PlayerController unpossessing pawn
LogMyGame: Player ... left the game
LogMyGame: Game State Changed: Playing -> Finished
LogMyGame: Game Ended: Player Lost