Hello!
I make controls for Oculus dk2 + Oculus touch. Moving by MotionController(R) Thumbstic X and Y, turning by MotionController(L) Thumbstic X. Grabing by MotionController Grip1 for R and L hands respectively.
I have a problem: when I try pressed grab button and moving, player moving very strange - changing directions and speed.
Code:
[SPOILER]
VRCharacter.h[SPOILER]
#pragma once
#include "GameFramework/Character.h"
#include "VRCharacter.generated.h"
UCLASS()
class VRFIRSTPERSON_API AVRCharacter : public ACharacter
{
GENERATED_BODY()
protected:
UPROPERTY(VisibleAnywhere, Category = "Components")
UCameraComponent* CameraComp;
/* Component to specify origin for the HMD */
UPROPERTY(VisibleAnywhere, Category = "Components")
USceneComponent* VROriginComp;
UPROPERTY(EditDefaultsOnly, Category = "VR")
bool bPositionalHeadTracking;
/* Motion Controllers */
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UChildActorComponent* LeftHandComponent;
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UChildActorComponent* RightHandComponent;
public:
// Sets default values for this character's properties
AVRCharacter();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
void SetupVROptions();
/* Resets HMD Origin position and orientation */
void ResetHMDOrigin();
/* Toggle between Seated and Standing VR Tracking */
void ToggleTrackingSpace();
void HandleLeftPress();
void HandleLeftRelease();
void HandleRightPress();
void HandleRightRelease();
private:
void MoveForward(float Value);
void MoveRight(float Value);
};
[/SPOILER]
VRCharacter.cpp [SPOILER]
#include "VRFirstPerson.h"
/* VR Includes */
#include "HeadMountedDisplay.h"
#include "MotionControllerComponent.h"
#include "VRHand.h"
#include "VRCharacter.h"
// Sets default values
AVRCharacter::AVRCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
VROriginComp = CreateDefaultSubobject<USceneComponent>(TEXT("VRCameraOrigin"));
VROriginComp->SetupAttachment(RootComponent);
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
/* Assign to the VR origin component so any reset calls to the HMD can reset to 0,0,0 relative to this component */
CameraComp->SetupAttachment(VROriginComp);
LeftHandComponent = CreateDefaultSubobject<UChildActorComponent>(TEXT("LeftHand"));
//LeftHandComponent->Hand = EControllerHand::Left;
LeftHandComponent->SetupAttachment(VROriginComp);
RightHandComponent = CreateDefaultSubobject<UChildActorComponent>(TEXT("RightHand"));
//RightHandComponent->Hand = EControllerHand::Right;
RightHandComponent->SetupAttachment(VROriginComp);
bPositionalHeadTracking = false;
}
// Called when the game starts or when spawned
void AVRCharacter::BeginPlay()
{
Super::BeginPlay();
SetupVROptions();
//AVRHand *LCurrent = Cast<AVRHand>(LeftHandComponent->GetChildActor());
//if (LCurrent)
//{
// LCurrent->SetOwnerChar(this);
//}
//AVRHand *RCurrent = Cast<AVRHand>(RightHandComponent->GetChildActor());
//if (RCurrent)
//{
// RCurrent->SetOwnerChar(this);
//}
}
// Called to bind functionality to input
void AVRCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("ToggleTrackingSpace", IE_Pressed, this, &AVRCharacter::ToggleTrackingSpace);
PlayerInputComponent->BindAction("ResetHMDOrigin", IE_Pressed, this, &AVRCharacter::ResetHMDOrigin);
PlayerInputComponent->BindAxis("MoveForward", this, &AVRCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AVRCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAction("GrabLeft", IE_Pressed, this, &AVRCharacter::HandleLeftPress);
PlayerInputComponent->BindAction("GrabLeft", IE_Released, this, &AVRCharacter::HandleLeftRelease);
PlayerInputComponent->BindAction("GrabRigjt", IE_Pressed, this, &AVRCharacter::HandleRightPress);
PlayerInputComponent->BindAction("GrabRigjt", IE_Released, this, &AVRCharacter::HandleRightRelease);
}
void AVRCharacter::MoveForward(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}
void AVRCharacter::MoveRight(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}
void AVRCharacter::SetupVROptions()
{
IHeadMountedDisplay* HMD = (IHeadMountedDisplay*)(GEngine->HMDDevice.Get());
if (HMD && HMD->IsStereoEnabled())
{
/* Disable/Enable positional movement to pin camera translation */
HMD->EnablePositionalTracking(bPositionalHeadTracking);
/* Remove any translation when disabling positional head tracking */
if (!bPositionalHeadTracking)
{
CameraComp->SetRelativeLocation(FVector(0, 0, 0));
}
}
}
void AVRCharacter::ResetHMDOrigin()
{
IHeadMountedDisplay* HMD = (IHeadMountedDisplay*)(GEngine->HMDDevice.Get());
if (HMD && HMD->IsStereoEnabled())
{
HMD->ResetOrientationAndPosition();
}
}
void AVRCharacter::ToggleTrackingSpace()
{
// TODO: Fix module includes for SteamVR
//@todo Make this safe once we can add something to the DeviceType enum. For now, make the terrible assumption this is a SteamVR device.
// FSteamVRHMD* SteamVRHMD = (FSteamVRHMD*)(GEngine->HMDDevice.Get());
// if (SteamVRHMD && SteamVRHMD->IsStereoEnabled())
// {
// ESteamVRTrackingSpace TrackingSpace = SteamVRHMD->GetTrackingSpace();
// SteamVRHMD->SetTrackingSpace(TrackingSpace == ESteamVRTrackingSpace::Seated ? ESteamVRTrackingSpace::Standing : ESteamVRTrackingSpace::Seated);
// }
}
void AVRCharacter::HandleLeftPress()
{
AVRHand *Current = Cast<AVRHand>(LeftHandComponent->GetChildActor());
if (Current)
{
Current->GrabActor();
}
}
void AVRCharacter::HandleLeftRelease()
{
AVRHand *Current = Cast<AVRHand>(LeftHandComponent->GetChildActor());
if (Current)
{
Current->ReleaseActor();
}
}
void AVRCharacter::HandleRightPress()
{
AVRHand *Current = Cast<AVRHand>(RightHandComponent->GetChildActor());
if (Current)
{
Current->GrabActor();
}
}
void AVRCharacter::HandleRightRelease()
{
AVRHand *Current = Cast<AVRHand>(RightHandComponent->GetChildActor());
if (Current)
{
Current->ReleaseActor();
}
}
[/SPOILER]
VRHand.h [SPOILER]
#pragma once
#include "GameFramework/Actor.h"
#include "VRCharacter.h"
#include "VRHand.generated.h"
UENUM(BlueprintType)
enum class EGripState : uint8
{
Open,
Index,
CanGrab,
Grab
};
UCLASS()
class VRFIRSTPERSON_API AVRHand : public AActor
{
GENERATED_BODY()
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = ( AllowPrivateAccess = "true" ) )
class USceneComponent *Scene;
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = (AllowPrivateAccess = "true"))
class UMotionControllerComponent *MotionController;
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = (AllowPrivateAccess = "true"))
class USkeletalMeshComponent *HandMesh;
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = ( AllowPrivateAccess = "true" ) )
class USphereComponent *GrabSphere;
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = ( AllowPrivateAccess = "true" ) )
class USplineComponent *ArcSpline;
UPROPERTY( VisibleAnywhere, BlueprintReadOnly, Category = "Code Components", meta = ( AllowPrivateAccess = "true" ) )
class UArrowComponent *ArcDirection;
FRotator InitialControllerRotation;
TArray<class USplineMeshComponent*> SplineMeshes;
public:
AVRHand();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
virtual void OnConstruction(const FTransform & Transform) override;
/** Is this the left or right hand */
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Code Constants" )
EControllerHand Hand;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Code Constants" )
FVector Extents;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Code Variables" )
bool WantsToGrip;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Code Variables" )
EGripState Grip;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Code Variables" )
class AActor *AttachedActor;
UFUNCTION()
void OnComponentBeginOverlap( UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult );
UFUNCTION( BlueprintNativeEvent, BlueprintCallable, Category = "Default" )
void RumbleController( float intensity );
UFUNCTION( BlueprintNativeEvent, BlueprintCallable, Category = "Default" )
void GrabActor();
UFUNCTION( BlueprintNativeEvent, BlueprintCallable, Category = "Default" )
void ReleaseActor();
//void SetOwnerChar(AVRCharacter* OwnerChar);
AActor* GetActorNearHand();
void UpdateAnimationGripState();
FRotator GetControllerRelativeRotation();
//private:
// AVRCharacter* OwnerChar;
};
[/SPOILER]
VRHand.cpp [SPOILER]
#include "VRFirstPerson.h"
#include "VRHand.h"
#include "IPickupable.h"
#include "HeadMountedDisplay.h"
#include "MotionControllerComponent.h"
#include "Runtime/Engine/Classes/Kismet/HeadMountedDisplayFunctionLibrary.h"
#include "Runtime/Engine/Classes/Components/SplineComponent.h"
#include "Runtime/HeadMountedDisplay/Public/IHeadMountedDisplay.h"
#include "Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
#include "Runtime/Engine/Classes/Components/SplineMeshComponent.h"
// Sets default values
AVRHand::AVRHand()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Hand = EControllerHand::Right;
Grip = EGripState::Open;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
MotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("MotionController"));
MotionController->SetupAttachment(Scene);
MotionController->Hand = Hand;
HandMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("HandMesh"));
HandMesh->SetupAttachment(MotionController);
GrabSphere = CreateDefaultSubobject<USphereComponent>(TEXT("GrabSphere"));
GrabSphere->SetupAttachment(HandMesh);
GrabSphere->InitSphereRadius(10.0f);
GrabSphere->OnComponentBeginOverlap.AddDynamic(this, &AVRHand::OnComponentBeginOverlap);
ArcDirection = CreateDefaultSubobject<UArrowComponent>(TEXT("ArcDirection"));
ArcDirection->SetupAttachment(HandMesh);
ArcSpline = CreateDefaultSubobject<USplineComponent>(TEXT("ArcSpline"));
ArcSpline->SetupAttachment(HandMesh);
}
void AVRHand::OnConstruction(const FTransform & Transform)
{
Super::OnConstruction(Transform);
if (Hand == EControllerHand::Left)
{
// Reflect hand mesh
HandMesh->SetWorldScale3D(FVector(1, 1, -1));
}
}
void AVRHand::RumbleController_Implementation(float intensity)
{
FLatentActionInfo actionInfo;
actionInfo.CallbackTarget = this;
APlayerController *playerController = GetWorld()->GetFirstPlayerController();
}
void AVRHand::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor->IsA(AVRCharacter::StaticClass()))
{
return;
}
UE_LOG(LogTemp, Warning, TEXT("start"));
Grip = EGripState::CanGrab;
if ((OtherComp != nullptr) && (OtherComp != GrabSphere))
{
UE_LOG(LogTemp, Warning, TEXT("if 1"));
UStaticMeshComponent *mesh = Cast<UStaticMeshComponent>(OtherComp);
if (mesh && mesh->IsSimulatingPhysics())
{
UE_LOG(LogTemp, Warning, TEXT("if 2"));
this->RumbleController(0.8);
}
}
}
// Called when the game starts or when spawned
void AVRHand::BeginPlay()
{
Super::BeginPlay();
MotionController->Hand = Hand;
if (Hand == EControllerHand::Left)
{
// Reflect hand mesh
MotionController->Hand = Hand;
HandMesh->SetWorldScale3D(FVector(1, 1, -1));
}
// Hide until activation (but wait for BeginPlay so it is shown in editor)
}
AActor* AVRHand::GetActorNearHand()
{
TArray<AActor*> overlappingActors;
GrabSphere->GetOverlappingActors(overlappingActors);
FVector handLocation = GrabSphere->GetComponentLocation();
AActor* nearest = nullptr;
float mindist = 99999999999;
// Find closest overlaping actor
for (AActor *actor : overlappingActors)
{
bool isPickupable = actor->GetClass()->ImplementsInterface(UPickupable::StaticClass());
if (isPickupable)
{
//UE_LOG(LogTemp, Warning, TEXT("isPickupable"));
float dist = (actor->GetActorLocation() - handLocation).SizeSquared();
if (dist < mindist)
{
mindist = dist;
nearest = actor;
}
}
}
// if ( GEngine && Hand == EControllerHand::Right )
// GEngine->AddOnScreenDebugMessage( -1, 0.16f, FColor::Red,
// FString::Printf( TEXT( "Actors near right hand %d, found pickupable: %d, %s" ),
// overlappingActors.Num(),
// count,
// nearest ? TEXT( "TRUE" ) : TEXT( "FALSE" ) ) );
return nearest;
}
void AVRHand::UpdateAnimationGripState()
{
// Default to Open
Grip = EGripState::Open;
if (AttachedActor)
{
// If holding an object, always keep fist closed
Grip = EGripState::Grab;
}
else
{
// React to player input
if (WantsToGrip)
{
Grip = EGripState::Grab;
}
// If not holding something, the hand should open or close
// slightly when passing over an interactable object
AActor *actor = GetActorNearHand();
if (actor)
{
Grip = EGripState::CanGrab;
}
}
// Only let hand collide with environment while gripping
if (Grip == EGripState::Grab)
{
HandMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
else
{
HandMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
void AVRHand::GrabActor_Implementation()
{
WantsToGrip = true;
AActor *actor = GetActorNearHand();
if (actor && actor->IsValidLowLevel())
{
UE_LOG(LogTemp, Warning, TEXT("IsValidLowLevel"));
AttachedActor = actor;
IPickupable::Execute_Pickup(actor, MotionController);
RumbleController(0.7);
}
}
void AVRHand::ReleaseActor_Implementation()
{
WantsToGrip = false;
AActor *actor = AttachedActor;
if (actor && actor->IsValidLowLevel())
{
// Make sure this hand is still holding the Actor (May have been taken by another hand / event)
if (MotionController == actor->GetRootComponent()->GetAttachParent())
{
IPickupable::Execute_Drop(actor);
RumbleController(0.2);
}
}
AttachedActor = nullptr;
}
// Called every frame
void AVRHand::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UpdateAnimationGripState();
}
FRotator AVRHand::GetControllerRelativeRotation()
{
const FTransform InitialTransform(InitialControllerRotation);
const FTransform CurrentTransform = MotionController->GetComponentTransform();
const FTransform RelativeTransform = CurrentTransform.GetRelativeTransform(InitialTransform);
return RelativeTransform.GetRotation().Rotator();
}
//void AVRHand::SetOwnerChar(AVRCharacter* OwnerChar)
//{
// this->OwnerChar = OwnerChar;
//
// if (OwnerChar != nullptr)
// {
// GrabSphere->IgnoreActorWhenMoving(OwnerChar, true);
// }
//}
[/SPOILER]
[/SPOILER]
Can someone help to understand what I doing wrong?