検索
連載

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

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

PC用表示 関連情報
Share
Tweet
LINE
Hatena

AIに「逃げる」動きを実装

 次は、AIに「逃げる」動きを実装します。通常の鬼ごっこのルールからは外れますが、鬼を倒すことのできる味方を追加しようと思います。鬼が味方から逃げる処理実装を通して、AIに「逃げる」動作を追加します。

味方キャラを追加する

 最初に画面上に味方を配置します。GameSceneを以下のように修正してください。

class GameScene: SKScene {
    // 省略
    var sweeper = SKShapeNode(circleOfRadius: 10) // 今回追加
    var sweeperAgent = GKAgent2D() // 今回追加
 
    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)
 
        // ここから今回追加
        sweeper.fillColor = UIColor(red: 255, green: 0.65, blue: 0.02, alpha: 1.0)
        sweeper.physicsBody = SKPhysicsBody(circleOfRadius: 10)
        addChild(sweeper)
 
        sweeperAgent.maxAcceleration = 50
        sweeperAgent.maxSpeed = 80
        sweeperAgent.delegate = self
        agentSystem.addComponent(sweeperAgent)
        // ここまで今回追加
 
        createObstacles()
        setCreateEnemyTimer()
        physicsWorld.gravity = CGVector()
    }
 
    // 省略
}

 画面上に味方(緑の丸)を追加しました。

味方が鬼を追いかける処理、味方と鬼が衝突したら鬼が消える処理

 次は、「味方が鬼を追いかける処理」「味方と鬼が衝突したら鬼が消える処理」を実装します。GameSceneを以下のように修正してください。

class GameScene: SKScene {
    // 省略
    func createEnemy() {
        let enemy = SKShapeNode(circleOfRadius: 10)
        enemy.position.x = size.width / 2
        enemy.fillColor = UIColor(red: 0.94, green: 0.14, blue: 0.08, alpha: 1.0)
        enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.width / 2)
        addChild(enemy)
        enemies.append(enemy)
 
        let anemyAgent = GKAgent2D()
        anemyAgent.maxAcceleration = 30
        anemyAgent.maxSpeed = 70
        anemyAgent.position = vector_float2(x: Float(enemy.position.x), y: Float(enemy.position.y))
        anemyAgent.delegate = self
        anemyAgent.behavior = GKBehavior(goals: [
            GKGoal(toSeekAgent: playerAgent),
            GKGoal(toAvoid: obstacles, maxPredictionTime: 10)
            ], andWeights: [NSNumber(value: 1), NSNumber(value: 50)])
        agentSystem.addComponent(anemyAgent)
        enemyAgents.append(anemyAgent)
 
        let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) } // 今回追加
        sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)]) // 今回追加
    }
    override func update(_ currentTime: TimeInterval) {
        if prevTime == 0 {
            prevTime = currentTime
            startTime = currentTime
        }
        agentSystem.update(deltaTime: currentTime - prevTime)
        playerAgent.position = vector_float2(x: Float(player.position.x), y: Float(player.position.y))
        if !isGameFinished {
            for enemy in enemies {
                let dx = enemy.position.x - player.position.x
                let dy = enemy.position.y - player.position.y
                if sqrt(dx*dx + dy*dy) < player.frame.width / 2 + enemy.frame.width / 2 {
                    isGameFinished = true
                    timer?.invalidate()
                    let label = SKLabelNode(text: "記録:\(Int(currentTime - startTime))秒")
                    label.fontSize = 80
                    label.position = CGPoint(x: 0, y: -100)
                    addChild(label)
                    break
                }
            }
 
            // ここから今回追加
            for (i, enemy) in enemies.enumerated() {
                let dx = enemy.position.x - sweeper.position.x
                let dy = enemy.position.y - sweeper.position.y
                if sqrt(dx*dx + dy*dy) < enemy.frame.width / 2 + sweeper.frame.width / 2 + 5 {
                    enemy.removeFromParent()
                    enemies.remove(at: i)
                    agentSystem.removeComponent(enemyAgents[i])
                    enemyAgents.remove(at: i)
                    let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) }
                    sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)])
                    break
                }
            }
            // ここまで今回追加
        }
 
        prevTime = currentTime
    }
}
 
extension GameScene: GKAgentDelegate {
    func agentDidUpdate(_ agent: GKAgent) {
        if let agent = agent as? GKAgent2D, let index = enemyAgents.index(where: { $0 == agent }) {
            let enemy = enemies[index]
            enemy.position = CGPoint(x: CGFloat(agent.position.x), y: CGFloat(agent.position.y))
        }
        if let agent = agent as? GKAgent2D, agent == sweeperAgent {
            sweeper.position = CGPoint(x: CGFloat(agent.position.x), y: CGFloat(agent.position.y)) // 今回追加
        }
    }
}

 これで味方が鬼を追いかけるようになりました。

鬼が味方から逃げる処理(GameplayKitを使う場合)

 最後に、鬼が味方から逃げるようにします。GameSceneのcreateEnemyメソッドを以下のように修正します。

class GameScene: SKScene {
    // 省略
 
    func createEnemy() {
        let enemy = SKShapeNode(circleOfRadius: 10)
        enemy.position.x = size.width / 2
        enemy.fillColor = UIColor(red: 0.94, green: 0.14, blue: 0.08, alpha: 1.0)
        enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.width / 2)
        addChild(enemy)
        enemies.append(enemy)
 
        let anemyAgent = GKAgent2D()
        anemyAgent.maxAcceleration = 30
        anemyAgent.maxSpeed = 70
        anemyAgent.position = vector_float2(x: Float(enemy.position.x), y: Float(enemy.position.y))
        anemyAgent.delegate = self
        anemyAgent.behavior = GKBehavior(goals: [
            GKGoal(toSeekAgent: playerAgent),
            GKGoal(toAvoid: obstacles, maxPredictionTime: 10),
            GKGoal(toFleeAgent: sweeperAgent) // 今回追加
            ], andWeights: [NSNumber(value: 5), NSNumber(value: 50), NSNumber(value: 1)]) // 今回修正
        agentSystem.addComponent(anemyAgent)
        enemyAgents.append(anemyAgent)
 
        let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) }
        sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)])
    }
 
    // 省略    
}

 今回は下記の修正をすることで、鬼が味方キャラから逃げるようになりました。

anemyAgent.behavior = GKBehavior(goals: [
    GKGoal(toSeekAgent: playerAgent),
    GKGoal(toAvoid: obstacles, maxPredictionTime: 10),
    GKGoal(toFleeAgent: sweeperAgent)
], andWeights: [NSNumber(value: 5), NSNumber(value: 50), NSNumber(value: 1)])

 今回追加したのは4行目の「GKGoal(toFleeAgent: sweeperAgent)」という箇所です。このGKGoal(振る舞いのルール)は「特定のAgentから逃げる」というルールになります。引数にsweeperAgent(味方キャラ)を渡すことで、鬼に「味方キャラから逃げる」という振る舞いを追加しました。

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る