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

18 Mayıs 2017 Perşembe

üçünü minik uygulama (Movement Component)

Merhabalar,

bugün Component ler ve Collision (çarpışma) konusunda bir mini uygulama yapacağız,

uygulamamızda kullanıcı girdileri (control) ile yönetilen basit bir hareketli obje (Pawn) ve ona bir kaç özellik katacak olan component leri nasıl kod ortamında ekleyeceğimizi göreceğiz; 

dahası eklediğimiz component leri bir hiyerarşi içine koymayı, ve onları gameplay esnasında kontrol etmeyi daha iyi anlayacağız; ve de onları Pawn ımızı katı (solid) objeler içeren world içinde çeşitli şekillerde hareket ettirmek için kullanacağız.


1. Component i yaratma ve attach etme:
önce yeni bir basit bir C++ code projesi başlatalım; 
sonra bir Pawn objesi yaratacağız, daha doğrusu var olan Pawn sınıfını miras alarak kendi özelleşmiş ve daha gelişmiş olan Pawn ımızı kod ortamında üreteceğiz.

Peki bu Pawn normal Pawn dan neden daha nitelik li olacak? çünkü ona bir kaç Component ilave edecek ve aralarındaki koordinasyonu sağlayacağız. 

Üreteceğimiz özelleşmiş Pawn, sonradan ekleyeceğimiz Component lerimizi de tutacak, ve de level ortamında dolaşmamızı sağlayacak; dolaşırken de katı (solid) objeler ile çarpışacak.

isterseniz hemen bir Pawn sınıfı yaratmaya başlayalım; bu yazıda adına CollidingPawn diyeceğiz: 






2- Visual Studio yine otomatik olarak açılacak ve CollidingPawn.h dosyası otomatik olarak yaratılmış ve de açık olarak gelecektir; şimdi header sınıf tanımına aşağıdaki kodu ekleyin:
UParticleSystemComponent *OurParticleSystem;
biz bu nesneyi daha sonra yaratacağımız Particle System Component ini takip etmek için kullanacağız; aslında bu şekilde pointer değişenler (variable) tanımlamadan da component ler yaratabiliriz; ama eğer ki component leri kodlarımızda kullanmak istiyorsak onları bir class member üyesinde depolamalıyız, tıpkı değişkenler gibi; bu yüzden bu satırı ekledik;

şimdi tam burada bir parantez açıp aklıma gelen bir soru hakkında biraz kafa yormak istiyorum; ParticleSystemComponent sınıfının header ini programımıza dahil ettik mi ? ettiysek nerde? etmediysek bu satırı ekledikten sonra programın hata vermesi gerekir ki derlenebildiğini göreceksiniz; peki bu header nerede eklendi?

bu gibi soruların yanıtı projemize verdiğimiz isim ile aynı ismi taşıyan ve otomatik olarak oluşturulan header dosyasında gizlidir; ben projeyi ilk oluştururken verdiğim isim cppTest1 idi,


o yüzden benim projemde cppTest1.h dosyası otomatik olarak oluşturuldu, sizinkinde de sizin projenize verdiğiniz isimdir malum; şimdi bu header in içine baktığımızda:


EngineMinimal.h  isimli bir header dosyasının projemize bize sorulmadan :) dahil edildiğini görüyoruz; peki bu header in içinde ne var diye baktığımızda (amma meraklıyım):



işte bizim ParticleSystemComponent hazretlerinin orada tanmlanmış olduğunu görüyorum; EngineMinimal heade dosyasını solution un arama kutusuna ismini yazarak bulabilirsiniz; ek olarak burada zibil gibi header tanımları olduğunu görüyoruz; buraları karıştırarak projemize otomatik olarak nelerin eklenip eklenmediğini anlayabiliriz; bu da küçük bir enekdot olsun kulağımıza;

devam edelim,

şimdi CollidingPawn.cpp dosyamızı açalım ve yapıcı (constructor) fonksyonu ilk olarak ekleyelim:

ACollidingPawn::ACollidingPawn ismi oluyor burada tabikine;


bu fonksyona ekleyeceğimiz kodlar bazı gerekli component leri spawn edecek/başlatacak, ve onları hiyerarşik biz düzen içine alacak, birazdan göreceğiz..

