效果图:
第一步,创建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") -
};