[언리얼엔진] 1. VR Grab/Release 기능 구현 (C++)

2026. 4. 4. 10:49·Unreal Engine 프로젝트/VR 공포게임

결과물

플레이어의 손 움직임을 Tracking 하고, 물체와 상호작용이 가능하도록 기능을 구현했습니다.

 


 

구현 내용

1. AVRHand.h / .cpp

VR Template은 왼손과 오른손을 각각 독립적으로 구현한 것을 알 수 있는데요. 이 때문에 중복적인 코드가 다수 발생하는 것을 알 수 있었습니다. 저는 C++로 왼손, 오른손의 베이스가 되는 VRHand 클래스를 구현함으로써 중복적인 코드를 방지하고자 했습니다.
VRHand는 Actor 클래스이기 때문에 InputAction에 대해 함수를 바인딩하는 SetupInputComponent 가상함수가 존재하지 않습니다. 이는 Player 클래스에서 오른손, 왼손에 대해 SetupInputComponent를 직접적으로 호출하여 입력을 바인딩하는 구조로 구현했습니다.

AVRHand.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VRHand.generated.h"

class IInteractable;
struct FInputActionValue;
class UVRHandAnimInstance;
class UInputAction;
class USphereComponent;
class UWidgetInteractionComponent;
class UMotionControllerComponent;

UCLASS()
class VIRTUALREALITY_API AVRHand : public AActor
{
	GENERATED_BODY()
	
public:
	AVRHand();
	virtual void OnConstruction(const FTransform& Transform) override;
	virtual void PostInitializeComponents() override;
	virtual void BeginPlay() override;
	
public:
	void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent);
	
	
// Action Binding Function Section	
protected:
	// HandGraspAction Binding Function
	void DoHandGrasp(const FInputActionValue& InValue);
	void StopHandGrasp();
	
	// HandIndexCurl Binding Function
	void DoHandIndexCurl(const FInputActionValue& InValue);
	void StopHandIndexCurl();
	
	// HandPoint Binding Function
	void DoHandPoint();
	void StopHandPoint();
	
	// HandThumbUp Binding Function
	void DoHandThumbUp();
	void StopHandThumbUp();
	
	// GrabAction Binding Function
	void GrabObject();
	void ReleaseObject();
	
	
// Component Section	
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<UMotionControllerComponent> MotionController;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<USkeletalMeshComponent> HandMesh;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<UWidgetInteractionComponent> WidgetInteractionComponent;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<USphereComponent> GrabCollision;
	
	
// Input Section
protected:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputAction> HandGraspAction;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputAction> HandIndexCurlAction;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputAction> HandPointAction;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputAction> HandThumbUpAction;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputAction> GrabAction;


// Variable Section	
protected:	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|손")
	EControllerHand HandType;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|손")
	uint8 bMirrorAnimation : 1 = false;
	
	
// Cached Section	
private:
	UPROPERTY()
	TObjectPtr<UVRHandAnimInstance> AnimInstance;
	
	TScriptInterface<IInteractable> CurrentlyGrabbedActor;
	
};

AVRHand.cpp

더보기
#include "VRHand.h"
#include "EnhancedInputComponent.h"
#include "InputActionValue.h"
#include "MotionControllerComponent.h"
#include "VirtualReality.h"
#include "Animation/VRHandAnimInstance.h"
#include "Components/SphereComponent.h"
#include "Components/WidgetInteractionComponent.h"
#include "Define/Define.h"
#include "Interface/Interactable.h"


AVRHand::AVRHand()
{
	// MotionController 초기화
	MotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("MotionController"));
	SetRootComponent(MotionController);
	
	// HandMesh 초기화
	HandMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("HandMesh"));
	HandMesh->SetupAttachment(MotionController);
	
	// WidgetInteractionComponent 초기화
	WidgetInteractionComponent = CreateDefaultSubobject<UWidgetInteractionComponent>(TEXT("WidgetInteractionComponent"));
	WidgetInteractionComponent->SetupAttachment(HandMesh);
	
	// GrabCollision 초기화
	GrabCollision = CreateDefaultSubobject<USphereComponent>(TEXT("GrabCollision"));
	GrabCollision->SetupAttachment(HandMesh);
	GrabCollision->SetSphereRadius(15.0f);
	GrabCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	GrabCollision->SetCollisionResponseToAllChannels(ECR_Ignore);
	GrabCollision->SetCollisionResponseToChannel(ECC_GRABBABLE, ECR_Overlap);
}

