しめ鯖日記

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

SKStoreProductViewControllerでアプリから移動せずにStore画面に行く

StoreKitのSKStoreProductViewControllerを使ってアプリ内でアプリのページに移動してみました。

まずはStoreKitを追加します。

f:id:llcc:20161212235732p:plain

SKStoreProductViewControllerはUIViewController同様にpresentをして表示します。
Storeの情報はloadProductメソッドで読み込みます。

import UIKit
import StoreKit

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let c = SKStoreProductViewController()
        c.delegate = self
        present(c, animated: true, completion: {
            let params = [SKStoreProductParameterITunesItemIdentifier: "1099462086"]
            c.loadProduct(withParameters: params, completionBlock: { _ in
                
            })
        })
    }
}

extension ViewController: SKStoreProductViewControllerDelegate {
    func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {
        // キャンセルを押した時に呼ばれる
    }
}

複数の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))