先创建一个交互接口(UInterface)

// This class does not need to be modified.
UINTERFACE()
class USGameplayInterface : public UInterface
{
GENERATED_BODY()
};

/**
*
*/
class ACTIONROGUELIKE_API ISGameplayInterface
{
GENERATED_BODY()

// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void Interact(APawn* InstigatorPawn);
};

在创建一个交互组件

// .h
class ACTIONROGUELIKE_API USInteractionComponent : public UActorComponent

事件交互检测

当开始交互时,检测到可交互的物品(这里的检测规则需要设置Actor碰撞为WorldDynamic)。再查看可交互物品是否实现对应的交互接口。(用if (HitActor->Implements<USGameplayInterface>())查看),实现对用接口后通过ISGameplayInterface::Execute_Interact(HitActor, MyPawn);调用对应的实现。

// .h
class ACTIONROGUELIKE_API USInteractionComponent : public UActorComponent
{
GENERATED_BODY()

...

public:
void PrimaryInteract();
};

// .cpp
void USInteractionComponent::PrimaryInteract()
{
FCollisionObjectQueryParams ObjectQueryParams;
ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic);

AActor* MyOwner = GetOwner();

FVector EyeLocation;
FRotator EyeRotation;
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector End = EyeLocation + (EyeRotation.Vector() * 1000);

#if 0 // 眼睛看到的位置,适用于大物体,小物体此方式检测不太适用
FHitResult HitResult;
GetWorld()->LineTraceSingleByObjectType(HitResult, EyeLocation, End, ObjectQueryParams);

AActor* HitActor = HitResult.GetActor();
if (HitActor)
{
if (HitActor->Implements<USGameplayInterface>())
{
APawn* MyPawn = Cast<APawn>(MyOwner);
ISGameplayInterface::Execute_Interact(HitActor, MyPawn);
}
}
DrawDebugLine(GetWorld(), EyeLocation, End, FColor::Red, false, 2.0f, 0, 2.0f);
#elif 1 // 眼睛前方范围内碰撞检测
TArray<FHitResult> HitResults;
float Radius = 30.f;
FCollisionShape CollisionShape;
CollisionShape.SetSphere(Radius);
bool bBlockingHit = GetWorld()->SweepMultiByObjectType(HitResults, EyeLocation, End, FQuat::Identity,
ObjectQueryParams, CollisionShape);
FColor LineColor = bBlockingHit ? FColor::Green : FColor::Red;

for (FHitResult Hit : HitResults)
{
AActor* HitActor = Hit.GetActor();
if (HitActor)
{
if (HitActor->Implements<USGameplayInterface>())
{
APawn* MyPawn = Cast<APawn>(MyOwner);

ISGameplayInterface::Execute_Interact(HitActor, MyPawn);
DrawDebugSphere(GetWorld(), Hit.ImpactPoint, Radius, 32, LineColor, false, 2.0f);
break;
}
}
DrawDebugSphere(GetWorld(), Hit.ImpactPoint, Radius, 32, LineColor, false, 2.0f);
}

DrawDebugLine(GetWorld(), EyeLocation, End, LineColor, false, 2.0f, 0, 2.0f);
#else // 角色范围内进行碰撞检测,不做眼睛前视检测
TArray<FOverlapResult> Overlaps;
FCollisionQueryParams Params;
Params.AddIgnoredActor(MyOwner);
bool bBlockingHit = GetWorld()->OverlapMultiByObjectType(Overlaps, MyOwner->GetActorLocation(), FQuat::Identity,
ObjectQueryParams, FCollisionShape::MakeSphere(200.f),
Params);
FColor LineColor = bBlockingHit ? FColor::Green : FColor::Red;
for (FOverlapResult& Overlap : Overlaps)
{
AActor* HitActor = Overlap.GetActor();
if (HitActor)
{
if (HitActor->Implements<USGameplayInterface>())
{
APawn* MyPawn = Cast<APawn>(MyOwner);

ISGameplayInterface::Execute_Interact(HitActor, MyPawn);
DrawDebugSphere(GetWorld(), HitActor->GetActorLocation(), 30.f, 32, LineColor, false, 2.0f);
break;
}
}
DrawDebugSphere(GetWorld(), HitActor->GetActorLocation(), 30.f, 32, LineColor, false, 2.0f);
}
DrawDebugLine(GetWorld(), EyeLocation, End, LineColor, false, 2.0f, 0, 2.0f);
#endif
}

什么时候触发交互呢?

这个以玩家按下F时进行交互,给玩家添加交互组件后,玩家就拥有交互的能力。

class ACTIONROGUELIKE_API ASCharacter : public ACharacter
{
...
UPROPERTY(VisibleAnywhere)
USInteractionComponent* InteractionComp;
...
}

void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);


// Action
UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("PrimaryInteract", EKeys::F));
PlayerInputComponent->BindAction("PrimaryInteract", IE_Pressed, this, &ASCharacter::PrimaryInteract);
}

void ASCharacter::PrimaryInteract()
{
if (InteractionComp)
{
InteractionComp->PrimaryInteract();
}
}

玩家可以交互了,还差可以交互的Actor

C++中的交互

直接继承接口类,实现重载接口实现逻辑。

class ACTIONROGUELIKE_API ASItemChest : public AActor, public ISGameplayInterface
{
...
virtual void Interact_Implementation(APawn* InstigatorPawn) override;
...
}

蓝图中的交互

Actor编辑面板中的菜单栏,选择Class Settings,在详细属性面板中选择Interfaces,添加对应的接口类。然后在My Blueprint中实现接口。

blueprint1

blueprint2