🧩 핵심 구성요소
이름 설명
BP_BlockStairTrigger 트리거 박스 액터. 플레이어가 밟으면 다리 생성 시작
BlockToSpawn 생성할 블록 액터. 큐브 형태의 블루프린트 사용
StartLocation 다리 생성 시작 위치 (트리거보다 앞쪽)
ForwardDir 다리 생성 방향 (플레이어 시선 방향 기준)
TimerHandle 하나씩 블록을 생성하는 시차를 컨트롤하는 타이머
다리 생성 시작
void ABlockStairTrigger::OnTriggerOverlap(...)
{
if (bHasBuiltBridge) return; // 이미 생성했으면 무시
bHasBuiltBridge = true;
ForwardDir = OtherActor->GetActorForwardVector().GetSafeNormal();
float ForwardStartOffset = 200.f;
StartLocation = GetActorLocation() + ForwardDir * ForwardStartOffset;
CurrentIndex = 0;
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
this,
&ABlockStairTrigger::SpawnBridgeStep,
StepDelay, true
);
}
💡 다리를트리거보다 200cm 앞쪽에서 생성 시작
💡bHasBuiltBridge플래그로 한 번만 작동하게 함
하나씩 블록 생성 (SpawnBridgeStep)
void ABlockStairTrigger::SpawnBridgeStep()
{
if (CurrentIndex >= NumSteps)
{
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
return;
}
FVector ForwardDir2D = FVector(ForwardDir.X, ForwardDir.Y, 0.f).GetSafeNormal();
FVector Right = FVector::CrossProduct(FVector::UpVector, ForwardDir2D);
float BlockZOffset = -45.f;
for (int32 x = -BridgeWidth / 2; x <= BridgeWidth / 2; ++x)
{
FVector RightOffset = Right * BlockSpacing * x;
FVector ForwardOffset = ForwardDir2D * BlockSpacing * CurrentIndex;
FVector SpawnLocation = StartLocation + ForwardOffset + RightOffset + FVector(0, 0, BlockZOffset);
AActor* Spawned = GetWorld()->SpawnActor<AActor>(BlockToSpawn, SpawnLocation, FRotator::ZeroRotator);
if (Spawned)
{
TArray<UStaticMeshComponent*> MeshComps;
Spawned->GetComponents<UStaticMeshComponent>(MeshComps);
for (auto Mesh : MeshComps)
{
Mesh->SetSimulatePhysics(false);
Mesh->SetEnableGravity(false);
Mesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
}
}
++CurrentIndex;
}
⏱ 블록이시간차(예: 0.2초)로 한 줄씩 생성됨
↔️BridgeWidth 만큼 양옆으로 넓게 생성
🧱 Z 보정값으로 바닥에 딱 붙게 배치
🧱 블록 배치 함수: OnLeftClick()
마우스 좌클릭 시, 미리보기 블록을 실제 블록으로 배치하는 함수입니다.
겹치는 경우엔 배치를 막고, 겹치지 않으면 선택된 블록을 생성합니다
void ALegoCharacter::OnLeftClick(const FInputActionValue& Value)
{
if (!PreviewBlock || !BlockClasses.IsValidIndex(SelectedBlockIndex)) return;
// 1. 미리보기 블록의 경계 정보 계산
FVector Origin, Extent;
PreviewBlock->GetActorBounds(false, Origin, Extent);
// 2. 충돌 무시 설정 (자기 자신 + 프리뷰 블록)
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(PreviewBlock);
TArray<UPrimitiveComponent*> PrimComponents;
PreviewBlock->GetComponents<UPrimitiveComponent>(PrimComponents);
for (UPrimitiveComponent* PrimComp : PrimComponents)
{
Params.AddIgnoredComponent(PrimComp);
}
// 3. 겹치는 물체가 있는지 검사
bool bOverlaps = GetWorld()->OverlapAnyTestByChannel(
Origin, FQuat::Identity, ECC_WorldStatic,
FCollisionShape::MakeBox(Extent), Params
);
if (bOverlaps)
{
// 겹치면 배치 취소
return;
}
// 4. 겹치지 않으면 블록 생성
FVector SpawnLocation = Origin - FVector(0, 0, Extent.Z);
FRotator SpawnRotation = PreviewBlock->GetActorRotation();
GetWorld()->SpawnActor<AActor>(BlockClasses[SelectedBlockIndex], SpawnLocation, SpawnRotation);
// 5. 프리뷰 블록 제거
PreviewBlock->Destroy();
PreviewBlock = nullptr;
}
📌 핵심 포인트
단계 설명
✅ GetActorBounds() 블록의 중심 위치, 크기 계산
✅ OverlapAnyTestByChannel() 충돌 검사 (겹치는 물체 확인)
✅ BlockClasses[SelectedBlockIndex] 선택된 블록 종류로 스폰
✅ PreviewBlock->Destroy() 배치 후 미리보기 제거