1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AppleのSpriteKitをAndroidネイティブで使う ― Kotlinライブラリ「SpriteKit」を公開しました

1
Posted at

AppleのSpriteKitをAndroidネイティブで使う ― Kotlinライブラリ「SpriteKit」を公開しました

シーングラフ、物理演算、パーティクル、タイルマップといった2Dゲームエンジンの定番機能を、AppleのSpriteKitと同じAPIでAndroidネイティブに実装したオープンソースのKotlinライブラリ「SpriteKit」を公開しました。あわせて、以前公開した「GameplayKit」との橋渡しを行うライブラリ「GKSKBridge」、そして両方を実際に組み合わせて動かすサンプル「Tic-Tac-Toe」も公開しています。この記事ではそれぞれの役割と使い方、そしてApple公式のSpriteKitと比べた場合の対応範囲・制限事項を紹介します。


目次


SpriteKitとは何か

SpriteKitは、Appleが2Dゲーム向けに提供しているフレームワークです。描画対象をノードとして階層的に管理するシーングラフを中心に、スプライトやシェイプ、ラベルの描画、アクションによるアニメーション、物理演算、パーティクル、タイルマップなど、2Dゲームに必要な機能をひとまとめに提供します。

今回公開した「SpriteKit」ライブラリは、このSpriteKitのAPI構成をそのままAndroid・Kotlinに移植したものです。クラス名や役割はAppleのSpriteKitに揃えつつ、実装はKotlinらしい書き方に直しています。以前公開した「GameplayKit」(ゲームAIのアルゴリズム群)と組み合わせることで、Androidネイティブだけで「ゲームロジック」と「2D描画」の両方をAppleと同じ設計で扱えるようになります。

何ができるか

現時点で、シーングラフから入力処理・オーディオ・シェーダーまで一通りの機能が実装されています。

機能 代表的なクラス 用途
シーン・ノード SKScene, SKNode 描画対象を階層構造で管理する
スプライト・シェイプ・ラベル SKSpriteNode, SKShapeNode, SKLabelNode テクスチャ・図形・テキストの表示
アクション SKAction 移動・回転・拡大縮小などのアニメーション
物理演算 SKPhysicsBody, SKPhysicsWorld 重力・衝突判定などの2D物理
パーティクル SKEmitterNode 爆発や煙などの粒子エフェクト
タイルマップ SKTileMapNode タイルベースの2Dマップ表現
カメラ・効果・制約 SKCameraNode, SKEffectNode, SKConstraint 画面追従やエフェクト、ノードの拘束
入力・トランジション タッチ入力, SKTransition タッチ操作の処理とシーン切り替え
オーディオ・シェーダー SKAudioNode, SKShader 音声再生とカスタムシェーダー

例1: シーンとノード

val scene = SKScene(size = Vector2(1080f, 1920f)).apply {
    scaleMode = SKSceneScaleMode.AspectFit
    backgroundColor = Color.BLACK
}

val player = SKNode().apply { position = Vector2(scene.size.x / 2f, 200f) }
scene.addChild(player)

skView.presentScene(scene)

例2: スプライト・シェイプ・ラベル

val ship = SKSpriteNode(texture = shipTexture, size = Vector2(64f, 64f))

val marker = SKShapeNode(path = circlePath).apply {
    fillColor = Color.CYAN
    strokeColor = Color.WHITE
    lineWidth = 4f
}

val scoreLabel = SKLabelNode(text = "Score: 0").apply {
    fontSize = 48f
    fontColor = Color.WHITE
}

scene.addChild(ship)
scene.addChild(marker)
scene.addChild(scoreLabel)

対応範囲と制限

このライブラリは、Apple公式のSpriteKitを一対一で丸ごと移植したものではありません。Xcodeのビルド時処理やCore Image、Metalなど、Androidに直接対応するものがない機能は移植の対象外としています。また、実装済みの機能でも、細部の挙動がAppleと異なる箇所や、簡略化して実装している箇所があります。

移植の対象外としている主な機能

  • SKVideoNode(動画テクスチャの再生)
  • SKLightNode(法線マップによるライティング・シェーダー依存の陰影)
  • SKReferenceNode.sksシーンアーカイブの読み込み。Android に相当する形式がない)
  • SKEffectNode.filter(Core Image フィルタ)
  • SKAction.follow(_:asOffset:orientToPath:duration:)(パス追従アクション)
  • パーティクルの .sks エディタ形式、タイルセットの .sks 形式
  • 位置的・空間的オーディオ、SKWarpGeometry(メッシュのワープ描画)

実装はしているが、挙動が簡略化・近似されている主な箇所

  • 物理演算:接触点由来のトルクは未対応で衝突応答は線形のみ、ブロードフェーズは O(n²)、ジョイントの角度制限や reactionForce / reactionTorque は未実装
  • タイルマップ:グリッド形状のみに対応(Appleの等角・六角形タイルは非対応)、行数・列数は生成時に固定
  • 入力:タッチはバッチではなく1つずつ配信、ヒットテストは簡易境界ボックスによる判定
  • トランジション:flipHorizontal / flipVertical などの3D的な効果は2Dでの近似表現
  • シェーダー:SKSpriteNode にのみ適用可能で、シェーダーモディファイアの差し込みは非対応

