Added GestureAction Ruler, added TableLineActor

This commit is contained in:
DuOtto
2026-07-29 00:07:47 +02:00
parent c9e46eeb41
commit 257f045515
19 changed files with 513 additions and 332 deletions
@@ -197,8 +197,8 @@ bool ADetectionManager::ParseHandPacket(const TArray<uint8>& Bytes, int32& OutSe
FHandPacketHand Hand;
Hand.HandId = (int32)HandId;
Hand.Handedness = (HandednessByte == 1) ? EHandedness::Left :
(HandednessByte == 2) ? EHandedness::Right :
Hand.Handedness = (HandednessByte == 1) ? EHandedness::Right :
(HandednessByte == 2) ? EHandedness::Left :
EHandedness::Unknown;
Hand.Confidence = Confidence;
+3 -1
View File
@@ -18,7 +18,7 @@ void AGestureAction::BeginPlay()
Super::BeginPlay();
}
void AGestureAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit)
void AGestureAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight)
{
HandA = Hand;
bRequiresSecondHand = Binding.LeftGesture != "" && Binding.RightGesture != "";
@@ -52,6 +52,8 @@ void AGestureAction::AddOtherHand_Implementation(AHandActor* OtherHand)
void AGestureAction::EndGesture_Implementation()
{
HandA = nullptr;
HandB = nullptr;
DestroyIcons();
Destroy();
}
@@ -31,6 +31,54 @@ void UGestureManagerComponent::BeginPlay()
LoadBindings("C:/git/Table/Test/Gestures/gesture_config.json");
}
void UGestureManagerComponent::Initialize(UDataTable* InGestureTable, UDataTable* InActionTable)
{
GestureDefinitionTable = InGestureTable;
GestureActionTable = InActionTable;
BuildDefinitionMap();
BuildActionMap();
}
void UGestureManagerComponent::BuildDefinitionMap()
{
if (!GestureDefinitionTable) return;
static const FString Context = TEXT("GestureDefinitionContext");
TArray<FGestureDefinitionRow*> Rows;
GestureDefinitionTable->GetAllRows(Context, Rows);
for (FGestureDefinitionRow* Row : Rows)
{
if (!Row || !Row->DefinitionClass) continue;
UGestureDefinition* Def = NewObject<UGestureDefinition>(this, Row->DefinitionClass);
DefinitionMap.Add(Def->GestureName, Def);
GestureDefinitions.Add(Def);
}
}
void UGestureManagerComponent::BuildActionMap()
{
if (!GestureActionTable) return;
static const FString Context = TEXT("GestureActionContext");
TArray<FName> RowNames = GestureActionTable->GetRowNames();
for (const FName& RowName : RowNames)
{
FGestureActionRow* Row = GestureActionTable->FindRow<FGestureActionRow>( RowName, Context);
if (!Row || !Row->ActionClass) continue;
ActionClassMap.Add(RowName, Row->ActionClass);
}
}
void UGestureManagerComponent::EvaluateHands()
{
TArray<AHandActor*>& Hands = HandManager->HandList;
@@ -86,7 +134,7 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath)
const TArray<TSharedPtr<FJsonValue>>* BindingsArray;
if (Root->TryGetArrayField(TEXT("bindings"), BindingsArray))
if (Root->TryGetArrayField(TEXT("rules"), BindingsArray))
{
for (const TSharedPtr<FJsonValue>& Value : *BindingsArray)
{
@@ -126,51 +174,9 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath)
}
}
void UGestureManagerComponent::Initialize(UDataTable* InGestureTable, UDataTable* InActionTable)
{
GestureDefinitionTable = InGestureTable;
GestureActionTable = InActionTable;
BuildDefinitionMap();
BuildActionMap();
}
void UGestureManagerComponent::BuildDefinitionMap()
{
if (!GestureDefinitionTable) return;
static const FString Context = TEXT("GestureDefinitionContext");
TArray<FGestureDefinitionRow*> Rows;
GestureDefinitionTable->GetAllRows(Context, Rows);
for (FGestureDefinitionRow* Row : Rows)
{
if (!Row || !Row->DefinitionClass) continue;
UGestureDefinition* Def = NewObject<UGestureDefinition>(this, Row->DefinitionClass);
DefinitionMap.Add(Def->GestureName, Def);
GestureDefinitions.Add(Def);
}
}
void UGestureManagerComponent::BuildActionMap()
{
if (!GestureActionTable) return;
static const FString Context = TEXT("GestureActionContext");
TArray<FGestureActionRow*> Rows;
GestureActionTable->GetAllRows(Context, Rows);
for (FGestureActionRow* Row : Rows)
{
if (!Row || !Row->ActionClass) continue;
ActionClassMap.Add( FName(Row->ActionClass->GetName()), Row->ActionClass );
}
}
void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hands)
{
@@ -272,7 +278,7 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
Hand->CandidateFrames = 1;
Hand->FailFrames = 0;
break;
case EHandGesturePhase::Ongoing: // Checking if the gesture is still ongoing
case EHandGesturePhase::Ongoing: // Checking if the gesture is still ongoinga
//if (Hand->ActiveGestureName != BestGesture) Hand->FailFrames++;
//else Hand->FailFrames = 0;
if (Hand->ActiveGestureName != BestGesture)
@@ -313,8 +319,9 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
{
if (Hand->bUsedThisTick) return;
if (TryExtendGestureAction(Hand)) return;
for (const FGestureBinding& Binding : GestureBindings)
@@ -323,9 +330,12 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
|| (Hand->Handedness == EHandedness::Right && Binding.RightGesture == Hand->ActiveGestureName);
if (!bMatches) continue;
AGestureAction* NewAction = SpawnGestureAction(Binding.ActionName, Hand);
NewAction->InitAction(Hand, Binding, UEUnitsPerUnit);
if (!NewAction) return;
NewAction->InitAction(Hand, Binding, UEUnitsPerUnit, TableHeight);
Hand->Phase = EHandGesturePhase::Ongoing;
return;
}
@@ -334,6 +344,7 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
void UGestureManagerComponent::UpdateOngoingActions()
{
for (AGestureAction* Action : ActiveGestureActions)
{
if (!Action) continue;
@@ -344,7 +355,6 @@ void UGestureManagerComponent::UpdateOngoingActions()
AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, AHandActor* Hand)
{
TSubclassOf<AGestureAction>* ActionClassPtr = ActionClassMap.Find(ActionName);
if (!ActionClassPtr || !Hand) return nullptr;
@@ -362,6 +372,7 @@ AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, A
if (!NewAction) return nullptr;
ActiveGestureActions.Add(NewAction);
Hand->ActiveAction = NewAction;
Hand->bUsedThisTick = true;
@@ -424,7 +435,11 @@ bool UGestureManagerComponent::TryCreateExtension(AHandActor* Hand)
AGestureAction* NewAction = SpawnGestureAction(ActionName, Hand);
NewAction->InitAction(Hand, OtherHand->ActiveAction->Binding, UEUnitsPerUnit);
if (!NewAction) return false;
ActiveGestureActions.Add(NewAction);
NewAction->InitAction(Hand, OtherHand->ActiveAction->Binding, UEUnitsPerUnit, TableHeight);
OtherHand->bUsedThisTick = true;
Hand->bUsedThisTick = true;
OtherAction->AddExtension(NewAction);
+48 -64
View File
@@ -4,80 +4,64 @@
#include "MyGestureActions.h"
#include "HandActor.h"
#include "TableIconActor.h"
#include "MyTableIconActors.h"
void ARulerAction::UpdateAction_Implementation()
void AGestureAction_Ruler::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight)
{
if (!HandA) return;
// Update end point to current fingertip
Anchors.Add("End", HandA->Points[8]);
FVector Start = Anchors["Start"];
FVector End = Anchors["End"];
float RawDist = FVector::Dist(Start, End);
float Inches = GetSnappedInches(RawDist);
// Move line icon between start and end
if (Icons.IsValidIndex(0) && IsValid(Icons[0]))
{
Icons[0]->SetActorLocation((Start + End) * 0.5f);
// You'll add a SetEndpoints(Start, End) function to ATableIconLineActor later
}
// Move label icon to midpoint, slightly above table
if (Icons.IsValidIndex(1) && IsValid(Icons[1]))
{
FVector Mid = (Start + End) * 0.5f + FVector(0, 0, 5.f);
Icons[1]->SetActorLocation(Mid);
// Icons[1]->SetLabelText(FString::Printf(TEXT("%d\""), (int32)Inches));
}
}
void ARulerAction::AbortAction_Implementation()
{
Super::AbortAction();
}
void ARulerAction::AddExtension_Implementation(AGestureAction* OtherAction)
{
return;
}
void ARulerAction::EndGesture_Implementation()
{
Super::EndGesture();
}
void ARulerAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit)
{
Super::InitAction(Hand, NewBinding, NewUEUnitsPerUnit);
Anchors.Add("Start", Hand->Points[8]);
Super::InitAction_Implementation(Hand, NewBinding, NewUEUnitsPerUnit, TableHeight);
DesiredHeight = -500;
StartPoint = HandA->Points[8];
StartPoint.Z = DesiredHeight;
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = GetOwner();
SpawnParams.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AGestureAction* NewAction = nullptr;
/*AGestureAction* NewAction = GetWorld()->SpawnActor<AGestureAction>(
*ActionClassPtr,
FVector::ZeroVector,
FRotator::ZeroRotator,
SpawnParams);
*/
if (!NewAction) return;
SpawnParams.Owner = this;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
if (LineIconClass)
{
LineIcon = GetWorld()->SpawnActor<ATableIconLineActor>(LineIconClass, StartPoint, FRotator::ZeroRotator, SpawnParams);
if (LineIcon)
{
LineIcon->OwningAction = this;
LineIcon->bPersistOnActionEnd = true;
LineIcon->LineThickness = 50;
Icons.Add(LineIcon);
}
}
RulerPoints.Add(FVector::ZeroVector);
RulerPoints.Add(FVector::ZeroVector);
}
float ARulerAction::GetSnappedInches(float RawDistance) const
void AGestureAction_Ruler::UpdateAction_Implementation()
{
if (!HandA || !IsValid(LineIcon)) return;
const FVector Current = HandA->Points[8];
//if (FVector::DistSquared(Current, RulerPoints[RulerPoints.Num() - 1]) < FMath::Square(UEUnitsPerUnit)) return;
RulerPoints[RulerPoints.Num() - 1] = Current - StartPoint;
RulerPoints[RulerPoints.Num() - 1].Z = 0;
LineIcon->SetPoints(RulerPoints);
}
float AGestureAction_Ruler::GetSnappedUnits(float RawDistance) const
{
if (UEUnitsPerUnit <= 0.f) return 0.f;
float Unit = RawDistance / UEUnitsPerUnit;
return FMath::RoundToFloat(Unit); // snap to whole inches
return FMath::RoundToFloat(RawDistance / UEUnitsPerUnit);
}
float AGestureAction_Ruler::GetTotalLength() const
{
float Total = 0.f;
for (int32 i = 0; i < RulerPoints.Num() - 1; i++)
Total += FVector::Dist(RulerPoints[i], RulerPoints[i + 1]);
return Total;
}
void AGestureAction_Ruler::AddRulerPoint()
{
FVector Current = HandA->Points[8];
RulerPoints.Add(Current - StartPoint);
}
@@ -10,118 +10,104 @@
ATableIconLineActor::ATableIconLineActor()
{
PrimaryActorTick.bCanEverTick = false; // purely driven by SetPoints
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
MeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("LineMesh"));
MeshComponent->SetupAttachment(RootComponent);
MeshComponent->SetCastShadow(false);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
void ATableIconLineActor::SetEndpoints(const FVector& Start, const FVector& End)
void ATableIconLineActor::SetPoints(const TArray<FVector>& NewPoints)
{
// Skip rebuild if nothing meaningful changed
if (Start.Equals(LastStart, 0.1f) && End.Equals(LastEnd, 0.1f)) return;
LastStart = Start;
LastEnd = End;
const FTransform& ActorTransform = GetActorTransform();
BuildLineMesh(Start, End);
if (bShowTickMarks) BuildTickMesh(Start, End);
Points.Reset(NewPoints.Num());
Points = NewPoints;
MeshComponent->ClearMeshSection(0);
BuildLineMesh();
if (LineMaterial) MeshComponent->SetMaterial(0, LineMaterial);
}
void ATableIconLineActor::BuildLineMesh(const FVector& Start, const FVector& End)
void ATableIconLineActor::BuildLineMesh()
{
FVector Dir = (End - Start).GetSafeNormal();
FVector Up = FVector::UpVector;
FVector Right = FVector::CrossProduct(Dir, Up).GetSafeNormal() * LineThickness * 0.5f;
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents;
// A flat quad along the line, facing up
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents;
FVector Direction = FVector::ZeroVector;
const float HalfLineThickness = LineThickness * 0.5f;
for (int32 i = 0; i < Points.Num() - 1; i++) {
const FVector& PointA = Points[i];
const FVector& PointB = Points[i + 1];
Vertices.Add(Start - Right); // 0
Vertices.Add(Start + Right); // 1
Vertices.Add(End + Right); // 2
Vertices.Add(End - Right); // 3
Direction = (PointB - PointA).GetSafeNormal();
Triangles = { 0, 1, 2, 0, 2, 3 };
const FVector Right = FVector::CrossProduct(FVector::UpVector, Direction).GetSafeNormal();
for (int i = 0; i < 4; i++)
{
Normals.Add(FVector::UpVector);
UVs.Add(FVector2D(i < 2 ? 0.f : 1.f, i % 2 == 0 ? 0.f : 1.f));
Colors.Add(LineColor.ToFColor(true));
Tangents.Add(FProcMeshTangent(Dir, false));
}
// Section 0 = main line
MeshComponent->CreateMeshSection(0, Vertices, Triangles, Normals, UVs,
Colors, Tangents, false);
}
void ATableIconLineActor::BuildTickMesh(const FVector& Start, const FVector& End)
{
if (UEUnitsPerUnit <= 0.f || TickInterval <= 0.f) return;
float TotalDist = FVector::Dist(Start, End);
float TickSpacing = TickInterval * UEUnitsPerUnit;
int32 NumTicks = FMath::FloorToInt(TotalDist / TickSpacing);
if (NumTicks <= 0)
{
MeshComponent->ClearMeshSection(1);
return;
}
FVector Dir = (End - Start).GetSafeNormal();
FVector Right = FVector::CrossProduct(Dir, FVector::UpVector).GetSafeNormal();
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents;
float TickHalfLen = LineThickness * 3.f;
float TickHalfW = LineThickness * 0.5f;
for (int32 i = 1; i <= NumTicks; i++)
{
FVector Center = Start + Dir * (TickSpacing * i);
FVector A = Center - Right * TickHalfLen - Dir * TickHalfW;
FVector B = Center + Right * TickHalfLen - Dir * TickHalfW;
FVector C = Center + Right * TickHalfLen + Dir * TickHalfW;
FVector D = Center - Right * TickHalfLen + Dir * TickHalfW;
const FVector Offset = Right * (LineThickness * 0.5f);
int32 Base = Vertices.Num();
Vertices.Add(A); Vertices.Add(B);
Vertices.Add(C); Vertices.Add(D);
Triangles.Add(Base); Triangles.Add(Base + 1); Triangles.Add(Base + 2);
Triangles.Add(Base); Triangles.Add(Base + 2); Triangles.Add(Base + 3);
Vertices.Add(PointA - Offset);
Vertices.Add(PointA + Offset);
Vertices.Add(PointB + Offset);
Vertices.Add(PointB - Offset);
for (int j = 0; j < 4; j++)
{
Normals.Add(FVector::UpVector);
UVs.Add(FVector2D::ZeroVector);
Colors.Add(LineColor.ToFColor(true));
Tangents.Add(FProcMeshTangent(Dir, false));
}
Triangles.Add(Base + 0);
Triangles.Add(Base + 1);
Triangles.Add(Base + 2);
Triangles.Add(Base + 0);
Triangles.Add(Base + 2);
Triangles.Add(Base + 3);
}
// Section 1 = tick marks
MeshComponent->CreateMeshSection(1, Vertices, Triangles, Normals, UVs,
Colors, Tangents, false);
for (int i = 0; i < Vertices.Num(); i++)
{
Normals.Add(FVector::UpVector);
}
for (int i = 0; i < Vertices.Num(); i++)
{
UVs.Add(FVector2D::ZeroVector);
}
for (int i = 0; i < Vertices.Num(); i++)
{
Colors.Add(FColor::Red);
}
for (int i = 0; i < Vertices.Num(); i++)
{
Tangents.Add(FProcMeshTangent(Direction, false));
}
MeshComponent->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, Colors, Tangents, false);
}
ATableIconWidgetActor::ATableIconWidgetActor()
{
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
PrimaryActorTick.bCanEverTick = true;
WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComponent"));
@@ -140,15 +126,13 @@ void ATableIconWidgetActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!bFaceCamera) return;
APlayerCameraManager* CamManager = UGameplayStatics::GetPlayerCameraManager(this, 0);
if (!CamManager) return;
FVector CamLocation = CamManager->GetCameraLocation();
FVector ToCamera = (CamLocation - GetActorLocation()).GetSafeNormal();
// Only rotate on yaw keeps widget flat relative to table
// Only rotate on yaw keeps widget flat relative to table
ToCamera.Z = 0.f;
if (!ToCamera.IsNearlyZero())
SetActorRotation(ToCamera.ToOrientationRotator());
@@ -164,4 +148,12 @@ void ATableIconWidgetActor::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClas
UUserWidget* ATableIconWidgetActor::GetWidget() const
{
return WidgetComponent->GetUserWidgetObject();
}
void ATableIconLabelActor::SetDistance(float RealWorldUnits, const FString& UnitSuffix)
{
int32 Snapped = FMath::RoundToInt(RealWorldUnits);
SetLabelText(FString::Printf(TEXT("%d%s"), Snapped, *UnitSuffix));
}
+15 -46
View File
@@ -26,53 +26,22 @@ protected:
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float UEUnitsPerUnit = 100.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float UEUnitsPerUnit = 10.f;
UPROPERTY(EditDefaultsOnly) bool bRequiresSecondHand = false;
UPROPERTY(EditDefaultsOnly) bool bExtendable = false;
UPROPERTY(EditDefaultsOnly) FGestureBinding Binding;
UPROPERTY(BlueprintReadWrite) TArray<ATableIconActor*> Icons;
UPROPERTY(BlueprintReadWrite) AHandActor* HandA;
UPROPERTY(BlueprintReadWrite) AHandActor* HandB;
UPROPERTY(BlueprintReadWrite) AGestureAction* LinkedActions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stuff") AHandManager* HandManager;
UPROPERTY(EditDefaultsOnly)
bool bRequiresSecondHand = false;
UPROPERTY(EditDefaultsOnly)
bool bExtendable = false;
UPROPERTY(EditDefaultsOnly)
FGestureBinding Binding;
UPROPERTY(BlueprintReadWrite)
TArray<ATableIconActor*> Icons;
UPROPERTY(BlueprintReadWrite)
TMap<FName, FVector> Anchors;
UPROPERTY(BlueprintReadWrite)
AHandActor* HandA;
UPROPERTY(BlueprintReadWrite)
AHandActor* HandB;
UPROPERTY(BlueprintReadWrite)
AGestureAction* LinkedActions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stuff")
AHandManager* HandManager;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void InitAction(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void UpdateAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AbortAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AddExtension(AGestureAction* OtherAction);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AddOtherHand(AHandActor* OtherHand);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void EndGesture();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void InitAction(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void UpdateAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AbortAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AddExtension(AGestureAction* OtherAction);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AddOtherHand(AHandActor* OtherHand);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void EndGesture();
void DestroyIcons();
};
@@ -24,34 +24,25 @@ protected:
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale")
float UEUnitsPerUnit = 100.f; // set via calibration rectangle
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float UEUnitsPerUnit = 10.f; // set via calibration rectangle
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float TableHeight = -650.f;
UPROPERTY(BlueprintReadOnly) AHandManager* HandManager;
UPROPERTY(BlueprintReadOnly)
AHandManager* HandManager;
UFUNCTION(BlueprintCallable, Category = "Gesture")
void EvaluateHands();
UFUNCTION(BlueprintCallable, Category = "Gesture") void EvaluateHands();
UFUNCTION()
void OnHandAdded(AHandActor* Hand);
UFUNCTION() void OnHandAdded(AHandActor* Hand);
UFUNCTION()
void OnHandRemoved(AHandActor* Hand);
UFUNCTION() void OnHandRemoved(AHandActor* Hand);
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
FString BindingConfigPath; // path to the per-game JSON
UPROPERTY(EditDefaultsOnly, Category = "Gesture") FString BindingConfigPath; // path to the per-game JSON
// Loaded from JSON — gesture name -> definition object
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
TArray<UGestureDefinition*> GestureDefinitions;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<UGestureDefinition*> GestureDefinitions;
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
TArray<AGestureAction*> ActiveGestureActions;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<AGestureAction*> ActiveGestureActions;
// Populated on BeginPlay from JSON
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
TArray<FGestureBinding> GestureBindings;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<FGestureBinding> GestureBindings;
void LoadBindings(const FString& FilePath);
void Initialize(UDataTable* InGestureTable, UDataTable* InActionTable);
@@ -59,19 +50,15 @@ public:
void BuildActionMap();
// Populated on BeginPlay from the tables
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
TMap<FName, UGestureDefinition*> DefinitionMap;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, UGestureDefinition*> DefinitionMap;
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
private:
//
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
UDataTable* GestureDefinitionTable;
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureDefinitionTable;
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
UDataTable* GestureActionTable;
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureActionTable;
void CheckGestureDefinitions(TArray<AHandActor*>& Hands);
+28 -10
View File
@@ -6,6 +6,8 @@
#include "GestureAction.h"
#include "MyGestureActions.generated.h"
class ATableIconLineActor;
class ATableIconLabelActor;
/**
*
*/
@@ -14,23 +16,39 @@ class TABLE_API AMyGestureActions : public AGestureAction
{
GENERATED_BODY()
//virtual void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) override;
//virtual void UpdateAction_Implementation() override;
//virtual void EndGesture_Implementation() override;
//virtual void AddOtherHand_Implementation(AHandActor* OtherHand) override;
};
UCLASS()
class TABLE_API ARulerAction : public AGestureAction
UCLASS(Blueprintable)
class TABLE_API AGestureAction_Ruler : public AGestureAction
{
GENERATED_BODY()
GENERATED_BODY()
public:
void UpdateAction_Implementation() override;
void AbortAction_Implementation() override;
void AddExtension_Implementation(AGestureAction* OtherAction) override;
void EndGesture_Implementation() override;
void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) override;
// Set these in BP defaults — point at your BP subclasses
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLineActor> LineIconClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLabelActor> LabelIconClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") FString UnitSuffix = TEXT("\"");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ruler") TArray<FVector> RulerPoints;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") float DesiredHeight = -650.f;
UFUNCTION(BlueprintCallable, BlueprintPure) float GetTotalLength() const;
UFUNCTION(BlueprintCallable) void AddRulerPoint();
virtual void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight) override;
virtual void UpdateAction_Implementation() override;
private:
float GetSnappedInches(float RawDistance) const;
UPROPERTY() ATableIconLineActor* LineIcon = nullptr;
UPROPERTY() ATableIconLabelActor* LabelIcon = nullptr;
UPROPERTY() FVector StartPoint;
float GetSnappedUnits(float RawDistance) const;
};
+24 -40
View File
@@ -30,34 +30,20 @@ class TABLE_API ATableIconLineActor : public ATableIconActor
public:
ATableIconLineActor();
UFUNCTION(BlueprintCallable)
void SetEndpoints(const FVector& Start, const FVector& End);
UFUNCTION(BlueprintCallable) void SetPoints(const TArray<FVector>& InPoints);
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float LineThickness = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") float LineThickness = 10.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") FLinearColor LineColor = FLinearColor::Blue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") UMaterialInterface* LineMaterial = nullptr;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FLinearColor LineColor = FLinearColor::White;
// Tick markers along the line — e.g. every inch
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
bool bShowTickMarks = true;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float TickInterval = 1.f; // in real-world units
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float UEUnitsPerUnit = 100.f;
protected:
UPROPERTY(BlueprintReadOnly, Category = "Line") TArray<FVector> Points;
private:
UPROPERTY()
UProceduralMeshComponent* MeshComponent;
UPROPERTY() UProceduralMeshComponent* MeshComponent;
void BuildLineMesh(const FVector& Start, const FVector& End);
void BuildTickMesh(const FVector& Start, const FVector& End);
FVector LastStart;
FVector LastEnd;
void BuildLineMesh();
};
@@ -70,24 +56,22 @@ class TABLE_API ATableIconWidgetActor : public ATableIconActor
public:
ATableIconWidgetActor();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
UWidgetComponent* WidgetComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
UWidgetInteractionComponent* WidgetInteraction;
// Face toward a target each tick — useful for always facing the camera
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
bool bFaceCamera = true;
// Set widget content — callable from GestureAction
UFUNCTION(BlueprintCallable)
void SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass);
// Get the underlying widget cast to a specific type in BP
UFUNCTION(BlueprintCallable, BlueprintPure)
UUserWidget* GetWidget() const;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetComponent* WidgetComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetInteractionComponent* WidgetInteraction;
UFUNCTION(BlueprintCallable) void SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass);
UFUNCTION(BlueprintCallable, BlueprintPure) UUserWidget* GetWidget() const;
protected:
virtual void Tick(float DeltaTime) override;
};
UCLASS(Blueprintable)
class TABLE_API ATableIconLabelActor : public ATableIconWidgetActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable) void SetLabelText(const FString& Text);
UFUNCTION(BlueprintCallable) void SetDistance(float RealWorldUnits, const FString& UnitSuffix = TEXT("\""));
};
+6 -6
View File
@@ -6,6 +6,8 @@
#include "GameFramework/Actor.h"
#include "TableIconActor.generated.h"
class AGestureAction;
UCLASS()
class TABLE_API ATableIconActor : public AActor
{
@@ -21,16 +23,14 @@ protected:
public:
// If true, GestureAction will not destroy this on EndGesture
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
bool bPersistOnActionEnd = false;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) bool bPersistOnActionEnd = false;
UPROPERTY(EditAnywhere) AGestureAction* OwningAction;
// Called by GestureAction on end — BP overrides this for fade/lock behavior
UFUNCTION(BlueprintNativeEvent)
void OnActionEnded();
UFUNCTION(BlueprintNativeEvent) void OnActionEnded();
// Called by GestureAction every tick
UFUNCTION(BlueprintNativeEvent)
void OnActionUpdated();
UFUNCTION(BlueprintNativeEvent) void OnActionUpdated();
};