14 Temmuz 2017 Cuma

beşinci minik uygulama - Oyuncu Kontrollü Kamera

merhabalar,

bu yazıda bir kamerayı nasıl aktive edeceğimizi ve aktif olan kamerayı bir diğeri ile nasıl değiştireceğimizi öğreneceğiz.


1. bir Panw a bir Camera Attach etme ile başlayalım,
yeni starter content li bir Basic Code project yaratalım, ve adına da HowTo_PlayerCamera diyelim. Hemen ardından kendi özel Pawn class ımızı yazacağız.

Örneğimizde bu yeni Pawn sınıfını PawnWithCamera olarak isimle
ndireceğiz.



şimdi Visual Studio ya geçin, PawnWithCamera.h i açın aşağıdaki kodu class tanmına ekleyin:
protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;

bu değişkenleri en son attach edeceğimiz SpringArmComponent ve CameraComponent lerini yaratmak için kullanacağız.

Spring Arm ler kameraları veya başka compoent leri attach etmek için harikadırlar; hareket ettikçe setr geçişleri yumuşatırlar.

Şimdi yapıcı constructor fonksyonumuz içinde header da tanımladığımız Component lerimizi yaratalım:

PawnWithCamera.cpp içine aşağıdaki kodu ekleyin:
APawnWithCamera::APawnWithCamera
//Create our components
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
OurCameraSpringArm->SetupAttachment(RootComponent);
OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
OurCameraSpringArm->TargetArmLength = 400.f;
OurCameraSpringArm->bEnableCameraLag = true;
OurCameraSpringArm->CameraLagSpeed = 3.0f;

bu, Component hiyerarşisi içinde root component olarak temel, boş (empty) bir SceneComponent yaratır, sonra da bir SpringArmComponent i ona attach eder;

ardından Spring Arm detauft pitch değeri -60 derece ayarladık; ki aşağıya doğru 60 derece açı ile bakabilsin, 
ve pozisyonunu da root un 50 unit yukarısına ayarladık; 
bunun yanında SpringArmComponent sınıfına bir kaç özel değer de sağladık ki uzunluğu ve hareket akışkanlığını belirlemek için; 

şimdi bir de CameraComponent yaratmalı ve SpringArmComponent in sonundaki soket e attach etmeliyiz. aşağıdaki şekilde:

OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

nihayet Pawn ımızı default olarak ayarlayabiliriz, local player bu kod parçası ile beraber otomatik olarak spawn olacaktır:

//Take control of the default Player
AutoPossessPlayer = EAutoReceiveInput::Player0;

şimdi artık kameramızı rahatça kontrol etmemizi sağlayan basit bir Pawn ımız var; şimdide Unreal Editor den input girişlerimizi configüre edelim ve bu cofig ile etkileşime girecek kodu yazalım.
PawnWithCamera.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties
    APawnWithCamera();

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

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

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

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;
};

PawnWithCamera.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

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

    //Create our components
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSprintArm->CameraLagSpeed = 3.0f
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

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

}

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

}

// Called to bind functionality to input
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

}



2. Şimdi camera yı kontro letmek için input ayarlayalım
Kamera kontrolümüzün ne yapacağına karar vermemiz ve ardından buna göre girişleri ayarlamamız (SET) gerekiyor.  

bu projede muose a sağ tıklandığında takip mesafesinin kısaltmasına ve FOV değerini zoom lamasına istiyoruz; dahası bakış açımızın mouse ile kontrol edilmesini de istiyoruz, ve son olarak elbette Pawn ımız WASD tuşları ile hareket etsin; bunları ayarlamak için, Edit menüsünden Project Settings i açın:


tuş takımını aşağıdaki şekilde ayarlayın:

InputConfig.png

input larımızı tanımladığımıza göre şimdi onlara tepki verecek kodları yazalım:

PawnWithCamera.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties
    APawnWithCamera();

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

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

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

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;
};


PawnWithCamera.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

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

    //Create our components
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSprintArm->CameraLagSpeed = 3.0f
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

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

}

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

}

// Called to bind functionality to input
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

}




3. Input a tepki verecek (react) C++ kodunu yazalım:
oyunumuz şimdi kullanabileceğimiz bir input mappings e sahip; dolayısı ile şimdi aldığımız (recive) dataları tutacak bazı üye değişkenleri ayarlayalım. 

- güncelleme esnasında ber biri iki boyutlu vector ler olan hareket değerlerimizi (values of our movement) ve mouse bakış değerini (mouse-looking axes) bilmemiz gerekli; 
- aynı zamanda zomlama bakış açısına dooğru hareket edip etmeyeceğimizi de bilmemiz gerekli; - ---- ayrıca bu iki durumun arasındaki mesafenin ne kadar olduğunu da. 

bunu başarmak için aşağıdaki kodları class tamınına ekliyoruz: 

PawnWithCamera.h
//Input variables
FVector2D MovementInput;
FVector2D CameraInput;
float ZoomFactor;
bool bZoomingIn;