- öncellikle evet bir Pawn sınıfı programatik olarak yarattık ama, onu editörden level e sürüklesek bile gameplay de hiçbir halt görünmeyecektir; çünkü Pawn hiçbir görsel temsilci öğeye sahip değildir; henüz; o yüzden ilk olarak Pawn ımıza bir görsellik katması için küre şeklindeki bir Sphere Component yaratan kodu ekleyeceğiz, ve onu Pawn ın Root una atayacağız.

- küre görüntüsü tek başına fiziksel dünya ile etkileşime giremez; o sadece bir görüntüdür; fiziksel çarpışmaları sağlamak için görünmeyen Collision niteliğine sahip bir component i de Pawn a ekleyeceğiz; buradaki püf noktası görünmez collision onjesinin mümkün mertebede görünen şekle yakın olması gerekliliğidir (çok detaylı olmasa bile kabaca yakın olması gerekir); bunun için debir Static Mesh Component ekleyeceğiz; böylece Pawn ımızın küre görüntüsü artık fizik simüle etmeye başlayacak; 

- sonra işin içine biraz da görsel effect eklemek istiyoruz; starter content te hazır gelen ateş effekti kullanmak için en kolaylardan biri; bunun için de programatik olarak bir Particle System Component ekleyeceğiz. 

- son olarak bir de Camera Component e atach edilmek üzere bir Spring Arm Component ekleyeceğiz ki bunu da oyun içi perspektifi ve akıllı kamera hareketleri yaratsın;

heralde beş etti; yani bir temel Pawn nesnesi ve ona bağlı hiyerarşik düzende eklenecek olan dört component olacak.

bu arada hiyerarşi deyip duruyoruz diyeceksiniz ki nedir bu hiyerarşi ?

bu, hangi component in hangisine bağımlı olacağının yapılandırmasıdır; şöyle düşünelim, yolda yürürken kolunuzdaki çanta size bağlı bir component tir; kolunuzu sallayıp sallamama seçimi size aittir; kolunuzu sallarsanız çanta da sallanır; yürürseniz çanta da sizinle birlikte hareket etmek zorundadır; ben yerimde duruyorum sen git diyemez; işte componentler de birbirlerine böyle bir düzende bağlıdırlar; doğrudan en üst objeye bağlanan component e RootComponent denir; hiyerarşide de ilk karar vermemiz gereken şey hangi componentin ROOT olacağıdır; 

biz uygulamamızda Pawn görüntüsü olarak küre kullanacağımızdan Sphere Component i root yapacağız, çünkü oyun dünyası ile etkileşime girecek fiziksel varlık olarak onu kullanacağız; unutmayın ki bir Actor kendi hiyerarşisi içinde bir den çok fiziğe sahip component e sahip olabilir; ama biz bu yazıda basit olsun mahiyetiyle sadece bir tane kullanıyoruz, o da küre şeklindeki Pawn.


3- şimdi küre şeklini ekleyecek olan kodları az önce eklediğimiz yapıcı fonksyonumuzun içine yazalım:
    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
burada ne yaptık ?

ilk satırda SphereComponent adında bir pointer tanımlandı; ancak tanımlama satırında eşitliğin sağ tarafında da CreateDefaultSubobject fonksyonu ile hemen ona bir  "değer" atandı. Burada bu iş için neden klasik new operatörü değilde bu fonksyonun kullanıldığını düşündüğümde, önce motorun karmaşık ve çok katmanlı yapısını düşündüm; sonra da new operatörü ile derleme anı tanımlamalarının Unreal Engine in dinamik / asenkron çalışması için olmayabileceği durumları ve ihtimalleri kolayca gözümün önüne getirebildim...

ikinci satırda yarattığımız sınıfın RootComponent property sine yarattığımız ve artık bir değere de işaret eden (null olmayan) küre pointeri atandı;

yani:  Pawn.RootComponent = Küre;  

