效果图:
第一步,创建C++ Basic Code
第二步,定义键盘和鼠标输入的映射
第三步,修改 Rendering 中的 Custom Depth - Stencil Pass
第四步,找到GlobalPostProcessVolume [如果没有的话自行拖放一个PostProcessVolume组件]
将 unbound 勾选上
再修改 Blendables 为 PPI_OutlineColored
完整代码如下:
MyPlayer.h
-
// Fill out your copyright notice in the Description page of Project Settings.
-
#pragma once
-
#include "GameFramework/Character.h"
-
#include "MyPlayer.generated.h"
-
UCLASS()
-
class OUTLINECPLUSPLUS_API AMyPlayer : public ACharacter
-
{
-
GENERATED_BODY()
-
public:
-
// Sets default values for this character's properties
-
AMyPlayer();
-
void MoveForward(float val);
-
void MoveRight(float val);
-
void LookYaw(float val);
-
void LookPitch(float val);
-
void Use();
-
class AInteractableActor* FindFocusedActor();
-
void HandleHighlight();
-
// Called when the game starts or when spawned
-
virtual void BeginPlay() override;
-
// Called every frame
-
virtual void Tick( float DeltaSeconds ) override;
-
// Called to bind functionality to input
-
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
-
private:
-
UPROPERTY(EditDefaultsOnly)
-
float InteractionDistance = 300.f; // 交互的范围
-
class AInteractableActor* FocusedActor;
-
// 用于 LineTraceSingleByChannel
-
FCollisionQueryParams TraceParams;
-
};
MyPlayer.cpp
-
// Fill out your copyright notice in the Description page of Project Settings.
-
#include "InteractableActor.h"
-
#include "MyPlayer.h"
-
// Sets default values
-
AMyPlayer::AMyPlayer()
-
{
-
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
-
PrimaryActorTick.bCanEverTick = true;
-
TraceParams = FCollisionQueryParams(FName(TEXT("TraceParams")), false, this);
-
TraceParams.bTraceComplex = false;
-
TraceParams.bTraceAsyncScene = false;
-
TraceParams.bReturnPhysicalMaterial = false;
-
}
-
// Called when the game starts or when spawned
-
void AMyPlayer::BeginPlay()
-
{
-
Super::BeginPlay();
-
}
-
// Called every frame
-
void AMyPlayer::Tick( float DeltaTime )
-
{
-
Super::Tick( DeltaTime );
-
if (Controller && Controller->IsLocalController())
-
{
-
HandleHighlight();
-
}
-
}
-
// Called to bind functionality to input
-
void AMyPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
-
{
-
Super::SetupPlayerInputComponent(PlayerInputComponent);
-
InputComponent->BindAxis("MoveForward", this, &AMyPlayer::MoveForward);
-
InputComponent->BindAxis("MoveRight", this, &AMyPlayer::MoveRight);
-
InputComponent->BindAxis("LookYaw", this, &AMyPlayer::LookYaw);
-
InputComponent->BindAxis("LookPitch", this, &AMyPlayer::LookPitch);
-
InputComponent->BindAction("Use", IE_Pressed, this, &AMyPlayer::Use);
-
}
-
// 前后移动
-
void AMyPlayer::MoveForward(float val)
-
{
-
FRotator Rotation(0, GetActorRotation().Yaw, 0); // Roll, Yaw, Pitch
-
FVector forward = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);
-
AddMovementInput(forward, val);
-
}
-
// 左右移动
-
void AMyPlayer::MoveRight(float val)
-
{
-
FRotator Rotation(0, GetActorRotation().Yaw, 0); // Roll, Yaw, Pitch
-
FVector right = FRotationMatrix(Rotation).GetScaledAxis(EAxis::Y);
-
AddMovementInput(right, val);
-
}
-
// 左右转向
-
void AMyPlayer::LookYaw(float val)
-
{
-
AddControllerYawInput(val);
-
}
-
// 上下转向
-
void AMyPlayer::LookPitch(float val)
-
{
-
// 注意方向相反
-
AddControllerPitchInput(val);
-
}
-
// 按 E 键与激活对象进行交互
-
void AMyPlayer::Use()
-
{
-
AInteractableActor* Interactable = FindFocusedActor();
-
if (Interactable)
-
{
-
// OnInteract_Implementation
-
Interactable->OnInteract(this);
-
}
-
}
-
AInteractableActor* AMyPlayer::FindFocusedActor()
-
{
-
if (!Controller)
-
{
-
return nullptr;
-
}
-
FVector Location;
-
FRotator Rotation;
-
FHitResult Hit(ForceInit);
-
Controller->GetPlayerViewPoint(Location, Rotation);
-
FVector Start = Location;
-
FVector End = Start + (Rotation.Vector() * InteractionDistance);
-
// 通过 “射线拾取” 选定对象
-
GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Camera, TraceParams);
-
if (Hit.bBlockingHit) // 击中
-
{
-
// 获取当前被击中的对象的引用
-
AInteractableActor* MyCastActor = Cast<AInteractableActor>(Hit.GetActor());
-
if (MyCastActor)
-
{
-
return MyCastActor;
-
}
-
}
-
return nullptr;
-
}
-
void AMyPlayer::HandleHighlight()
-
{
-
AInteractableActor* NewHighlight = FindFocusedActor();
-
if (NewHighlight)
-
{
-
// 如果当前描边和新激活的对象不是同一个
-
if (FocusedActor != NewHighlight)
-
{
-
if (FocusedActor)
-
{
-
// 当前描边对象取消描边
-
FocusedActor->OnEndFocus();
-
}
-
// 描边新激活对象
-
NewHighlight->OnBeginFocus();
-
FocusedActor = NewHighlight;
-
}
-
}
-
else
-
{
-
if (FocusedActor)
-
{
-
// 取消描边
-
FocusedActor->OnEndFocus();
-
FocusedActor = nullptr;
-
}
-
}
-
}
InteractableActor.h
-
// Fill out your copyright notice in the Description page of Project Settings.
-
#pragma once
-
#include "GameFramework/Actor.h"
-
#include "OutlineCPlusPlus.h"
-
#include "InteractableActor.generated.h"
-
UCLASS()
-
class OUTLINECPLUSPLUS_API AInteractableActor : public AActor
-
{
-
GENERATED_BODY()
-
public:
-
// Sets default values for this actor's properties
-
AInteractableActor();
-
// Called when the game starts or when spawned
-
virtual void BeginPlay() override;
-
// Called every frame
-
virtual void Tick( float DeltaSeconds ) override;
-
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Interaction)
-
void OnInteract(AActor* Caller) ;
-
virtual void OnInteract_Implementation(AActor* Caller);
-
void OnBeginFocus();
-
void OnEndFocus();
-
private:
-
UPROPERTY(EditDefaultsOnly)
-
uint32 bCanInteract : 1;
-
TArray<UMeshComponent*> Meshes;
-
UPROPERTY(EditDefaultsOnly)
-
EStencilColor Color = EStencilColor::SC_Green;
-
};
InteractableActor.cpp
-
// Fill out your copyright notice in the Description page of Project Settings.
-
#include "MyPlayer.h"
-
#include "InteractableActor.h"
-
// Sets default values
-
AInteractableActor::AInteractableActor()
-
{
-
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
-
PrimaryActorTick.bCanEverTick = true;
-
}
-
// Called when the game starts or when spawned
-
void AInteractableActor::BeginPlay()
-
{
-
Super::BeginPlay();
-
for (UActorComponent* Mesh : GetComponentsByClass(UMeshComponent::StaticClass()))
-
{
-
UMeshComponent* thisMesh = Cast<UMeshComponent>(Mesh);
-
if (thisMesh)
-
{
-
Meshes.Push(thisMesh);
-
}
-
}
-
}
-
// Called every frame
-
void AInteractableActor::Tick( float DeltaTime )
-
{
-
Super::Tick( DeltaTime );
-
}
-
void AInteractableActor::OnInteract_Implementation(AActor* Caller)
-
{
-
AMyPlayer* Player = Cast<AMyPlayer>(Caller);
-
if (Player)
-
{
-
GEngine->AddOnScreenDebugMessage(-1,
-
5.f,
-
FColor::Red,
-
FString::Printf(TEXT("Now deleting the interactable actor! "))
-
);
-
// 销毁自己
-
Destroy();
-
}
-
}
-
void AInteractableActor::OnBeginFocus()
-
{
-
if (bCanInteract)
-
{
-
for (UMeshComponent* Mesh : Meshes)
-
{
-
Mesh->SetRenderCustomDepth(true);
-
Mesh->SetCustomDepthStencilValue((uint8)Color);
-
}
-
}
-
}
-
void AInteractableActor::OnEndFocus()
-
{
-
if (bCanInteract)
-
{
-
for (UMeshComponent* Mesh : Meshes)
-
{
-
Mesh->SetRenderCustomDepth(false);
-
}
-
}
-
}
颜色 的 Enum
-
UENUM(BlueprintType)
-
enum class EStencilColor : uint8
-
{
-
SC_Green = 250 UMETA(DisplayName = "Green"),
-
SC_Blue = 251 UMETA(DisplayName = "Blue"),
-
SC_Red = 252 UMETA(DisplayName = "Red"),
-
SC_White = 253 UMETA(DisplayName = "White")
-
};