EXP14.单向跳跃平台(碰撞体积)
大约 6 分钟
提要



响应通道(Collision Response Channels)

UE 的碰撞系统由三部分组成:
碰撞通道(Collision Channel)
用于标记对象所属类型。
UE 内置两种类型:
- Object Channels(对象通道):标记对象类别,比如
Pawn
、WorldStatic
、WorldDynamic
。 - Trace Channels(射线通道):用于射线/扫描(LineTrace、SphereTrace)检测。
- Object Channels(对象通道):标记对象类别,比如
碰撞预设(Collision Presets)
UE 提供常用预设组合,比如:
BlockAll
→ 阻挡所有OverlapAll
→ 全部触发重叠事件Pawn
→ 阻挡世界静态物体,忽略玩家等
每个预设内部其实就是 对每个通道的响应配置。
碰撞响应(Collision Response)
- 决定了对象对某个通道的具体行为:
碰撞响应 | 类型 | 描述 |
---|---|---|
ECR_Block | 阻挡 | 产生物理碰撞,角色/物体无法穿过 |
ECR_Overlap | 重叠 | 允许穿过,但会触发 OnComponentBeginOverlap/EndOverlap 事件 |
ECR_Ignore | 忽略 | 不会碰撞,也不会触发重叠事件 |
方案
方案1

- 每个平台上方加一个 TriggerBox/BoxComponent(只用于感知角色是否接近)
- 平台默认 Pawn 通道忽略阻挡
- 当角色进入 Trigger → 开启 平台阻挡(ECR_Block)
- 当角色离开 Trigger → 关闭阻挡(ECR_Ignore)
APlatformBase::APlatformBase()
{
PrimaryActorTick.bCanEverTick = false;
Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("FootTrigger"));
Trigger->SetupAttachment(GetRootComponent());
// 设置大小,稍微比脚底大一点
Trigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
Trigger->SetCollisionResponseToAllChannels(ECR_Overlap);
Trigger->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
Trigger->OnComponentBeginOverlap.AddDynamic(this, &APlatformBase::OnTriggerOverlap);
Trigger->OnComponentEndOverlap.AddDynamic(this, &APlatformBase::OnTriggerEndOverlap);
}
void APlatformBase::SetBlocking(bool bEnable)
{
if (bIsBlocking == bEnable) return;
bIsBlocking = bEnable;
GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, bEnable ? ECR_Block : ECR_Ignore);
}
void APlatformBase::OnTriggerOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor && OtherActor->IsA<ACharacter>())
{
SetBlocking(true);
}
}
void APlatformBase::OnTriggerEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor && OtherActor->IsA<ACharacter>())
{
SetBlocking(false);
}
}
方案1|缺陷多人问题
// 记录 Trigger 内的角色数量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Collision")
int32 OverlappingCharacters = 0;
void APlatformBase::OnTriggerOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor && OtherActor->IsA<ACharacter>())
{
OverlappingCharacters++;
SetBlocking(true);
}
}
void APlatformBase::OnTriggerEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor && OtherActor->IsA<ACharacter>())
{
OverlappingCharacters = FMath::Max(OverlappingCharacters - 1, 0);
SetBlocking(false);
}
}
方案1|缺陷平台过近导致卡死。

方案2|代码

void ADoodleJumpCharacter::OnFootTriggerOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
if (!OtherActor) return;
if (APaperFlipbookActor* Platform = Cast<APaperFlipbookActor>(OtherActor))
{
const FVector CharLocation = FootTrigger->GetComponentLocation();
const FVector PlatformLocation = Platform->GetActorLocation();
if (CharLocation.Z > PlatformLocation.Z)
{
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
//跳跃
Jump();
}
else
{
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
}
}
}
方案2|缺陷——跳跃问题
为了更加直观,用一张图演示问题:


// copyright by sigaohe
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "PlatformInterface.generated.h"
// This class does not need to be modified.
UINTERFACE()
class UPlatformInterface : public UInterface
{
GENERATED_BODY()
};
/**
* 平板接口
*/
class DOODLEJUMPGAME_API IPlatformInterface
{
GENERATED_BODY()
public:
//初始化操作
UFUNCTION(BlueprintNativeEvent,BlueprintCallable)
void InitAction();
//触摸操作
UFUNCTION(BlueprintNativeEvent,BlueprintCallable)
void TouchAction(AActor* InActor);
};
void ADoodleJumpCharacter::OnFootTriggerOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
if (!OtherActor) return;
if (APaperFlipbookActor* Platform = Cast<APaperFlipbookActor>(OtherActor))
{
const FVector CharLocation = FootTrigger->GetComponentLocation();
const FVector PlatformLocation = Platform->GetActorLocation();
if (CharLocation.Z > PlatformLocation.Z)
{
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
if (Platform->GetClass()->ImplementsInterface(UPlatformInterface::StaticClass()))
{
IPlatformInterface::Execute_TouchAction(Platform, this);
}
}
else
{
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
}
}
}
void APlatformBase::TouchAction_Implementation(AActor* OtherActor)
{
CharacterJump(OtherActor);
if(JumpSound)
{
UGameplayStatics::PlaySoundAtLocation(this,JumpSound, GetActorLocation(),GetActorRotation());
}
}
void APlatformBase::CharacterJump(AActor* OtherActor)
{
if (const auto Character = Cast<ACharacter>(OtherActor))
{
Character->Jump();
Character->LaunchCharacter(FVector(0.f, 0.f, JumpStrength), true, true);
}
}
void ADoodleJumpCharacter::OnFootTriggerOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
if (!OtherActor) return;
if (const APaperFlipbookActor* Platform = Cast<APaperFlipbookActor>(OtherActor))
{
const FVector CharLocation = FootTrigger->GetComponentLocation();
const FVector PlatformLocation = Platform->GetActorLocation();
// 条件:脚在平台上方 + 正在下坠
if (CharLocation.Z > PlatformLocation.Z)
{
// 临时允许角色踩在平台上
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
}
else
{
Platform->GetRenderComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
}
}
}
void ADoodleJumpCharacter::Landed(const FHitResult& Hit)
{
Super::Landed(Hit);
//判断是不是平台
if (AActor* LandedActor = Hit.GetActor())
{
if (LandedActor->GetClass()->ImplementsInterface(UPlatformInterface::StaticClass()))
{
//通知平台接口
IPlatformInterface::Execute_TouchAction(LandedActor, this);
}
}
}