üçüncü satırda küremizin yarıçapı 40 Unreal Engine unit değeri olarak belirlendi; burada kullandığımız InitSphereRadius fonksyonu da zaten SphereComponent.h heade i içinde tanımlıdır; yani küre nesnesinin kendi fonksyonudur; bu arada küre hangi cehennemden geldi diye soracak olursanız ki ben sormuştum; onun da yine EngineMinimal.h i içinde "Components/SphereComponent.h" satırı ile tanımlanmış olduğunu görebilirsiniz.

son satırda da sanıyorum ki bir tür çarpışma profili atandı.


4- peki bu küremizin ekranda cisimlerle çarpışması için yeterli mi ? Hayır... çünkü küremiz sadece bir görüntüden ibaret; sınırlarını belirleyecek olan bir collision niteliğine sahip değil; bunun için yapılması gereken şey de bir collision component in küreye veya yine RootComponent e attach edilmesidir:

bunun için de yine küre şeklinde ve radius değeri yani yarıçapı 50 unit olan bir static mesh asset yaratalım ve attach edelim; eğer eklediğimiz tam hizalanmıyor ise onu %80 oranında scale edelim; son olarak da onu biraz (40 unit kadar) hareket ettirmemiz de gerekecek; çüknü kürenin merkezi ile collision objesinin merkezinin aynı olması gerekiyor; gerçek çarpışma fizik simulasyonuna yakın olması için.
    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }
yukarıdaki kodları ilk satırdaki CreateDefaultSubobject daha önce sözünü ettiğimiz Unreal engine deki bir objeden instance yaratmanın güvenli yolu idi, Object.h içinde bulunan bu fonksyon un kodlarda gerçekten çok yaygın nir şekilde kullanıldığını gördüm, daha sonra detaylarına bakmanın motorun derinliklerini daha iyi anlama açısından faydalı olacağı görüşündeyim; yaptığı işe gelince,

ikinci satırdaki SetupAttachment fonksyonu ne işe yarar ve nerede tanımlıdır diye düşündüm; sonra bu fonksyonun da SceneComponent.h içinde tanımlı olduğunu USphereComponent in de bir kaç kalıtım kademesi altında bu komponenti miras almış olduğunu gördüm; dolayısı ile bu fonksyon base class tan yani USceneComponent sınıfından geliyor;

SphereVisual->SetupAttachment(RootComponent);

SphereVisual mesh objesini RootComponent e attach ediyor.

üçüncü satırda gözüme takılan ConstructorHelpers::FObjectFinder komutunun ne olduğu ve nereden geldiği ?


görünen o ki, 
ConstructorHelpers bir yapı (struct) ve içinde objelerin bulunması vb. işlemleri barındırıyor; aynı dosya içindeki ConstructorHelpersInternal isim uzayı (namespace) içinde de yine üç benzer arama bulma fonksyonu var; kısaca ConstructorHelpers.h header dosyası daha çok bu arama bulma işleri ile ilgili diyebiliriz;  

FObjectFinder  da bu header içindeki bir (struct) yapıdır; Template tipinde olan bu yapı değişik tipler için hizmet verebilir.



dolayıs ile koddaki bu:
static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
satırında olan şey, içinde parametre olarak verilen yoldaki (path) asset ten UStaticMesh tipinde ve SphereVisualAsset adında bir obje yaratılmasıdır. Devam eden kodlarda da obje yaratımı başarılı ise bir kaç küçük ayarlama yapıldı.

buraya kadar olan kodlamalar ile özel Pawn nesnemiz küre şeklindeki bir görsel görüntüye, ve o görüntüye eklenen yine küre şeklindeki bir collision fizik özelliğine sahip olmuş oldu;

devam edelim,

5- kod içinde Static Mesh asset imizin yolunu (path) da kod içinde ayarladık, ama, bu şekilde kod ortamında path vermek genellikle en uygun çözüm olarak kabul edilmez; bu şekil genellikle, onu kullanacak olan bir class varsa ve anlık ve dinamik yaratması gerekiyorsa daha uygundur; ayrıca bu yöntem programcılara daha hızlı hata ayıklama (debugging) ve yeni özellikleri daha hızlı entegre etme imkanı verir. 

