본문 바로가기

강동새싹언리얼수업/언리얼

231215 VR Character

https://github.com/onPlex/SesacVR.git

 

 

BP_GrabItem Actor를 부모로 블루프린트 만듬. 인터페이스붙여줌

이벤트추가후 노드 작성

 

C++ 재구성 

Charater 상속해서 AVRCharacter 작성

Character.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class SESACVR_API AVRCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AVRCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, Category = "Controller")
	class UMotionControllerComponent* LeftController;

	UPROPERTY(EditAnywhere, Category = "Controller")
	class UMotionControllerComponent* RightController;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};

Character.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "VRCharacter.h"
#include <Components/CapsuleComponent.h>
#include "MotionControllerComponent.h"
#include "Components/InputComponent.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;

	LeftController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftCon"));
	LeftController->MotionSource = FName("Left");

	RightController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightCon"));
	RightController->MotionSource = FName("Left");

	GetCapsuleComponent()->SetCapsuleSize(1.0f, 1.0f);
}

// Called when the game starts or when spawned
void AVRCharacter::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AVRCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void AVRCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

Build.cs에 EnhanceInput, HeadMountedDisplay 추가하고 VS ProjectFile 재생성하고 리빌드

// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;
public class SesacVR : ModuleRules
{
	public SesacVR(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine",
        	"InputCore" , "EnhancedInput", "HeadMountedDisplay"});
		PrivateDependencyModuleNames.AddRange(new string[] {  });

 

 

 

 

일단은 이것저것 짜집기해서 움직이게는 만들었다.

VRCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputAction.h"
#include "VRCharacter.generated.h"

UCLASS()
class SESACVR_API AVRCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AVRCharacter();
public:
	UPROPERTY(VisibleAnywhere, Category = Camera)
	class USpringArmComponent* springArmComp;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	class UCameraComponent* tpsCamComp;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	// 좌측 모션 컨트롤러 
	UPROPERTY(EditAnywhere, Category = "VR")
	class UMotionControllerComponent* LeftController;
	// 우측 모션 컨트롤러 
	UPROPERTY(EditAnywhere, Category = "VR")
	class UMotionControllerComponent* RightController;

	// 좌측 모션 컨트롤러 메쉬 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR")
	class USkeletalMeshComponent * LeftHand;
	// 우측 모션 컨트롤러 메쉬 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR")
	class USkeletalMeshComponent* RightHand;

public:
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputMappingContext* PlayerMappingContext;

	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* MoveIA;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* LookUpIA;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* JumpIA;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* FireIA;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* IA_1;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* IA_2;
	UPROPERTY(EditAnywhere, Category = "Input")
	UInputAction* IA_Aim;

	void Move(const FInputActionValue& Value);
	void LookUp(const FInputActionValue& Value);
	//void Turn(const FInputActionValue& Value);
	void InputJump(const FInputActionValue& Value);
	void InputFire(const FInputActionValue& Value); 	// 총알 발사 처리 함수

	UFUNCTION(BlueprintCallable, Category = "Pawn")
	virtual void MoveForward(float Val);
	UFUNCTION(BlueprintCallable, Category = "Pawn")
	virtual void MoveRight(float Val);
	UFUNCTION(BlueprintCallable, Category = "Pawn")
	virtual void MoveUp_World(float Val);
	UFUNCTION(BlueprintCallable, Category = "Pawn")
	virtual void TurnAtRate(float Rate);
	UFUNCTION(BlueprintCallable, Category = "Pawn")
	virtual void LookUpAtRate(float Rate);

	/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn")
	float BaseTurnRate;

	/** Base lookup rate, in deg/sec. Other scaling may affect final lookup rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn")
	float BaseLookUpRate;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	/** If true, adds default input bindings for movement and camera look. */
	UPROPERTY(Category = Pawn, EditAnywhere, BlueprintReadOnly)
	uint32 bAddDefaultMovementBindings : 1;

private:
	void Locomotion();

	FVector moveDirection;
	bool fireReady;
	float fireTimerTime;

};

VRCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "VRCharacter.h"
#include "Components/InputComponent.h"
#include <Camera/CameraComponent.h>
#include <GameFramework/SpringArmComponent.h>
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "MotionControllerComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/StaticMeshComponent.h" 
#include "Components/SkeletalMeshComponent.h" 
#include <Kismet/GameplayStatics.h>
#include "UObject/ConstructorHelpers.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;

	// 씬 루트 컴포넌트 
	//RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));

	// 좌측 모션 컨트롤러 초기화 
	LeftController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftCon"));
	LeftController->MotionSource = FName("Left");
	//LeftController->Hand = EControllerHand::Left;
	LeftController->SetupAttachment(RootComponent);

	// 우측 모션 컨트롤러 초기화 
	RightController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightCon"));
	LeftController->MotionSource = FName("Right");
	//RightController->Hand = EControllerHand::Right;
	RightController->SetupAttachment(RootComponent);

	GetCapsuleComponent()->SetCapsuleSize(1.f, 1.f);

	// 좌측 모션 컨트롤러 메쉬 초기화 
	LeftHand = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("LeftHand"));
	LeftHand->SetupAttachment(LeftController);

	static ConstructorHelpers::FObjectFinder<USkeletalMesh> LeftHandMesh(TEXT("/Game/Characters/MannequinsXR/Meshes/SKM_MannyXR_left.SKM_MannyXR_left"));
	if (LeftHandMesh.Succeeded())
	{
		if(GEngine)
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("LeftHandMesh.Succedded"));
		LeftHand->SetSkeletalMesh(LeftHandMesh.Object);
	}
	else {
		if (GEngine)
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("LeftHandMesh.Succedded"));
	}

	//// 우측 모션 컨트롤러 메쉬 초기화 
	RightHand = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("RightHand"));
	RightHand->SetupAttachment(RightController);

	static ConstructorHelpers::FObjectFinder<USkeletalMesh> RightHandMesh(TEXT("/Game/Characters/MannequinsXR/Meshes/SKM_MannyXR_right.SKM_MannyXR_right"));
	if (RightHandMesh.Succeeded())
	{
		if (GEngine)
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("RightHandMesh.Succedded()"));
		RightHand->SetSkeletalMesh(RightHandMesh.Object);
	}
	else {
		if (GEngine)
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("LeftHandMesh.Succedded"));
	}
	LeftHand->SetRelativeLocationAndRotation(FVector(0, -50, 0), FRotator(0, -90, 0));
	RightHand->SetRelativeLocationAndRotation(FVector(0, 50, 0), FRotator(0, -90, 0));


	// 3-1. SpringArm 컴포넌트 붙이기
	springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
	springArmComp->SetupAttachment(RootComponent);
	springArmComp->SetRelativeLocation(FVector(0, 70, 90));
	springArmComp->TargetArmLength = 400;
	springArmComp->bUsePawnControlRotation = true;
	// 3-2. Camera 컴포넌트 붙이기
	tpsCamComp = CreateDefaultSubobject<UCameraComponent>(TEXT("TpsCamComp"));
	tpsCamComp->SetupAttachment(springArmComp);
	tpsCamComp->bUsePawnControlRotation = false;

	bUseControllerRotationYaw = true;
	// 2단 점프
	JumpMaxCount = 2;

	// 플레이어 빙의 
	AutoPossessPlayer = EAutoReceiveInput::Player0;
}

// Called when the game starts or when spawned
void AVRCharacter::BeginPlay()
{
	Super::BeginPlay();
	Super::BeginPlay();
	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem
			= ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(PlayerMappingContext, 0);
		}
	}
}

// Called every frame
void AVRCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	Locomotion();
}

void InitializeDefaultPawnInputBindings()
{
	static bool bBindingsAdded = false;
	if (!bBindingsAdded)
	{
		if (GEngine)
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("InputBindings"));
		bBindingsAdded = true;

		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::W, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::S, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Up, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Down, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveForward", EKeys::Gamepad_LeftY, 1.f));

		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::A, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::D, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveRight", EKeys::Gamepad_LeftX, 1.f));

		// HACK: Android controller bindings in ini files seem to not work
		//  Direct overrides here some to work
#if !PLATFORM_ANDROID
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_LeftThumbstick, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_RightThumbstick, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_FaceButton_Bottom, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::LeftControl, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::SpaceBar, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::C, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::E, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Q, -1.f));
#else
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_LeftTriggerAxis, -0.5f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_MoveUp", EKeys::Gamepad_RightTriggerAxis, 0.5f));
#endif

		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Gamepad_RightX, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Left, -1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_TurnRate", EKeys::Right, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_Turn", EKeys::MouseX, 1.f));

		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_LookUpRate", EKeys::Gamepad_RightY, 1.f));
		UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("DefaultPawn_LookUp", EKeys::MouseY, -1.f));
	}
}

