为什么不做进角色里
一开始交互逻辑是写在 BP_Farmer 里的 —— 射线检测、找可交互物、显示提示、执行动作。
写到第四个可交互物的时候问题就来了:机器要交互、载具要交互、NPC 也要交互,
难道每个都往 BP_Farmer 里加分支?
于是抽成组件:谁装谁就能被交互。
UCLASS(ClassGroup=(Tomato), meta=(BlueprintSpawnableComponent))
class UInteractionManagerComponent : public UActorComponent
{
GENERATED_BODY()
public:
/** 每帧(或定时)从摄像机前方做一次检测,找最近的候选目标 */
UFUNCTION(BlueprintCallable, Category="Interaction")
void UpdateFocus();
/** 执行当前聚焦目标的交互 */
UFUNCTION(BlueprintCallable, Category="Interaction")
void CommitInteraction();
/** 当前聚焦目标,UI 层据此显示提示 */
UPROPERTY(BlueprintReadOnly, Category="Interaction")
TObjectPtr<AActor> FocusedActor;
protected:
/** 检测距离 */
UPROPERTY(EditDefaultsOnly, Category="Interaction")
float TraceDistance = 350.f;
/** 检测用的通道,用来把「能看的」和「能摸的」分开 */
UPROPERTY(EditDefaultsOnly, Category="Interaction")
TEnumAsByte<ECollisionChannel> TraceChannel;
};
关键在于 meta=(BlueprintSpawnableComponent) —— 挂上这个标记,组件才会出现在
蓝图的「添加组件」列表里,视觉层不需要为它写一行代码。
约定
任何想被交互的东西,实现一个统一的接口就行:
UINTERFACE(MinimalAPI, Blueprintable)
class UInteractable : public UInterface { GENERATED_BODY() };
class IInteractable
{
GENERATED_BODY()
public:
/** 聚焦时给玩家看的提示文字 */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Interaction")
FText GetInteractionPrompt() const;
/** 真正执行 */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Interaction")
void Interact(AActor* Instigator);
};
BlueprintNativeEvent 是刻意的:C++ 给一个默认实现,蓝图可以覆盖。
箱子、机器、作物都用同一套接口,交互组件完全不需要知道对面是什么。
输入
玩家的输入走 Enhanced Input:
| 资产 | 作用 |
|---|---|
IA_LeftClick |
主交互,短按执行 / 长按用于拖拽类操作 |
IMC_Default |
默认输入映射上下文 |
IA_LeftClick 绑到 CommitInteraction(),「拖拽中」由组件内部的状态决定走哪条分支 ——
一个按键承担两种语义,玩家不会觉得别扭。
光标
WBP_CustomCursor 做了一套自绘光标,兼顾三件事:
- 不同聚焦状态下形状变化(可交互 / 不可交互 / 拖拽中)
- 鼠标位置不动时轻微呼吸,避免玩家把它忘了
- 跟随暂停菜单一起显隐
目前的进展
-
UInteractionManagerComponentC++ 实现 + 蓝图继承 -
IInteractable接口与蓝图覆盖 -
IA_LeftClick/IMC_Default输入绑定 -
WBP_CustomCursor自绘光标 - 长按与短按的判定阈值调优
- 拖拽物体时的物理反馈