hatırlayın, bir de particle system ekleyecektik, bu ateş effecti olacak, küremiz hareket ederken ardından ateşler çıksın istiyoruz ve bu özelliği de kontrol tuşları ile açıp kapayabilmek istiyoruz. 
Particle System Component, Static Mesh Component e doğrudan attach etsek daha iyi çünkü altından görünsün istiyoruz o yüzden bu componenti mesh e attach edelim; böylece oyun esnasında daha çok görünebilir.
    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

6- sırada ne var ? 
bir Spring Arm Component,
kameramızı bazen hızlandırarak bazen de takip ettiği Pawn dan yavaşlatarak daha pürüzsüz / yumuşak kamera geçişleri verir;  Spring Arm Component aynı gömülü (built-in) bazı özelliklere de sahiptir; örneğin bir third-person shooter game de oyuncu bir köşeyi döndüğünde kameranın katı cisimlerin içinden geçmesini önler; şimdi bunu yaratıp yine Root a attach edelim:
    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

7- Camera Component yaratmak kolaydır, özel ayarlamalar yapmaya ihtiyacımız yoktur; Spring Arm in (temel sınıf yerine) ona attach edebileceğimiz gömülü (built-in) bir soketi vardır.

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

8- şimdi component lerimiz yaratıldı ve atrach landı, artık oyuncuyu (default player) kontrol etmek için pawn ı ayarlamalaıyız;  tüm ihtiyacımız olan aşağıdaki koddur:

    // Take control of the default player
    AutoPossessPlayer = EAutoReceiveInput::Player0;
şimdi kendisine attach lanmış yararlı bir component koleksiyonuna sahip yepyeni bir Pawn sınıfımız var; kodun tamamı şu şekilde:

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

#pragma once

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

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
};

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"

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

    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

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

}

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

}

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

}
bundan sonra editör ortamına kullanıcı kontrolleri tanımlayacağız bunun için unreal editöre dönelim;

kullanıcı kontrollerinden kasıt input girişleridir; bu girişleri yaratmak, gruplamak ve bazı hareketlere bağlamaktır (bind);

ve hazır hareket kontrollerinden söz etmiş iken bu hareketlerden sorumlu olan Pawn Movement Componen de bahsedelim,


input girdileriile başlayalım, bu ayarlar Edit menüsü içindeki Project Settings kısmı altındadır,



açılan pencerenin soldaki panelinde Engine kısmının altındaki Input a gelin, yazacağımız minik oyunda bir particle sistem olacak ve onu bir tuş ile açıp kapamak istiyoruz; bu gibi durumlar için bir Action Mappings e ihtiyacımız olur ve onu burada tanımlayacağız;

Axis Mappings ise doğrudan Pawn ı hareket ettirmek, döndürmek vs. için tanımlananır; Action Mappings in farkı tanımlanıp bir fonksyon gibi isimlendirilen bir tuş haritasının, daha sonra oyundaki bazı yordamları çağırabilmesi, ve bunun dinamik olarak ayarlanabilmesidir; 


açılan aşağıdaki pencerede resimde görünen kontrolleri ekleyin, özellikle de Axis Mappings kısmındaki PraticleTogle adında bir tuş kombinasyonu tanımlamayı unutmamanız önemli, ekranda space tuşunu ayarlamış sadece, siz de öyle yapın.



Pawn sınıfımız için tüm hareketleri (movement) doğrudan buradan ayarlamaktansa, biz onu yönetmek için bir Movement Component yaratacağız; bu makalede Pawn Movement Component class ını genişleteceğiz (extend); yani onu miras alıp üzerine yeni özellikler ekleyecek, kendimize göre biraz daha özelleştireceğiz;

öyleyse yine file menüsünden Add Code to Project diyerek işe başlayalım; sonra da hemen File menüsünden Add New C++ Class diyelim;

bizim Pawn class ın aksine, Pawn Movement Component default olarak ekranda görünmez. onu bulmak için, Show All Classes optionunu seçili hale getirmemiz gerekir:



şimdi arama kutusuna "movement" yazarak , hızlıca listeleyebiliriz:



Pawn Movement Component, genel fiziksel fonksyonelliklere yardımcı olması için bazı güçlü gömülü (built-in) özelliklere sahiptir ve bunlar hareketleri pek çok Pawn tipi arasında paylaşmak / ortak kullanmak için iyi bir yoldur.

