Components and Collision

Learn how Components can enable Pawns to interact with physics, use particle effects, and more.

Windows
MacOS
Linux

This tutorial will show you how to create Components, put them into a hierarchy, control them during gameplay, and use them to move your Pawn around a world that contains solid objects.

1. Creating and Attaching Components

If you are new to Unreal Engine 4 (UE4), you might want to read our Programming Quick Start tutorial first. For this tutorial, we will assume you are familiar with creating a project and adding C++ code to it, as well as configuring input in the Unreal Editor. If you are unfamiliar with creating your own Pawn class or configuring input, the Player Input and Pawns tutorial might be a good place to start.

  1. We will begin by creating a new, Basic Code project, with starter content, named HowTo_Components. The first thing we'll need to add to this project will be a customized Pawn that will hold our Components, move around our level, and collide with solid objects. For this tutorial, we'll name it CollidingPawn.

    ChooseParentClass.png

    NamePawnClass.png

  2. In Visual Studio, we should open CollidingPawn.h and add the following code to the bottom of our class definition:

    UParticleSystemComponent *OurParticleSystem;

    We will use this variable to keep track of a Particle System Component that we will create later. We can create Components without making variables to track them, but if we want to use those Components in our code, we should store them in class member variables like this.

  3. We can now open CollidingPawn.cpp and edit the constructor function, ACollidingPawn::ACollidingPawn, by adding code that will spawn a variety of useful Components and arrange them in a hierarchy. We will create a Sphere Component to interact with the physical world, a Static Mesh Component to represent our collision shape visually, a Particle System Component that we can turn on or off at will, and a Spring Arm Component that we can use to attach a Camera Component to control our in-game perspective.

  4. First up is deciding which Component will be the root of our hierarchy. In this tutorial, we'll go with the Sphere Component, since it is the physical presence that can interact and collide with our game world. Note that an Actor can have multiple physics-enabled Components in its hierarchy, but for this tutorial, we only need one.

        // 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"));
  5. Next, we will create and attach a visible sphere from a Static Mesh asset that has a radius of 50. Since this doesn't perfectly line up with the 40-radius Sphere Component we just created, we'll scale it down to 80%. We also need to move it down by 40 units in order to have its center line up with the center of the Sphere Component.

        // 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));
        }

    Although you can see the asset location of our Static Mesh asset in the code, hard-coding asset paths is not generally considered the best way to load assets. It is usually preferred to create the Component itself in code if the class needs to use it, and then select the assets in the Unreal Editor. However, it is possible to do this directly in code, and could be faster for programmers debugging or building new features.

  6. We can now attach an inactive Particle System Component to our hierarchy. We can manipulate this component in code, and will later set up an input to turn it on or off. Notice that the Particle System Component is attached directly to the Static Mesh Component, not to the root. It is also slightly offset from the bottom-center of the mesh so that it will be more visible during play.

        // 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);
        }
  7. A Spring Arm Component can provide us with a smoother attachment point for our camera by allowing our camera to accelerate and decelerate more slowly than the Pawn it is following. It also has a built-in feature that prevents the camera from going through solid objects, for situations such as when a player backs into a corner in a third-person game. While it isn't necessary, it can be a very fast and easy way to get smoother-feeling camera work into a game.

        // 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;
  8. The actual Camera Component is easy to create, and we don't require any special settings. The Spring Arm has a special built-in socket that we can attach to, rather than attaching to its base.

        // Create a camera and attach to our spring arm
        UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
        Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
  9. Now that our components are created and attached, we should set this Pawn to be controlled by the default player. The following code is all we need:

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

Our new Pawn now has a collection of useful Components attached to it, and is ready to be configured for user control. We can now return to the Unreal Editor.

Work-In-Progress Code

CollidingPawn.h

// Copyright 1998-2018 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-2018 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);

}

2. Configuring Input and Creating a Pawn Movement Component

  1. Returning to the Unreal Editor, it's time to configure our project's input settings. These settings can be found under Project Settings in the Edit dropdown menu.

    EditProjectSettings.png

    Once there, we can select Input from the Engine section in the left panel. We will need an Action Mapping to set up our particle-system toggle, two Axis Mappings to move our Pawn, and one more Axis Mapping to turn our Pawn.

    Action Mapping

    ParticleToggle

    Space Bar

    Axis Mapping

    MoveForward

    W

    1.0

    S

    -1.0

    MoveRight

    A

    -1.0

    D

    1.0

    Turn

    Mouse X

    1.0

    InputSettings.png

  2. Instead of handling all our movement in our Pawn class directly, we will create a Movement Component to manage it for us. For this tutorial, we'll extend the Pawn Movement Component class. We'll start by selecting the Add Code to Project command from the File dropdown menu.

    AddCPPCode.png

    Unlike our Pawn class, Pawn Movement Component is not visible by default. To find it, we need to check the Show All Classes option.

    ShowAllClasses.png

    Typing "movement" into the search bar can help narrow the list quickly.

    ChooseParentClass2.png

    Pawn Movement Components have some powerful, built-in features to help with common physics functionality, and are a good way to share movement code between many Pawn types. Using Components to separate functionality is a good practice to reduce clutter as your project grows and your Pawns become more complex.

    Since we called our Pawn class CollidingPawn, let's call this CollidingPawnMovementComponent.

    NameComponent.png

