Compare commits
6 Commits
b1bac66256
...
453450d7eb
| Author | SHA1 | Date | |
|---|---|---|---|
| 453450d7eb | |||
| 301efee982 | |||
| 92a8b70a7f | |||
| 23e216b211 | |||
| 6c081e1207 | |||
| c6aed472f9 |
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
[/Script/EngineSettings.GameMapsSettings]
|
[/Script/EngineSettings.GameMapsSettings]
|
||||||
GameDefaultMap=/PS_AI_ConvAgent/Demo_VoiceOnly.Demo_VoiceOnly
|
GameDefaultMap=/PS_AI_ConvAgent/Demo_Metahuman.Demo_Metahuman
|
||||||
EditorStartupMap=/PS_AI_ConvAgent/Demo_VoiceOnly.Demo_VoiceOnly
|
EditorStartupMap=/PS_AI_ConvAgent/Demo_Metahuman.Demo_Metahuman
|
||||||
|
|
||||||
[/Script/Engine.RendererSettings]
|
[/Script/Engine.RendererSettings]
|
||||||
r.AllowStaticLighting=False
|
r.AllowStaticLighting=False
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "PS_AI_ConvAgent_ElevenLabsComponent.h"
|
#include "PS_AI_ConvAgent_ElevenLabsComponent.h"
|
||||||
#include "PS_AI_ConvAgent_MicrophoneCaptureComponent.h"
|
#include "PS_AI_ConvAgent_MicrophoneCaptureComponent.h"
|
||||||
|
#include "PS_AI_ConvAgent_PostureComponent.h"
|
||||||
#include "PS_AI_ConvAgent_InteractionSubsystem.h"
|
#include "PS_AI_ConvAgent_InteractionSubsystem.h"
|
||||||
#include "PS_AI_ConvAgent.h"
|
#include "PS_AI_ConvAgent.h"
|
||||||
|
|
||||||
@ -312,6 +313,17 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::StartListening()
|
|||||||
Mic->StartCapture();
|
Mic->StartCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable body tracking on the sibling PostureComponent (if present).
|
||||||
|
// Voice input counts as conversation engagement, same as text.
|
||||||
|
if (AActor* OwnerActor = GetOwner())
|
||||||
|
{
|
||||||
|
if (UPS_AI_ConvAgent_PostureComponent* Posture =
|
||||||
|
OwnerActor->FindComponentByClass<UPS_AI_ConvAgent_PostureComponent>())
|
||||||
|
{
|
||||||
|
Posture->bEnableBodyTracking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const double T = TurnStartTime - SessionStartTime;
|
const double T = TurnStartTime - SessionStartTime;
|
||||||
UE_LOG(LogPS_AI_ConvAgent_ElevenLabs, Log, TEXT("[T+%.2fs] [Turn %d] Mic opened%s — user speaking."),
|
UE_LOG(LogPS_AI_ConvAgent_ElevenLabs, Log, TEXT("[T+%.2fs] [Turn %d] Mic opened%s — user speaking."),
|
||||||
T, TurnIndex, bExternalMicManagement ? TEXT(" (external)") : TEXT(""));
|
T, TurnIndex, bExternalMicManagement ? TEXT(" (external)") : TEXT(""));
|
||||||
@ -404,6 +416,17 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::SendTextMessage(const FString& Text)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WebSocketProxy->SendTextMessage(Text);
|
WebSocketProxy->SendTextMessage(Text);
|
||||||
|
|
||||||
|
// Enable body tracking on the sibling PostureComponent (if present).
|
||||||
|
// Text input counts as conversation engagement, same as voice.
|
||||||
|
if (AActor* Owner = GetOwner())
|
||||||
|
{
|
||||||
|
if (UPS_AI_ConvAgent_PostureComponent* Posture =
|
||||||
|
Owner->FindComponentByClass<UPS_AI_ConvAgent_PostureComponent>())
|
||||||
|
{
|
||||||
|
Posture->bEnableBodyTracking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UPS_AI_ConvAgent_ElevenLabsComponent::InterruptAgent()
|
void UPS_AI_ConvAgent_ElevenLabsComponent::InterruptAgent()
|
||||||
|
|||||||
@ -4,10 +4,12 @@
|
|||||||
#include "PS_AI_ConvAgent_InteractionSubsystem.h"
|
#include "PS_AI_ConvAgent_InteractionSubsystem.h"
|
||||||
#include "PS_AI_ConvAgent_ElevenLabsComponent.h"
|
#include "PS_AI_ConvAgent_ElevenLabsComponent.h"
|
||||||
#include "PS_AI_ConvAgent_MicrophoneCaptureComponent.h"
|
#include "PS_AI_ConvAgent_MicrophoneCaptureComponent.h"
|
||||||
|
#include "PS_AI_ConvAgent_PostureComponent.h"
|
||||||
|
|
||||||
#include "GameFramework/Pawn.h"
|
#include "GameFramework/Pawn.h"
|
||||||
#include "GameFramework/PlayerController.h"
|
#include "GameFramework/PlayerController.h"
|
||||||
#include "Camera/PlayerCameraManager.h"
|
#include "Camera/PlayerCameraManager.h"
|
||||||
|
#include "TimerManager.h"
|
||||||
|
|
||||||
DEFINE_LOG_CATEGORY_STATIC(LogPS_AI_ConvAgent_Select, Log, All);
|
DEFINE_LOG_CATEGORY_STATIC(LogPS_AI_ConvAgent_Select, Log, All);
|
||||||
|
|
||||||
@ -48,6 +50,13 @@ void UPS_AI_ConvAgent_InteractionComponent::BeginPlay()
|
|||||||
|
|
||||||
void UPS_AI_ConvAgent_InteractionComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
void UPS_AI_ConvAgent_InteractionComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
{
|
{
|
||||||
|
// Cancel any pending posture timers.
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
World->GetTimerManager().ClearTimer(PostureAttachTimerHandle);
|
||||||
|
World->GetTimerManager().ClearTimer(PostureDetachTimerHandle);
|
||||||
|
}
|
||||||
|
|
||||||
if (MicComponent)
|
if (MicComponent)
|
||||||
{
|
{
|
||||||
MicComponent->StopCapture();
|
MicComponent->StopCapture();
|
||||||
@ -57,6 +66,16 @@ void UPS_AI_ConvAgent_InteractionComponent::EndPlay(const EEndPlayReason::Type E
|
|||||||
// Fire deselection event for cleanup.
|
// Fire deselection event for cleanup.
|
||||||
if (UPS_AI_ConvAgent_ElevenLabsComponent* Agent = SelectedAgent.Get())
|
if (UPS_AI_ConvAgent_ElevenLabsComponent* Agent = SelectedAgent.Get())
|
||||||
{
|
{
|
||||||
|
// Stop listening and clear posture immediately on shutdown — no delay.
|
||||||
|
if (bAutoManageListening)
|
||||||
|
{
|
||||||
|
Agent->StopListening();
|
||||||
|
}
|
||||||
|
if (bAutoManagePosture)
|
||||||
|
{
|
||||||
|
DetachPostureTarget(Agent);
|
||||||
|
}
|
||||||
|
|
||||||
SelectedAgent.Reset();
|
SelectedAgent.Reset();
|
||||||
OnAgentDeselected.Broadcast(Agent);
|
OnAgentDeselected.Broadcast(Agent);
|
||||||
}
|
}
|
||||||
@ -114,7 +133,7 @@ UPS_AI_ConvAgent_ElevenLabsComponent* UPS_AI_ConvAgent_InteractionComponent::Eva
|
|||||||
AActor* AgentActor = Agent->GetOwner();
|
AActor* AgentActor = Agent->GetOwner();
|
||||||
if (!AgentActor) continue;
|
if (!AgentActor) continue;
|
||||||
|
|
||||||
const FVector AgentLocation = AgentActor->GetActorLocation();
|
const FVector AgentLocation = AgentActor->GetActorLocation() + FVector(0.0f, 0.0f, AgentEyeLevelOffset);
|
||||||
const FVector ToAgent = AgentLocation - ViewLocation;
|
const FVector ToAgent = AgentLocation - ViewLocation;
|
||||||
const float DistSq = ToAgent.SizeSquared();
|
const float DistSq = ToAgent.SizeSquared();
|
||||||
|
|
||||||
@ -162,6 +181,7 @@ UPS_AI_ConvAgent_ElevenLabsComponent* UPS_AI_ConvAgent_InteractionComponent::Eva
|
|||||||
void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_ElevenLabsComponent* NewAgent)
|
void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_ElevenLabsComponent* NewAgent)
|
||||||
{
|
{
|
||||||
UPS_AI_ConvAgent_ElevenLabsComponent* OldAgent = SelectedAgent.Get();
|
UPS_AI_ConvAgent_ElevenLabsComponent* OldAgent = SelectedAgent.Get();
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
|
||||||
// Deselect old agent.
|
// Deselect old agent.
|
||||||
if (OldAgent)
|
if (OldAgent)
|
||||||
@ -174,6 +194,41 @@ void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_El
|
|||||||
OldAgent->GetOwner() ? *OldAgent->GetOwner()->GetName() : TEXT("(null)"));
|
OldAgent->GetOwner() ? *OldAgent->GetOwner()->GetName() : TEXT("(null)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Listening: stop ──────────────────────────────────────────────
|
||||||
|
if (bAutoManageListening)
|
||||||
|
{
|
||||||
|
OldAgent->StopListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable body tracking on deselection.
|
||||||
|
if (bAutoManagePosture)
|
||||||
|
{
|
||||||
|
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(OldAgent))
|
||||||
|
{
|
||||||
|
Posture->bEnableBodyTracking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Posture: detach ──────────────────────────────────────────────
|
||||||
|
if (bAutoManagePosture && World)
|
||||||
|
{
|
||||||
|
// Cancel any pending attach — agent left before attach fired.
|
||||||
|
World->GetTimerManager().ClearTimer(PostureAttachTimerHandle);
|
||||||
|
|
||||||
|
if (PostureDetachDelay > 0.0f)
|
||||||
|
{
|
||||||
|
TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> WeakOld = OldAgent;
|
||||||
|
World->GetTimerManager().SetTimer(PostureDetachTimerHandle,
|
||||||
|
FTimerDelegate::CreateUObject(this,
|
||||||
|
&UPS_AI_ConvAgent_InteractionComponent::DetachPostureTarget, WeakOld),
|
||||||
|
PostureDetachDelay, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DetachPostureTarget(OldAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OnAgentDeselected.Broadcast(OldAgent);
|
OnAgentDeselected.Broadcast(OldAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +249,34 @@ void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_El
|
|||||||
MicComponent->StartCapture();
|
MicComponent->StartCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Listening: start ─────────────────────────────────────────────
|
||||||
|
// Body tracking is enabled by ElevenLabsComponent itself (in StartListening
|
||||||
|
// and SendTextMessage) so it works for both voice and text input.
|
||||||
|
if (bAutoManageListening)
|
||||||
|
{
|
||||||
|
NewAgent->StartListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Posture: attach ──────────────────────────────────────────────
|
||||||
|
if (bAutoManagePosture && World)
|
||||||
|
{
|
||||||
|
// Cancel any pending detach — agent came back before detach fired.
|
||||||
|
World->GetTimerManager().ClearTimer(PostureDetachTimerHandle);
|
||||||
|
|
||||||
|
if (PostureAttachDelay > 0.0f)
|
||||||
|
{
|
||||||
|
TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> WeakNew = NewAgent;
|
||||||
|
World->GetTimerManager().SetTimer(PostureAttachTimerHandle,
|
||||||
|
FTimerDelegate::CreateUObject(this,
|
||||||
|
&UPS_AI_ConvAgent_InteractionComponent::AttachPostureTarget, WeakNew),
|
||||||
|
PostureAttachDelay, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AttachPostureTarget(NewAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OnAgentSelected.Broadcast(NewAgent);
|
OnAgentSelected.Broadcast(NewAgent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -252,6 +335,57 @@ void UPS_AI_ConvAgent_InteractionComponent::ClearSelection()
|
|||||||
SetSelectedAgent(nullptr);
|
SetSelectedAgent(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Posture helpers
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
UPS_AI_ConvAgent_PostureComponent* UPS_AI_ConvAgent_InteractionComponent::FindPostureOnAgent(
|
||||||
|
UPS_AI_ConvAgent_ElevenLabsComponent* Agent)
|
||||||
|
{
|
||||||
|
if (!Agent) return nullptr;
|
||||||
|
AActor* AgentActor = Agent->GetOwner();
|
||||||
|
if (!AgentActor) return nullptr;
|
||||||
|
return AgentActor->FindComponentByClass<UPS_AI_ConvAgent_PostureComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPS_AI_ConvAgent_InteractionComponent::AttachPostureTarget(
|
||||||
|
TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> Agent)
|
||||||
|
{
|
||||||
|
UPS_AI_ConvAgent_ElevenLabsComponent* AgentPtr = Agent.Get();
|
||||||
|
if (!AgentPtr) return;
|
||||||
|
|
||||||
|
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(AgentPtr))
|
||||||
|
{
|
||||||
|
Posture->TargetActor = GetOwner();
|
||||||
|
// Eyes+head only at first — body tracking is enabled when listening starts.
|
||||||
|
Posture->bEnableBodyTracking = false;
|
||||||
|
|
||||||
|
if (bDebug)
|
||||||
|
{
|
||||||
|
UE_LOG(LogPS_AI_ConvAgent_Select, Log, TEXT("Posture attached (eyes+head only): %s -> %s"),
|
||||||
|
AgentPtr->GetOwner() ? *AgentPtr->GetOwner()->GetName() : TEXT("(null)"),
|
||||||
|
GetOwner() ? *GetOwner()->GetName() : TEXT("(null)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPS_AI_ConvAgent_InteractionComponent::DetachPostureTarget(
|
||||||
|
TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> Agent)
|
||||||
|
{
|
||||||
|
UPS_AI_ConvAgent_ElevenLabsComponent* AgentPtr = Agent.Get();
|
||||||
|
if (!AgentPtr) return;
|
||||||
|
|
||||||
|
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(AgentPtr))
|
||||||
|
{
|
||||||
|
Posture->TargetActor = nullptr;
|
||||||
|
|
||||||
|
if (bDebug)
|
||||||
|
{
|
||||||
|
UE_LOG(LogPS_AI_ConvAgent_Select, Log, TEXT("Posture detached: %s"),
|
||||||
|
AgentPtr->GetOwner() ? *AgentPtr->GetOwner()->GetName() : TEXT("(null)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Mic routing
|
// Mic routing
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -240,12 +240,16 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Body smoothly interpolates toward its persistent target
|
// Body smoothly interpolates toward its persistent target
|
||||||
const float BodyDelta = FMath::FindDeltaAngleDegrees(
|
// (only when body tracking is enabled — otherwise only head+eyes move).
|
||||||
Owner->GetActorRotation().Yaw, TargetBodyWorldYaw);
|
if (bEnableBodyTracking)
|
||||||
if (FMath::Abs(BodyDelta) > 0.1f)
|
|
||||||
{
|
{
|
||||||
const float BodyStep = FMath::FInterpTo(0.0f, BodyDelta, SafeDeltaTime, BodyInterpSpeed);
|
const float BodyDelta = FMath::FindDeltaAngleDegrees(
|
||||||
Owner->AddActorWorldRotation(FRotator(0.0f, BodyStep, 0.0f));
|
Owner->GetActorRotation().Yaw, TargetBodyWorldYaw);
|
||||||
|
if (FMath::Abs(BodyDelta) > 0.1f)
|
||||||
|
{
|
||||||
|
const float BodyStep = FMath::FInterpTo(0.0f, BodyDelta, SafeDeltaTime, BodyInterpSpeed);
|
||||||
|
Owner->AddActorWorldRotation(FRotator(0.0f, BodyStep, 0.0f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 3. Compute DeltaYaw after body interp ──────────────────────────
|
// ── 3. Compute DeltaYaw after body interp ──────────────────────────
|
||||||
@ -274,7 +278,7 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent(
|
|||||||
BodyTargetFacing, TargetWorldYaw);
|
BodyTargetFacing, TargetWorldYaw);
|
||||||
|
|
||||||
bool bBodyOverflowed = false;
|
bool bBodyOverflowed = false;
|
||||||
if (FMath::Abs(DeltaFromBodyTarget) > MaxHeadYaw + MaxEyeHorizontal)
|
if (bEnableBodyTracking && FMath::Abs(DeltaFromBodyTarget) > MaxHeadYaw + MaxEyeHorizontal)
|
||||||
{
|
{
|
||||||
// Body realigns to face target
|
// Body realigns to face target
|
||||||
TargetBodyWorldYaw = TargetWorldYaw - MeshForwardYawOffset;
|
TargetBodyWorldYaw = TargetWorldYaw - MeshForwardYawOffset;
|
||||||
@ -337,15 +341,8 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent(
|
|||||||
{
|
{
|
||||||
// ── No target: smoothly return to neutral ──────────────────────────
|
// ── No target: smoothly return to neutral ──────────────────────────
|
||||||
|
|
||||||
// Body: return to original facing via sticky target
|
// Body: keep current orientation — don't rotate back to original facing.
|
||||||
TargetBodyWorldYaw = OriginalActorYaw - MeshForwardYawOffset;
|
// The body stays wherever the last tracking left it; only head and eyes reset.
|
||||||
const float NeutralDelta = FMath::FindDeltaAngleDegrees(
|
|
||||||
Owner->GetActorRotation().Yaw, TargetBodyWorldYaw);
|
|
||||||
if (FMath::Abs(NeutralDelta) > 0.1f)
|
|
||||||
{
|
|
||||||
const float NeutralStep = FMath::FInterpTo(0.0f, NeutralDelta, SafeDeltaTime, ReturnToNeutralSpeed);
|
|
||||||
Owner->AddActorWorldRotation(FRotator(0.0f, NeutralStep, 0.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Head + Eyes: return to center
|
// Head + Eyes: return to center
|
||||||
TargetHeadYaw = 0.0f;
|
TargetHeadYaw = 0.0f;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
class UPS_AI_ConvAgent_ElevenLabsComponent;
|
class UPS_AI_ConvAgent_ElevenLabsComponent;
|
||||||
class UPS_AI_ConvAgent_MicrophoneCaptureComponent;
|
class UPS_AI_ConvAgent_MicrophoneCaptureComponent;
|
||||||
|
class UPS_AI_ConvAgent_PostureComponent;
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Delegates
|
// Delegates
|
||||||
@ -76,12 +77,52 @@ public:
|
|||||||
ToolTip = "Sticky cone half-angle for the current agent (degrees).\nMust be >= ViewConeHalfAngle for hysteresis to work.\n60 = agent stays selected until 120-degree total cone is exceeded."))
|
ToolTip = "Sticky cone half-angle for the current agent (degrees).\nMust be >= ViewConeHalfAngle for hysteresis to work.\n60 = agent stays selected until 120-degree total cone is exceeded."))
|
||||||
float SelectionStickyAngle = 60.0f;
|
float SelectionStickyAngle = 60.0f;
|
||||||
|
|
||||||
|
/** Height offset (cm) added to the agent's origin for the view cone check.
|
||||||
|
* Agent origins are typically at foot level; this shifts the target point
|
||||||
|
* up to chest/head height so close-range look-at checks don't fail. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction",
|
||||||
|
meta = (ClampMin = "0",
|
||||||
|
ToolTip = "Height offset (cm) above the agent origin for the view cone check.\nPrevents losing selection at close range because the camera looks above the agent's feet.\n150 = roughly chest height for a MetaHuman."))
|
||||||
|
float AgentEyeLevelOffset = 150.0f;
|
||||||
|
|
||||||
/** When false, only distance matters for selection (no view cone check).
|
/** When false, only distance matters for selection (no view cone check).
|
||||||
* The closest agent within MaxInteractionDistance is always selected. */
|
* The closest agent within MaxInteractionDistance is always selected. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction",
|
||||||
meta = (ToolTip = "Require the player to look at an agent to select it.\nWhen false, the closest agent within range is always selected."))
|
meta = (ToolTip = "Require the player to look at an agent to select it.\nWhen false, the closest agent within range is always selected."))
|
||||||
bool bRequireLookAt = true;
|
bool bRequireLookAt = true;
|
||||||
|
|
||||||
|
// ── Posture management ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Automatically set/clear the agent's PostureComponent TargetActor
|
||||||
|
* when the agent is selected/deselected. When false, posture must
|
||||||
|
* be managed from Blueprint (e.g. on conversation start). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction|Posture",
|
||||||
|
meta = (ToolTip = "Automatically point the agent's posture at the pawn on selection.\nDisable for manual control (e.g. set target only when conversation starts)."))
|
||||||
|
bool bAutoManagePosture = true;
|
||||||
|
|
||||||
|
/** Delay (seconds) before setting the agent's posture target after selection.
|
||||||
|
* 0 = immediate. Useful to let the agent "notice" the player with a beat. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction|Posture",
|
||||||
|
meta = (EditCondition = "bAutoManagePosture", ClampMin = "0",
|
||||||
|
ToolTip = "Seconds to wait before the agent looks at the pawn.\n0 = immediate."))
|
||||||
|
float PostureAttachDelay = 0.0f;
|
||||||
|
|
||||||
|
/** Delay (seconds) before clearing the agent's posture target after deselection.
|
||||||
|
* 0 = immediate. Useful to have the agent keep looking briefly as the player leaves. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction|Posture",
|
||||||
|
meta = (EditCondition = "bAutoManagePosture", ClampMin = "0",
|
||||||
|
ToolTip = "Seconds to wait before the agent stops looking at the pawn.\n0 = immediate."))
|
||||||
|
float PostureDetachDelay = 0.0f;
|
||||||
|
|
||||||
|
// ── Listening management ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Automatically call StartListening/StopListening on the agent's
|
||||||
|
* ElevenLabsComponent when selected/deselected. When false, listening
|
||||||
|
* must be managed from Blueprint. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Interaction|Listening",
|
||||||
|
meta = (ToolTip = "Automatically open/close the agent's mic turn on selection.\nDisable for manual control (e.g. push-to-talk)."))
|
||||||
|
bool bAutoManageListening = true;
|
||||||
|
|
||||||
// ── Debug ────────────────────────────────────────────────────────────────
|
// ── Debug ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Enable debug logging for this component. */
|
/** Enable debug logging for this component. */
|
||||||
@ -144,6 +185,17 @@ private:
|
|||||||
/** Get the pawn's view location and direction (uses camera or control rotation). */
|
/** Get the pawn's view location and direction (uses camera or control rotation). */
|
||||||
void GetPawnViewPoint(FVector& OutLocation, FVector& OutDirection) const;
|
void GetPawnViewPoint(FVector& OutLocation, FVector& OutDirection) const;
|
||||||
|
|
||||||
|
// ── Posture helpers ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Find the PostureComponent on an agent's owner actor (null if absent). */
|
||||||
|
static UPS_AI_ConvAgent_PostureComponent* FindPostureOnAgent(UPS_AI_ConvAgent_ElevenLabsComponent* Agent);
|
||||||
|
|
||||||
|
/** Set the agent's PostureComponent target to the pawn (attach). */
|
||||||
|
void AttachPostureTarget(TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> Agent);
|
||||||
|
|
||||||
|
/** Clear the agent's PostureComponent target (detach). */
|
||||||
|
void DetachPostureTarget(TWeakObjectPtr<UPS_AI_ConvAgent_ElevenLabsComponent> Agent);
|
||||||
|
|
||||||
// ── Mic routing ──────────────────────────────────────────────────────────
|
// ── Mic routing ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Forward captured mic audio to the currently selected agent. */
|
/** Forward captured mic audio to the currently selected agent. */
|
||||||
@ -160,4 +212,9 @@ private:
|
|||||||
|
|
||||||
/** True while ForceSelectAgent is active (suppresses automatic re-evaluation for one frame). */
|
/** True while ForceSelectAgent is active (suppresses automatic re-evaluation for one frame). */
|
||||||
bool bForceSelectionActive = false;
|
bool bForceSelectionActive = false;
|
||||||
|
|
||||||
|
// ── Posture timers ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
FTimerHandle PostureAttachTimerHandle;
|
||||||
|
FTimerHandle PostureDetachTimerHandle;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -73,6 +73,12 @@ public:
|
|||||||
meta = (ToolTip = "Target actor to look at.\nSet to null to return to neutral."))
|
meta = (ToolTip = "Target actor to look at.\nSet to null to return to neutral."))
|
||||||
TObjectPtr<AActor> TargetActor;
|
TObjectPtr<AActor> TargetActor;
|
||||||
|
|
||||||
|
/** When false, body rotation is frozen — only head and eyes track the target.
|
||||||
|
* Useful to have the agent notice the player (eyes+head) before fully engaging (body). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Posture",
|
||||||
|
meta = (ToolTip = "Enable body rotation toward the target.\nWhen false, only head and eyes track."))
|
||||||
|
bool bEnableBodyTracking = true;
|
||||||
|
|
||||||
/** Offset from the target actor's origin to aim at.
|
/** Offset from the target actor's origin to aim at.
|
||||||
* Useful for actors without a skeleton (e.g. (0,0,160) for eye-level). */
|
* Useful for actors without a skeleton (e.g. (0,0,160) for eye-level). */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS AI ConvAgent|Posture",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user