Fonksyonellikleri ayrıştırmak için componentleri kullanmak, projenin gelişimi ve kod karmaşasını ve dağınıklığı azaltmak için iyi bir uygulamadır.

yeni yarattığımız componen timizi ımızı CollidingPawnMovementComponent olarak isimlendirelim:



sadece input konfigurasyon umuzu tanımladık ve özel Pawn Movement Component yarattık, şimdi tekrar Visual Studio ya dönmeye hazırız, ve orada Pawn Movement Component kullanıldığında Pawn ların nasıl hareket edeceğini tanımlayabiliriz..

ekleyeceğimiz kod şu şekilde:


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

#pragma once

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

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
};

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"

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

    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

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

}

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

}

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

}

şimdi Visual Studio ya dönelim ve özelleşmiş Pawn Movement Component izmizi programlamaya başlayalım; tüm ihtiyacımız TickComponent fonksyonuna kod eklemek; bu fonksyon bir Actor' ün Tick fonksyouna benzer; amacı yine her bir frame de nasıl hareket edeceğini programlayabilmemizdir; bunun için de CollidingPawnMovementComponent.h ,dosyasının içinde TickComponent fonksyonunu override etmeliyiz; class tanımı aşağıdadır:

public:
    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override; 

şimdi bu fonksyonun açık kodlamasını CollidingPawnMovementComponent.cpp içinde yapalım:

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // Make sure that everything is still valid, and that we are allowed to move.
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        // If we bumped into something, try to slide along it
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
};
bu kod Pawn ımızı dünya içinde yumuşak bir şekilde hareket ettirecek, Pawn ımız yüzeyden adeta kayarak gidecek; 

Pawn a uygulanmış bir yer çekimi yoktur, ve maksimum hızı her bir saniyede 150 Unreal Unit e kodlanmıştır; 

UPawnMovementComponent class tarafından pek çok güçlü özellik sağlanır, biz burada TickComponent fonksyonu içinde bir kaçını kullanmış oluyoruz.

ConsumeInputVector sınıfı/fonksyonu hareket input larımızı (movement inputs) depolamak (store) için kullanacağımız gömülü (built-in) değişkenin değerini raporlar ve temizler.

SafeMoveUpdatedComponent, katı (solid) engeller (barriers) hesaba katıldığı sürece Pawn Movement Component ini hareket ettirmek için Unreal Engine fiziğini kullanır.

SlideAlongSurface bir hareketin sonucu bir çarpışma olduğunda kolayca durdurma ve duvara veya rampaya yapıştırma yerine hesaplamaları ve çarpışma (collision) yüzeyi ile ilgili fiziği halleder, duvarlar ve rampalar gibi.

aslında Pawn Movement Component inde çok daha fazla özellik var; fakat bu yazıdaki örneğin kapsamında gerekli olmadıklarından kullanılmadılar; diğer class lara da bakın örneğin Floating Pawn Movement, Spectator Pawn Movement, veya Character Movement Component, bunlar size ilave kullanışlı örnekler ve fikirler verebilir.

kodun tamamı aşağıdaki şekilde olmalıdır:


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

#pragma once

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

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
};

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"

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

    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

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

}

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

}

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

}


9- Pawn ve Component lerin birlikte kullanımı:

özel Pawn Movement Component imizi kullanmak için ilk ihtiyacımız olan şey Pawn class ına bir değişken eklemektir ki onu takip edebilelim;

CollidingPawn.h dosyasında class tanımlalasının altında, "OurParticleSystem" değişkeninni eklediğimin yerin yanına şunu eklemeliyiz:

class UCollidingPawnMovementComponent* OurMovementComponent;

