오늘은 룬 시스템에서 복잡한 모양의 룬을 배치할 때 UX가 직관적이지 않은 문제를 해결했음
처음엔 단순히 마우스 좌표를 기준점으로 사용했는데, 사용자가 빈 공간 어디를 클릭하든 예상한 대로 배치되도록 로직을 수정했음
예를 들어 룬이 [0,0], [0,1] 모양일 때 사용자 경험에 문제가 있었음

기존 방식의 문제점
[0,0], [0,1] 모양의 룬이라면 세로로 2칸이 비어있으면 그 공간의 어디를 클릭하던 배치가 되는게 맞는데 현재 로직으로는 윗칸에 마우스가 올라올 때만 배치가 가능
사용자 입장에서는 룬이 차지할 빈 공간 어디든 클릭하면 같은 결과를 기대하는데, 실제로는 클릭 위치에 따라 배치 결과가 달라져서 혼란스러운 UX를 만들고 있었음
기존 배치 로직
// 클릭한 위치를 그대로 룬의 기준점으로 사용
FIntPoint CellPos = TargetCell->GetCellPos();
bool bPlaceSuccess = BoardManager->PlaceRune(SelectedRuneID, CellPos);

핵심 아이디어
클릭한 셀을 포함할 수 있는 모든 가능한 배치 위치를 계산하고, 그 중 가장 직관적인 위치를 자동 선택
우선순위 규칙
배치가능 검증 로직
// 룬의 각 셀에 대해 클릭한 셀이 해당 위치에 올 수 있는지 확인
for (const FIntPoint& ShapeOffset : RuneShape)
{
// 클릭한 셀이 ShapeOffset 위치에 올 때의 기준점 계산
FIntPoint CandidatePosition = ClickedCell - ShapeOffset;
// 배치 가능한지 확인
if (PreviewRunePlacement(RuneID, CandidatePosition, AffectedCells))
{
ValidPositions.Add(CandidatePosition);
}
}
우선순위 선택 로직
// 첫 번째 후보를 초기값으로 설정
OutBestPosition = ValidPositions[0];
FIntPoint BestShapeOffset = ClickedCell - ValidPositions[0];
for (int32 i = 1; i < ValidPositions.Num(); ++i) // 인덱스 1부터 시작
{
FIntPoint Position = ValidPositions[i];
// 이 배치에서 클릭한 셀이 룬의 어느 부분에 해당하는지 계산
FIntPoint ShapeOffset = ClickedCell - Position;
// 룬의 좌상단([0,0])에 가까운 순서로 우선순위 결정
bool bIsBetter = false;
if (ShapeOffset.Y < BestShapeOffset.Y)
{
bIsBetter = true; // 룬의 더 위쪽 행
}
else if (ShapeOffset.Y == BestShapeOffset.Y && ShapeOffset.X < BestShapeOffset.X)
{
bIsBetter = true; // 같은 행에서 더 왼쪽 열
}
if (bIsBetter)
{
OutBestPosition = Position;
BestShapeOffset = ShapeOffset;
}
}
구현 후 새로운 UX 이슈 발견
문제: 배치 불가능한 위치에서는 아무 프리뷰도 표시되지 않아 사용자가 혼란스러워함
해결: 실패 시에도 시각적 피드백 제공
if (BoardManager->FindOptimalPlacementPosition(RuneID, GridPos, OptimalPosition))
{
// 배치 가능 → 초록색 프리뷰
}
else
{
// 배치 불가능 → 클릭 위치 기준으로 빨간색 프리뷰
BoardManager->PreviewRunePlacement(RuneID, GridPos, AffectedCells);
// 항상 Invalid (빨간색)로 표시
}

Before & After
핵심 깨달음