Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 781 - 立ち止まってます(T_T)

アプリ開発を取り巻く環境がSwift 8とiOS10に様変わりして適応するのに時間がかかってます。

仕事の忙しさから毎日コードを眺める時間が取れていません。しばらくコードを見ないと直ぐ忘れてしまう悲しさ(T_T)

さあ少しでもコードを見続けよう。

ビーカーが爆発したら爆発にあわせてcloudが発生してPhysicsWorldが生成され物理空間から接触が判定されます。そして猫がゾンビ化するってところまで、コードしました。次はその接触したときにアニメーションをスタートさせるメソッドを実装します。前にも実装しましたが、これはdeloegateメソッドとして用意されています。

まず接触したときに表示させるSKTextureを用意します。GameScene.swiftの最初に次のコードを宣言します。

let sleepyTexture = SKTexture(imageNamed: "cat_sleepy")
let scaredTexture = SKTexture(imageNamed: "cat_awake")

この2つの猫の表情を使い分けるんです。

さて、「SKPhysicsContactDelegateプロトコールに準拠するためにどうしたらいいのでしょう?

プロトコール準拠する方法は、私の知っている限り2通りあります。一般的なのは「プロトコールとデリゲーション」です。この場合、デリゲーションメソッドをすべて実装することになります。

もう一つが「Extension」です。これは必要なメソッドだけ一時的に拡張することができるので非常に便利です。簡単に拡張できるため乱用するとコードが無秩序になるので注意も必要です。

GameScene.swiftのGameScene{}の下に次のコードを加えます。

// MARK: - SKPhysicsContactDelegate
extension GameScene: SKPhysicsContactDelegate {
 
  func didBegin(_ contact: SKPhysicsContact) {
    if (contact.bodyA.categoryBitMask == PhysicsType.cat) {
      if let catNode = contact.bodyA.node as? SKSpriteNode {
        catNode.texture = scaredTexture
      }
    }
 
    if (contact.bodyB.categoryBitMask == PhysicsType.cat) {
      if let catNode = contact.bodyB.node as? SKSpriteNode {
        catNode.texture = scaredTexture
      }
    }
  }
 
  func didEnd(_ contact: SKPhysicsContact) {
    if (contact.bodyA.categoryBitMask == PhysicsType.cat) {
      if let catNode = contact.bodyA.node as? SKSpriteNode {
        catNode.texture = sleepyTexture
      }
    }
 
    if (contact.bodyB.categoryBitMask == PhysicsType.cat) {
      if let catNode = contact.bodyB.node as? SKSpriteNode {
        catNode.texture = sleepyTexture
      }
    }
  }
}

これだけじゃ不十分です。

とまあこのコードも少し考えるので今日はここまで。

Swiftで遊ぼう! - 780 - SpriteKitも簡単そうで奧が深い

ビットマスクの判定もまだ完全理解していないことを改めて突きつけられたような感じです(^_^;)

ゾンビ化した猫を生成して、コードに埋め込んでいきます。expandContractCloudのアクションを拡張する必要があります。

let expandContractCloud = 
 SKAction.sequence([expandCloud, zombifyContactedCat, contractCloud])

更に初期設定としてnewProjectile()メソッドのコンタクトビットマスクも拡張

beakerBody.collisionBitMask = 
    PhysicsType.wall | PhysicsType.cat | PhysicsType.zombieCat

じゃあビーカーを投げてみましょう。

f:id:yataiblue:20160914174558j:plain

ビーカーが爆発したらちゃんと猫がゾンビ化しました。

これだけ。

Swiftで遊ぼう! - 779 - なかなか進まぬチュートリアル

Sprite Kitチュートリアルのコードを眺めながら勉強しているんですが、なかなか理解が進みません(T_T)

ビーカーが爆発したときの接触判定をするコードをtossBeaker(:strength)メソッドに組み込んでいます。

爆発が生じたときに「cloud」と’「explosionRadius」が生成された場合に判定が進むような仕組みにしました。

let zombifyContactedCat = SKAction.run() {
   if let physicsBody = explosionRadius.physicsBody {
       for contactedBody in physicsBody.allContactedBodies() {
          if (physicsBody.contactTestBitMask 
       & contactedBody.categoryBitMask) != 0  ||
              (contactedBody.contactTestBitMask 
       & physicsBody.categoryBitMask) != 0  {
              contactedBody.node?.run(turnGreen)
              contactedBody.categoryBitMask = PhysicsType.ZombieCat
           }
        }
    }
}

