[SpriteKit] 고양이 퍼즐 게임 만들기 day.03

Emily·2025년 6월 6일
1

GridPopGame

목록 보기
3/8
post-thumbnail

귀여운 고양이 그리드를 만들었으니 이제는 고양이를 누르면 주변의 같은 고양이들과 함께 사라지도록 구현해볼 것이다.

01) touchesBegan : 터치한 지점 좌표 추출하기

SKSceneSKNode의 하위 클래스인 SKEffectNode의 하위 클래스라는 것을 day.01에서 언급했었다. 즉, SKNode > SKEffectNode > SKScene의 상속 관계가 성립된다.

여기서 끝이 아니다. SKNodeSKView의 하위 클래스고, SKViewUIView를 상속 받는다. 그리고, UIView의 상위 클래스는 UIResponder다.

UIResponder > UIView > SKView > SKNode > SKEffectNode > SKScene

이런 상속 구조가 되는 것이다. 이걸 언급하는 이유는 UIResponder의 메소드 중 하나인 touchesBeganGameScene에서 활용하기 때문이다.

https://developer.apple.com/documentation/uikit/uiresponder/touchesbegan(_:with:)

touchesBegan은 위에 쓰여있듯이 하나 이상의 터치 이벤트가 발생했을 경우 호출되는 메소드다. 사용자가 고양이를 눌렀을 때의 동작을 이 메소드 내에서 호출하면 된다.

class GameScene: SKScene {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 터치된 지점 가져오기 : UITouch
        // 고양이 게임에서는 터치 영역에 하나의 노드만 있기 때문에 첫번째 값 추출
        guard let touch = touches.first else { return }

        // 터치된 지점 좌표 가져오기 : CGPoint
        let location = touch.location(in: self)

        // ... //
    }
}

UITouch 클래스의 location(in: ) 메소드를 통해 터치된 지점의 CGPoint를 반환 받아 이 값을 활용할 것이다.

02) findItem : 좌표에 해당하는 노드 찾기

SKNode클래스의 nodes(at: ) 메소드는 CGPoint 타입의 매개변수를 받아 해당 좌표에 위치한 노드들([SKNode])을 반환한다.

func findItem(point: CGPoint) -> Item? {
	// point에 있는 노드(SKNode)를 Item으로 캐스팅하여 반환
	let item = nodes(at: point).compactMap { $0 as? Item }
    return item.first
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
	guard let touch = touches.first else { return }
    let location = touch.location(in: self)
    
    // 터치된 지점에 해당하는 Item 추출
    guard let tappedItem = findItem(point: location) else { return }
    
    // ... tappedItem이랑 똑같은 고양이 찾기 ... //
}

03) findMatch : tappedItem 인접 노드 탐색하기

눌린 고양이 주변 똑같은 고양이들을 찾는 과정을 생각해보자.

  1. 상하좌우 고양이가 똑같은 고양이인지 검사
  2. 똑같은 고양이가 있다면 그 고양이들의 상하좌우도 검사
  3. 반복

재귀 호출을 통해 반복하며, 이 과정에서 이미 검사한 고양이라면 다시 검사하지 않도록 해야 한다.

var matchedItems = Set<Item>()	// 똑같은 고양이로 확인 완료된 item 모아넣을 값

func findMatch(currentItem: Item) {
	// 확인 완료 집합에 현재 고양이 삽입
    matchedItems.insert(currentItem)

	// 검사 대상 고양이 담을 값
    var checkItems = [Item?]()
    
    // 좌표로 상하좌우 고양이 찾아서 checkItems에 추가
    let position = currentItem.position
    checkItems.append(findItem(point: CGPoint(x: position.x, y: position.y - itemSize)))
    checkItems.append(findItem(point: CGPoint(x: position.x, y: position.y + itemSize)))
    checkItems.append(findItem(point: CGPoint(x: position.x - itemSize, y: position.y)))
    checkItems.append(findItem(point: CGPoint(x: position.x + itemSize, y: position.y)))
    
    // checkItems에서 nil이 아닌 값들만 반복
    for case let check? in checkItems {
    	// 확인 완료된 고양이는 패스
        if matchedItems.contains(check) {
        	continue
        }
        
        // 똑같은 고양이 찾으면 그 고양이 인접 고양이 검사 시작(재귀 호출)
        if check.name == currentItem.name {
        	findMatch(currentItem: check)
        }
    }
}

04) removeMatches : 검사 통과한 고양이들 scene에서 없애기

SKNoderemoveFromParent() 메소드를 활용한다.

func removeMatches() {
	// 검사 결과 고양이 노드가 1개라면 없애지 않는다
    guard matchedItems.count > 1 else { return }
    
	for item in matchedItems {
        item.removeFromParent()
    }
}

touchesBegan 메소드에서 정의한 함수들을 호출해준다.

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
	guard let touch = touches.first else { return }
        
    let location = touch.location(in: self)
    guard let tappedItem = findItem(point: location) else { return }
    
    // 기존 검사 내용 삭제
    matchedItems.removeAll()
    // 똑같은 고양이 찾기
    findMatch(currentItem: tappedItem)
    // 찾은 고양이 노드들을 씬에서 제거
    removeMatches()
}

다음에는 없어진 고양이 수만큼 새로운 노드들을 생성하고, 위에서부터 아래로 빈자리를 채우는 것을 구현할 것이다.

profile
iOS Junior Developer

1개의 댓글

comment-user-thumbnail
2025년 6월 9일

한 세 수 배워갑니다

답글 달기