Swift
SpiteKit

Swift SperiteKitで物理演算

More than 1 year has passed since last update.

SpriteKitを利用して物理演算を実装する場合にハマった部分が多々あったため、備忘録として記載。

目次は以下の通り(筆者の理解が進むに従い加筆するかも)

目次

  1. 基本
  2. 重力を弄りたい場合
  3. 衝突判定させたくない場合

基本

SpriteKitによる物理演算を実装する際、最も端的には以下のようなコードを書けば良いようである。
今回作成するものはシンプルな矩形の物体を想定。

GameScene.swift
class GameScene: SKScene{
<中略>
  override func didModeToView(view: SKView){
    // color引数で作成する矩形の色を指定し、sizeで矩形の大きさを指定している。
    let sprite:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size:CGSizeMake(150,150));`

    // spriteが最初に出現する地点を指定
    sprite.position = CGPointMake(<好きな座標を指定>);

    // 物理演算の対象となる大きさを指定している。
    sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size);

    // SKSceneへ作成したSpriteを追加する。
    self.addChild(sprite);
  }

physicsBodyプロパティを入れてやることで、物理演算の対象としている。

重力を弄りたい

何か地球じゃないところを舞台にしたゲームを作りたい。だとか、"次元転換システム"を作りたい。とか、そういう要望も時にはある。
弄ろう。重力を。

GameScene.swift
class GameScene: SKScene{
<中略>
  override func didModeToView(view: SKView){
    // x軸方向とy軸方向の重力を変えている。
    // self.physicsWorld.gravityの初期値は(0.0, -9.8)
    self.physicsWorld.gravity = CGVectorMake(0.0, -1);

    <省略>

  }

特定の物体だけ重力を効きを弱くしたい、という場合にどうすれば良いかはまだ良く分かっていない。
physicsBodyにはmassという質量を指定するプロパティがあるが、これは重力に対しては効力を発揮せず、
単に衝突した際の計算にのみ使用される(?)ようである。
 ※mass(質量)でも重量計算に影響しているようです。density(密度)もあるので、どちらを選ぶかはお好みで。私はdensityを使っています。

なお、特定のspriteにのみ重力を"全く加えない"場合には以下のようにすればよい。

sprite.physicsBody?.affectedByGravity = NO;

衝突判定をさせたくない場合

物理演算はさせたいが、他のオブジェクトと衝突させたくない。
或いは、特定のオブジェクトに対してだけ衝突させたくない場合という状況がある。
そういった場合は、physicsBodyのcategoryBitMaskプロパティ、並びにcollisionBitMaskプロパティを利用する。

categoryBitMask は自身の物体種別を指定し、collisionBitMaskは自身がどの物体と接触した場合に衝突させるかを判定する。

なお、ここでの衝突はあくまで物理的な衝突のことを指しており、例えば"あるオブジェクトとあるオブジェクトが接触した際に○○の処理を行わせたい"といったユーザ定義の処理については別途contactTestBitMaskというプロパティが存在している。
 ※使い方はcollisionBitMaskと同様

GameScene.swift
class GameScene: SKScene{
  // Spriteの種類を示す列挙型
  enum SpriteCategory:UInt32{
    case ground= 1
    case person = 2
    case skelton = 4
  }

<中略>
  override func didModeToView(view: SKView){
    // groundを作成
    let ground:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size:CGSizeMake(150,150));`
    ground.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size);

    // カテゴリ"Ground"として登録
    ground.physicsBody?.categoryBitMask = SpriteCategory.ground.rawValue;

    // カテゴリ"Ground"は、"Ground"、"Person"と衝突するよう設定
    ground.physicsBody?.collisionBitMask = SpriteCategory.ground.rawValue | SpriteCategory.person.rawValue;



    // personを作成
    let person:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size:CGSizeMake(150,150));`
    person.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size);

    // カテゴリ"Person"として登録
    person.physicsBody?.categoryBitMask = SpriteCategory.person.rawValue;
    
    // カテゴリ"Person"は"Ground"、"Person"と衝突するよう設定
    person.physicsBody?.collisionBitMask = SpriteCategory.ground.rawValue | SpriteCategory.person.rawValue;



    // skelton を作成
    let skelton:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size:CGSizeMake(150,150));`
    skelton.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size);

    // カテゴリ"skelton "として登録
    skelton.physicsBody?.categoryBitMask = SpriteCategory.skelton.rawValue;

    // カテゴリ"skelton "はどのオブジェクトとも衝突しない。
    skelton.physicsBody?.collisionBitMask = 0;

    // 各々登録
    self.addChild(ground);
    self.addChild(person);
    self.addChild(skelton);
  }

なお、categoryBitMask、collisionBitMask、contactTestBitMaskはUInt32であるため、最大で32種類までしか種類を指定出来ないことには注意したい。