SKPhysicsVodyが持っているallContactedBodies()メソッドも重要です。戻り値が [SKPhysicsBody] になっています。このPhysicsBodyで接触が生じると、新たにPhysicsBodyが次々に作られ(アレー型)保存されます。そのアレー型で作られたPhysicsBodyと元のPsysicsBodyのビットマスクの論理オペレーションで判定させて、2つの物体で接触が生じていれば、physicsContactオブジェクトが生成されます。そのphysicsBodyのノードでturnGreenを実行させて色が変わると。

そしてカテゴリービットマスクをゾンビに変更です。

もう少し考えないとちゃんと理解できていないような...

Swiftで遊ぼう! - 778 - Xcode 8 GM seedでた!

iPhone 7が発表されましたね。

インパクトに欠けたんでAppleの株価は下がったようです。日本市場を意識した防水とFelica搭載という売り込みは、世界市場での魅力に欠けたんでしょうね。

今も昔も変わらずApple信者の私は躊躇いなくiPhone 7 plusを購入するでしょう。処理速度とバッテリー持続時間の向上だけで買い換えの意味があります。

iPhone 6s plusのイヤホンジャックなんて使ったことありません。今まで通りBluetoothイヤホンを使い続けるし、新しいW1チップを使ったワイヤレス環境に興味があります。現在愛用している「Beats by Dr.Dre Powerbeats2 Wireless」がW1チップ搭載の「Powerbeats3」にパワーアップしているのが楽しみです。

そうそうXcodeGM(ゴールデンマスター)がリリースされました。

記事をSwift3仕様に改訂していく必要があるので、まずは以下のページを見直しました。

yataiblue.hatenablog.com

Swiftで遊ぼう! - 番外編5 - Garmin Edge 520Jのバッテリーテスト...

そろそろ新しいiPhoneの発表やiOS10、Swift3.0やXcode 8の正式発表がされると思いますが、今日はちょっと自転車の話題。

7月に手に入れた自転車の整備も終え(自分で)、50km位の短いライドをしてみました。

今までタイヤの太いMTBしか乗ったことが無かったので、空気圧が8 barでパンパンに硬くなった23Cの細いタイヤの乗り心地は衝撃的でした。ダイレクトに路面の状態が身体に伝わってくるところがレーシングカーのようで舗装道路でも路面が悪いと両腕にかなりの衝撃を受けます。絶対に未舗装は走れないですね。それどころか歩道でも段差を乗り越える時が恐怖です。

路面を意識して乗っている余裕の無い状況でサイクルコンピュータを確認する暇がありません。画面切りかえしなくてもいいように知りたい情報を1画面に表示させて走っています。

まだ長距離ライドをしたことが無いのでなんとも言えませんが、Edge 520Jのバッテリーの持ち具合が気になっていました。

皆さんにとって長距離ライドってどの位の距離なんでしょうね。

私はウルトラランナーなのでメインレースで走る距離は100キロです。でも最後に走ったウルトラの大会は2年前、2014年の秋田リゾートカップ100キロ。それ以降、2015年はフルマラソンもまともに走っていなかったんで長距離練習ができていませんでした。

ウルトラの苦しさ楽しさを再び味わうために4月から練習を再開。といっても通勤ランなんで距離は1回の練習で最長15kmまで、月200kmクリアするのがやっとの状態で5ヶ月が過ぎ、本当に長距離走れるのか不安を感じてました。来年ウルトラの大会復帰するために長距離練習をしなければという気持ちが強くなり香川県高松駅(JR)から伊予三島、正確に言えば翠波高原(標高700m)まで83kmを走ることを思い立ったんです。

いつか萩往還という夢もあったんですが、第30回大会で終了(平成30年)という悲しい事実を先ほど知りました(T_T) ナイトラン対策と言いながら夏場の日中を避ける夜走りをしました。

そしてもう一つ、ガジェット大好きランナーの端くれとして、14時間制限のウルトラの大会で持って走れるガジェットのテストもしたかったんで、3つのデバイスのバッテリー駆動時間をチェックしました。1) iPhone6s PlusのNike Running Club使用、2) Garimin Fenix 3J HRのランニング記録、3) サイクルコンピュータではあるけどEdge 520Jのナビゲーション。

http://yataiblue.hatenablog.com/entry/2016/08/20/000000

Nike+Runningは、いつの間にかNike+Run Clubにバージョンアップされていました。前バージョンでは、Facebook連携の問題があって私の環境でアップできなかったんですが、新しくしてFacebookと連携が復活しました(^^)/ 

Garmin Edge 520Jに連携する心拍数モニターとしてGarmin Fenix 3J HRも手に入れました。