onu takip etmek için bir konuma (place) bir kez sahip olduğumuzda, yeni değişkenlerimiz i depolamak için Colliding Pawn Movement Component yaratmamız lazım; yani şimdi CollidingPawn.cpp dosyasını açın ve dosyanın en üstüne aşağıdaki kodu yerleştirin, (#include "GameFramework/Pawn.h" satırının altına) bu kod yeni sınıfımıza referans olabilir:

#include "CollidingPawnMovementComponent.h"
son include ifadesinin generated.h ifadesini kodlarımıza dahil ettiğimize emin olalım; tam adı: "#include "CollidingPawn.generated.h" oluyor kendileri… aksi durumda derleme hatalarına neden olacaktır.

bir Pawn Movement Component yaratma ve onu bizim Pawn ile birleştirmek kolaydır; ACollidingPawn::ACollidingPawn in altına, şu kodu ekleyebiliriz:

// Create an instance of our movement component, and tell it to update the root.
OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
OurMovementComponent->UpdatedComponent = RootComponent;
buraya kadar gördüğümüz diğer componentlerin aksine,, bu componenti kendi component hiyerarşisine attach etmemiz gerekmiyor; bunun nedeni diğer componentlerimizin doğal olarak fiziksel lokasyon gerektiren Scene Component lerin tipleri olmasıdır;

Movement Controllers , her nasılsa Scene Component leri değildir, ve fiziksel objeyi temsil etmez, dolayısı ile fiziksel bir lokasyondaki varlık konsepti veya onlara uygulanmayan başka bir FİZİKSEL componente attaç lanmış fiziksel oluş

(so the concept of existing at a physical location or being physically attached to another Component does not apply to them.) 


Pawn lar GetMovementComponent motordaki diğer class ları Pawn güncel olarak (current) kullanılıyorken Pawn Movement Component e erişmeleri için aktive eden (enable) adındaki bir fonksyona sahiptirler. bu fonksyonu override etmemiz lazım böylece bizim özel Pawn Movement Component i döndürür.

CollidingPawn.h dosyası içindeki class tanımı içinde, şunu eklemeliyiz:
virtual UPawnMovementComponent* GetMovementComponent() const override;
ve CollidingPawn.cpp dosyasının içinde override ettiğimiz fonksyon tanımını eklememiz gerekiyor, aşağıdaki şekilde :
UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
    return OurMovementComponent;
}
yeni Pawn Movement Component kurulumu ile, Pawn ın alacağı input u halledecek kodu yaratmış olacağız. CollidingPawn.h dosyası içindeki class tanımına bir kaç fonksyon tanımı ekleyerek başlayacağız:
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void Turn(float AxisValue);
void ParticleToggle();
CollidingPawn.cpp dosyasının içine de bu fonksyonların tam/gerçek tanımlarını ekleyeceğiz:
    void ACollidingPawn::MoveForward(float AxisValue)
    {
        if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
        {
            OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
        }
    }

    void ACollidingPawn::MoveRight(float AxisValue)
    {
        if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
        {
            OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
        }
    }

    void ACollidingPawn::Turn(float AxisValue)
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += AxisValue;
        SetActorRotation(NewRotation);
    }

    void ACollidingPawn::ParticleToggle()
    {
        if (OurParticleSystem && OurParticleSystem->Template)
        {
            OurParticleSystem->ToggleActive();
        }
    }
tüm geriye kalan bu fonksyonlarımızı input eventlerimize bağlamak olacaktır, hadi onları da ilgili kod ile ekleyelim :

ACollidingPawn::SetupPlayerInputComponent:

InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);

InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
programlamayı bitirdik,, şimdi editör e dönebilir ve compile buttonuna basıp değişiklikleri ortama yükleyebiliriz; nihayetinde de özel Pawn ımızı dünya (world) içinde sürükle bırak ile kullanmaya başlayabiliriz :)

kodun son hali aşağıdaki şekilde:

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

#pragma once

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

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
    class UCollidingPawnMovementComponent* OurMovementComponent;

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void Turn(float AxisValue);
    void ParticleToggle();
};

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"
#include "CollidingPawnMovementComponent.h"

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

    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

    // Create an instance of our movement component, and tell it to update our root component.
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;
}

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

}

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

}

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

    InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);

    InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
    InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
}

UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
    return OurMovementComponent;
}

void ACollidingPawn::MoveForward(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
    }
}

void ACollidingPawn::MoveRight(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
    }
}

void ACollidingPawn::Turn(float AxisValue)
{
    FRotator NewRotation = GetActorRotation();
    NewRotation.Yaw += AxisValue;
    SetActorRotation(NewRotation);
}

