UI-Kitkat - 선택지(이진트리)와 MVC 연습

·2025년 3월 11일
0

Ui-KitKat Study

목록 보기
1/1
post-thumbnail

이진 트리를 활용한 선택지 시스템

이 게임에서 스토리는 트리 구조를 따른다.
즉, N 번째 스토리에서 플레이어가 선택을 하면 다음 스토리는 아래 규칙에 따라 결정된다.

이게 2차원 배열로해서 처리해주기에는 매 row마다 길이도 달라지고 번거로워질 것 같아서, 한 번 쭉 1차원 배열로 한다는 가정하에 노드에 인덱스를 매겨봤다.

tree

그랬더니 다음과 같이

1번 -> 다음 스토리단계(N) = `N -> 2N + 1`  
2번 -> 다음 스토리단계(N) = `N -> 2N + 2`  

1번과 2번을 어떻게 인지하냐!?


이렇게 View속성에서 tag를 달아줬다. 각각 1과 2를 달았으니 그냥
2 * step + choice 를 하면 되는 것이다. choiceuibtn.tag로 가져왔다.

mutating func nextStep(_ choice: Int) {
    step = 2 * step + choice  // 선택지(태그값 1 또는 2) - 다음 스토리로 이동
}

처음으로 돌아가기

📌 다시 시작 버튼 (IBAction)

@IBAction func restartGame(_ sender: UIButton) {
    StoryBrain.shared.step = 0  // 처음 스토리로 되돌리기
    updateUI() // UI 업데이트
}

StoryBrain 구조

MVC패턴 강의 챌린지의 일환이라 이를 StoryBrain이라는 Model에서 관리한다.
스토리 데이터는 Story 구조체를 배열로 저장하며, 현재 진행 중인 스토리(step)를 기준으로 선택지에 따라 이동해준다 !

StoryBrain.swift

struct StoryBrain {
    let StoryArr: [Story] = [
        Story(
            title: """
            당신은 고대 신전 앞에 서 있습니다.
            신전 안으로 들어갈까요, 아니면 주변을 먼저 살펴볼까요?
            """,
            choice: ["신전 안으로 들어간다.", "주변을 살펴본다."]
        ),
        Story(
            title: """
            신전 안으로 들어오니, 왼쪽 복도와 오른쪽 복도로 나뉘어 있습니다.
            왼쪽 복도는 어둡고 축축하며, 오른쪽 복도는 희미한 빛이 새어 나옵니다.
            어느 쪽으로 갈까요?
            """,
            choice: ["왼쪽 복도로 간다.", "오른쪽 복도로 간다."]
        ),
        ...
    ]
    
    var step = 0  // 현재 스토리 위치

    func getStoryText() -> String {
        return StoryArr[step].title
    }

    func getStoryChoices() -> [String] {
        return StoryArr[step].choice
    }

    mutating func nextStep(_ choice: Int) {
        if 2 * step + choice < StoryArr.count {
            step = 2 * step + choice  // 다음 스토리로 이동
        } else {
            handleEnding(choice)  // 엔딩 처리
        }
    }

    private mutating func handleEnding(_ choice: Int) {
        if choice == 1 {
            step = 0  
        } else {
            exitApp()  
        }
    }

    private func exitApp() {
        UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
    }
}
  • handleEnding(_:) 함수 추가 → 엔딩 시 처리 로직을 분리해 가독성을 높임.
  • exitApp() 함수 추가 → 앱 종료 기능을 별도로 관리할 수 있도록 분리.

앱 종료 기능 (주의 사항)

일반적으로 iOS에서는 exit(0)을 사용하여 앱을 강제 종료하는 것을 권장하지 않는다고 해서 .
how to exit ios app properly

그래서 이렇게 좀 알아본결과 홈 화면으로 튕기게? 할 수 있는 방식을 추천받았다.

나름의 ? 앱 종료 처리 (UIApplication.shared.perform)

private func exitApp() {
    UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
}

근데 여론을 보니 이것도 그렇게 칭찬받는 방법은 아니고,
"종료" 팝업을 띄워서 사용자가 직접 나가도록 유도하는 것이 UX적으로도 더 타당하다고 한다 !


공부한 것

  • 이진 트리 구조를 활용한 선택지 시스템 (2N + 1, 2N + 2 공식 적용)
  • MVC 패턴 활용해보기
  • UIKit 기반 UI 처리 (isHidden 활용하여 버튼 가시성 조절)
  • 앱 종료 기능 경험 (UIApplication.shared.perform)
profile
기억보단 기록을

0개의 댓글