はじめに
約4年前に書いた「【備忘録】SpriteKitのテンプレート」は、Storyboardを使っていましたが、最近はSwiftUIを使う機会が増えたので、今回は、SwiftUI向けの「SpriteKitのテンプレート」をまとめておきます。
この記事を執筆したときの環境は以下の通り
- macOS : 13.4.1 (Ventura)
- Xcode : 14.3.1
- Swift : 5.8.1
- SwiftUI : 4.0 (for iOS16)
Xcode新規プロジェクトの作成
Xcodeのメニューから「File」→「New」→「Project」の
「Game」テンプレートを選択してプロジェクトを作成すると、従来のStoryboardを使った(SwiftUIでは無い)プロジェクトになってしまうため、通常の「App」テンプレートを選択する。
続く「Choose options for your new project:」ダイアログにて以下を指定する。
- Interface : SwifuUI
- Language : Swift
- 他 : 任意
ContentView.swiftの内容を書き換える
「ContentView.swift」を以下の内容に書き換える。
SpriteView
(従来のSKView
)のOption
とDebugOptions
は、必要により指定する。
import SwiftUI
import SpriteKit
struct ContentView: View {
var body: some View {
let spriteViewOptions: SpriteView.Options = [
//.allowsTransparency,
//.ignoresSiblingOrder,
//.shouldCullNonVisibleNodes,
]
let spriteViewDebugOptions: SpriteView.DebugOptions = [
.showsDrawCount,
.showsFPS,
//.showsFields,
.showsNodeCount,
//.showsPhysics,
//.showsQuadCount,
]
GeometryReader {
SpriteView(scene: GameScene(size: $0.size),
options: spriteViewOptions,
debugOptions: spriteViewDebugOptions)
//.scaledToFill()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
GameScene.swiftを新規ファイルで作成する
「New File...」→「Swift File」からGameScene.swift
を新規作成する。
「GameScene.swift」を以下の内容に書き換える。
実は、GameScene.swift
は従来のまま。もし、Storyboardで開発した旧プロジェクトがあるなら、そこからGameScene.swift
をコピーして使用できるはず。
import SpriteKit
class GameScene: SKScene {
enum ColliderType {
static let xxx1: UInt32 = 1 << 0
static let xxx2: UInt32 = 1 << 1
static let xxx3: UInt32 = 1 << 2
static let xxx4: UInt32 = 1 << 3
static let xxx5: UInt32 = 1 << 4
}
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)
self.physicsWorld.contactDelegate = self
//TODO
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let touchedNode = atPoint(location)
switch touchedNode.name {
case "xxx":
//TODO
break
default:
break
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
//TODO
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//TODO
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
//TODO
}
override func update(_ currentTime: TimeInterval) {
//TODO
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
//TODO
}
}
SpriteKitテンプレートとしては、以上でおわり。
おまけ
以前の記事で紹介した内容より、もっといい感じで書く方法があったので、紹介します。
SpriteKitでノードを構築していく場合に、インスタンス生成後に各プロパティを設定するコードがだらだら続くが、これをいい感じに書く方法を見つけたので、追記しておく。
let label = SKLabelNode(fontNamed: "HelveticaNeue")
label.horizontalAlignmentMode = .center
label.verticalAlignmentMode = .center
label.text = "LABEL"
label.fontSize = 32
label.fontColor = .white
label.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
addChild(label)
let label: SKLabelNode = {
$0.horizontalAlignmentMode = .center
$0.verticalAlignmentMode = .center
$0.text = "LABEL"
$0.fontSize = 32
$0.fontColor = .white
$0.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
return $0
} (SKLabelNode(fontNamed: "HelveticaNeue"))
addChild(label)
let label = SKLabelNode(fontNamed: "HelveticaNeue").apply {
$0.horizontalAlignmentMode = .center
$0.verticalAlignmentMode = .center
$0.text = "LABEL"
$0.fontSize = 32
$0.fontColor = .white
$0.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
}
addChild(label)
apply
メソッドは、次のように定義する。
public protocol Applicable { }
public extension Applicable {
@discardableResult
func apply(block: (Self) -> Void) -> Self {
block(self)
return self
}
}
extension NSObject: Applicable { }
NSObject
のextensionとして定義するため、ほとんどのオブジェクトで使うことができる。これは便利。(周知の内容かと思いますが・・・)