"bir yazılımcının not defteri.."

14 Temmuz 2017 Cuma

altıncı mini uygulama - Player Input ve Pawn

merhaba,

bu makale boyunca, Pawn sınıfını genişleterek oyuncu input girdileri ile etkileşime girmeyi öğreneceğiz,

1. bir Pawn ı özellşetirelim (Customize):
yeni bir Pawn yaratarak işe başlayalım, adı da MyPawn olsun. Pawn lar da bir Actor tipidir; insan oyuncu veya AI tarafından kontrol edilmek üzere tasarlanmışlardır.



ilk yapacağımız şey oyun başladığında otomatik olarak oyuncu girdilerine (player input) tepki verecek bir MyPawn ı ayarlamak olacak.

NameYourNewPawn.png

Pawn class başlatma anında bizim set edebileceğimiz bir değişkene sahiptir.
MyPawn.cpp nin içinde AMyPawn::AMyPawn içine aşağıdaki satırları ekleyin:

// Set this pawn to be controlled by the lowest-numbered player
AutoPossessPlayer = EAutoReceiveInput::Player0;

şimdi bir kaç temel Component yaratalım; Component takibi için MyPawn.h a class tanımının altına aşağıdaki satırları ekleyip obje yaratalım:

UPROPERTY(EditAnywhere)
USceneComponent* OurVisibleComponent;

bu değişken Unreal Engine de görünebilmesi için UPROPERTY ile etiketlendi , bu önemlidir çünkü oyun başladığında veya proje veya level kapandığında veya yeniden yüklendiğinde (reload) değişkenin resetlenmesini önler.

şimdi MyPawn.cpp dosyuasına geri dönelim, AMyPawn::AMyPawn fonksyonuna aşağıdaki saqtırları ekleyelim

// Create a dummy root component we can attach things to.
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
// Create a camera and a visible object
UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));
OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OurVisibleComponent"));
// Attach our camera and visible object to our root component. Offset and rotate the camera.
OurCamera->SetupAttachment(RootComponent);
OurCamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));
OurCamera->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
OurVisibleComponent->SetupAttachment(RootComponent);

şimdi değişiklikleri kaydetmek ve oyunu derlemek için hazırız: Build command in Visual Studio veya Unreal Editör deki Compile buttonuna basalım;

şimdi artık oyunumuzun input ları ile etkileşecek (react) özelleşmiş bir Pawn ımız var; şimdi bu input ların neler olacağını tanımlamamız lazım; bunu yapmak için de projenin Input Settings kısmına gireceğiz editörden. 

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

#pragma once

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

UCLASS()
class HOWTO_PLAYERINPUT_API AMyPawn : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values
    AMyPawn();

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;

    UPROPERTY(EditAnywhere)
    USceneComponent* OurVisibleComponent;
};

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

#include "HowTo_PlayerInput.h"
#include "MyPawn.h"

// Sets default values
AMyPawn::AMyPawn()
{
    // 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;

    // Set this pawn to be controlled by the lowest-numbered player
    AutoPossessPlayer = EAutoReceiveInput::Player0;

    // Create a dummy root component we can attach things to.
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    // Create a camera and a visible object
    UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));
    OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OurVisibleComponent"));
    // Attach our camera and visible object to our root component. Offset and rotate the camera.
    OurCamera->SetupAttachment(RootComponent);
    OurCamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));
    OurCamera->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
    OurVisibleComponent->SetupAttachment(RootComponent);
}

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

}

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

}

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

}

2. Game Input ı ayarlayalım (Configure):
iki farklı tipte input mapping vardır: Action ve Axis.

Action Mappings
Action Mappings leri evet / hayır inputları olarak düşünmek kullanışlıdır; bir mouse veya joistick üzerindeki buttonlar gibi. Onların basıldığını (pressed), serbest bırakıldığını (released), çift kliklendiğini veya kısa bir süre için basılı tutulduğunu rapor eder. Daha farklı / ayrık aksiyonlar yani Zıplama (jumping), ateş etme (shooting), veya obje ler ile etkileşme (interacting with objects) bu mapping tipine iyi örneklerdir.

Axis Mappings
bunlar sürekli devam eden girdiler (inputlar) olarak düşünülür ve bunlardaki soru “ne kadar input girdisi?” şeklindedir. Bir joistik sopasının veya mouse un konumu gibi… bunlar her frame de bu değerleri rapor ederler; hareketsiz durumda bile olsalar...!

bu büyüklük (magnitude) veya yön (direction) bilgileriyle, yürüme (walking), etrafa bakınma bakma (looking around), bir aracı idare etme gibi işler halledilir.

input mappings doğrudan kod içinden de tanımlanabilir ancak yaygın yöntem onların Editör içinden tanımlanmasıdır (define); bu makalede de böyle yapıcaz.

editörde Edit menüsünden Project Settings e girin:



ekrandaki (aşağıdaki) ayarları action ve axis mapping de yaratın.



şimdi input configure olduğundan, level imiz içinde MyPawn objemizi ayarlayalım. Bu sınıf Conten Browser içinde görünecek ve onu sürükleyerek Level içinde kullanacağız.






MyPawn ı aytarlamak için bir adım dana gerekli; ona bir static mesh vermeliyiz; böylece onu oyun ortamında görebiliriz; bunun için önce MyPawn ı seçip Details Panel içnideki componentler menüsünden OurVisibleComponent (Inherited) i ekleyin; ve ardından dropdawn ın static mesh kategorisinden dan ona bir asset atayın, bu örnekte biz silindir seçtik.



şimdi kaydedelim ve visual studio ya dönelim ve Pawn ımızı tanımladığımız input lara tekpki verecek (react) duruma getirelim.

MyPawn.h

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

#pragma once

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

UCLASS()
class HOWTO_PLAYERINPUT_API AMyPawn : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values
    AMyPawn();

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;

    UPROPERTY(EditAnywhere)
    USceneComponent* OurVisibleComponent;
};

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

#include "HowTo_PlayerInput.h"
#include "MyPawn.h"

// Sets default values
AMyPawn::AMyPawn()
{
    // 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;

    // Set this pawn to be controlled by the lowest-numbered player
    AutoPossessPlayer = EAutoReceiveInput::Player0;

    // Create a dummy root component we can attach things to.
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    // Create a camera and a visible object
    UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));
    OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OurVisibleComponent"));
    // Attach our camera and visible object to our root component. Offset and rotate the camera.
    OurCamera->SetupAttachment(RootComponent);
    OurCamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));
    OurCamera->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
    OurVisibleComponent->SetupAttachment(RootComponent);
}

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

}

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

}

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

}



3. oyun aksiyonlarını bağlayalım:
MyPawn.h i açın ve aşağıdaki kodları .h deki class tanımına ekleyin:


//Input functions
void Move_XAxis(float AxisValue);
void Move_YAxis(float AxisValue);
void StartGrowing();
void StopGrowing();

//Input variables
FVector CurrentVelocity;
bool bGrowing;

bu dört input fonksyonu bizim input event lerini bağlayacak. Çalıştıklarında MyPawn ın oyun esnasında ne yapması gerektiğine karar vermek için kullanacağı yeni input değişkenlerimizde depolanmış olan değerleri update edecekler.

şimdi MyPawn.cpp ye geçelim ve dört fonlsyonumuzu tanımlayalım,

void AMyPawn::Move_XAxis(float AxisValue)
{
    // Move at 100 units per second forward or backward
    CurrentVelocity.X = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

void AMyPawn::Move_YAxis(float AxisValue)
{
    // Move at 100 units per second right or left
    CurrentVelocity.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

void AMyPawn::StartGrowing()
{
    bGrowing = true;
}

void AMyPawn::StopGrowing()
{
    bGrowing = false;
}

input lardan gelen değeri belli bir min - max aralığında tutma için FMath::Clamp kelepçe / kısıtlama fonksyonu kullanılır; burada kısıtlama aralık değerleri -1 ve +1 dir. Bu örnekte bir sorun olmadığı halde, eğer bir axis i etkileyen birde fazla anahtar olsa idi, ve oyuncu bu input lara aynı anda bassa idi, değerleri (values) birlikte eklenirdi. Örneğin W tuşu ile ileri yön tuşu 1.0 ağırlık değerinde Axis e sahip olsun, ikisi aynı anda basıldığında eğer Clamp (sınırlama) yapmamış isek oyuncu 2.0 hızında koşacaktır.

Grow fonksyonu float değil iken iki hareket “Move” fonksyonunun axis değerini float olarak aldığını farkedebiliriz; bunun nedeni onlar Axis maplaridir ve MoveX ve MoveY olarak map edildiler, bu yüzden float parametreleri vardır. Action MAppaings ler bu tipte parametreye sahip olmazlar.

şimdi artık kendi tanımladığımız input fonksyonlarımıza sahibiz, şimdi onları bağlamamız lazım ki gerekli input lar ile etkileşime girebilsinler. aşağıdaki kodda bunu yapalım, 

SetupPlayerInputComponent fonksyonunun içinde, 
AMyPawn::SetupPlayerInputComponent:
// Respond when our "Grow" key is pressed or released.
InputComponent->BindAction("Grow", IE_Pressed, this, &AMyPawn::StartGrowing);
InputComponent->BindAction("Grow", IE_Released, this, &AMyPawn::StopGrowing);

// Respond every frame to the values of our two movement axes, "MoveX" and "MoveY".
InputComponent->BindAxis("MoveX", this, &AMyPawn::Move_XAxis);
InputComponent->BindAxis("MoveY", this, &AMyPawn::Move_YAxis);
şimdi inputlar tarafından update olan değişkenleri ayarlamış olduk. artık yapmamız gereken tek şey onların birşeyler yapması için kod yazmak.

AMyPawn::Tick e aşağıdaki kodları ekleyelim şimdi:

// Handle growing and shrinking based on our "Grow" action
{
    float CurrentScale = OurVisibleComponent->GetComponentScale().X;
    if (bGrowing)
    {
        // Grow to double size over the course of one second
        CurrentScale += DeltaTime;
    }
    else
    {
        // Shrink half as fast as we grow
        CurrentScale -= (DeltaTime * 0.5f);
    }
    // Make sure we never drop below our starting size, or increase past double size.
    CurrentScale = FMath::Clamp(CurrentScale, 1.0f, 2.0f);
    OurVisibleComponent->SetWorldScale3D(FVector(CurrentScale));
}

// Handle movement based on our "MoveX" and "MoveY" axes
{
    if (!CurrentVelocity.IsZero())
    {
        FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);
        SetActorLocation(NewLocation);
    }
}

derleme sonrası Unreal Editor e dönün ve Play a basın, göreceğiniz üzere Pawn ımızı WASD tuşları ile kontrol edebilmeliyiz. ve space bar ile onu büyütebilmeliyiz, ve bıraktığımızda küçüldüğünü görebilmeliyiz.
PlayingGame1.png

PlayingGame2.png
kodun tamamı şu şekilde:

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

#pragma once

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

UCLASS()
class HOWTO_PLAYERINPUT_API AMyPawn : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values
    AMyPawn();

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;

    UPROPERTY(EditAnywhere)
    USceneComponent* OurVisibleComponent;

    // Input functions
    void Move_XAxis(float AxisValue);
    void Move_YAxis(float AxisValue);
    void StartGrowing();
    void StopGrowing();

    // Input variables
    FVector CurrentVelocity;
    bool bGrowing;
};

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

