Class GameDev* SheepAdult

[Unreal Engine 4.27] Climbing Pole/Rope System C++ 본문

Unreal Project

[Unreal Engine 4.27] Climbing Pole/Rope System C++

SheepAdult 2022. 5. 4. 01:48

결과물 Youtube Link :

https://www.youtube.com/watch?v=KpPfvmAMPW8 

 

Swing이 되지 않는 위아래로 움직이며 높이가 다른 플랫폼으로 이동하기 위한 Pole을 구현했다.

 본 프로젝트에서는 1인칭과 사이드 뷰를 전환해가며 플레이하는 게임인데 해당 기능은 사이드 뷰 모드에서만 동작하게 할 것이다. 먼저 Pole 모드 가능 범위 내에서 왼쪽 마우스 버튼 홀딩 시엔 계속 매달려있는 상태여야 하고, W, S 키로 올라가고 내려가는 기능이 있다. 그리고 Pole에서 점프 시, 폴과 반대 방향으로 점프할 수 있다. 폴 방향으로 점프할 순 있으나 폴에 막힐 것이다.

 

 먼저 다른 오브젝트들과 비슷하게 왼쪽 마우스 버튼을 누르면 스피어 트레이스를 메인 캐릭터 캡슐콜라이더 앞에 조사하고 조사 결과 반환 오브젝트가 Pole이면 매달리게 해 줬다. 트레이스 채널에 Pole을 추가했으며, 디폴트 값은 Ignore, Pole기능을 추가하고 싶은 경우만 Block에 체크해 줬다.

 그리고 폴인지 확인하고 매달리는 코드는 아래와 같다.

// 해당 함수는 Tick에서 호출된다.
void AMainCharacter::PressLMBForClimbingPole()
{
	if (!bIsFP)	// 사이드 뷰 일 때만
	{
		FVector StartLoc = TriggerCapsuleComponent->GetRelativeLocation() + TriggerCapsuleComponent->GetForwardVector() * 40.0f + FVector(0.0f, 0.0f, -30.0f);
		FVector EndLoc = StartLoc;
		TArray<AActor*> ToIgnore;
		FHitResult OutHit;
		bool bIsPole = UKismetSystemLibrary::SphereTraceSingle(
			GetWorld(),
			StartLoc,
			EndLoc,
			40.0f,
			ETraceTypeQuery::TraceTypeQuery7,	// 새로 추가한 Pole Trace 채널이다.
			false,
			ToIgnore,
			EDrawDebugTrace::ForOneFrame,
			OutHit,
			true);
		if (bIsPole)
		{
			bIsOnPole = true;
			CharacterMovementComponent->SetMovementMode(EMovementMode::MOVE_Flying);
			_Pole = Cast<AMaster_Pole>(OutHit.Actor);
			_Pole->StaticMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			CharacterMovementComponent->StopMovementImmediately();

			FVector HangingLoc = FVector(OutHit.Actor->GetActorLocation().X, OutHit.Actor->GetActorLocation().Y, OutHit.Location.Z) + GetActorForwardVector() * -15 + FVector(0.0f, 0.0f, 60.0f);
			FRotator HangingRot = GetActorRotation();
			FLatentActionInfo Info;
			Info.CallbackTarget = this;
			UKismetSystemLibrary::MoveComponentTo(
				TriggerCapsuleComponent,
				HangingLoc,
				HangingRot,
				true,
				true,
				0.2f,
				false,
				EMoveComponentAction::Type::Move,
				Info);
		}
	}
}

 캡슐 트레이스를 조사한 뒤 Pole이 Block되면 bIsOnPole로 Pole상태인지를 나타내어 주고, MovementMode를 Flying으로 바꿔 Z 축으로 움직일 수 있게 해 준다. 그리고 메인 캐릭터의 캡슐과 Pole과의 충돌이 나면 자연스럽지 못하므로 폴의 콜리전을 NoCollision으로 바꿨다. 그대로 두면 Flying 상태로 계속 캐릭터가 움직이므로 StopMovementImmediately()로 캐릭터를 멈추어 주면 캐릭터는 폴에 붙어있는 모습일 것이다. 하지만 위치가 부자연스러우므로 MoveComponentTo로 폴의 위치에 맞게 조절했다.

 그리고 아래는 왼쪽 마우스버튼을 놓았을 때 코드이다.

void AMainCharacter::ReleaseLMBClimbingPole()
{
	if (bIsOnPole)
	{
		if (bIsFalling)
		{
			CharacterMovementComponent->SetMovementMode(EMovementMode::MOVE_Falling);
		}
		else
		{
			CharacterMovementComponent->SetMovementMode(EMovementMode::MOVE_Walking);
		}
		bIsOnPole = false;
		_Pole->StaticMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
		_Pole = nullptr;

		FVector HangingLoc = GetActorLocation();
		FRotator HangingRot = FRotator(GetActorRotation().Roll, GetActorRotation().Yaw, GetActorRotation().Pitch);
		FLatentActionInfo Info;
		Info.CallbackTarget = this;
		UKismetSystemLibrary::MoveComponentTo(
			TriggerCapsuleComponent,
			HangingLoc,
			HangingRot,
			true,
			true,
			0.2f,
			false,
			EMoveComponentAction::Type::Move,
			Info);
	}
}

일단 Flying 상태이므로 놓쳤을 때 적절한 모드로 바꿔줘야 한다. 공중에서 놓쳤을 때는 Falling으로 바꿔주고 바닥에서 놓쳤을 때는 Walking 상태로 바꿔줘 바닥에서 날아다니거나 공중에서 걸어 다니는 일은 없도록 했다. 그리고 MoveComponentTo는 일정 시간이 지나야 완료되는 함수이므로 설정해 둔 시간 안에 다른 키 입력이 있을 시 입력받은 Relative 한 트랜스폼 값을 World상의 트랜스 폼 값으로 설정해 버려 벌리 이동해버리는 문제가 있을 수 있다. 그러므로 왼쪽 마우스 버튼을 놓쳤을 때도 MoveComponentTo를 해주어 그런 문제를 상쇄시켜준다.

 마지막으로 bIsOnPole인 상태에서 W, S축을 입력했을 때 아래와 같이 작성해주면 된다.

void AMainCharacter::MoveForward(float AxisValue)
{
	if (bIsControllable && Controller && !bIsOnLedge)
	{
	.
              .
              .
		else if (bIsOnPole)
		{
			TriggerCapsuleComponent->AddWorldOffset(FVector(0.0f, 0.0f, AxisValue / 2), true);
		}
               .
               .
               .
}

 

 이제 애니메이션이다. 먼저 애니메이션은 Mixamo의 Climbing Ladder의 본 키값을 변경해 Pole을 오르는 것처럼 만들어 줬다. 애니메이션 블루프린트는 아래와 같다.

Pole state로 가는 화살표엔 위의 bIsOnPole을 AnimInstance로 불러와 넘겨준 값을 사용한다. 아래는 Pole state이다.

위 / 아래 방향만 존재하므로 1D BlendSpace를 사용했다. OnPoleVelocity는 사실 속력은 아니고 메인 캐릭터의 XAxisValue값이다.

저 AxisValue를 사용해 올라가는 모션과 이를 Reverse하여 내려가는 모션을 만들어 사용했다.

 

- 구현기간: 1일

큰 어려움 없이 구현했다. 물론 엄청난 퀄리티와 완벽한 그림은 아니지만 게임에 적용시키거나 이질감 없이 플레이할 정도는 되므로 큰 문제는 없었다. 추후에 데모 버전이 만들어지고 다듬을 필요성이 생기면 다듬어야겠다.