在虚幻引擎中,BoxComponent是一种碰撞组件,它定义了一个长方体形状的边界,用于检测碰撞和重叠事件。它通常用于构建触发器区域,即当其他物体进入或离开这个长方体区域时,会触发相应的事件。
BoxComponent的触发器功能: 当将BoxComponent设置为触发器(即不阻挡物体,但检测重叠)时,它可以用来检测其他碰撞体是否进入或离开它的区域。触发器不会阻挡物体移动,但会发送重叠事件(OnComponentBeginOverlap和OnComponentEndOverlap),从而允许我们在蓝图中或C++中编写逻辑来响应这些事件。
触发器功能在很多游戏场景中非常有用,例如:
-
陷阱区域:当玩家进入一个区域时,触发陷阱。
-
检查点:在赛车游戏中,当车辆通过检查点时,记录时间或位置。
-
门触发器:当玩家靠近门时,自动打开门。
-
拾取物品:当玩家经过一个物品时,自动拾取。
如何创建一个BoxComponent组件:



如何配置BoxComponent组件:
不知道为啥,这个BoxComponent组件创建后,很多基本的函数和定义不具备需要我们自己写,或者从其他的类中直接复制过来:
//TriggerComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/BoxComponent.h"
#include "TriggerComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class DUNGEONESCAPE_API UTriggerComponent : public UBoxComponent
{
GENERATED_BODY()
public:
/*此构造函数用于初始化,只调用一次,初始化的方式还有很多种:
在构造函数初始化,在beginPlay()里面初始化,在UE的细节面板初始化
它们之间的执行顺序是:
构造函数 -> 细节面板(属性覆盖)-> BeginPlay()*/
UTriggerComponent();
protected:
// 此组件的初始化
virtual void BeginPlay() override;
public:
/*此函数每帧调用,;必须在构造函数里面
写了PrimaryComponentTick.bCanEverTick = true;才能使用*/
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
}
//TriggerComponent.cpp
UTriggerComponent::UTriggerComponent()
{
/*将此组件设置为在游戏启动时初始化,并在每一帧都进行更新。
如果您不需要这些功能,可以将其关闭以提高性能。*/
PrimaryComponentTick.bCanEverTick = true;
UE_LOG(LogTemp, Display, TEXT("Trigger component is constructed"));
}
void UTriggerComponent::BeginPlay()
{
Super::BeginPlay();
}
void UTriggerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
如何理解两个委托类实例:
OnComponentBeginOverlap委托: 其声明, 定义和实例化都在我们引用的祖宗头文件和继承的祖宗类里,无需我们自己操作,直接拿来用就行, 其作用就是存放函数并在触发时调用函数集合;触发的条件是物体重合;检测触发的是UE的物理引擎PhysX,它会检测场景中所有的激活的碰撞组件,这个物理引擎无需程序猿自己调用.
OnComponentEndOverlap委托: 和上面类似,不过这个是在物体从重合状态变成非重合状态时调用的.
OnComponentBeginOverLap委托是一个类的实例,其类的定义如下:
// 委托的固定签名(你不能改变)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(
FComponentBeginOverlapSignature, //委托类名
UPrimitiveComponent* OverlappedComponent, //参数1
AActor* OtherActor, //参数2
UPrimitiveComponent* OtherComp, //参数3
int32 OtherBodyIndex, //参数4
bool bFromSweep, //参数5
const FHitResult& SweepResult //参数6
);
//创建一个实例
FComponentBeginOverlapSignature OnComponentBeginOverlap;
| OverlappedComponent | 发出重叠事件的组件(即拥有这个事件的组件) |
| OtherActor | 参与重叠的另一个Actor(不是当前组件的拥有者,而是另一个Actor) |
| OtherComp | 参与重叠的另一个组件(属于OtherActor) |
| OtherBodyIndex | 用于物理身体索引(在物理模拟中,多个身体可能共享一个组件,这个索引指定了是哪个身体)。 |
| bFromSweep | 一个布尔值,表示重叠是否是由扫描(Sweep)引起的(扫描通常用于移动过程中检测碰撞),如果物体是走进来的为true,传送进来的为false |
| SweepResult | 如果bFromSweep为真,这个结构体包含了扫描命中的详细信息(比如命中位置、法线等) |
由于委托在定义的时候就强制规定了所有的函数都必须具备这6个参数, 所以即使你的函数用不到这六个参数,也必须在函数参数列表写入这六个参数.
//TriggerComponent.cpp
void UTriggerComponent::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
/*
标记未用的参数,明确告诉编译器"此物无用"(若有警报参数未使用,可写此物以消警报)
(void)OtherBodyIndex;
(void)bFromSweep;
(void)SweepResult;
*/
if (Mover) {
Mover->ShouldMove = true;
}
}
void UTriggerComponent::OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OherBodyIndex)
{
if (Mover) {
Mover->ShouldMove = false;
}
}
//此函数在BeginPlay()调用即可完成绑定委托
void UTriggerComponent::BindDelegates(){
OnComponentBeginOverlap.AddDynamic(this, &UTriggerComponent::OnOverlapBegin);
OnComponentEndOverlap.AddDynamic(this, &UTriggerComponent::OnOverlapEnd);
}
简单的例子:
假设我们需要创建一个压力板,用来控制一个房间的门,压力板被触发--门上升打开,松开--门下降关闭.