void AVRHand::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent))
	{
		if (HandGraspAction)
		{
			EIC->BindAction(HandGraspAction, ETriggerEvent::Triggered, this, &AVRHand::DoHandGrasp);
			EIC->BindAction(HandGraspAction, ETriggerEvent::Canceled, this, &AVRHand::StopHandGrasp);
			EIC->BindAction(HandGraspAction, ETriggerEvent::Completed, this, &AVRHand::StopHandGrasp);
		}
		
		if (HandIndexCurlAction)
		{
			EIC->BindAction(HandIndexCurlAction, ETriggerEvent::Triggered, this, &AVRHand::DoHandIndexCurl);
			EIC->BindAction(HandIndexCurlAction, ETriggerEvent::Canceled, this, &AVRHand::StopHandIndexCurl);
			EIC->BindAction(HandIndexCurlAction, ETriggerEvent::Completed, this, &AVRHand::StopHandIndexCurl);
		}
		
		if (HandPointAction)
		{
			EIC->BindAction(HandPointAction, ETriggerEvent::Started, this, &AVRHand::DoHandPoint);
			EIC->BindAction(HandPointAction, ETriggerEvent::Canceled, this, &AVRHand::DoHandPoint);
			EIC->BindAction(HandPointAction, ETriggerEvent::Completed, this, &AVRHand::StopHandPoint);
		}
		
		if (HandThumbUpAction)
		{
			EIC->BindAction(HandThumbUpAction, ETriggerEvent::Started, this, &AVRHand::DoHandThumbUp);
			EIC->BindAction(HandThumbUpAction, ETriggerEvent::Canceled, this, &AVRHand::DoHandThumbUp);
			EIC->BindAction(HandThumbUpAction, ETriggerEvent::Completed, this, &AVRHand::StopHandThumbUp);
		}
		
		if (GrabAction)
		{
			EIC->BindAction(GrabAction, ETriggerEvent::Started, this, &AVRHand::GrabObject);
			EIC->BindAction(GrabAction, ETriggerEvent::Canceled, this, &AVRHand::ReleaseObject);
			EIC->BindAction(GrabAction, ETriggerEvent::Completed, this, &AVRHand::ReleaseObject);
		}
	}
}

void AVRHand::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);

	switch (HandType)
	{
		case EControllerHand::Left:
			MotionController->MotionSource = FName("Left");
			break;
		case EControllerHand::Right:	
			MotionController->MotionSource = FName("Right");
			break;
		default:
			break;
	}
}

void AVRHand::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	AnimInstance = Cast<UVRHandAnimInstance>(HandMesh->GetAnimInstance());
}

void AVRHand::BeginPlay()
{
	Super::BeginPlay();
	
	AnimInstance->bIsMirror = bMirrorAnimation;
}

void AVRHand::GrabObject()
{
	TArray<AActor*> OverlappedActors;
	GrabCollision->GetOverlappingActors(OverlappedActors);
	if (OverlappedActors.IsEmpty()) return;
	
	AActor* FirstActorUnderCollision = OverlappedActors[0];
	if (!FirstActorUnderCollision) return;
	
	CurrentlyGrabbedActor = TScriptInterface<IInteractable>(FirstActorUnderCollision);
	if (CurrentlyGrabbedActor)
	{
		CurrentlyGrabbedActor->OnGrab(HandMesh, GrabCollision->GetComponentLocation());
	}
}

void AVRHand::ReleaseObject()
{
	if (CurrentlyGrabbedActor)
	{
		CurrentlyGrabbedActor->OnRelease(HandMesh);
		CurrentlyGrabbedActor = nullptr;
	}
}


void AVRHand::DoHandGrasp(const FInputActionValue& InValue)
{
	const float ActionValue = InValue.Get<float>();
	
	AnimInstance->PoseAlphaGrasp = ActionValue;
}

void AVRHand::DoHandIndexCurl(const FInputActionValue& InValue)
{
	const float ActionValue = InValue.Get<float>();

	AnimInstance->PoseAlphaIndexCurl = ActionValue;
}

void AVRHand::DoHandPoint()
{	
	AnimInstance->PoseAlphaPoint = 0.0f;
}

void AVRHand::DoHandThumbUp()
{	
	AnimInstance->PoseAlphaThumbUp = 0.0f;
}

void AVRHand::StopHandGrasp()
{
	AnimInstance->PoseAlphaGrasp = 0.0f;
}

void AVRHand::StopHandIndexCurl()
{
	AnimInstance->PoseAlphaIndexCurl = 0.0f;
}

void AVRHand::StopHandPoint()
{
	AnimInstance->PoseAlphaPoint = 1.0f;
}

void AVRHand::StopHandThumbUp()
{
	AnimInstance->PoseAlphaThumbUp = 1.0f;
}

 


 

2. AVRPlayer.h / .cpp

왼손, 오른손에 대한 메시만 가지도록 구현을 했습니다. 오른손, 왼손은 AVRHand 클래스를 기반으로 동작합니다. BeginPlay시 각 손을 SpawnActor를 통해 Player의 SceneComponent에 부착하는 구조로 동작합니다.

AVRPlayer.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "VRPlayer.generated.h"

class UInputMappingContext;
class UVRHandAnimInstance;
class AVRHand;
class UCameraComponent;

UCLASS()
class VIRTUALREALITY_API AVRPlayer : public ACharacter
{
	GENERATED_BODY()
	
public:
	AVRPlayer();
	virtual void NotifyControllerChanged() override;
	virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
	virtual void PostInitializeComponents() override;
	virtual void BeginPlay() override;
	
protected:
	/** VR 트래킹 원점을 초기화하는 함수입니다. */
	void InitVRSetting();
	
	/** VR 손 액터를 스폰하는 함수입니다. LeftHandClass와 RightHandClass가 설정되어 있어야 합니다. */
	void SpawnVRHands();
	
	
// Component Section	
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<USceneComponent> VROrigin;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<UCameraComponent> CameraComponent;
	
	
// Hand Class Section	
protected:	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|손")
	TSubclassOf<AVRHand> LeftHandClass;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|손")
	TSubclassOf<AVRHand> RightHandClass;
	
	
// Input Section
protected:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputMappingContext> DefaultMappingContext;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|입력")
	TObjectPtr<UInputMappingContext> HandsMappingContext;
	
	
// Cached Variable Section	
private:
	UPROPERTY()
	TObjectPtr<AVRHand> LeftHand;
	
	UPROPERTY()
	TObjectPtr<AVRHand> RightHand;
	
	UPROPERTY()
	TObjectPtr<UVRHandAnimInstance> AnimInstance;
	
	UPROPERTY()
	TObjectPtr<UEnhancedInputComponent> EIC;

};

AVRPlayer.cpp

더보기
#include "VRPlayer.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "VirtualReality.h"
#include "VRHand.h"
#include "Animation/VRHandAnimInstance.h"
#include "Camera/CameraComponent.h"