一緒に走った連れはEpsonGPSウオッチを試していました。

結論を言えば、ウルトラ(100km)までのお供にGarmin Fenix 3J HRは素晴らしいの一言でした。

バッテリーの持ちがいいiPhone6s plusと新しいNike+Run Clubの組み合わせの結果ですが、iPhoneWi-Fi設定はオフにして、Bluetoothはオンのまま。これはBluetoothイヤホンと連動させて1km毎のラップを聞くためで、途中で写真を撮ったり調べ物をしながら14時間使用しましたが、終了時にバッテリー残量は26%程残っていました。意外にiPhone6s plusのバッテリーは持つのでウルトラの大会で困る事は無いでしょう。ただアプリのNike+Run Clubが不安定なのが気になります。記録をセーブする段階でクラッシュして14時間分のランデータが全て消えてしまったからです(^_^;) そしてもう一つ、BluetoothイヤホンにBeats by Dr.Dre Powerbeats2 Wireless を使っているんですが、バッテリーは10時間ぐらいしか持たなかったんです。これは既知のことで、いつも2つペアで持ちますが、今回は荷物を持ちたくなかったんで1つだけ持って出ました。1km毎のラップはiPhoneから直接流れるようになりました。

Edge 520Jをランに使った結果ですが、本来ならセンサーとしてスピード、ケイデンス、そして心拍数をモニターしながら使用すると思います。しかし、今回は事前に登録したマップを使ってナビゲーション機能をランニングで使用してみたんです。ちょうど13時間で充電を促すメッセージが出てモニターが中断されました。センサーと連動させていないので実際のバッテリーの持ちはもう少し短いのかもしれません。

Fenix 3J HRは、GPSの補足モードを「ウルトラトラック」にすると、素晴らしいバッテリーの持続力を発揮しました。距離データが少し曖昧(ウルトラトラックのため?)かもしれませんが(iPhone6s plus、Epsonで全長83km、Fenix 3J HRで80km)、14時間の記録を終え、その後丸1日普通の時計として使用してバッテリー残量をみると、なんと45%も残っていました! これは凄いです。想像以上でした。そしてこのFenix 3J HRはスイミングでも使えるんです。トライアスリート御用達というのも頷けます。ウルトラランナーにも便利なガジェットです。これがソーラー駆動なら完璧ですね。

f:id:yataiblue:20160907173417j:plain

Edge 520Jをサイクルコンピュータとして使うのであれば、外部バッテリーで電力供給しながら走れるので、問題ないレベルかもしれません。自転車で10時間ぐらい走ることもあると思います。そういう時にどういう結果になるか検証していく必要がありますね。

Swiftで遊ぼう! - 777 - 色のブレンディングも自由自在

接触が判定されたら次に猫の色を緑色に変更していきます。

色の扱いですが、UIColorクラスしか扱ったことなかったんですが、Sprite Kitでは独自のカラークラスが用意されているんですね。それもSKColorクラス。扱い方はUIColorと似てますね。

ますtossBeaker()メソッド内のlet animete〜以下に次のコードを加えていきます。

let greenColor = SKColor(red: 57.0/255.0, 
                       green: 250.0/255.0, 
                        blue: 146.0/255.0, 
                       alpha: 1.0)

これで少しくすんだ緑色を設定します。

let turnGreen = SKAction.colorize(with: greenColor, 
                      colorBlendFactor: 0.7, 
                              duration: 0.3)

「colorize」メソッドで色のブレンディングができるんです。非常に便利なメソッドですね。

今日もこれだけ。

Swiftで遊ぼう! - 776 - 爆発はアニメーション!

爆発に物理空間を実装したので、ここから本当に衝突判定をします。

おっと、「衝突」という言葉を使うのは語弊がありました。Sprite Kitの物理空間の世界では、「collision」と「contact」を明確に区別しているからです。衝突はcollisionになります。しかし、explosionRadiusオブジェクトのphysics bodyのcollisionBitMasukを「0」にしているので、衝突は無視されます。衝突判定をするんじゃなく接触判定(contact)を実装していくんです。

今までの内容で間違った説明があるかもしれません。復習する時に気がつけば修正しますm(_ _)m

さて、接触判定ですが、tossBeaker()メソッド内のif let cloud内を次のように修正を加えます。

if let cloud = beaker.childNode(withName: "cloud"), 
  let explosionRadius = 
  beaker.childNode(withName: "explosionRadius") {

beakerにcloudノードを加えたオブジェクトとbeakerにexplosionRadiusノードを加えたオブジェクトを作ることができたらif節内を実行します。

今日はこれだけ。