// Called to bind functionality to input
void AVRCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	check(PlayerInputComponent);
	if (GEngine)
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("SetupInputComponent"));
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		EnhancedInputComponent->BindAction(MoveIA, ETriggerEvent::Triggered, this, &AVRCharacter::Move);
		EnhancedInputComponent->BindAction(LookUpIA, ETriggerEvent::Triggered, this, &AVRCharacter::LookUp);
		EnhancedInputComponent->BindAction(JumpIA, ETriggerEvent::Triggered, this, &AVRCharacter::InputJump);
		EnhancedInputComponent->BindAction(FireIA, ETriggerEvent::Triggered, this, &AVRCharacter::InputFire);
		//EnhancedInputComponent->BindAction(IA_1, ETriggerEvent::Triggered, this, &AVRCharacter::ChangeToGrenadeGun);
		//EnhancedInputComponent->BindAction(IA_2, ETriggerEvent::Triggered, this, &AVRCharacter::ChangeToSniperGun);
		//EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Started, this, &AVRCharacter::SniperAim);
		//EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Completed, this, &AVRCharacter::SniperAim);
	}

	//if (bAddDefaultMovementBindings)
	//{
	//	InitializeDefaultPawnInputBindings();
	//	if (GEngine)
	//		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("SetupInputComponent"));
	//	PlayerInputComponent->BindAxis("DefaultPawn_MoveForward", this, &AVRCharacter::MoveForward);
	//	PlayerInputComponent->BindAxis("DefaultPawn_MoveRight", this, &AVRCharacter::MoveRight);
	//	PlayerInputComponent->BindAxis("DefaultPawn_MoveUp", this, &AVRCharacter::MoveUp_World);
	//	PlayerInputComponent->BindAxis("DefaultPawn_Turn", this, &AVRCharacter::AddControllerYawInput);
	//	PlayerInputComponent->BindAxis("DefaultPawn_TurnRate", this, &AVRCharacter::TurnAtRate);
	//	PlayerInputComponent->BindAxis("DefaultPawn_LookUp", this, &AVRCharacter::AddControllerPitchInput);
	//	PlayerInputComponent->BindAxis("DefaultPawn_LookUpRate", this, &AVRCharacter::LookUpAtRate);
	//}
}

//void AVRCharacter::UpdateNavigationRelevance()
//{
//	if (CollisionComponent)
//	{
//		CollisionComponent->SetCanEverAffectNavigation(bCanAffectNavigationGeneration);
//	}
//}
void AVRCharacter::Locomotion()
{
	moveDirection = FTransform(GetControlRotation()).TransformVector(moveDirection);
	AddMovementInput(moveDirection);
	moveDirection = FVector::ZeroVector; //ZeroVector; == FVector(0,0,0)
}
void AVRCharacter::Move(const FInputActionValue& Value)
{
	const FVector _currentValue = Value.Get<FVector>();
	//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
	if (GEngine)
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Move"));
	
	if (Controller)
	{
		moveDirection.Y = _currentValue.X;
		moveDirection.X = _currentValue.Y;
	}
}

void AVRCharacter::LookUp(const FInputActionValue& Value)
{
	const FVector _currentValue = Value.Get<FVector>();
	//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
	AddControllerPitchInput(_currentValue.Y);
	AddControllerYawInput(_currentValue.X);
}


void AVRCharacter::InputJump(const FInputActionValue& Value)
{
	Jump();
}

void AVRCharacter::InputFire(const FInputActionValue& Value)
{
}
void AVRCharacter::MoveRight(float Val)
{
	if (Val != 0.f)
	{
		if (Controller)
		{
			FRotator const ControlSpaceRot = Controller->GetControlRotation();

			// transform to world space and add it
			AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y), Val);
		}
	}
}

void AVRCharacter::MoveForward(float Val)
{
	if(GEngine)
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, FString::Printf(TEXT("%f"),Val));
	if (Val != 0.f)
	{
		if (Controller)
		{
			FRotator const ControlSpaceRot = Controller->GetControlRotation();

			// transform to world space and add it
			AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::X), Val);
		}
	}
}

void AVRCharacter::MoveUp_World(float Val)
{
	if (Val != 0.f)
	{
		AddMovementInput(FVector::UpVector, Val);
	}
}

void AVRCharacter::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
}

void AVRCharacter::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
}

VRCharacter.h

'강동새싹언리얼수업 > 언리얼' 카테고리의 다른 글

231218 Unreal VR Site  (1) 2023.12.18
C++ Oculus Touch(Motion Controller)를 포함한 Pawn 생성하기  (0) 2023.12.18
OPENXR  (0) 2023.12.15
231214 VR First  (0) 2023.12.14
프로그래밍패턴  (0) 2023.12.13