AVRPlayer::AVRPlayer()
{
	static ConstructorHelpers::FObjectFinder<UInputMappingContext> IMC_Default(TEXT("/Game/VRTemplate/Input/IMC_Default"));
	if (IMC_Default.Succeeded())
	{
		DefaultMappingContext = IMC_Default.Object;
	}
	
	static ConstructorHelpers::FObjectFinder<UInputMappingContext> IMC_Hands(TEXT("/Game/VRTemplate/Input/IMC_Hands"));
	if (IMC_Hands.Succeeded())
	{
		HandsMappingContext = IMC_Hands.Object;
	}
	
	static ConstructorHelpers::FClassFinder<AVRHand> BP_LeftHand(TEXT("/Game/_VirtualReality/Blueprint/Player/BP_LeftHand"));
	if (BP_LeftHand.Succeeded())
	{
		LeftHandClass = BP_LeftHand.Class;
	}

	static ConstructorHelpers::FClassFinder<AVRHand> BP_RightHand(TEXT("/Game/_VirtualReality/Blueprint/Player/BP_RightHand"));
	if (BP_RightHand.Succeeded())
	{
		RightHandClass = BP_RightHand.Class;
	}
	
	VROrigin = CreateDefaultSubobject<USceneComponent>("VROrigin");
	VROrigin->SetupAttachment(GetMesh());
	
	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
	CameraComponent->SetupAttachment(VROrigin);
}

void AVRPlayer::NotifyControllerChanged()
{
	Super::NotifyControllerChanged();
	
	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer())
		{
			if (UEnhancedInputLocalPlayerSubsystem* EnhancedInput = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
			{
				EnhancedInput->AddMappingContext(DefaultMappingContext, 0);
				EnhancedInput->AddMappingContext(HandsMappingContext, 0);
			}
		}
	}
}

void AVRPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	
	EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
}

void AVRPlayer::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	AnimInstance = Cast<UVRHandAnimInstance>(GetMesh()->GetAnimInstance());
}

void AVRPlayer::BeginPlay()
{
	Super::BeginPlay();
	
	InitVRSetting();
	SpawnVRHands();
}

void AVRPlayer::InitVRSetting()
{
	// HMD가 연결되어 있으면 발 기준 트래킹 원점으로 설정합니다.
	if (UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled())
	{
		UHeadMountedDisplayFunctionLibrary::SetTrackingOrigin(EHMDTrackingOrigin::LocalFloor);
		LOG(TEXT("HMD 감지 — 트래킹 원점을 Floor로 설정했습니다."));
	}
	else
	{
		LOG(TEXT("HMD가 감지되지 않았습니다. 일반 카메라 모드로 실행됩니다."));
	}
}

void AVRPlayer::SpawnVRHands()
{
	if (!LeftHandClass || !RightHandClass)
	{
		LOG(TEXT("손 클래스가 설정되지 않았습니다. 블루프린트에서 LeftHandClass와 RightHandClass를 할당해 주세요."));
		return;
	}
	
	FActorSpawnParameters SpawnParams;
	SpawnParams.Owner = this;
	SpawnParams.Instigator = GetInstigator();
	
	LeftHand = GetWorld()->SpawnActor<AVRHand>(LeftHandClass, FTransform::Identity, SpawnParams);
	if (LeftHand)
	{
		LeftHand->AttachToComponent(VROrigin, FAttachmentTransformRules::SnapToTargetIncludingScale);
		LeftHand->SetupPlayerInputComponent(EIC);
	}
	
	RightHand = GetWorld()->SpawnActor<AVRHand>(RightHandClass, FTransform::Identity, SpawnParams);
	if (RightHand)
	{
		RightHand->AttachToComponent(VROrigin, FAttachmentTransformRules::SnapToTargetIncludingScale);
		RightHand->SetupPlayerInputComponent(EIC);
	}
}

 


 

3. AVRActorBase.h / .cpp

각 오브젝트마다 물리적인 상호작용이 가능하게 구현을 할 계획입니다. 따라서 물리적인 상호작용을 추가할 오브젝트들이 기본적으로 상속받을 기본 클래스를 구현했습니다. 현재는 SceneComponent와 StaticMeshComponent 만 소유하고 있습니다.

AVRActorBase.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VRActorBase.generated.h"

UCLASS()
class VIRTUALREALITY_API AVRActorBase : public AActor
{
	GENERATED_BODY()
	
public:
	AVRActorBase();
	
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<USceneComponent> Root;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<UStaticMeshComponent> Mesh;

};