We have just defined our input configuration and created a custom Pawn Movement Component. We're ready to go back to Visual Studio and write code to define how Pawns will move when our Pawn Movement Component is in use.

3. Coding Our Pawn Movement Component's Behavior

  1. We can go back to Visual Studio now and program our customized Pawn Movement Component. All we really need to write is a TickComponent function (similar to an Actor's Tick function) to tell us how to move each frame. In CollidingPawnMovementComponent.h, we'll need to override TickComponent in the class definition:

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

    We'll define this function in CollidingPawnMovementComponent.cpp:

    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);
            }
        }
    };

    This code will move our Pawn smoothly around the world, sliding off of surfaces where appropriate. There is no gravity applied to our Pawn, and its maximum speed is hard-coded to 150 Unreal Units per second.

    This TickComponent function makes use of a few of the powerful features offered by the UPawnMovementComponent class.

    ConsumeInputVector reports and clears the value of a built-in variable that we will use to store our movement inputs.

    SafeMoveUpdatedComponent uses Unreal Engine physics to move our Pawn Movement Component while respecting solid barriers.

    SlideAlongSurface handles the calculations and physics involved with sliding smoothly along collision surfaces like walls and ramps when a move results in a collision, rather than simply stopping in place and sticking to the wall or ramp.

    There are more features included in Pawn Movement Components that are worthy of examination, but they are not needed for the scope of this tutorial. Looking at other classes, such as Floating Pawn Movement, Spectator Pawn Movement, or Character Movement Component, could provide additional usage examples and ideas.

With our Pawn Movement Component's behavior defined, we can now write the code that will tie it all together in our customized Pawn class.

Work-In-Progress Code

CollidingPawn.h

// Copyright 1998-2018 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-2018 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);

}

CollidingPawnMovementComponent.h

// Copyright 1998-2018 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-2018 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);
        }
    }
};

4. Using Our Pawn and Components Together

  1. In order to use our custom Pawn Movement Component, we will first need to add a variable to our Pawn class to keep track of it. At the bottom of our class definition in CollidingPawn.h, near where we added the "OurParticleSystem" variable, we should add:

    class UCollidingPawnMovementComponent* OurMovementComponent;
  2. Once we have a place to keep track of it, we will need to create a Colliding Pawn Movement Component to store in our new variable, so let's open CollidingPawn.cpp and add the following to the top of the file, underneath the #include "GameFramework/Pawn.h" line, so that our code will be able to reference our new class:

    #include "CollidingPawnMovementComponent.h"

    Be sure that the last #include in the list is the generated.h (#include "CollidingPawn.generated.h" in this case) as it will cause a compile error otherwise.

    Creating a Pawn Movement Component and associating it with our Pawn is simple. At the bottom of ACollidingPawn::ACollidingPawn, we can add this code:

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

    Unlike the other Components we've seen so far, we don't need to attach this Component to our own Component hierarchy. This is because our other Components are all types of Scene Components, which inherently require physical locations. Movement Controllers, however, are not Scene Components and do not represent physical objects, so the concept of existing at a physical location or being physically attached to another Component does not apply to them.

  3. Pawns have a function called GetMovementComponent that is used to enable other classes in the engine to access the Pawn Movement Component that the Pawn is currently using. We will need to override that function so that it returns our custom Pawn Movement Component. In the class definition in CollidingPawn.h, we need to add:

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    And in CollidingPawn.cpp, we need to add the definition of our overridden function, as follows:

    UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
    {
        return OurMovementComponent;
    }
  4. With our new Pawn Movement Component set up, we can create code to handle the input that our Pawn will receive. We will start by declaring a few functions in our class definition in CollidingPawn.h:

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

    In CollidingPawn.cpp, we will add the definitions of those functions as follows:

        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();
            }
        }
  5. All that remains is to bind our functions to our input events. Let's add the following code to 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);
  6. We are done programming and can now return to the Unreal Editor and press the Compile button to load our changes.

Our programming work is done, and we can now place our custom Pawn in the world and move it around.

Finished Code

CollidingPawn.h

// Copyright 1998-2018 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-2018 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-2018 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-2018 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);
        }
    }
};

5. Playing in Editor

  1. In the Unreal Editor, we can press the Compile button to load our code changes.

    CompileFromEditor.png

  2. We need to drop an instance of our CollidingPawn into the world. We can find the class in the Content Browser under "C++ Classes/HowTo_Components/CollidingPawn".

    ClassInContentBrowser.png

    LevelEditorBeforePlay.png

  3. Press Play and our sphere will move with WASD, turn with the mouse, and collide-and-slide along world objects, such as our table and chairs, or any other physical objects you might want to place in the world. It can also light itself on fire (or put itself out) with the space bar!

    CollidingAndOnFire.png

Unreal Engine 4 offers a wide variety of Components - we've only scratched the surface with a few of the more common ones. Try exploring the built-in Components, or writing some of your own! They are flexible, powerful, and help to keep your project's code organized and reusable.

6. On Your Own!

Using what you have learned, try to do the following:

  • Create a Component that automatically orbits its parent.

  • Build a Component that spawns up to three children, each of which despawn on their own after a set amount of time.

  • Learn how to attach complete Actors to other Actors via Components.

As for the specifics covered in this tutorial:

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback