1
1

More than 3 years have passed since last update.

【Swift】 SpriteKit 入門 ④

Last updated at Posted at 2021-01-31

SpriteKitの使い方を解説します。
SpriteKit 入門 ③ で自機(画像)から弾を発射することができるようになりました。今回は敵を表示して衝突判定を実装していきます。
(環境:Xcode12.1  Swift5.3)

敵の表示

前回学んだSKShapeNodeを使用して敵を表示します。今回は簡易的に、画面上部から出現するようにしてみました。出現するx座標はCGFloat.random()を使用してランダムにしています。

MyScene.swift
    var enemy: SKShapeNode?
    var lastUpdateTime2 : TimeInterval = 0
    //...
    //敵
    func enemies(){
        self.enemy = SKShapeNode.init(circleOfRadius: 50)
        if let enemy = self.enemy {
            enemy.position = CGPoint(x: CGFloat.random(in: -self.size.width/2 + 100 ... self.size.width/2 - 100), y: self.size.height/2 + enemy.yScale)
            enemy.fillColor = SKColor.blue
            enemy.lineWidth = 5
            enemy.strokeColor = SKColor.white
            enemy.run(SKAction.sequence([SKAction.move(by: CGVector(dx: 0, dy: -self.size.height - 100), duration: 10),SKAction.removeFromParent()]))
            self.addChild(enemy)
        }
    }
    //...
    //一定時間毎の処理
    override func update(_ currentTime: TimeInterval) {
        //...
        if (lastUpdateTime2 == 0) {
            self.lastUpdateTime2 = currentTime
        }
        if (lastUpdateTime2 + 1 <= currentTime) {
            //1秒毎に敵を表示
            self.enemies()
            lastUpdateTime2 = currentTime
        }
    }

SK1.png

衝突判定

物理的な挙動にかかる処理にはSKPhysicsBodyを使用します。衝突判定にはSKPhysicsBodyのプロパティとして

  • categoryBitMask
  • collisionBitMask
  • contactTestBitMask

を使用します。これらの詳細については以下の記事が参考になると思います。

contactTestBitMaskで指定したNode(正確にはcategoryBitMask)と接触した際には、

  • didBegin(_ contact: SKPhysicsContact)
  • didEnd(_ contact: SKPhysicsContact)

のメソッドが呼ばれます。これらのメソッドを利用するためにはあらかじめSKPhysicsContactDelegateを追加しておく必要があります。
コードは次のようになります。

MyScene.swift
class MyScene: SKScene, SKPhysicsContactDelegate {
    //...
    override func sceneDidLoad() {
        //...
        //Delegateの指定
        physicsWorld.contactDelegate = self
    }

    //自機から発射する弾
    func repeatShoots(){
        self.enemy = SKShapeNode.init(circleOfRadius: 50)
        if let shoot = self.shoot {
            shoot.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 30, height: 30))
            shoot.physicsBody?.affectedByGravity = false
            shoot.physicsBody?.categoryBitMask = 0b0001
            shoot.physicsBody?.collisionBitMask = 0b0000
            shoot.physicsBody?.contactTestBitMask = 0b0001
            //...
        }
    }

    //敵
    func enemies(){
        self.enemy = SKShapeNode.init(circleOfRadius: 50)
        if let enemy = self.enemy {
            enemy.physicsBody = SKPhysicsBody(circleOfRadius: 50)
            enemy.physicsBody?.affectedByGravity = false
            enemy.physicsBody?.categoryBitMask = 0b0001
            enemy.physicsBody?.collisionBitMask = 0b0000
            enemy.physicsBody?.contactTestBitMask = 0b0001
            //...
        }
    }

    func didBegin(_ contact: SKPhysicsContact) {
        if (contact.bodyA.categoryBitMask == self.enemy?.physicsBody!.categoryBitMask || contact.bodyB.categoryBitMask == self.enemy?.physicsBody!.categoryBitMask){
            self.removeChildren(in: [contact.bodyA.node!,contact.bodyB.node!])
        }
    }

didBegin(_ contact: SKPhysicsContact)で衝突したNodeは.bodyA.bodyBで取得することができます。ただし、AとBの順番は保証されないので注意が必要です。またself.removeChildren(in: )は取り除くNodeが1つの場合でも配列の形で引数を与える必要があります。

実行結果はこのようになりました。
ezgif.com-video-to-gif.gif

まとめ

ここまでで

  • SpriteKitSceneファイル
  • SKView
  • SKScene
  • SKSpriteNode
  • SKShapeNode
  • SKAction
  • SKPhysicsBody
  • TimeInterval
  • タッチ操作

について扱いました。これらだけでもアイデア次第でいろんなゲームが作れそうですね。
次回はまだ扱っていないSKEmitterNodeの使い方について解説します。
次回は一度に発射する弾の数を増やしてみようと思います。

参考

この記事は以下の情報を参考にして執筆しました。

1
1
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
1