AVRActorBase.cpp

더보기
#include "VRActorBase.h"

AVRActorBase::AVRActorBase()
{
	Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
	SetRootComponent(Root);
	
	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	Mesh->SetupAttachment(Root);
	
}

 


 

4. AVRGrabbableActor.h / .cpp

상호작용을 하면 손으로 잡을 수 있는 오브젝트들이 상속받는 클래스를 구현했습니다. GrabCollision 내에 손이 진입한 상태에서 컨트롤러의 특정 키를 누르게 되면 OnGrab이 호출되면서 물체가 손에 붙게 되고, 특정 키를 누른 상태에서 떼게 되면 OnRelease가 호출되면서 물체를 손에서 놓게 됩니다.
OnGrab, OnRelease는 인터페이스를 통해 오버라이딩하는 함수입니다.

AVRGrabbableActor.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "Actor/VRActorBase.h"
#include "Interface/Interactable.h"
#include "VRGrabbableActor.generated.h"

class UBoxComponent;

UENUM(BlueprintType)
enum class EGrabType : uint8
{
	Free,
	Snap,
	None
};

UCLASS()
class VIRTUALREALITY_API AVRGrabbableActor : public AVRActorBase, public IInteractable
{
	GENERATED_BODY()
	
public:
	AVRGrabbableActor();
	
public:
	virtual void OnGrab(USkeletalMeshComponent* InComponent, const FVector& GrabLocation) override;
	virtual void OnRelease(USkeletalMeshComponent* InComponent) override;
	
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "변수|컴포넌트")
	TObjectPtr<UBoxComponent> GrabRegion;
	
protected:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "변수|타입")
	EGrabType GrabType;
	
protected:
	uint8 bIsHeld : 1;
	
	UPROPERTY()
	USkeletalMeshComponent* GrabbedBySkeletalMesh;
	
};

AVRGrabbableActor.cpp

더보기
#include "VRGrabbableActor.h"
#include "Components/BoxComponent.h"

AVRGrabbableActor::AVRGrabbableActor()
{
	GrabRegion = CreateDefaultSubobject<UBoxComponent>(TEXT("GrabBoxRegion"));
	GrabRegion->SetupAttachment(Mesh);
	GrabRegion->SetCollisionProfileName(TEXT("Grabbable"));
}

void AVRGrabbableActor::OnGrab(USkeletalMeshComponent* InComponent, const FVector& GrabLocation)
{
	if (!InComponent) return;
	
	switch (GrabType)
	{
		case EGrabType::Free:
			Mesh->SetSimulatePhysics(false);
			bIsHeld = Mesh->AttachToComponent(InComponent, FAttachmentTransformRules::KeepWorldTransform);
			if (bIsHeld)
			{
				GrabbedBySkeletalMesh = InComponent;
			}
			break;
		case EGrabType::Snap:
			break;
		case EGrabType::None:
			break;
	}
}

void AVRGrabbableActor::OnRelease(USkeletalMeshComponent* InComponent)
{
	if (!InComponent) return;
	
	switch (GrabType)
	{
	case EGrabType::Free:
		if (bIsHeld)
		{
			if (GrabbedBySkeletalMesh == InComponent)
			{
				Mesh->SetSimulatePhysics(true);
				//DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
				//GrabbedBySkeletalMesh = nullptr;
				bIsHeld = false;
			}
		}
		break;
	case EGrabType::Snap:
		break;
	case EGrabType::None:
		break;
	}
}

 


 

5. IInteractable

상호작용이 가능한 오브젝트들을 다소 가볍게 접근하기 위한 인터페이스를 구현했습니다. 현재는 OnGrab, OnRelease를 선언한 상태입니다.

IInteractable.h

더보기
#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"

UINTERFACE()
class UInteractable : public UInterface
{
	GENERATED_BODY()
};


