SpriteKit, 제자리에서 움직이는 이미지 만들기
움직이는 이미지 만들기
준비물
Xcode, 움직임을 줄 이미지 묶음, 인터넷
과정
- 뷰와 장면 생성
- 장면 구성하기
- 텍스처 만들기
- 노드 만들기
- 노드에 움직임 주기
- 감상하기
뷰, 장면 생성
프로젝트 생성 & 파일 정리
Xcode를 열어 Game용 프로젝트 하나를 생성한다.
Game으로 프로젝트를 생성하면 자동으로 코드들이 생성되어 있다. 우선 .sks 확장자를 가진 파일들을 삭제해준다. .sks는 장면 에디터라고 생각하면 될 것 같다. 나는 그냥 코드로 장면을 만들어줄 것이기 때문에 삭제 해 주었다.
뷰와 장면을 생성하려면 Controller로 가야 한다. 기본적으로 생성된 GameViewController.swift의 viewDidLoad()함수에 super.viewDidLoad()만 제외하고 모두 삭제 해 주었다.
뷰, 장면 생성 코드 추가
깔–끔한 컨트롤러에 뷰와 씬을 넣어준다.
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// 움직이는 이미지가 만들어질 장면
let scene = GameScene()
// 장면과 뷰 설정
scene.scaleMode = .resizeFill
view.ignoreSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
// 뷰에 장면 띄우기
view.presentScene(scene)
}
}
view의 속성으로 세 불리언 값을 주었다. 직관적으로 이름을 따라간다고 생각하면 된다.
ignoreSiblingOrder는 부모-자식 혹은 형제 관계가 노드들이 렌더링될 때 그 순서에 영향을 미치는지 아닌지에 대한 설정값이다.showsFPS는 Frame Per Second를 보여주는 것으로, 1초에 화면이 얼마나 그려지는지에 대한 단위를 화면에 표시할지 아닌지에 대한 것이다.showsNodeCount역시 노드의 개수를 보여줄지 말지를 결정하는 부분이다.
scene의 속성으로 scaleMode를 주었는데, 장면의 크기를 결정하는 부분이다. resizeFill을 하면 뷰에 딱 맞게 장면이 재조정된다.
장면 구성하기
코드 정리하기
뭐가 많이 쓰여있을텐데, 아무고토 필요 없다. didMove 틀만 남기고 다 지워준다.
텍스처 만들기
atlas 폴더
준비해 놓은 이미지 묶음을 프로젝트파일에 끌어다 놓는다. 그냥 놓으면 안되고, 이미지들을 .atlas로 끝나는 폴더에 묶고 그걸 넣어준다. 먼말이고?
![]()
위와 같이 해 주고, 해당 이미지를 사용해준다.
아래에서 사용할 SKTextureAtlas클래스는 .atlas폴더를 알아서 찾아 만들어주기 때문에, 폴더 이름에 .atlas를 붙여주었다.
SKTextureAtlas
불러온 이미지를 텍스처화시켜 배열로 만들어준 후, 배열을 영원히 반복하면서 움직이는 것처럼 보이게 할 것이다.
앞서 말한 SKTextureAtlas를 이용해 이미지를 텍스처화한다.
override func didMove(to view: SKView) {
let textureAtlas = SKTextureAtlas(named: "images")
var textureArray = [SKTexture]()
}
SKTextureAtlas는 이미지를 렌더링할 때 저장공간과 이미지 그리기를 최적화할 수 있는 텍스처 컬렉션이라고 한다.
지금은 텍스처 컬렉션이기 때문에, 이를 하나씩 텍스처로 만들어서 앞에 선언한 textureArray에 추가할 것이다. 역시 하나씩 뭘 하는 덴 for문을 쓰면 된다.
override func didMove(to view: SKView){
...
// 반복문을 돌되, 텍스처 컬렉션의 개수만큼 해 준다.
// 나는 이미지 이름이 1부터 시작해서 1로 시작했다.
for i in 1...textureAtlas.textureNames.count {
// 이 이름은 앞에서 불러온 폴더 안 이미지 이름, 확장자명과 동일하다.
let name = "image_\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
}
노드 만들기
노드 만들고 첫 번째 이미지 지정해주기
텍스처 준비는 끝났다. 노드를 선언하고, 그에 첫 번째 이미지를 지정해준다. 나는 슬라임 이미지를 가져왔기 때문에 노드의 이름을 slime이라고 했다.
override func didMove(to view: SKView) {
...
let firstImage = textureAtlas.textureNames[0]
var slime = SKSpriteNode(imageNamed: firstImage)
}
textureAtlas.textureNames[0] 대신 textureArray[0] 혹은 첫 번째로 삼고 싶은 이미지의 인덱스 혹은 그 이미지의 이름을 바로 넣어도 된다.
라고 했는데 안 된다. 왜 안되냐면 firstImage에 들어가는 요소는 String 타입이어야 하는데, textureArray에 있는 요소는 SKTexture기 때문. 이를 문자열로 바꾸는 방법을 찾아봤는데 아직 모르겠다.
노드 설정하기
노드의 크기나 위치, 이름등을 정해줄 수 있다.
적당한 크기로, 장면의 정 가운데에 위치할 수 있도록 해 주었다.
override func didMove(to view: SKView) {
...
slime.size = CGSIZE(width: 80, heigth: 80)
slime.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
slime.name = "slime"
}
노드에 이름을 추가해주면 노드가 많을 때 이름으로 노드를 탐색할 수 있다.
노드에 움직임 주기
SKAction
장면에 있는 노드에 의해 실행되는 애니메이션을 SKAction이라고 한다. 특정 애니메이션을 줄 텍스처 배열과 프레임 하나당 유지할 시간을 인자로 받는 animate메서드를 이용하면 된다.
override func didMove(to view: SKView) {
...
let slimeAnimation = SKAction.animate(with: textureArray, timePerFrame: 0.2)
slime.run(SKAction.repeatForever(slimeAnimation))
}
만들어진 액션을 slime이 영원히 실행할 수 있도록 해 준다.
터치로 움직임을 주고 싶다면?
방금 구현한 움직임 부분을 touchesBegan함수에 오버라이드 해 주면 된다.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let slimeAnimation = SKAction.animate(with: textureArray, timePerFrame: 0.2)
slime.run(SKAction.repeatForever(slimeAnimation))
}
이렇게 하면 slime이 없다고 에러가 뜰 것이다. didMove 함수 안에서 선언한 slime을 함수 밖, GameScene 클래스 안에 선언해주면 해결된다.
소스코드와 결과
실행해본 소스코드
// GameViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let scene = GameScene()
scene.scaleMode = .resizeFill
view.ignoreSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.presentScene(scene)
}
}
// GameScene.swift
override func didMove(to view: SKView) {
let textureAtlas = SKTextureAtlas(named: "images")
var textureArray = [SKTexture]()
for i in 1...textureAtlas.textureNames.count {
let name = "image_\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
let firstImage = textureAtlas.textureNames[0]
var slime = SKSpriteNode(imageNamed: firstImage)
slime.size = CGSIZE(width: 80, heigth: 80)
slime.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
slime.name = "slime"
let slimeAnimation = SKAction.animate(with: textureArray, timePerFrame: 0.2)
slime.run(SKAction.repeatForever(slimeAnimation))
}
![]()
slime asset은?!
잘못된 부분이나 더 나은 방향이 있다면 언제든 알려주세요!
댓글남기기