連載
» 2017年04月03日 05時00分 UPDATE

ゲームの「敵」キャラで分かる「人工知能」の作り方(2):SwiftのGameplayKitでAIに追いかける、避ける、逃げる処理を追加するには (1/4)

iPhone向け鬼ごっこアプリを作りながら人工知能(AI)について学んでいく連載。今回は、SwiftのGameplayKitでAIの敵キャラに、追いかける、避ける、逃げる処理を追加する方法を解説する。

[杉本裕樹,マネーフォワード]

 本連載『ゲームの「敵」キャラで分かる「人工知能」の作り方』では、iPhone向け鬼ごっこアプリを作りながら人工知能(AI)について学んでいきます。鬼の動きにAIを活用して、自動でプレイヤーを追いかけるようなAIを作りたいと思います。

 前回の「iOS GameplayKitの「Agents, Goals, and Behaviors」で作る、鬼ごっごの鬼AI」では「Agents, Goals, and Behaviors」を使って、鬼がプレイヤーを追いかける処理を実装しました。

 今回はAIをさらに進化させて、「追いかける」だけではなく「避ける」「逃げる」といった挙動をするAIを作ろうと思います。

黄色い丸がプレイヤー、赤い丸が鬼

 前回のソースコードはこちらです。今回はこのソースコードを元に機能追加していきます。

フィールドに障害物を配置する

 まずはAIに「避ける」動作を追加します。フィールド上に障害物を配置し、鬼がそれを避けるような実装をしていきます。

 その前に画面に障害物を設置します。今回は障害物は5つランダムに配置される仕様とします。「GameScene.swift」を以下のように修正してください。

class GameScene: SKScene {
    // 省略
 
    override func didMove(to view: SKView) {
        player.fillColor = UIColor(red: 0.93, green: 0.96, blue: 0.00, alpha: 1.0)
        addChild(player)
 
        createObstacles() // 今回追加
        setCreateEnemyTimer()
        physicsWorld.gravity = CGVector()
    }
 
    // ここから今回追加
    func createObstacles() {
        guard let viewFrame = view?.frame else {
            return
        }
 
        var obstacleNodes = [SKShapeNode]()
        while obstacleNodes.count < 5 {
            let point = CGPoint(
                x: CGFloat(arc4random_uniform(UInt32(viewFrame.width))) - viewFrame.width / 2,
                y: CGFloat(arc4random_uniform(UInt32(viewFrame.height))) - viewFrame.height / 2)
            let radius = CGFloat(arc4random_uniform(50) + 50)
 
            // 障害物かPlayerが衝突していたら設置しない
            let isObstacleOverlapped = obstacleNodes.contains {
                let dx = (point.x - $0.position.x)
                let dy = (point.y - $0.position.y)
                if sqrt(dx*dx + dy*dy) < $0.frame.width + radius {
                    return true
                }
                return false
            }
            let dx = point.x - player.position.x
            let dy = point.y - player.position.y
            let isPlayerOverlapped = sqrt(dx*dx + dy*dy) < radius + player.frame.width
            if isObstacleOverlapped || isPlayerOverlapped {
                continue
            }
 
            let obstacleNode = SKShapeNode(circleOfRadius: CGFloat(radius))
            obstacleNode.fillColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1.0)
            obstacleNode.position = point
            obstacleNode.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(radius))
            obstacleNode.physicsBody?.pinned = true
            addChild(obstacleNode)
            obstacleNodes.append(obstacleNode)
        }
    }
    // ここまで今回追加
 
    // 省略
}

 アプリを起動すると、画面上に5つの障害物が配置されていることが分かります。

プレイヤーが障害物と重ならないようにする

 次はプレイヤーを修正します。今はプレイヤーが障害物と重なるところができてしまうので、重ならないように修正します。

 GameScene.swiftのdidMoveメソッドに処理を追加します。

class GameScene: SKScene {
    // 省略
 
    override func didMove(to view: SKView) {
        player.fillColor = UIColor(red: 0.93, green: 0.96, blue: 0.00, alpha: 1.0)
        player.physicsBody = SKPhysicsBody(circleOfRadius: 10) // 今回追加
        addChild(player)
 
        createObstacles()
        setCreateEnemyTimer()
        physicsWorld.gravity = CGVector()
    }
    // 省略
}

 これでプレイヤーが障害物と重ならなくなりました。

コラム physicsBodyとは

 今回追加したphysicsBodyは物理演算に使うプロパティーです。Node同士の衝突時の挙動をコントールしたり、重力などの外部からの力を加えるときに使います。

 今回はプレイヤーと障害物にphysicsBodyを設定することで、お互いが衝突して重ならないようにしました。


       1|2|3|4 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。