-
Camera Interp locationGame Programming/언리얼 2023. 10. 21. 21:56
아이템을 주울 때 최종 위치를 구현했다.
void AMyCharacter::SelectButtonPressed() { if (TraceHitItem) { auto TraceHitWeapon = Cast<AWeapon>(TraceHitItem); TraceHitWeapon->StartItemCurve(this); } }
아이템을 줍는 키를 누르면 StartItemCurve 함수를 호출한다.
void AWeapon::StartItemCurve(AMyCharacter* Char) { /* Store a handle to the Charater*/ Character = Char; ItemInterpStartLocation = GetActorLocation(); bInterping = true; SetItemState(EItemState::EIS_EquipInterping); GetWorldTimerManager().SetTimer( ItemInterpTimer, this, &AWeapon::FinishInterping, ZCurveTime); // Get initial Yaw of the Camera const double CameraRotationYaw{ Character->GetFollowCamera()->GetComponentRotation().Yaw }; // Get initial Yaw of the Item const double ItemRotationYaw{ GetActorRotation().Yaw }; // Initial Yaw offset between Camera and Item InterpInitialYawOffset = ItemRotationYaw - CameraRotationYaw; }
StartItemCurve 함수는 아이템의 위치와 회전 차이를 가져오고 타이머를 동작시킨다.
void AWeapon::ItemInterp(float DeltaTime) { if (!bInterping) return;
bInterping 변수가 false이면 tick에서 돌아가는 ItemInterp함수가 호출되지 않는다.
case EItemState::EIS_EquipInterping: PickupWidget->SetVisibility(false); ItemMesh->SetSimulatePhysics(false); ItemMesh->SetEnableGravity(false); ItemMesh->SetVisibility(true); ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); // Set AreaSphere properties AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision); // Set CollisionBox properties CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision); break;
중력의 영향을 받지 않도록 물리 상태를 설정했다.
// Get initial Yaw of the Camera const double CameraRotationYaw{ Character->GetFollowCamera()->GetComponentRotation().Yaw }; // Get initial Yaw of the Item const double ItemRotationYaw{ GetActorRotation().Yaw }; // Initial Yaw offset between Camera and Item InterpInitialYawOffset = ItemRotationYaw - CameraRotationYaw;
카메라 정면을 바라보도록 회전량을 보간하기 위해 회전차이를 구했다.
Tick에서 동작하는 ItemInterp함수
void AWeapon::ItemInterp(float DeltaTime) { if (!bInterping) return; if (Character && ItemZCurve) { ZCurve와 타이머를 동기화 시켜 Z값 이동이 Curve와 비례하도록한다. const float ElapsedTime = GetWorldTimerManager().GetTimerElapsed(ItemInterpTimer); const float CurveValue = ItemZCurve->GetFloatValue(ElapsedTime); 아이템 시작 위치 FVector ItemLocation = ItemInterpStartLocation; 케릭터의 손 위치 const FVector HandInterpLocation{ Character->GetHandInterpLocation() }; 아이템과 케릭터의 손 위치간의 편차 const FVector ItemToHand{ FVector(0.f, 0.f, (HandInterpLocation - ItemLocation).Z) }; 편차를 크기로 바꾼다. const float DeltaZ = ItemToHand.Size(); 현재 아이템의 위치 const FVector CurrentLocation{ GetActorLocation() }; 현재 위치와 손 위치의 X, Y 값을 보간한다. const float InterpXValue = FMath::FInterpTo( CurrentLocation.X, HandInterpLocation.X, DeltaTime, 1.f); const float InterpYValue = FMath::FInterpTo( CurrentLocation.Y, HandInterpLocation.Y, DeltaTime, 1.f); 변경 할 아이템 위치로 Set ItemLocation.X = InterpXValue; ItemLocation.Y = InterpYValue; if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("%f %f"), ItemLocation.X, ItemLocation.Y)); Curve값과 아이템과 손위치간의 편차를 곱해 Z값이 Curve의 값대로 상승, 하강 하도록한다. ItemLocation.Z += CurveValue * DeltaZ; 아이템의 위치를 세팅한다. SetActorLocation(ItemLocation, true, nullptr, ETeleportType::TeleportPhysics); 카메라의 Z축 회전량을 가져오고, 카메라의 정면을 바라 보도록 편차를 더 해준다. const FRotator CameraRotation{ Character->GetFollowCamera()->GetComponentRotation() }; FRotator ItemRotation{ 0.f, CameraRotation.Yaw + InterpInitialYawOffset, 0.f }; SetActorRotation(ItemRotation, ETeleportType::TeleportPhysics); 아이템 크기 커브 if (ItemScaleCurve) { 타이머와 커브를 동기화 시켜 스케일을 세팅한다. const float ScaleCurveValue = ItemScaleCurve->GetFloatValue(ElapsedTime); SetActorScale3D(FVector(ScaleCurveValue, ScaleCurveValue, ScaleCurveValue)); } } }
타이머 동작이 완료되면 호출되는 FinishInterping함수
void AWeapon::FinishInterping() { Tick에서 ItemInterp 함수가 호출되지 않도록한다. bInterping = false; 케릭터가 아이템을 집도록한다. if (Character) { Character->GetPickupItem(this); } 아이템 스케일을 1로 마춘다. SetActorScale3D(FVector(1.f)); }
아이템의 최종 위치는 아래와 같이 구현했다.
FVector AMyCharacter::GetCameraInterpLocation() { const FVector CameraWorldLocation{ FollowCamera->GetComponentLocation() }; const FVector CameraForward{ FollowCamera->GetForwardVector() }; // Desired = CameraWorldLocation + Forward * A + Up * B return CameraWorldLocation + CameraForward * CameraInterpDistance + FVector(0.f, 0.f, CameraInterpElevation); }
CameraWorldLocation + Forward * A + Up * B (A는 카메라의 정면으로부터 떨어진 거리, B는 위로부터 떨어진 거리)
FVector AMyCharacter::GetHandInterpLocation() { const USkeletalMeshSocket* HandSocket = GetMesh()->GetSocketByName(FName("hand_rSocket")); if (HandSocket) { FQuat fquad; FTransform transfrom = HandSocket->GetSocketTransform(GetMesh()); const FVector HandWorldLocation{ transfrom.GetLocation()}; const FVector HandForward{ transfrom.TransformRotation(fquad).Vector().ForwardVector}; const FVector HandUp{ transfrom.TransformRotation(fquad).Vector().UpVector }; // Desired = CameraWorldLocation + Forward * A + Up * B return HandWorldLocation + HandForward * HandInterpDistance + HandUp * FVector(0.f, 0.f, HandInterpElevation); } return FVector(); }
나는 손으로 아이템이 부착 되는 효과를 내려고 코드를 수정했다. 손의 행렬을 읽어와서 포어드, 업 벡터를 구했다.
Z Curve를 만들었다.
AddKey를 추가해 Z Cruve의 (t, y)위치를 수정 할 수 있다.
좌측 상단의 Zoom To Fit 기능을 사용해서 Key를 누른 후 버튼을 누르면 Key에 맞는 해상도로 변경이된다.
전체 Key를 드래그 후 Zoom To Fit 버튼을 누르면 전체 키에 맞는 해상도로 변경된다.
Key를 선택하면 기울기를 조절할 수 있는 막대선이 나오는데, Break를 선택하면 왼쪽과 오른쪽 막대를 각각 조절 할 수 있다.
'Game Programming > 언리얼' 카테고리의 다른 글
총알 클래스 제작 (0) 2023.10.22 Create Door Trigger (1) 2023.10.20 문 소켓 추가 (0) 2023.10.19 문 물리 작용 업데이트 (0) 2023.10.19 무기 교체 (0) 2023.10.18