class VIRTUALREALITY_API IInteractable
{
	GENERATED_BODY()

public:
	virtual void OnGrab(USkeletalMeshComponent* InComponent, const FVector& GrabLocation) = 0;
	virtual void OnRelease(USkeletalMeshComponent* InComponent) = 0;

};

 


 

마무리

VR 게임의 구현이 처음이다 보니 구현을 진행하면서 새롭게 알아가는 사실들이 되게 많은 것 같습니다 ! 대부분의 강의 영상도 Blueprint로 되어있더라구요. 아무래도 Blueprint로 구현한 기능들을 다시 C++로 전환하는 데에 시간을 좀 쏟게 되는 것 같네요.

'Unreal Engine 프로젝트 > VR 공포게임' 카테고리의 다른 글

[언리얼엔진] 6. VR Hand 애니메이션 제작  (0) 2026.04.09
[언리얼엔진] 5. VR 최적화  (0) 2026.04.09
[언리얼엔진] 4. VR 레버 구현  (2) 2026.04.08
[언리얼엔진] 3. VR Hand 물리 효과 구현  (0) 2026.04.04
[언리얼엔진] 2. VR 버튼 구현  (0) 2026.04.04
'Unreal Engine 프로젝트/VR 공포게임' 카테고리의 다른 글
  • [언리얼엔진] 5. VR 최적화
  • [언리얼엔진] 4. VR 레버 구현
  • [언리얼엔진] 3. VR Hand 물리 효과 구현
  • [언리얼엔진] 2. VR 버튼 구현
Meoyoung's Development Logs
Meoyoung's Development Logs
내가 보려고 만든 블로그
  • Meoyoung's Development Logs
    이게뭐영
    Meoyoung's Development Logs
  • 전체
    오늘
    어제
    • 분류 전체보기 (289)
      • Unreal Engine 프로젝트 (36)
        • 더 퍼스트 버서커 : 카잔 (16)
        • VR 공포게임 (13)
        • Paper-ZD (7)
      • 언리얼 엔진 (72)
        • GAS (10)
        • 트러블슈팅 (27)
        • 캐릭터 (2)
        • VR (1)
        • Lighting (2)
        • 멀티스레드 (2)
        • Lyra (1)
      • C++ (31)
        • 문법 정리 (8)
        • [서적] Fundamental C++ 프로그래밍 .. (5)
        • [서적] 이것이 C++이다 (11)
        • [서적] Effective C++ (7)
      • 게임잼 (3)
      • 강의 (36)
        • [강의] 이득우의 언리얼 프로그래밍 Part1 (13)
        • [강의] 이득우의 언리얼 프로그래밍 Part2 (2)
        • [강의] 이득우의 언리얼 프로그래밍 Part3 (12)
        • [강의] 소울라이크 개발 A-Z (4)
        • [강의] Udemy-2D (5)
      • C# (1)
        • [서적] 이것이 C#이다 (1)
      • 코딩테스트 (26)
        • 프로그래머스 (6)
        • 알고리듬 (13)
        • 자료구조 (7)
      • 컴퓨터 과학 (27)
        • 운영체제 (11)
        • 데이터베이스 (0)
        • 디자인패턴 (0)
        • 자료구조 (5)
        • 네트워크 (0)
        • 컴퓨터구조 (11)
      • 면접준비 (0)
        • C++ (0)
        • 운영체제 (0)
        • 자료구조 (0)
      • 기타 (48)
        • [팀프로젝트] The Fourth Descenda.. (5)
        • GetOutOf (15)
        • [개인프로젝트] FPS 구현 맛보기 (5)
        • [서적] 인생 언리얼5 (4)
        • 스파르타코딩클럽 (15)
        • 객체지향프로그래밍 (2)
        • 컴퓨터회로 (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자료구조
    쉘정렬
    게임개발
    경북게임잼
    셸정렬
    선택정렬
    참가후기
    버블정렬
    게임잼
    알고리즘
    삽입정렬
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Meoyoung's Development Logs
[언리얼엔진] 1. VR Grab/Release 기능 구현 (C++)
상단으로

티스토리툴바