しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

複数のGKGoalを使って複雑なAIを作成する

昨日調べた事の続きです。
この記事では、GKGoalを使って特定のオブジェクトに向かって移動するAgentを作成しました。
今回は「特定のオブジェクトに向かって移動」「別のオブジェクトから逃げる」の複数を指定した場合を試してみます。

GKGoalとGKAgentで目的に向かって動くオブジェクトを作ってみる - しめ鯖日記

昨日同様に、Gameプロジェクトを作成します。

f:id:llcc:20161211185859p:plain

GameScene.swiftを以下のように置き換えます。
昨日の記事と違い、GKBehaviorに複数のGKGoalを与えています。
こうする事でagent3にagent2から遠ざかりつつagent1へ移動する動きをさせる事ができます。

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    let agentSystem = GKComponentSystem(componentClass: GKAgent2D.self)
    let agent1 = GKAgent2D()
    let agent2 = GKAgent2D()
    let agent3 = GKAgent2D()
    let node1 = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 10, height: 10))
    let node2 = SKSpriteNode(color: UIColor.green, size: CGSize(width: 10, height: 10))
    let node3 = SKSpriteNode(color: UIColor.red, size: CGSize(width: 10, height: 10))
    var prevTime: TimeInterval = 0
    
    override func didMove(to view: SKView) {
        agent1.position = vector_float2(x: 100, y: 100)
        agent2.position = vector_float2(x: 300, y: 100)
        node1.position = CGPoint(x: Double(agent1.position.x), y: Double(agent1.position.y))
        node2.position = CGPoint(x: Double(agent2.position.x), y: Double(agent2.position.y))
        
        agent3.position = vector_float2(x: 300, y: 500)
        agent3.maxAcceleration = 10
        agent3.maxSpeed = 10
        agent3.delegate = self
        agent3.behavior = GKBehavior(goals: [
            GKGoal(toSeekAgent: agent1),
            GKGoal(toFleeAgent: agent2)
            ], andWeights: [100, 10]
        )
        agentSystem.addComponent(agent3)
        
        addChild(node1)
        addChild(node2)
        addChild(node3)
    }
    
    override func update(_ currentTime: TimeInterval) {
        let deltaTime = prevTime == 0 ? 0 : currentTime - prevTime
        prevTime = currentTime
        agentSystem.update(deltaTime: deltaTime)
    }
}

extension GameScene: GKAgentDelegate {
    func agentDidUpdate(_ agent: GKAgent) {
        if let agent = agent as? GKAgent2D {
            node3.position = CGPoint(x: Double(agent.position.x), y: Double(agent.position.y))
            print(agent.position)
        }
    }
}

この状態で起動するとagent2を避けつつagent1に向かう動きをします。
下図の右上がagent3、右下がagent2、左下はagent1を表しています。

f:id:llcc:20161211190512p:plain

もしここでandWeightsを両方100にした場合は少し動きが変わってきます。

agent3.behavior = GKBehavior(goals: [
    GKGoal(toSeekAgent: agent1),
    GKGoal(toFleeAgent: agent2)
    ], andWeights: [100, 100]
)

agent1に近づく動きとagent2から遠ざかる動きが重なり、下図のように真っ直ぐ左側に移動する事になります。

f:id:llcc:20161211190810p:plain

GKGoalとGKAgentで目的に向かって動くオブジェクトを作ってみる

GameplayKitの「Agents, Goals, and Behaviors」という機能を試してみました。

GameplayKit Programming Guide: Agents, Goals, and Behaviors

これは特定のオブジェクトに向かって移動したり、特定のオブジェクトから逃げるようなものを作れる機能です。
早速実装をしてみます。

まずはXcodeでゲームプロジェクトを作成します。

f:id:llcc:20161210231421p:plain

GameScene.swiftを以下のように置き換えます。
こちらはagent2がagent1に向かって移動するサンプルです。

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    var agentSystem = GKComponentSystem(componentClass: GKAgent2D.self)
    var agent1 = GKAgent2D()
    var agent2 = GKAgent2D()
    var prevTime: TimeInterval = 0
    
    override func didMove(to view: SKView) {
        agent1.position = vector_float2(x: 100, y: 100)
        
        agent2.position = vector_float2(x: 300, y: 500)
        agent2.delegate = self
        agent2.behavior = GKBehavior(goal: GKGoal(toSeekAgent: agent1), weight: 100)
        agentSystem.addComponent(agent2)
    }
    
    override func update(_ currentTime: TimeInterval) {
        let deltaTime = prevTime == 0 ? 0 : currentTime - prevTime
        prevTime = currentTime
        agentSystem.update(deltaTime: deltaTime)
    }
}

