2 분 소요

Unreal/C++ 과제 6 - 회전 발판과 움직이는 장애물 퍼즐 스테이지

과제 소개 및 목표

언리얼 C++ 과 엔진의 기능을 이용하여 간단한 ‘퍼즐’ 스테이지를 만드는 과제

  • 회전/이동 플랫폼을 만들고 C++ 로직Tick 함수로 제어
  • 리플렉션 시스템을 통해 ‘변수’의 세부 사안을 에디터에서 수정할 수 있도록 노출하여
    효율적인 게임 플레이 튜닝 과정 경험

구현 기능


  • 이동(상하/좌우) , 회전, 일정 시간 후 Drop 되는 액터 구현
  • SpawnManager를 통하여 위 액터들을 World의 특정 지점들에 배치
    (SpawnActor 사용 + RandRange를 통해 세부 수치 조정)

  • UProperty 사용으로 BP에서 수치Static Component 등을 조정 가능

  • 개인 편의를 위한 구현
    • 레벨 다시 시작 (Restart)
    • 2중 점프(Dash)

클래스 설명과 기타 파일

  • 클래스
TaskPlayer 
- SetupPlayerInputComponent를 통해 
  Move, Look, Jump(+2단 점프), Sprint, Restart 에 대한 Input Bind & Function 구현
- SkeltalMesh, CameraComponent 등을 생성

TaskPlayerController
- InputAction을 매개변수로 가지고,
  Player에게 IMC를 연결

TaskGameMode
- DefaultPawn 및 PlayerController 세팅용 게임 모드

MoveActor
- 이동용 플랫폼
- 상하 / 좌우 선택하여 '지정된 속도 / 거리'를 통해 이동

RotateActor
- 회전용 플랫폼
- Tick을 이용하여 일정한 회전

DropActor
- 일정 시간후 떨어지는 플랫폼
- SetTimer 를 이용하여 지정된 시간 이후 Physics를 켜주어 땅에 떨어진다

SpawnManager
- BeginPlay 시 TArray<TSubclassOf<AActor>> SpawnClasses; 로 저장된 클래스들(위쪽 액터들)을
  일정 범위에 Spawn 시키는 클래스
  (for을 통해 일정 범위 반복)
  (SpawnActor를 통해 생성)
  (클래스를 랜덤한 선택)
- RandRange를 통해 해당 클래스 내부의 세부 데이터들을 지정

트러블 슈팅 - MoveActor가 ‘제자리’로 돌아오지 않는다?

일단 MoveActor를 처음 구현한 방식은 이렇다

void AMoveActor::BeginPlay()
{
	Super::BeginPlay();

	StartLocation = GetActorLocation();
	TargetLocation = GetActorLocation();

	if (false == bMoveY)
	{
		TargetLocation += GetActorForwardVector() * MoveDistance;
	}
	else
	{
		TargetLocation += GetActorUpVector() * MoveDistance;
	}
}

void AMoveActor::Tick(float deltaTime)
{
	Super::Tick(deltaTime);

	if (false == bMoveY)
	{
		AddActorWorldOffset(GetActorForwardVector() * MoveSpeed * deltaTime);
	}
	else
	{
		AddActorWorldOffset(GetActorUpVector() * MoveSpeed * deltaTime);
	}

	if (TargetLocation.Equals(GetActorLocation(),5.0))
	{
        MoveSpeed *= -1;
		Swap(StartLocation, TargetLocation);
	}
}

  • 개인적인 생각으로는 TargetLocation에 도달하게 되면 ‘멈출 것’이며
    MoveSpeed를 반대로 입력하면
    Swap을 통해 다시 StartLocation으로 이동할 것이라 생각하였다

결과??

이상하게도 자꾸 ‘예상 범위’를 지나치기에 디버그를 찍어 보았다
그리고 보니…

Image

actLoc가 TargetLocation을 ‘지나쳤다‘…?
(actLoc 는 GetActorLocation이다)

생각해보니
Equals는 IsNearlyZero 처럼
두 위치가 ‘유사한지’를 체크하는 함수이지
‘지나갔는지’를 검사하는 함수는 아니였다

코드 수정

void AMoveActor::Tick(float deltaTime)
{
	Super::Tick(deltaTime);

	FVector current = GetActorLocation();
	FVector targetDir = TargetLocation - current;

	SetActorLocation(current + targetDir.GetSafeNormal() * MoveSpeed * deltaTime);

	if (targetDir.Size() < 20.0)
	{
		SetActorLocation(TargetLocation);
		Swap(StartLocation, TargetLocation);
	}
}

일단 이동하는 ‘벡터’를 얻어 해당 방향으로 진행하는 편이 더 깔끔하겠다 싶었다
(물론 MoveSpeed *= -1 자체도 나쁘지는 않지만)

  • TargetLocation - GetActorLocation() 을 사용하면
    목표 위치를 바라보는 현재 액터’ 의 ‘방향’ 을 알 수 있으므로
    해당 방식을 사용하기로 결정
    (그리고 방향만 가져올거라 ‘Normalize’하였다)

  • 그리고 AddActorWorldOffset 에서 SetActorLocation으로 수정하였다
    AddActorWorldOffset는 상대적인 이동이지만
    개인적으로는 SetActorLocation이 더 익숙하기에 해당 방식으로 수정하였다
    (이 부분은 아마 별 차이가 없을 것이라 생각한다)

  • 그리고 그 벡터의 ‘길이’가 20.0 미만인 경우
    SetActorLocation을 통해 고정시킨후 Swap해 주었다
    (어찌보면 이 부분도 Equals의 ‘허용 범위’를 넓혔으면 가능했을지도?)

위와 같이 수정한 후
MoveActor가 정상적으로 목적지에 도달한 후
원래 위치로 돌아오는 반복적인 이동을 할 수 있게 되었다

후기

개인적으로 Restart나 2단 점프 등을 가볍게 구현해보았지만
뭔가 아쉽기는 하였다
하지만 당장 여기에 무언가를 추가하기 보다는
다음 과제와 공부 등에 더 시간을 투자하는 것이 괜찮지 않을까?
싶어 이상만 작성하려 한다

댓글남기기