これらはAndroidという異なるプラットフォーム上で、実務で使う範囲を優先して移植した結果です。詳細な差異はリポジトリ内の API_COMPATIBILITY.md、実装済み・対象外の機能一覧は ROADMAP.md にまとめています。導入の際は、自分のプロジェクトで必要な機能が対応範囲に含まれているか、事前にこれらのドキュメントで確認することをおすすめします。

GameplayKitとの橋渡し:GKSKBridge

GameplayKitのエンティティ・コンポーネントと、SpriteKitのシーングラフは、本来それぞれ独立したフレームワークです。エンティティにキャラクターのロジックを持たせても、それだけでは画面に表示されるノードと自動的には結び付きません。「GKSKBridge」は、この2つのライブラリをAndroid上で連携させるためのライブラリです。

主な機能は次の3つです。

  • GKSKNodeComponent:エンティティとシーングラフのノードを双方向に結合します
  • GKScene:エンティティとノードをまとめて管理するシーンコンテナです
  • GKAgentNodeComponent:GameplayKitのエージェント・ステアリングの計算結果(位置・回転)を、毎フレーム自動的にノードへ反映します
val node = SKNode()
val entity = GKEntity()

entity.addComponent(GKSKNodeComponent(node))
node.entity // == entity, set automatically by GKSKNodeComponent

entity.removeComponent<GKSKNodeComponent>()
node.entity // == null again
val scene =
    GKScene().apply {
        rootNode = SKScene(size = Vector2(1080f, 1920f))
        addEntity(entity)
    }

エージェントのステアリングとノードを同期させる例です。

val agent =
    GKAgent2D().apply {
        maxSpeed = 4f
        behavior = GKBehavior.of(GKGoal.toSeekAgent(targetAgent), weight = 1f)
    }
entity.addComponent(agent)
entity.addComponent(GKAgentNodeComponent())

class GameScene(size: Vector2) : SKScene(size) {
    override fun update(deltaTime: Duration) {
        entity.update(deltaTime) // advances the agent's steering simulation
    }

    override fun didFinishUpdate() {
        // このフレームで確定したエージェントの位置・回転を、
        // アクションや物理演算のシミュレーション後に各エンティティのノードへコピーする
        scene.syncAgentNodes()
    }
}

GameplayKitとSpriteKitはそれぞれ独自のVector2型を持っているため、両者を変換するユーティリティも用意されています。

val skPosition = agent.position.toSKVector2()
val gkPosition = node.position.toGKVector2()

使ってみる

GameplayKitはJitPack経由での配信を用意していましたが、SpriteKitとGKSKBridgeは現時点でMaven Central・JitPackへの配信は行っておらず、Gitサブモジュールとしてホストアプリに組み込む形を想定しています。

// settings.gradle.kts
include(":GameplayKit:gameplaykit")
project(":GameplayKit:gameplaykit").projectDir = file("GameplayKit/gameplaykit")

include(":SpriteKit:spritekit", ":SpriteKit:spritekit-compose")
project(":SpriteKit:spritekit").projectDir = file("SpriteKit/spritekit")
project(":SpriteKit:spritekit-compose").projectDir = file("SpriteKit/spritekit-compose")

include(":GKSKBridge:gkskbridge")
project(":GKSKBridge:gkskbridge").projectDir = file("GKSKBridge/gkskbridge")

いずれもminSdk 24、compileSdk / targetSdk 34、Kotlin 2.0+に対応しており、MITライセンスで公開しています。

サンプル:Tic-Tac-Toeで組み合わせて動かす

「Tic-Tac-Toe」は、GameplayKit・SpriteKit・GKSKBridgeを実際に組み合わせて動かす、○×ゲームのサンプルアプリです。同じゲームをiOS版とAndroid版の両方で実装しています。

  • iOS版:Apple公式のSpriteKit・GameplayKitで実装
  • Android版:今回のbitzgroup/SpriteKit・bitzgroup/GameplayKit・bitzgroup/GKSKBridgeで実装

両者は同じシーングラフ構成、GKGameModel / GKMinmaxStrategist / GKMonteCarloStrategistによる同じAI、GKStateMachineによる同じターン管理で作られており、AndroidのポートがApple公式フレームワークと同等に動作することを確認できるようになっています。ミニマックス法・モンテカルロ法による3段階の難易度、日英ローカライズにも対応しています。

現在の状況

SpriteKitはシーングラフ・スプライト・アクション・物理演算・パーティクル・タイルマップ・カメラ・入力・トランジション・オーディオ・シェーダーまでの実装が完了しています。GKSKBridgeもエンティティ・ノードの結合、シーンコンテナ、エージェント・ステアリングの同期という計画分の機能が実装・テスト済みです。前述の通り、現状はGitサブモジュールとしての組み込みが前提で、パッケージ配信は今後の課題です。

まとめ

GameplayKitに続けてSpriteKitとGKSKBridgeを公開したことで、Androidネイティブのコードだけで「ゲームロジック」と「2D描画」の両方を、Appleの設計に揃えた形で扱えるようになりました。Tic-Tac-Toeは、この3つのライブラリを実際に組み合わせて動かした最小のサンプルとして公開しています。

参考資料


元記事(Bitz Notebook): AppleのSpriteKitをAndroidネイティブで使う ― Kotlinライブラリ「SpriteKit」を公開しました

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?