extension GameScene: GKAgentDelegate {
    func agentDidUpdate(_ agent: GKAgent) {
        print((agent as? GKAgent2D)?.position)
    }
}

実際に起動すると以下のようなログが表示されます。
agent2を表示しているのですが、agent1(x: 100, y: 100)に向かって移動している事が分かるります。

f:id:llcc:20161210232853p:plain

agent1(x: 100, y: 100)の座標に移動したあとは、バネのような動きをします。

f:id:llcc:20161210233457p:plain

【Swift】BuildTimeAnalyzer-for-Xcodeでコンパイルが遅い原因を探ってみる

こちらのXcodeプラグインを試してみました。

github.com

インストールすると下のようなフォルダ構成になっているのでプロジェクトを立ち上げます。

f:id:llcc:20161208235748p:plain

立ち上げると以下のような画面になります。

f:id:llcc:20161209000033p:plain

ウインドウの指示通り、Other Swift Flagsにフラグを追加します。

f:id:llcc:20161209000120p:plain

フラグを追加したら、Cleanとプロジェクトのビルドを行います。

その後、先程のWindowのプロジェクト名を選択すると下のような画面になります。
どのメソッドがどの程度時間がかかったかが分かるようになりました。

f:id:llcc:20161209000429p:plain

iOS10でAutolayoutのアニメーションが効かなくなってた対策

iOS10になって、以下のようにAutolayoutのconstantを変更 & layoutIfNeededでアニメーションしてくれないという問題に遭遇しました。

constraint.constant = x
UIView.animate(withDuration: 0.1, animations: {
    self.contentView.layoutIfNeeded()
})

ひとまず直接constantとxを代入する事でアニメーションしてくれるようにはなりました。

constraint.constant = x
UIView.animate(withDuration: 0.1, animations: {
    self.contentView.frame.origin.x = x
})

【iOS10】UISearchBarのUITextFieldを取得する

UISearchBarのUITextFieldを取得する方法です。
subviewから無理やり取っているので、今後取れなくなる可能性もあるので注意が必要です。

let searchBar = UISearchBar()
let textField = searchBar.subviews.first?.subviews.flatMap { $0 as? UITextField }.first

Swift3.0でインスタンスのクラス名を取得する

  • 2017/10: Swift4.0でも動作確認済

Swift3.0でクラス名の取得方法が少し変わっていたのでメモ。
dynamicTypeでなくtypeという大域関数を使うようになりました。

// Swift2.0
let view = UIView()
NSStringFromClass(view.dynamicType)
// Swift3.0
let view = UIView()
NSStringFromClass(type(of: view))

NSStringFromClassを使わずにStringを使うことも可能です。

let view = UIView()
String(reflecting: type(of: view))

Apple Pay と In-App Purchase の使い分け方を調べてみた

最近Suicaが使えると話題のApple Payについてです。
Apple Payについて最近調べていたところ、アプリ内の課金にも使える事がわかりました。

しかしアプリ内課金はIn App purchaseが既にあるので、その辺りの使い分けをどうしたらいいかを調べてみました。

Apple PayとIn App Purchaseの使い分け

Getting Started with Apple Pay - Apple Developer

It is important to understand the difference between Apple Pay and In-App Purchase. In apps, use Apple Pay to sell physical goods such as groceries, clothing, and appliances. Also use Apple Pay for services such as club memberships, hotel reservations, and tickets for events. On the other hand, use In-App Purchase to sell virtual goods such as premium content for your app and subscriptions for digital content. Coming this fall, websites can use Apple Pay for physical goods purchases as well as virtual goods purchases that will not be consumed within an iOS app.

The Apple Pay Programming Guide provides details on how to use the PassKit framework to integrate Apple Pay. The In-App Purchase Programming Guide provides details on how to use the StoreKit framework to integrate In-App Purchases.

公式ドキュメントを読んだ所、アプリ内で食料品、衣服、家電製品などの物理的な商品を購入する際に使うようです。
アプリ内のプレミアム会員や機能開放などのデジタルコンテンツは今まで通りアプリ内課金を使えば良さそうです。