#include "HowTo_PlayerInput.h"
#include "MyPawn.h"

// Sets default values
AMyPawn::AMyPawn()
{
    // 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;

    // Set this pawn to be controlled by the lowest-numbered player
    AutoPossessPlayer = EAutoReceiveInput::Player0;

    // Create a dummy root component we can attach things to.
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    // Create a camera and a visible object
    UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));
    OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OurVisibleComponent"));
    // Attach our camera and visible object to our root component. Offset and rotate the camera.
    OurCamera->SetupAttachment(RootComponent);
    OurCamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));
    OurCamera->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
    OurVisibleComponent->SetupAttachment(RootComponent);
}

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

}

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

    // Handle growing and shrinking based on our "Grow" action
    {
        float CurrentScale = OurVisibleComponent->GetComponentScale().X;
        if (bGrowing)
        {
            // Grow to double size over the course of one second
            CurrentScale += DeltaTime;
        }
        else
        {
            // Shrink half as fast as we grow
            CurrentScale -= (DeltaTime * 0.5f);
        }
        // Make sure we never drop below our starting size, or increase past double size.
        CurrentScale = FMath::Clamp(CurrentScale, 1.0f, 2.0f);
        OurVisibleComponent->SetWorldScale3D(FVector(CurrentScale));
    }

    // Handle movement based on our "MoveX" and "MoveY" axes
    {
        if (!CurrentVelocity.IsZero())
        {
            FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);
            SetActorLocation(NewLocation);
        }
    }
}

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

    // Respond when our "Grow" key is pressed or released.
    InputComponent->BindAction("Grow", IE_Pressed, this, &AMyPawn::StartGrowing);
    InputComponent->BindAction("Grow", IE_Released, this, &AMyPawn::StopGrowing);

    // Respond every frame to the values of our two movement axes, "MoveX" and "MoveY".
    InputComponent->BindAxis("MoveX", this, &AMyPawn::Move_XAxis);
    InputComponent->BindAxis("MoveY", this, &AMyPawn::Move_YAxis);
}

void AMyPawn::Move_XAxis(float AxisValue)
{
    // Move at 100 units per second forward or backward
    CurrentVelocity.X = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

void AMyPawn::Move_YAxis(float AxisValue)
{
    // Move at 100 units per second right or left
    CurrentVelocity.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

void AMyPawn::StartGrowing()
{
    bGrowing = true;
}

void AMyPawn::StopGrowing()
{
    bGrowing = false;
}


4. ev ödevi !
öğrendiklerimizi kullanarak aşağıdakileri yapmayı deneyin:

- basılı tutulduğunda doğrudan hızı artıran kontroller yapın
- bir girdi sırası yaratın, kullanıcı bir Axis Mapping tuşuna basmaya başladıktan hemen sonra Action Mapping tuşuna basıldığında anlık olarak objeyi scale ile anında genişletsin.


evet geldik bu yaznın da sonuna,
birsonraki yazıda görüşene kadar hoşçakalın,

Hiç yorum yok: