PhysicsHandle 컴포넌트
: 물리(Physics)가 적용된 컴포넌트를 다루기 위한 도구
물체를 잡으려면 물리 시스템과 상호작용을 해야하는데 PhysicsHandle 컴포넌트를 사용하여 물리가 적용된 오브젝트를 잡을 수 있다
PhysicsHandle은 Actor Component로 블루프린트에서 추가하면 하단에 들어간다
UPhysicsHandleComponent* PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
FindComponentByClass<>()
: 현재 객체가 가지고 있는 컴포넌트 중에서 <> 안에 들어가는 클래스에 해당하는 컴포넌트 포인터를 반환한다.
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);
}
}
GrabComponentAtLocationWithRotation
: 오브젝트나 액터의 특정 위치와 회전값을 기반으로 컴포넌트를 선택하고 상호작용할 때 사용if(HasHit)
{
PhysicsHandle->GrabComponentAtLocationWithRotation(
HitResult.GetComponent(), //잡으려고하는 컴포넌트
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;
}
//현재 위치를 가져와 전방으로 특정 거리만큼 바깥쪽으로 움직이도록한다
FVector TargetLocation = GetComponentLocation() + GetForwardVector() * HoldDistance;
PhysicsHandle->SetTargetLocationAndRotation(TargetLocation, GetComponentRotation());
}
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()
: 잡고있던 컴포넌트를 놓는다