先解决门的问题: 门的实现已经完成,由一个Mover组件的参数ShouldMove控制,ShouldMove=true 开门,ShouldMove= false 关门;我们可以先获取到门Actor的指针,然后再在门的里面找Mover组件,然后可以得到Mover的指针,然后就可以用Mover指针控制ShouldMove变量了.
1. 在UTriggerComponent类里面定义两个成员变量AActor* MoverActor;UMover* Mover;
UPROPERTY(EditAnywhere)
AActor* MoverActor;
UMover* Mover;
2. 进入UE的UTriggerComponent面板找到MoverActor变量,然后把场景里的门绑定到这个变量;

3. 在BeginPlay()里,利用Mover = MoverActor->FindComponentByClass<UMover>();找到Mover指针
if (MoverActor != nullptr)
{
Mover = MoverActor->FindComponentByClass<UMover>();
if (Mover != nullptr)
{
UE_LOG(LogTemp, Display, TEXT("Successfully found the mover component"));
}
else
{
UE_LOG(LogTemp, Display, TEXT("Mover is nullptr"));
}
}
else
{
UE_LOG(LogTemp, Display, TEXT("MoverActor is nullptr"));
}
解决压力板的问题:
向委托OnComponentBeginOverlap和OnComponentEndOverlap中写入函数OnOverlapBegin()和OnOverlapEnd();
然后在场景内加入立方体用来搭载触发器组件:
在组件面板找到触发器:
黄色物体就是碰撞箱:

哎呀太麻烦了,直接贴全部的代码吧...
//TriggerComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/BoxComponent.h"
#include "Mover.h"
#include "TriggerComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class DUNGEONESCAPE_API UTriggerComponent : public UBoxComponent
{
GENERATED_BODY()
public:
UTriggerComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(EditAnywhere)
bool IsPressurePlate = false;
UPROPERTY(EditAnywhere)
AActor* MoverActor;
UMover* Mover;
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult&
SweepResult);
UFUNCTION()
void OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OherBodyIndex);
};
//TriggerComponent.cpp
#include "TriggerComponent.h"
//构造函数(只调用一次,组件初始化时)
UTriggerComponent::UTriggerComponent()
{
//这里bCanEverTick=true可以允许组件使用TickComponent()函数,每帧调用
PrimaryComponentTick.bCanEverTick = true;
}
//此函数每帧调用
void UTriggerComponent::BeginPlay()
{
Super::BeginPlay();
if (MoverActor != nullptr) {
Mover = MoverActor->FindComponentByClass<UMover>();
if (Mover != nullptr) {
UE_LOG(LogTemp, Display, TEXT("Successfully found the mover component"));
}
else {
UE_LOG(LogTemp, Display, TEXT("Mover is nullptr"));
}
}
else {
UE_LOG(LogTemp, Display, TEXT("MoverActor is nullptr"));
}
UE_LOG(LogTemp, Display, TEXT("Trigger component is alive!"));
//IsPressurePlate是一个bool类型的成员变量,用于控制是否开启压力板检测,本质是控制是否绑定门开启的函数
if (IsPressurePlate) {
OnComponentBeginOverlap.AddDynamic(this, &UTriggerComponent::OnOverlapBegin);
OnComponentEndOverlap.AddDynamic(this, &UTriggerComponent::OnOverlapEnd);
}
}
void UTriggerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
/*第一个绑定函数:此函数通过修改门的Mover组件的ShouldMove变量, 控制门的移动,
函数名可自定义,参数必须严格符合OnComponentBeginOverlap委托的定义*/
void UTriggerComponent::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
/*
标记未用的参数,明确告诉编译器"此物无用"(若有警报参数未使用,可写此物以消警报)
(void)OtherBodyIndex;
(void)bFromSweep;
(void)SweepResult;
*/
if (Mover) {
Mover->ShouldMove = true;
}
}
//第二个绑定函数:函数名可自定义,参数必须严格符合OnComponentEndOverlap委托的定义
void UTriggerComponent::OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OherBodyIndex)
{
if (Mover) {
Mover->ShouldMove = false;
}
}
下面是关于门的Mover组件的代码:
//Mover.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Mover.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DUNGEONESCAPE_API UMover : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UMover();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(EditAnywhere)
FVector MoveOffset;
UPROPERTY(EditAnywhere)
float MoveTime = 4.0f;
UPROPERTY(EditAnywhere)
bool ShouldMove = false;
UPROPERTY(VisibleAnywhere)
bool ReachedTarget = false;
FVector TargetLocation;
FVector StartLocation;
};
//Mover.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Math/UnrealMathUtility.h"
#include "Mover.h"
// Sets default values for this component's properties
UMover::UMover()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UMover::BeginPlay()
{
Super::BeginPlay();
StartLocation = GetOwner()->GetActorLocation();
// ...
ShouldMove = false; // 强制设置为false
TargetLocation = StartLocation;
}
// Called every frame
void UMover::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UE_LOG(LogTemp, Display, TEXT("%d"),ShouldMove);
if (ShouldMove==true) {
TargetLocation = StartLocation + MoveOffset;
}
else if (ShouldMove == false) {
TargetLocation = StartLocation;
}
FVector CurrentLocation = GetOwner()->GetActorLocation();
ReachedTarget = CurrentLocation.Equals(TargetLocation);
if (ReachedTarget == false) {
float Speed = MoveOffset.Length() / MoveTime;
FVector NewLocation = FMath::VInterpConstantTo(CurrentLocation,
TargetLocation, DeltaTime, Speed);
GetOwner()->SetActorLocation(NewLocation);
UE_LOG(LogTemp, Display, TEXT("%s is at location %s"),
*GetOwner()->GetActorNameOrLabel(), *CurrentLocation.ToCompactString());
}
}
1119

被折叠的 条评论
为什么被折叠?



