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

Emily·2025년 6월 2일
0

GridPopGame

목록 보기
2/8
post-thumbnail

배경 이미지를 적용했다면 이제 귀여운 고양이 얼굴 이미지를 퍼즐로 넣어줄 차례다. 레이아웃을 동적으로 넣는 방법을 공부하면서 작업했다.

01) 커스텀 노드 클래스 만들기

고양이 얼굴 이미지가 그리드 형태로 여러개 들어가기 때문에, 커스텀 노드 클래스를 만들어 재활용하는 것이 좋아보인다. 배경 이미지 노드와 마찬가지로 SKSpriteNode로 만든다.

class Item: SKSpriteNode {
    var column: Int		// 열
    var row: Int		// 행
    
    // 생성자를 통해 고양이 이미지 이름과 행, 열을 주입한다.
    init(imageNamed: String, column: Int, row: Int) {
        self.column = column
        self.row = row
        // SKTexture를 통해 이미지를 렌더링한다.
        super.init(texture: SKTexture(imageNamed: imageNamed), color: .clear, size: .zero)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

코드를 보면 노드 생성 시 행, 열, 이미지 이름을 주입하도록 되어있다. size는 화면 크기에 따라 다르게 지정할 것이기 때문에 zero로 기본값을 해두었다.

02) 고양이 그리드 크기 정하기

day.01에서 GameScene 클래스를 만들고 그 안에 배경 이미지 노드를 생성해주었는데, 고양이 그리드도 그렇게 할 것이다. scene의 사이즈가 곧 화면 사이즈기 때문에 그 값을 활용한다.

내가 보면서 따라했다는 유튜브 영상에서는 itemSize를 정적인 값을 가진 저장 프로퍼티로 선언했지만, 나는 동적인 값을 위해 계산 프로퍼티를 활용했다.

class GameScene: SKScene {

	// 8x8개의 고양이 노드를 만들 것
	private let itemsPerColumn: Int = 8
    private let itemsPerRow: Int = 8
	
    // 고양이 노드 크기 계산 프로퍼티
	private var itemSize: CGFloat {
    	// 가로, 세로 화면 크기의 1/8 구하기
		let horizontalGridSize = size.width / CGFloat(itemsPerRow)
        let verticalGridSize = size.height / CGFloat(itemsPerColumn)
        
        // 둘 중 작은 값의 95%로 축소 >> 너무 꽉차면 안예쁘기 때문에 적당한 공백을 주기 위함
        return min(horizontalGridSize, verticalGridSize) * 0.95
    }
    
    // ... //
}

03) 고양이 이미지 노드 생성하기

UIKit을 공부할 때 레이아웃의 기본요소 2가지를 강조했던 게 생각난다. 바로 크기와 위치다. 크기는 위에서 지정해주었으니, 이제 위치를 어떻게 잡을지 생각해야 한다.

  1. 그리드 전체의 시작점 잡기 : 그리드 영역을 화면 가운데에 맞추기 위해서는 화면 전체 길이에서 노드들의 크기를 뺀 뒤 1/2을 하고(양옆 간격이 같아야 하니까) 아이템 하나의 절반 만큼을 더해줘야 한다. 이유는 day.01에서 언급했듯이 노드의 기본 앵커포인트가 스스로의 중심점이기 때문이다.
class GameScene: SKScene {
	// ... //
    
    // X축 시작점 (화면의 시작점인 leading과 첫 노드 간 간격)
    private var gridStartX: CGFloat {
    	// 그리드 영역 전체의 너비 : 노드 크기 * 노드 개수
        let gridWidth = itemSize * CGFloat(itemsPerRow)
        // 화면 너비에서 그리드 영역을 뺀 부분
        let spareWidth = size.width - gridWidth
        
        // 화면 전체에서 그리드 영역을 빼준 뒤 반을 나누고 노드의 절반 크기만큼 더해준다
        return (spareWidth / 2) + (itemSize / 2)
    }
    
    // Y축 시작점 (화면의 시작점인 bottom과 첫 노드 간 간격)
    private var gridStartY: CGFloat {
    	let gridHeight = itemSize * CGFloat(itemsPerColumn)
        let spareHeight = size.height - gridHeight
        return (spareHeight / 2) + (itemSize / 2)
    }
    
    // ... //
}

CGPoint가 (0, 0)일 때 노드 위치

CGPoint가 (gridStartX, gridStartY)일 때 노드 위치
  1. 노드의 위치 지정하기 : 노드의 행, 열에 따라 시작점으로부터의 거리를 계산해 CGPoint를 반환하는 메소드를 정의한다. (ex. 0행 0열이면 당연히 시작점과 일치하는 자리에 위치할 것이고, 1행 1열이면 시작점으로부터 노드 1개 크기만큼 x, y축을 이동하여 위치할 것이다)
class GameScene: SKScene {
	// ... //
    
    private func positionItem(for item: Item) -> CGPoint {
    	// 시작점 + 코드 크기 * 행 or 열
        let x: CGFloat = gridStartX + (itemSize * CGFloat(item.column))
        let y: CGFloat = gridStartY + (itemSize * CGFloat(item.row))
        
        return CGPoint(x: x, y: y)
    }
}
  1. 노드 생성하기 : 8x8 이중 반복문을 활용하여 노드를 생성한다.
class GameScene: SKScene {
	// ... //
    
    // 노드 생성 메소드
    private func createItem(column: Int, row: Int) -> Item {
    	let itemImages = ["calico", "cheese", "gray", "mackerel", "vanila"]
        
        // 0 ~ 4 사이의 랜덤 index로 1개의 이미지 이름 추출
        let itemImage = itemImages[GKRandomSource.sharedRandom().nextInt(upperBound: itemImages.count)]
        
        // 추출된 이미지 + 매개변수로 주입 받은 행, 열의 노드 생성
		let item = Item(imageNamed: itemImage, column: column, row: row)
        
        // position 및 size 지정해준 뒤 scene에 추가
        item.position = positionItem(for: item)
        item.size = CGSize(width: itemSize, height: itemSize)
        addChild(item)
        
        return item
    }
    
	// 열 x 행 노드(Item) 배열
    private var columns: [[Item]] = []
    
    // 반복문으로 노드 생성 메소드 호출
	private func createGrid() {
        for x in 0..<itemsPerRow {
            var column = [Item]()
            
            for y in 0..<itemsPerColumn {
                let item = createItem(column: x, row: y)
                column.append(item)
            }
            
            columns.append(column)
        }
    }
    
    override func didMove(to view: SKView) {
    	// ... //
        
        createGrid()
    }
}

기기에 따라 귀여운 고양이 그리드에 오토레이아웃이 잘 적용된 모습
profile
iOS Junior Developer

0개의 댓글