void ACollidingPawn::ParticleToggle()
{
    if (OurParticleSystem && OurParticleSystem->Template)
    {
        OurParticleSystem->ToggleActive();
    }
}

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

#pragma once

#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"

/**
 * 
 */
UCLASS()
class HOWTO_COMPONENTS_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
    GENERATED_BODY()

public:
    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;  
};


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

#include "HowTo_Components.h"
#include "CollidingPawnMovementComponent.h"

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // Make sure that everything is still valid, and that we are allowed to move.
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        // If we bumped into something, try to slide along it
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
};


10- editör içinde oynama:


Unreal Editor içinde, compile buttonuna basıp kod değişikliklerini yükleyelim:



aşağıda content brıwseriçinde artık görünmeye başlayan CollidingPawn ı tutup dünya içine sürükleyerek bir instance ini yaratın.






şimdi Play e basıp WASD tuşları ile küremizi hareket ettirin. onu mouse ile döndürüp, kaydırın ve world içindeki objeler ile çarpıştırın, fiziksel bir objbe olması şartı ile ne ile çarpıştırdığınız hiç farketmez, yoksa önce birkaç tane ekleyin. küremiz aynı zamanda ateş saçacaktır SPACE bar tuşu ile !




Unreal Engine 4 bize türlü çeşitli component ler sunar. engin bir denizdir. burada sadece bir kaçını kullanarak yüzeye bir çizik atmış olduk o kadar. gömülü (built-in) componentleri bi deneyin, hatta kendiniz kendi objenizi yazın. Onlar esnektir (flexible), güçlüdür (powerfull) ve de projnizin kod organizasyonuna ve yeniden kullanılabilir kodların olmasına yardımcıdır.


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

#pragma once

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

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
    class UCollidingPawnMovementComponent* OurMovementComponent;

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void Turn(float AxisValue);
    void ParticleToggle();
};

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"
#include "CollidingPawnMovementComponent.h"

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

    // Our root component will be a sphere that reacts to physics
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // Create and position a mesh component so we can see where our sphere is
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // Create a particle system that we can activate or deactivate
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // Create a camera and attach to our spring arm
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

    // Create an instance of our movement component, and tell it to update our root component.
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;
}

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

}

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

}

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

    InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);

    InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
    InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
}

UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
    return OurMovementComponent;
}

void ACollidingPawn::MoveForward(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
    }
}

void ACollidingPawn::MoveRight(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
    }
}

void ACollidingPawn::Turn(float AxisValue)
{
    FRotator NewRotation = GetActorRotation();
    NewRotation.Yaw += AxisValue;
    SetActorRotation(NewRotation);
}

void ACollidingPawn::ParticleToggle()
{
    if (OurParticleSystem && OurParticleSystem->Template)
    {
        OurParticleSystem->ToggleActive();
    }
}


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

#pragma once

#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"

/**
 * 
 */
UCLASS()
class HOWTO_COMPONENTS_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
    GENERATED_BODY()

public:
    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;  
};


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

#include "HowTo_Components.h"
#include "CollidingPawnMovementComponent.h"

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // Make sure that everything is still valid, and that we are allowed to move.
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        // If we bumped into something, try to slide along it
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }


Son olarak öğrendiklerimizi kullanarak, 


- kendi parenti etrafında dönen bir component yaratmaya çalışabilir ve pekiştirebiliriz;

- veya, her biri belli bir süre sonra despawn olan (yokolan) üç alt (child) objeyi spawn eden bir component yaratın,

- son olarak cpmponentler ile Actor lerin diğer Actor lere nasıl Attach olduğunu örenin araştırmak da ev ödevimiz olsun :)


epic games dökümantasyonu bu ödevleri vermiş :)


şimdilik burada durup olan biteni bir sindirsek iyi olacak; kitaptaki örneği yapıp kendisi yeni bir şey yapamama durumu hep bu olan bitenin yeterince iyi sindirilmemesinden kaynaklanıyor; bir şeyin nedenini nasılını anlamadan sadece kodu yapıştırıp oynatmak aslında hiçbir şeye yetmiyor;

o yüzden biraz ara..

tekrar görüşünceye dek hoşçakalın;

Hiç yorum yok: