Unreal - 상호작용(승강기)

2025. 11. 12. 07:17·Unreal 프로젝트 다이어리/두번째 프로젝트

이 글은 전 글과 이어집니다

2025.11.09 - [Unreal5 프로젝트 다이어리2] - Unreal - 상호작용(Edge Traversal)

 

Unreal - 상호작용(Edge Traversal)

플레이어가 원래는 갈수없는 길을 벽을기대고 아슬아슬하게 넘어가는Edge Traversal을 구현하였습니다 구현방식은 이와같습니다1. 캐릭터한테 커스텀 컴포넌트인 InteractionComponent를 달아주기2.. Coll

lucodev.tistory.com

 

승강기를 만들어보았습니다

승강기의 작동원리는 이와 같습니다

1. 승강기는 스플라인을 따라 이동하며 스플라인의 0번 (제일위 시작지점) 부터 .last (제일밑 끝지점) 까지 이동합니다

2. 작동은 레버를 당겨서 작동합니다

3. 엘레베이터의 움직임은 처음에 점점 빨라졌다가 도착할떄 점점 느려지는 InterpEaseInOut 움직임을 가집니다

 

제작과정은 이렇습니다

1. Interaction Collision으로 현재 인터렉션 타입이 엘레베이터인것을 구분합니다

2. 레버 를 만들고 레버의 애니메이션을 재생하는 함수를 만듭니다

3.엘레베이터 액터를 만들고 spline을 따라 위 아래 로 이동하는 로직을 구현합니다

4.노티파이로 엘레베이터 작동시점을 제어합니다

5.모션워핑을 사용하여 레버 작동 위치를 계산하고 해당 위치로 이동합니다

6. 디테일 (투명벽 + 양쪽기어) 제작

 

1. Interaction Collision으로 인터렉션 타입 구분

엘레베이터가 여러개가 있을수도있으니 현재 엘레베이터가 내가 지금 닿은 콜리전의 엘레베이터인것을

참조연결하여 해당 작동 엘레베이터를 알아옵니다

 

public:
	UPROPERTY()
	class AMovingElevator* movingElevator;

 

InteractionCollision내에 맵에 바인드된 함수 즉 오버랩되었을때 실행되는함수와 엔드오버랩되었을때 실행되는 함수에서

현재 Collision이 먼지 구분하고 상호작용 객체로 등록합니다

void AAInteractionCollision::DoActiveElevator()
{
	if (mainChar && mainChar->interactionComp)
		mainChar->interactionComp->currentCollision = this;
}

void AAInteractionCollision::DeActiveElevator()
{
	if (mainChar && mainChar->interactionComp)
		mainChar->interactionComp->currentCollision = nullptr;
}

 

 

2. 레버 제작

UPROPERTY(EditAnywhere)
TArray<UAnimMontage*> leverAnims;

void PlayAnimationIdx(int32 idx);
void AInteractionLever::PlayAnimationIdx(int32 idx)
{
	CustomTimeDilation = 1.f;
	if (!leverMesh || leverAnims.Num() == 0)
		return;
	UAnimMontage* selectMontage = leverAnims[idx];
	UAnimInstance* animInst = leverMesh->GetAnimInstance();
	if (animInst)
		animInst->Montage_Play(selectMontage, 1.f);


}

동적배열로 몽타쥬를 실행하며 0번은 Push 1번은 Pull로 한다고 약속해주었습니다

 

레버는 CustomTimeDelation을 사용하여 작동후 작동했을때의 위치를 고정해주었습니다

void ULeverAnimInstance::AnimNotify_EndInteraction()
{
	if (owner)
		owner->CustomTimeDilation = 0.f;
}

void ULeverAnimInstance::AnimNotify_StartInteraction()
{
	if (owner)
		owner->CustomTimeDilation = 1.f;
}

3. 엘레베이터 움직이는 로직을 만들고 위->아래 / 아래 -> 위로 가는 엘레베이터 활성화 함수 생성

bElevateActive bool 변수가 활성화되면 Spline을 따라 PlatformMesh 가 위->아래 or 아래->위 로 가는 함수입니다

움직임은 InterpEaseInOut을 사용하여 Spline의 위치를 따라 처음에는 느렸다가 점점 빨라지며

도착할떄 점점 느려지게 설계하였습니다

void AMovingElevator::MovingPlatform(float deltaTime)
{
	if (!pathSpline || !platFormMesh || !bElevateActive)
		return;

	splineLength = pathSpline->GetSplineLength();
	accelElapsed += deltaTime;

	float alpha = FMath::Clamp(accelElapsed / (accelTime * maxSpeedRatio), 0.f, 1.f);
	float easedAlpha = FMath::InterpEaseInOut(0.f, 1.f, alpha, 3.f);

	// directionMap: None=0, Down=-1, Up=1
	TArray<int32> directionMap = { 0, -1, 1 };
	int32 moveDir = directionMap[static_cast<int32>(elevatorState)];

	if (moveDir == -1) 
		splineDis = easedAlpha * splineLength;
	else if (moveDir == 1) 
		splineDis = (1.f - easedAlpha) * splineLength;
	//////////////////////////////////////////////
	splineDis = FMath::Clamp(splineDis, 0.f, splineLength);
	if ((moveDir == -1 && splineDis >= splineLength) || (moveDir == 1 && splineDis <= 0.f))
	{
		if (moveDir == -1) splineDis = splineLength;
		else splineDis = 0.f;
		elevatorState = EElavatorState::None;
		accelElapsed = 0.f;
		bElevateActive = false;
	}
	platFormMesh->SetWorldLocation(pathSpline->GetLocationAtDistanceAlongSpline(splineDis, ESplineCoordinateSpace::World));
	UpdateInvisibleCollision();
	
}

 

함수를 호출할때 아래->위 / 위->아래 인것을 알려주고 state을 정의해주는 함수입니다

void AMovingElevator::ActivateElavator()
{
	if (elevatorState != EElavatorState::None)
		return; 
	AInteractionLever* lever = GetLeverActor();
	if (!lever)
		return;
	//아래인경우 위로
	if (splineDis <= 0.f)
	{
		lever->PlayAnimationIdx(0);
		elevatorState = EElavatorState::Down;
	}
	//위에있을때 아래
	else if (splineDis >= pathSpline->GetSplineLength())
	{
		lever->PlayAnimationIdx(1);
		elevatorState = EElavatorState::Up;
	}
	accelElapsed = 0.f;
	
}

 

 

3-2 TrypInteraction함수(E 상호작용키) 에서 조건체크하는 함수와 실행함수를 생성

조건
bool UUInteractionComponent::CanActiveElavator()
{
	//충돌x 타입이 맞지않으면 작동x
	if (!currentCollision || currentCollision->interactionType != EInteractionType::Elevator)
		return false;

	movingElevator = currentCollision->linkElevator;
	if (!movingElevator)
		return false;

	if (movingElevator->bElevateActive)
		return false;

	//움직이지않으면 true
	return movingElevator->elevatorState == EElavatorState::None;
}
실행
void UUInteractionComponent::DoActiveElavator()
{
	currentInteractionState = EInteractionState::Elavator;
	const FInteractionDataRow* row = GetInteractionData("Elevator");
	if (!row)
		return;
	if (movingElevator)
		movingElevator->ActivateElavator();
	UAnimMontage* playMontage = nullptr;
	bool bPushLever;
	switch (movingElevator->elevatorState)
	{
	case EElavatorState::Down:  // 위 -> 아래
		bPushLever = true;
		playMontage = row->startPlayMontage;
		break;
	case EElavatorState::Up:    // 아래 -> 위
		bPushLever = false;
		playMontage = row->endPlayMontage;
		break;
	default:
		break;
	}

	if (playMontage)
	{
		MotionWarpElevator(bPushLever);
		ownerCharacter->PlayAnimMontage(playMontage, row->playRatio);
	}
}

 