input u takip edecek fonksyonu yaratmamız lazım, şimdi gelin onu da class tanımına ekleyelim:

//Input functions
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void PitchCamera(float AxisValue);
void YawCamera(float AxisValue);
void ZoomIn();
void ZoomOut();
ve şimdi PawnWithCamera.cpp dosyaını şu şekilde kodlayalım:
//Input functions
void APawnWithCamera::MoveForward(float AxisValue)
{
    MovementInput.X = FMath::Clamp(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::MoveRight(float AxisValue)
{
    MovementInput.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::PitchCamera(float AxisValue)
{
    CameraInput.Y = AxisValue;
}

void APawnWithCamera::YawCamera(float AxisValue)
{
    CameraInput.X = AxisValue;
}

void APawnWithCamera::ZoomIn()
{
    bZoomingIn = true;
}

void APawnWithCamera::ZoomOut()
{
    bZoomingIn = false;
}
ZoomFactor ile ilgili bir şey yapmadık henüz; button durumuna bağlı olarak değiştiğinden bu değişken Pawn ımızın Tick fonksyonunda update olacak.

input datayı depolayacak kodu elde ettiğimizde, sadece Unreal Engine e bu kodu ne zaman çağıracağını söylememiz gerekir. 

Pawn lara Input event lerini bağlayan fonksyonlar (Binding Functions) yani binding kodu eklemek aşağıdaki kadar kolaydır:


APawnWithCamera::SetupPlayerInputComponent

//Hook up events for "ZoomIn"
InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);

//Hook up every-frame handling for our four axes
InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);

artık nihayet Pawn ve Camera yı her bir frame de güncellemek için Tick fonksyonumuz içinde bu değerleri kullanabiliriz.

aşağıdaki kodlar PawnWithCamera.cpp içindeki APawnWithCamera::Tick e eklenmelidir:
//Zoom in if ZoomIn button is down, zoom back out if it's not
{
    if (bZoomingIn)
    {
        ZoomFactor += DeltaTime / 0.5f;         //Zoom in over half a second
    }
    else
    {
        ZoomFactor -= DeltaTime / 0.25f;        //Zoom out over a quarter of a second
    }
    ZoomFactor = FMath::Clamp(ZoomFactor, 0.0f, 1.0f);
    //Blend our camera's FOV and our SpringArm's length based on ZoomFactor
    OurCamera->FieldOfView = FMath::Lerp(90.0f, 60.0f, ZoomFactor);
    OurCameraSpringArm->TargetArmLength = FMath::Lerp(400.0f, 300.0f, ZoomFactor);
}
bu kod bir kaç hard-coded değeri kullanır; yarım saniye veya çeyrek saniyedeki zoom lama zamanı gibi.. 

90 derece zoomed-out, 
60 derece zoomed-in, 
FOV view değerlerği 
ve 400 zoomed-out 
ve 300 zoomed-in camera mesefeleri (distances) gibi..

bu gibi değerler editörde genellikle UPROPERTY(EditAnywhere) ile etiketlenen (tagged) değişkenler olarak ortaya çıkar, böylece programcı olmayan birileri de onları değiştirebilir, veya programcılar da kodu yeniden derlemeden değiştirebilir, hatta editördeki oyun esnasında bile bu değişim mümkündür.

//Rotate our actor's yaw, which will turn our camera because we're attached to it
{
    FRotator NewRotation = GetActorRotation();
    NewRotation.Yaw += CameraInput.X;
    SetActorRotation(NewRotation);
}

//Rotate our camera's pitch, but limit it so we're always looking downward
{
    FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
    NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
    OurCameraSpringArm->SetWorldRotation(NewRotation);
}

bu kod bloğu mouseX axis i ile Pawn ı yaw ekseninde çevirir, fakat sadece kamera sistemi mouse in Y axis ine tepki verip pitch eksenin, değiştirmeden sorumludur.

bir Actor ü veya Actor subclass ını döndürmek (rotate) aslında root düzeyindeki (root-level) Component in döndürülmesidir; ve root component döndürüldüğünde zaten ona bağlı olan her şey dolaylı olarak etkilenir.

//Handle movement based on our "MoveX" and "MoveY" axes
{
    if (!MovementInput.IsZero())
    {
        //Scale our movement input axis values by 100 units per second
        MovementInput = MovementInput.SafeNormal() * 100.0f;
        FVector NewLocation = GetActorLocation();
        NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
        NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
        SetActorLocation(NewLocation);
    }
}

GetActorForwardVector ve GetActorRightVector ünü kullanmak oyuncunun baktığı yöne göre göreceli olarak hareket etmemize izin verir.

Camera, Actor ile aynı yöne baktığından, bu ileri yön tuşunun (forward key) her zaman oyuncun gördüğü / baktığı yöne doğru göreceli olarak değişebilmesini sağlar. Yani oyuncu hangi yöne bakıyorsa ileri yönün o yön olmasını sağlar. 

kodlamayı bitirdik, şimdi derleyelim ve Content Browser içinden yeni sınıfımızın instance ini Level Editör penceresinin içine sürükleyelim. 


bir Static Mesh veya başka bir görsel component eklemek isterseniz rahat ve özgür olun; yahutta olmadan oynayın.

sizi level de takip eden kamera hareketlerinde yumuşak / düzgün hızlanma ve yavaşlama hissetmelisiniz, ancak dönüşleriniz (rotation) ani ve sıkı hissedeceksiniz.

SpringArmComponent üzerinde bazı property lerin değerlerini değiştirmeyi deneyin ki bu değişikliklerin kontrolleri nasıl etkileyeceğini görün örneğin  "Camera Rotation Lag" eklemeyi deneyin veya “Camera Lag” ı artırıp azaltın.



döndürülmüş bir coni (cone) mesh ile sonuç aşağıdaki gibi olacaktır:


Kodun Tamamı ise Şöyle:
PawnWithCamera.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties
    APawnWithCamera();

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

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

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

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;

    //Input variables
    FVector2D MovementInput;
    FVector2D CameraInput;
    float ZoomFactor;
    bool bZoomingIn;

    //Input functions
    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void PitchCamera(float AxisValue);
    void YawCamera(float AxisValue);
    void ZoomIn();
    void ZoomOut();
};

