PhysicsHandle로 객체 잡기

Jihyun·2023년 12월 20일
0

Unreal

목록 보기
9/11

PhysicsHandle

PhysicsHandle 컴포넌트 : 물리(Physics)가 적용된 컴포넌트를 다루기 위한 도구
물체를 잡으려면 물리 시스템과 상호작용을 해야하는데 PhysicsHandle 컴포넌트를 사용하여 물리가 적용된 오브젝트를 잡을 수 있다

C++ 컴포넌트에서 다른 컴포넌트 연결하기

PhysicsHandle은 Actor Component로 블루프린트에서 추가하면 하단에 들어간다

UPhysicsHandleComponent* PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();

FindComponentByClass<>() : 현재 객체가 가지고 있는 컴포넌트 중에서 <> 안에 들어가는 클래스에 해당하는 컴포넌트 포인터를 반환한다.

DrawDebugSphere()로 월드에 구체 그리기


void UGrabber::Grab()
{
	UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
	if(PhysicsHandle == nullptr)
	{
		return;
	}

	FVector Start = GetComponentLocation();
	FVector End = Start + GetForwardVector() * MaxGrabDistance;

	DrawDebugLine(GetWorld(), Start, End, FColor::Red);
	
	//DrawDbugLine으로 확인한 지점(End)에 구체를 그린다
	DrawDebugSphere(GetWorld(), End, 10, 10, FColor::Blue, false, 5);


	FCollisionShape Sphere = FCollisionShape::MakeSphere(GrabRadius);
	FHitResult HitResult;
	bool HasHit = GetWorld()->SweepSingleByChannel(HitResult, Start, End, FQuat::Identity, ECC_GameTraceChannel2, Sphere);

	if(HasHit)
	{
    	//트레이스의 형태(Sphere)가 물체와 충돌했을 때, 월드 공간에서 트레이스가 물체와 닿았을 때의 위치
		DrawDebugSphere(GetWorld(),HitResult.Location, 10, 10, FColor::Green, false, 5);
        
        //트레이스의 형태가 물체와 실체로 충돌한 지점
		DrawDebugSphere(GetWorld(),HitResult.ImpactPoint, 10, 10, FColor::Red, false, 5);	
	}
	
}

객체 잡기

  1. 잡을 객체를 지정한다
    GrabComponentAtLocationWithRotation : 오브젝트나 액터의 특정 위치와 회전값을 기반으로 컴포넌트를 선택하고 상호작용할 때 사용
if(HasHit)
{		
		PhysicsHandle->GrabComponentAtLocationWithRotation(
			HitResult.GetComponent(), //잡으려고하는 컴포넌트
			NAME_None,
			HitResult.ImpactPoint, //잡을 위치
			GetComponentRotation()//해당 컴포넌트의 현재 회전 상태 또는 해당 액터의 현재 회전 상태
		); 
}
  1. 잡은 객체를 놓을 위치를 지정한다
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
	if(PhysicsHandle == nullptr)
	{
		return;
	}

	//현재 위치를 가져와 전방으로 특정 거리만큼 바깥쪽으로 움직이도록한다
	FVector TargetLocation = GetComponentLocation() + GetForwardVector() * HoldDistance;
	PhysicsHandle->SetTargetLocationAndRotation(TargetLocation, GetComponentRotation());	
}

const 함수로 액터 포인터 얻어오기

const로 만들어 메서드를 호출할 때마다 값이 변경되지 않도록 한다

//.h
UPhysicsHandleComponent* GetPhysicsHandle() const;

//.cpp
UPhysicsHandleComponent* UGrabber::GetPhysicsHandle() const
{
	UPhysicsHandleComponent* Result = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();

	if(Result == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("Grabber requires a UPhysicsHandleComponent"));
	}

	return Result;
}

잡은 컴포넌트를 내려놓기

물리 시뮬레이션이 켜져있는 오브젝트는 사용하지 않으면 일정 시간 이후에 절전 모드로 전환된다. 그럴 때, UPrimitiveComponent::WakeAllRigidBodies()을 사용하여 PhysicsHandle에 반응하도록 컴포넌트를 깨울 수 있다.

if(HasHit)
	{
		//PhysicsHandle에 반응하도록 컴포넌트를 깨운다
		UPrimitiveComponent* HitComponent = HitResult.GetComponent();
		HitComponent->WakeAllRigidBodies();

		DrawDebugSphere(GetWorld(),HitResult.Location, 10, 10, FColor::Green, false, 5);        
		DrawDebugSphere(GetWorld(),HitResult.ImpactPoint, 10, 10, FColor::Red, false, 5);	

		PhysicsHandle->GrabComponentAtLocationWithRotation(			
			HitComponent, //잡으려고 하는 컴포넌트
			NAME_None,
			HitResult.ImpactPoint, //잡을 위치
			GetComponentRotation()//해당 컴포넌트의 현재 회전 상태 또는 해당 액터의 현재 회전 상태
		); 
	}

잡은 물체 내려 놓기

void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
	if(PhysicsHandle == nullptr)
	{
		return;
	}

	//잡은 물체가 있는 상태에서만 TargetLocation 설정
	if(PhysicsHandle->GetGrabbedComponent()!=nullptr)
	{
		//현재 위치를 가져와 전방으로 특정 거리만큼 바깥쪽으로 움직이도록한다
		FVector TargetLocation = GetComponentLocation() + GetForwardVector() * HoldDistance;
		PhysicsHandle->SetTargetLocationAndRotation(TargetLocation, GetComponentRotation());

	}
}


void UGrabber::Release()
{
	//PhysicsHandle에 접근
	UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
	if(PhysicsHandle == nullptr)
	{
		return;
	}

	//PhysicsHandle이 잡고있는 컴포넌트가 있는지 확인
	if(PhysicsHandle->GetGrabbedComponent() != nullptr)
	{
		//컴포넌트를 잡은 후에 일정 시간 동안 움직이지 않으면 다시 절전 모드가 되므로
		//리지드바디를 한 번 깨워주는 것이 좋다
		PhysicsHandle->GetGrabbedComponent()->WakeAllRigidBodies();
		PhysicsHandle->ReleaseComponent();
	}	

}

PhysicsHandle::GetGrabbedComponent() : 잡고있는 컴포넌트를 반환
PhysicsHandle::ReleaseComponent() : 잡고있던 컴포넌트를 놓는다

profile
잊어버려도 다시 리마인드 할 수 있도록 공부한 것을 기록합니다

0개의 댓글