4. 노티파이에서 변수를 호출하여 실행

void UUInteractionComponent::NotifyForElevator()
{
	if (!movingElevator)
		return;
	movingElevator->bElevateActive = true;
	mainChar->LockAction("Jump", false);
	mainChar->LockAction("Draw", false);
	mainChar->LockAction("Attack", false);
}
void UMainCharacterAnimInstance::AnimNotify_ElevatorMovingStart()
{
	if (!interactionComp || !mainCharacter)
		return;
	mainCharacter->interactionComp->NotifyForElevator();
}

 

5. 모션워핑을 사용하여 위치를 계산하고 자연스럽게 회전, 이동

위치는 레버를 작동하여 레버와 캐릭터의 위치를 맞추기위해 엘레베이터에 pushMarkerPoint / pullMarkerPoint로 

SceneComponent를 달아주었습니다

void UUInteractionComponent::MotionWarpElevator(bool bPush)
{
	if (!ownerCharacter || !currentCollision)
		return;

	FVector targetForward = currentCollision->GetInteractionVector();
	targetForward.Z = 0.f;
	targetForward.Normalize();
	FRotator targetRot = targetForward.Rotation();
	FVector targetLoc;
	if (bPush)
		targetLoc = movingElevator->pushMarkerPointComp->GetComponentLocation();
	else
		targetLoc = movingElevator->pullMarkerPointComp->GetComponentLocation();
	

	motionWarpComp->AddOrUpdateWarpTargetFromLocationAndRotation(
		FName("MarkerPoint"),
		targetLoc, targetRot
	);

}

 

6. 도착후 투명벽 + 양쪽기어 처리

양쪽에 기어 바퀴를 달아서 Roll로 회전시켰으며 투명벽을 만들어 도착했을때만 NoCollision 아닐시 전부 Block시켜주었습니다

void AMovingElevator::UpdateInvisibleCollision()
{
	if (!invisibleWall)
		return;
	const float floorTHR = 1.0f; //부동소수점 오차 임계값 설정
	if (splineDis >= splineLength - floorTHR)
		invisibleWall->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	else
		invisibleWall->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}


캐릭터가 z축으로 너무 빨리 이동하면 카메라붐이 같이 못따라와서 시점이 어색해줄수있으니

카메라붐의 maxdistance값을 제한해줍니다

결과물

 

모션워핑을 사용하여 플레이어가 정확하게 레버를 당기게 연출하였습니다

위 -> 아래

 

아래 -> 위

 

 

 

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - StatComponent  (0) 2025.11.21
Unreal - 상호작용(사다리 타기)  (0) 2025.11.15
Unreal - 상호작용(Edge Traversal)  (0) 2025.11.09
Unreal - 파쿠르 벽차기  (0) 2025.11.03
Unreal - 파쿠르 볼트(Vault)  (0) 2025.11.02
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - StatComponent
  • Unreal - 상호작용(사다리 타기)
  • Unreal - 상호작용(Edge Traversal)
  • Unreal - 파쿠르 벽차기
lucodev
lucodev
커피와 노트북 그리고 개발
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (211) N
      • Unreal 프로젝트 다이어리 (108) N
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (35) N
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (4)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    언리얼 시퀀스
    언리얼 ui
    unreal 시퀀스
    언리얼 프로그래스바
    언리얼 parkour
    unreal 파쿠르
    언리얼 상호작용
    언리얼 비헤이비어트리
    unreal inventory
    unreal 인벤토리
    언리얼 motionmatching
    언리얼 behavior tree
    unreal 모션매칭
    언리얼 모션매칭
    Unreal Parkour
    언리얼 behaviortree
    언리얼 인벤토리
    언리얼 파쿠르
    언리얼
    언리얼 컷씬
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - 상호작용(승강기)
상단으로

티스토리툴바