replace QueueVibroLevels with SendCustomWaveform
This commit is contained in:
@@ -22,6 +22,16 @@ This minor release focuses on delivering performance improvements, made possible
|
||||
- Added third-party module `SGLoguruThirdPartyLibs`.
|
||||
- Added third-party module `SGWjwwoodSerialThirdPartyLibs` to replace `SGSerialThirdPartyLibs` while retaining `SGSerialThirdPartyLibs` for a different purpose. See the relevant comment in the Changed section below.
|
||||
- Added UPROPERTY `SGPawn::HandVelocitySamplerFramerate` to control the hand velocity sampler's timer interval. This allows getting rid of `SGPawn::Tick()` for improved performance and reliability, which also improves chopiness on low frame-rates.
|
||||
- Added UPROPERTY `USGTouchComponent::VibrotactileAmplitude`.
|
||||
- Added UPROPERTY `USGTouchComponent::VibrotactileFrequency`.
|
||||
- Added method `USGTouchComponent::GetVibrotactileAmplitude()`.
|
||||
- Added method `USGTouchComponent::SetVibrotactileAmplitude()`.
|
||||
- Added method `USGTouchComponent::GetVibrotactileFrequency()`.
|
||||
- Added method `USGTouchComponent::SetVibrotactileFrequency()`.
|
||||
- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::GetVibrotactileAmplitude()`.
|
||||
- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::SetVibrotactileAmplitude()`.
|
||||
- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::GetVibrotactileFrequency()`.
|
||||
- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::SetVibrotactileFrequency()`.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -42,6 +52,8 @@ This minor release focuses on delivering performance improvements, made possible
|
||||
- `USGWristTrackerComponent::GetHandTrackingState()` signature has changed.
|
||||
- `USGVirtualHandComponentKismetLibrary::GetMotionControllerData()` signature has changed.
|
||||
- `USGVirtualHandComponentKismetLibrary::GetHandTrackingState()` signature has changed.
|
||||
- `SGPawn` and `SGTouchComponent` no longer use the legacy `QueueVibroLevels` method for applying vibrotactile feedback. Instead it's been replaced with `SendCustomWaveform`.
|
||||
- Changed `USGTouchComponent::VibrotactileDuration` UPROPERTY's maximum value to `1.0f`. Previously it was uncapped, and now any value beyond `1.0f` seconds is clamped.
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -55,11 +67,17 @@ This minor release focuses on delivering performance improvements, made possible
|
||||
- `USGWristTrackerComponent::GetMotionControllerState()` has been removed.
|
||||
- `USGVirtualHandComponentKismetLibrary::GetMotionControllerState()` has been removed.
|
||||
- `USGWristTrackerComponentKismetLibrary::GetMotionControllerState()` has been removed.
|
||||
- `USGTouchComponent::VibrotactileLevel` UPROPERTY has been removed.
|
||||
- `USGTouchComponent::GetVibrotactileLevel()` method has been removed.
|
||||
- `USGTouchComponent::SetVibrotactileLevel()` method has been removed.
|
||||
- `USGTouchComponentKismetLibrary::GetVibrotactileLevel()` method has been removed and is no longer available to Blueprint.
|
||||
- `USGTouchComponentKismetLibrary::SetVibrotactileLevel()` method has been removed and is no longer available to Blueprint.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Added [{fmt} Formatting Library License](../license/fmt-formatting-library.html) section.
|
||||
- Added [Loguru Logging Library License](../license/loguru-loggin-library.html) section.
|
||||
- Updated [Setting up the Touch System](../getting-started/setup-touch-system/index.html) section to reflect the recent touch system changes.
|
||||
|
||||
## [2.6.3] - 2025-06-27
|
||||
|
||||
|
||||
Binary file not shown.
@@ -103,8 +103,9 @@ USGTouchComponent::USGTouchComponent(const FObjectInitializer& ObjectInitializer
|
||||
{
|
||||
ForceFeedbackLevel = 1.0f;
|
||||
|
||||
VibrotactileDuration = 0.25f;
|
||||
VibrotactileLevel = 0.0f;
|
||||
VibrotactileAmplitude = 1.0f;
|
||||
VibrotactileDuration = 1.0f;
|
||||
VibrotactileFrequency= 500.0f;
|
||||
}
|
||||
|
||||
USGTouchComponent::FImpl::FImpl(USGTouchComponent* InOwner)
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
#include "SenseGlove/GameFramework/SGPlayerController.h"
|
||||
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Engine/TimerHandle.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
#include "SenseGlove/Components/SGTouchComponent.h"
|
||||
#include "SenseGlove/Components/SGVirtualHandComponent.h"
|
||||
@@ -45,6 +43,7 @@
|
||||
#include "SenseGlove/Haptics/SGGrabState.h"
|
||||
#include "SenseGlove/Haptics/SGTouchState.h"
|
||||
#include "SGBuildHacks/SGPlatform.h"
|
||||
#include "SGCore/SGCustomWaveform.h"
|
||||
#include "SGCore/SGHapticGlove.h"
|
||||
|
||||
struct ASGPlayerController::FImpl
|
||||
@@ -55,11 +54,6 @@ struct ASGPlayerController::FImpl
|
||||
|
||||
static float GetForceFeedbackLevel(const AActor* Actor);
|
||||
|
||||
static float GetVibrotactileDuration(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching,
|
||||
const AActor* ActorMiddleTouching, const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching);
|
||||
static float GetVibrotactileLevel(const AActor* Actor);
|
||||
|
||||
static TArray<float> GetForceFeedbackLevels(
|
||||
const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
@@ -67,19 +61,10 @@ struct ASGPlayerController::FImpl
|
||||
const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching);
|
||||
|
||||
static TArray<float> GetVibrotactileLevels(
|
||||
const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
const AActor* ActorMiddleTouching,
|
||||
const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching);
|
||||
|
||||
/************************
|
||||
* Member variables
|
||||
************************/
|
||||
|
||||
FTimerHandle TouchVibrotactileStopTimer;
|
||||
|
||||
/************************
|
||||
* Owner object
|
||||
************************/
|
||||
@@ -104,6 +89,8 @@ struct ASGPlayerController::FImpl
|
||||
* Methods
|
||||
************************/
|
||||
|
||||
USGCustomWaveform* GetCustomWaveform(const AActor* Actor);
|
||||
|
||||
void UpdateHapticsFeedback(const FSGTouchState& TouchState);
|
||||
};
|
||||
|
||||
@@ -121,74 +108,6 @@ float ASGPlayerController::FImpl::GetForceFeedbackLevel(const AActor* Actor)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float ASGPlayerController::FImpl::GetVibrotactileDuration(const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
const AActor* ActorMiddleTouching,
|
||||
const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching)
|
||||
{
|
||||
if (IsValid(ActorThumbTouching))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorThumbTouching);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileDuration();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsValid(ActorIndexTouching))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorIndexTouching);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileDuration();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsValid(ActorMiddleTouching))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorMiddleTouching);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileDuration();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsValid(ActorRingTouching))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorRingTouching);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileDuration();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsValid(ActorPinkyTouching))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorPinkyTouching);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileDuration();
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float ASGPlayerController::FImpl::GetVibrotactileLevel(const AActor* Actor)
|
||||
{
|
||||
if (IsValid(Actor))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
return TouchComponent->GetVibrotactileLevel();
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
TArray<float> ASGPlayerController::FImpl::GetForceFeedbackLevels(
|
||||
const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
@@ -213,30 +132,6 @@ TArray<float> ASGPlayerController::FImpl::GetForceFeedbackLevels(
|
||||
return ForceFeedbackLevels;
|
||||
}
|
||||
|
||||
TArray<float> ASGPlayerController::FImpl::GetVibrotactileLevels(
|
||||
const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
const AActor* ActorMiddleTouching,
|
||||
const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching)
|
||||
{
|
||||
const float ThumbVibrotactileLevel = GetVibrotactileLevel(ActorThumbTouching);
|
||||
const float IndexVibrotactileLevel = GetVibrotactileLevel(ActorIndexTouching);
|
||||
const float MiddleVibrotactileLevel = GetVibrotactileLevel(ActorMiddleTouching);
|
||||
const float RingVibrotactileLevel = GetVibrotactileLevel(ActorRingTouching);
|
||||
const float PinkyVibrotactileLevel = GetVibrotactileLevel(ActorPinkyTouching);
|
||||
|
||||
const TArray<float> ForceFeedbackLevels{
|
||||
ThumbVibrotactileLevel,
|
||||
IndexVibrotactileLevel,
|
||||
MiddleVibrotactileLevel,
|
||||
RingVibrotactileLevel,
|
||||
PinkyVibrotactileLevel,
|
||||
};
|
||||
|
||||
return ForceFeedbackLevels;
|
||||
}
|
||||
|
||||
ASGPlayerController::ASGPlayerController(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer),
|
||||
Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter))
|
||||
@@ -330,6 +225,29 @@ ASGPlayerController::FImpl::FImpl(ASGPlayerController* InOwner)
|
||||
|
||||
ASGPlayerController::FImpl::~FImpl() = default;
|
||||
|
||||
USGCustomWaveform* ASGPlayerController::FImpl::GetCustomWaveform(const AActor* Actor)
|
||||
{
|
||||
float Amplitude = 0.0f;
|
||||
float Duration = 0.0f;
|
||||
float Frequency = 0.0f;
|
||||
|
||||
if (IsValid(Actor))
|
||||
{
|
||||
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor);
|
||||
if (IsValid(TouchComponent))
|
||||
{
|
||||
Amplitude = TouchComponent->GetVibrotactileAmplitude();
|
||||
Duration = TouchComponent->GetVibrotactileDuration();
|
||||
Frequency = TouchComponent->GetVibrotactileFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
USGCustomWaveform* CustomWaveform{
|
||||
USGCustomWaveform::NewCustomWaveform(Owner, Amplitude, Duration, Frequency)
|
||||
};
|
||||
return CustomWaveform;
|
||||
}
|
||||
|
||||
void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& TouchState)
|
||||
{
|
||||
static const TArray<float> VibrotactileOffCommand{0.0f, 0.0f, 0.0, 0.0, 0.0f};
|
||||
@@ -359,44 +277,43 @@ void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& Touc
|
||||
};
|
||||
Glove->QueueForceFeedbackLevels(MoveTemp(ForceFeedbackLevels));
|
||||
|
||||
// Queue the Vibrotactile command...
|
||||
TArray<float> VibrotactileLevels{
|
||||
GetVibrotactileLevels(
|
||||
TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching,
|
||||
TouchState.ActorRingTouching, TouchState.ActorPinkyTouching)
|
||||
};
|
||||
Glove->QueueVibroLevels(MoveTemp(VibrotactileLevels));
|
||||
|
||||
// Send the haptics commands!
|
||||
Glove->SendHaptics();
|
||||
|
||||
// Stop the Vibrotactile command after the timeout if any haptic feedback is ongoing.
|
||||
const float VibrotactileDuration = FImpl::GetVibrotactileDuration(
|
||||
TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching,
|
||||
TouchState.ActorRingTouching, TouchState.ActorPinkyTouching);
|
||||
|
||||
Owner->GetWorldTimerManager().ClearTimer(TouchVibrotactileStopTimer);
|
||||
|
||||
FTimerDelegate TouchVibrotactileStopHandler;
|
||||
TouchVibrotactileStopHandler.BindLambda([= SG_CAPTURE_THIS]()-> void
|
||||
// Send Vibrotactile to the thumb finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorThumbTouching))
|
||||
{
|
||||
if (!IsValid(Glove))
|
||||
{
|
||||
return;
|
||||
}
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorThumbTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::ThumbTip);
|
||||
}
|
||||
|
||||
const bool bGloveConnected = Glove->IsConnected();
|
||||
if (!bGloveConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Send Vibrotactile to the index finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorIndexTouching))
|
||||
{
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorIndexTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::IndexTip);
|
||||
}
|
||||
|
||||
Glove->QueueVibroLevels(VibrotactileOffCommand);
|
||||
Glove->SendHaptics();
|
||||
});
|
||||
// Send Vibrotactile to the middle finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorMiddleTouching))
|
||||
{
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorMiddleTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::MiddleTip);
|
||||
}
|
||||
|
||||
Owner->GetWorldTimerManager().SetTimer(
|
||||
TouchVibrotactileStopTimer, TouchVibrotactileStopHandler, VibrotactileDuration, false, -1.0f);
|
||||
// Send Vibrotactile to the ring finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorRingTouching))
|
||||
{
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorRingTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::RingTip);
|
||||
}
|
||||
|
||||
// Send Vibrotactile to the pinky finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorPinkyTouching))
|
||||
{
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorPinkyTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::PinkyTip);
|
||||
}
|
||||
}
|
||||
|
||||
void ASGPlayerController::FImplDeleter::operator()(const FImpl* P) const
|
||||
|
||||
@@ -57,12 +57,17 @@ private:
|
||||
meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
|
||||
float ForceFeedbackLevel;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false", ClampMin = "0.0", UIMin = "0.0"))
|
||||
UPROPERTY(EditAnywhere, Category="SenseGlove",
|
||||
meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
|
||||
float VibrotactileAmplitude;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="SenseGlove",
|
||||
meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
|
||||
float VibrotactileDuration;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="SenseGlove",
|
||||
meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
|
||||
float VibrotactileLevel;
|
||||
meta=(AllowPrivateAccess="false", ClampMin = "10.0", ClampMax = "500.0", UIMin = "10.0", UIMax = "500.0"))
|
||||
float VibrotactileFrequency;
|
||||
|
||||
private:
|
||||
struct FImpl;
|
||||
@@ -86,6 +91,16 @@ public:
|
||||
ForceFeedbackLevel = Level;
|
||||
}
|
||||
|
||||
FORCEINLINE float GetVibrotactileAmplitude() const
|
||||
{
|
||||
return VibrotactileAmplitude;
|
||||
}
|
||||
|
||||
FORCEINLINE void SetVibrotactileAmplitude(const float Amplitude)
|
||||
{
|
||||
VibrotactileAmplitude = Amplitude;
|
||||
}
|
||||
|
||||
FORCEINLINE float GetVibrotactileDuration() const
|
||||
{
|
||||
return VibrotactileDuration;
|
||||
@@ -95,13 +110,14 @@ public:
|
||||
{
|
||||
VibrotactileDuration = Duration;
|
||||
}
|
||||
FORCEINLINE float GetVibrotactileLevel() const
|
||||
|
||||
FORCEINLINE float GetVibrotactileFrequency() const
|
||||
{
|
||||
return VibrotactileLevel;
|
||||
return VibrotactileFrequency;
|
||||
}
|
||||
|
||||
FORCEINLINE void SetVibrotactileLevel(const float Level)
|
||||
FORCEINLINE void SetVibrotactileFrequency(const float Frequency)
|
||||
{
|
||||
VibrotactileLevel = Level;
|
||||
VibrotactileFrequency = Frequency;
|
||||
}
|
||||
};
|
||||
@@ -60,14 +60,14 @@ void UTouchComponentKismetLibrary::SetForceFeedbackLevel(USGTouchComponent* Touc
|
||||
TouchComponent->SetForceFeedbackLevel(Level);
|
||||
}
|
||||
|
||||
float UTouchComponentKismetLibrary::GetVibrotactileLevel(const USGTouchComponent* TouchComponent)
|
||||
float UTouchComponentKismetLibrary::GetVibrotactileAmplitude(const USGTouchComponent* TouchComponent)
|
||||
{
|
||||
return TouchComponent->GetVibrotactileLevel();
|
||||
return TouchComponent->GetVibrotactileAmplitude();
|
||||
}
|
||||
|
||||
void UTouchComponentKismetLibrary::SetVibrotactileLevel(USGTouchComponent* TouchComponent, const float Level)
|
||||
void UTouchComponentKismetLibrary::SetVibrotactileAmplitude(USGTouchComponent* TouchComponent, const float Amplitude)
|
||||
{
|
||||
TouchComponent->SetVibrotactileLevel(Level);
|
||||
TouchComponent->SetVibrotactileAmplitude(Amplitude);
|
||||
}
|
||||
|
||||
float UTouchComponentKismetLibrary::GetVibrotactileDuration(const USGTouchComponent* TouchComponent)
|
||||
@@ -79,3 +79,14 @@ void UTouchComponentKismetLibrary::SetVibrotactileDuration(USGTouchComponent* To
|
||||
{
|
||||
TouchComponent->SetVibrotactileDuration(Duration);
|
||||
}
|
||||
|
||||
float UTouchComponentKismetLibrary::GetVibrotactileFrequency(const USGTouchComponent* TouchComponent)
|
||||
{
|
||||
return TouchComponent->GetVibrotactileFrequency();
|
||||
}
|
||||
|
||||
void UTouchComponentKismetLibrary::SetVibrotactileFrequency(USGTouchComponent* TouchComponent, const float Frequency)
|
||||
{
|
||||
TouchComponent->SetVibrotactileFrequency(Frequency);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,14 +61,20 @@ public:
|
||||
static void SetForceFeedbackLevel(UPARAM(ref) USGTouchComponent* TouchComponent, float Level);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component")
|
||||
static float GetVibrotactileLevel(const USGTouchComponent* TouchComponent);
|
||||
static float GetVibrotactileAmplitude(const USGTouchComponent* TouchComponent);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component")
|
||||
static void SetVibrotactileLevel(UPARAM(ref) USGTouchComponent* TouchComponent, float Level);
|
||||
static void SetVibrotactileAmplitude(UPARAM(ref) USGTouchComponent* TouchComponent, float Amplitude);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component")
|
||||
static float GetVibrotactileDuration(const USGTouchComponent* TouchComponent);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component")
|
||||
static void SetVibrotactileDuration(UPARAM(ref) USGTouchComponent* TouchComponent, float Duration);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component")
|
||||
static float GetVibrotactileFrequency(const USGTouchComponent* TouchComponent);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component")
|
||||
static void SetVibrotactileFrequency(UPARAM(ref) USGTouchComponent* TouchComponent, float Frequency);
|
||||
};
|
||||
Reference in New Issue
Block a user