PawnWithCamera.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

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

    //Create our components
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f;
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSpringArm->CameraLagSpeed = 3.0f;
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

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

}

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

    //Zoom in if ZoomIn button is down, zoom back out if it's not
    {
        if (bZoomingIn)
        {
            ZoomFactor += DeltaTime / 0.5f;         //Zoom in over half a second
        }
        else
        {
            ZoomFactor -= DeltaTime / 0.25f;        //Zoom out over a quarter of a second
        }
        ZoomFactor = FMath::Clamp(ZoomFactor, 0.0f, 1.0f);
        //Blend our camera's FOV and our SpringArm's length based on ZoomFactor
        OurCamera->FieldOfView = FMath::Lerp(90.0f, 60.0f, ZoomFactor);
        OurCameraSpringArm->TargetArmLength = FMath::Lerp(400.0f, 300.0f, ZoomFactor);
    }

    //Rotate our actor's yaw, which will turn our camera because we're attached to it
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += CameraInput.X;
        SetActorRotation(NewRotation);
    }

    //Rotate our camera's pitch, but limit it so we're always looking downward
    {
        FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
        NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
        OurCameraSpringArm->SetWorldRotation(NewRotation);
    }

    //Handle movement based on our "MoveX" and "MoveY" axes
    {
        if (!MovementInput.IsZero())
        {
            //Scale our movement input axis values by 100 units per second
            MovementInput = MovementInput.SafeNormal() * 100.0f;
            FVector NewLocation = GetActorLocation();
            NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
            NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
            SetActorLocation(NewLocation);
        }
    }
}

// Called to bind functionality to input
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

    //Hook up events for "ZoomIn"
    InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
    InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);

    //Hook up every-frame handling for our four axes
    InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
    InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
    InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
}

//Input functions
void APawnWithCamera::MoveForward(float AxisValue)
{
    MovementInput.X = FMath::Clamp(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::MoveRight(float AxisValue)
{
    MovementInput.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::PitchCamera(float AxisValue)
{
    CameraInput.Y = AxisValue;
}

void APawnWithCamera::YawCamera(float AxisValue)
{
    CameraInput.X = AxisValue;
}

void APawnWithCamera::ZoomIn()
{
    bZoomingIn = true;
}

void APawnWithCamera::ZoomOut()
{
    bZoomingIn = false;
}



evet bu kadar, 

Epic Games dökümantasyonu bu noktada aşağıdaki uygulamaları öğrendiklerimiz ile yapmamızı öneriyor:

- tuşa basıldığında ki bu koşma tuşu olsun, Pawn ımız hareket kızını artırsın.

- otomatik ve input ile yönetilen (input-driven) kamera hareketini deneyimlensin; burası çok odalı oyun geliştirmenin çok derin bir konusudur.

- genel kamera hissiyatını nasıl etkileyeceğini daha iyi anlamak için Spring Component teki lag değerini artırın, azaltın veya kaldırarak deneyin.

- küçük bir miktarda periodik hareket yapın, kameranızı hissetmek için muhtemelen hafiften random veya Curve Asset kullanabilirsiniz,

- kameranıza bir miktar otomatik dönüş verin , böylece kamera yavaş yavaş (kademeli olarak) (gradually) hareket eden oyuncu nesnesinin arkasına geçer, ve oyuncunun hareket ettiği yöne bakar.


evet bu kadar,

bu dökümantasyonların teorik soğuk anlatımları yıldırıcı olabilse de, yavaş yavaş devam edeceğiz :)

bir başka yazıda görüşmek üzere hoşçakalın,
Yorum Gönder