2016年12月8日木曜日

今年のベストAppに2つのアプリが選ばれた!!

App Storeで 今年のベスト App - Best of 2016 が発表されました。
Flask から、なんと2つのアプリが選出! ブラボー!




Standland
iPhone App of The Year : Japan



Zones
Apple Watch of The Year : Japan



ベストAppに選ばれるのは初めて。しかも一気にダブル!
なんということでしょう...

しかも、


この扱いということは... 日本のiPhone App Storeでは1番を取ったということになるんでしょうか?! あってる??
ふ...ふ...震えます...

日本のStoreでのみの受賞ですが、逆に言うと「日本のデベロッパー頑張れ」という App Store Japan の方々の応援が聞こえるようであります。
本当にありがたい。ありがとうございます(;_;)
海外でも知られるような日本発のデベロッパーになるよう、頑張ます!

次は...
Editor's Choice マークが付いて、アプリ詳細画面の背景に色がつくようになりたいw


せっかくなので、受賞できた理由を自分なりに解析してみる。


iPhoneの機能を生かした、一風変わったアプリであったこと

Standlandはヘルスケアのスタンド、歩数を使った立ち上がり促進アプリ。
SceneKitを使っている数少ないアプリ。

ZonesはApple Watchの心拍数を使った運動強度測定アプリ。
「運動しているWatchユーザ」という狭いターゲットに絞ったからこそできる、心拍ゾーンを前面に出したアプリ。

Flaskのアプリ達は、
OSの新機能の発表を見て、「お、それを使ったこんなアプリよくない?」
と発想を得て作成することが多いので、iPhoneの機能を生かした形になりやすいのかも。

目的が先か、技術が先か...という意味では良くないのですが、iPhone/Apple Watchのアプリとして存在する意味は保たれるし、開発のモチベーションも上がる。モチベ大切!

プライバシーポリシーが良かったこと

Appleさんはプライバシーにはとても気を使っているので、これも大きかったのではないかと想像。
Flaskのアプリは全てそうなのですが、ユーザの登録情報は収集してません。
例外的に収集しているのはクラッシュレポートのみ。
大手サンのアプリではそんな運用無いでしょうね...

ヘルス分野であったこと

今年のAppleさんはヘルス分野に力を入れていたように思うので


あともちろん、Flaskで心がけている「使いやすくて、楽しい」を実現するための、デザインや設計も評価されたと信じております!

来年も頑張ろうー


• • •

2016年11月11日金曜日

Search Ads on iOS を試してみた

Search Ads とは、App Storeで検索した際に広告として自社アプリを表示することができる機能です。
現在、U.S.ストアのみのテスト展開中で$100のプロモコードを配布中です。
プロモコードの期限は December 31, 2016 at 11:59:59 UTC まで。

Redemption of your Search Ads Launch Promo Code (including full use of the Launch Promo Credit) must be completed by December 31, 2016 at 11:59:59 UTC (the “Promotion Expiration Date”). Search Ads Launch Promo Codes not redeemed by the Promotion Expiration Date will not be eligible for redemption. Any portion of the Launch Promo Credit not used by the Promotion Expiration Date will be deemed expired and no longer available for use. 
 12月31日までに$100を使い切った方がよさげ。
デベロッパーアカウントに登録している人は、お早めに!

Search Ads 設定

以前個人で作成した 12ホワイトボード アプリで試してみました。
価格は無料。
iPhone,iPadのユニバーサルアプリ。
アプリ内にバナー広告があります。

Search Ads の登録画面


Default Max CPT Bid が、広告をタップした時に支払う金額です。
※ CPT = cost-per-tap
Default Max CPT Bid
The maximum amount you're willing to pay for a tap on your ad. This bid will apply to any keyword you add to this ad group unless you apply an individual keyword bid.Tip: Use what you know an average customer is worth to you as your starting point.
Then decide how much of that you can afford to spend to acquire each new customer. For example:
  • You know you can afford to spend about $2.50 per customer.
  • You estimate 40% of people who tap your ad will download your app.
  • You decide you can pay: 40% of $2.50 per tap = $1

ここは真面目に計算すべき所ですが、様子を見てみるという軽い気持ちで $0.05 を設定してみました。
この金額はデフォルトのものになり、別途、キーワードごとに値段を変えることもできます。

私はキーワード指定なし、Search Matchをオンにしておまかせ状態でみてみました。

結果

日に$4ぐらい使い、約1ヶ月で$100に達しました。



レポート機能から、キーワード毎のCPA, CPT, Impressions, TTR, CRなどが見えます。
※ CPA = cost-per-acquisiton
※ CPT = cost-per-tap
※ TTR = tap-through-rate
※ CR = conversion-rate




whiteboard のキーワードの場合、
4,150回表示され、18.24%(TTR) の人がタップ。
タップした人のうち、48%(CR) がインストール。結果、1インストールされるのに 約 $0.10。
...とみればいいのかな??
広告経験がないので、いい値なのかどうかがわからないが。




iPhone と iPad で使った金額はほぼ同じでした。
ユーザ的にはiPhoneの方が多いハズなので、iPhoneでは値段で負けて表示されなかったパターンが多かったかもしれません。
プロモコードの期限も近づいているので、年末にかけてどんどん値上がりするかも?


この期間の収入はどうだったか?ですが、なぜかU.S.よりも日本の方がダウンロード数が延びていたので比較は難しいかったというオチ。
ただ、収入源はバナー広告だけのこのアプリでは、広告費をpayできませんね....


ま、でも簡単に試せるというのは良いと思った。
Apple Watchユーザ限定などのフィルタ条件が加わるといいな!


• • •

2016年11月2日水曜日

LLP-有限責任事業組合 について

Flask LLP を設立してからもう3年が経ちました。
この LLP-有限責任事業組合 についてあまり情報がないと思うので、これまでの経験を元に書いてみようと思います。

※ 私は専門家ではないので、間違いがあったらゴメンナサイ

LLP設立のきっかけ

よく聞かれるのですが、理由は2つあります

1. iOSのApp Storeの販売元の名前を、「Flask LLP」にしたかった
2. 個人事業主との兼業が前提

App Storeの販売元の名前を個人名以外にしたい場合には、法人用の開発アカウントを作成しないといけないのですが、その際に D-U-N-S Number が必要になります。
D-U-N-S Numberとは、全世界を対象とした企業識別コードです。
この番号を取得するには、企業として存在する証明が必要で、登記証明などを求められます。
※ 3年前だとLLPでもD-U-N-S Numberが登録できましたが、今も出来るかどうかはわかりません...。

ですが、自社のアプリ開発だけで生活できるお金は稼げないのは分かっていたので、個人事業主を兼業して内職する必要がありました。

で、パススルー課税が認められている LLPを選択したわけです。


LLPのデメリット

  • 法人格がない
  • 法人番号 をもらえない
  • 銀行口座がつくりにくい
  • クレジットカードが作れない
  • 提出資料が増える
  • 利益の分配率が自由に変更できない
  • 会計ソフトがLLPに対応していない
  • 情報が少ない

いやー、数えるとホントいっぱいあります。
口座作るにしても、会計書類について相談しにいくにしても、
は? LLP?
って所から始まります。

設立自体は書類を揃えるだけで簡単にで来てしまうので、オススメするサイトもありますが、私は正直オススメしません(笑)

LLP決算で必要な書類


LLP作成後どんな決算書類が必要なのか?
ちゃんとまとまっているサイトがあまりないんですよね。
最低限必要な書類は以下になります。


事業年度経過後2ヶ月以内に作成
  1. 貸借対照表
  2. 損益計算
  3. 附属明細書
特に提出する必要はない。
これらに基づいて損益の分配を行ったり、1月提出の書類を作る。


1月末までに税務署に提出
合計表はLLP全体のもの。
計算書は組合員毎に作成。


個人の確定申告時に一緒に提出
  1. 有限責任事業組合の組合事業に係る所得に関する計算書
  2. 有限責任事業組合用の青色申告決算書

青色申告決算書は、LLPと個人の2つが必要ということです。
確定申告書Bでは、LLPと個人の合計したものを記述する必要があります。


組合員毎に作成する「***に関する計算書」や、LLPの青色申告決算書では、組合員の分配比率分の数値を書く必要があります。
たとえば、100万売上の場合、分配比率50%の人は50万とか。
(出資割合と損益分配割合を変えたい場合には更にややこしいことに)

つまり、会計ソフトがLLP対応していない限り、計算は自分でしないといけない。
LLP用青色申告決算書や、確定申告書Bは、会計ソフトで自動出力できない。
対応しているソフトあるんでしょうかねー。私は知りません....

ま、決算も結構たいへんなんだよということで。


LLPを運用する際のポイント

LLPをdisってきましたが、パススルー課税したい場合にはLLPしかないわけで。
これから作ろうとしている人に向けてのアドバイスなど。


銀行口座

企業から振込してもらう口座はやはり会社名がいいですよね。信用的にも。
ゆうちょ銀行で、名前を フラスク有限責任事業組合 で作成ができました。
但し、住所のところに、住所 + 代表者名 が記載されます。
クレジットカードは作れませんでした。

しかし、経費の引き落としを考えると、やっぱりクレジットカードがあると便利です。
が、LLPで法人カードが作れるかどうかは不明。さらに、開業後3年以上で黒字が続いているなどの制限もあってハードルがあります。

でも個人用のクレジットカードは、口座名義が同じ個人名でないと作れない。
なので、個人名義の口座を別途作って、クレジットカードを作ってしまうのが楽そうです。


決済期間

LLPでも設立時に決済日を決めることができます。
ですが、個人事業主と同じく 01/01 - 12/31 がオススメです。

Flaskは7月決算にしていたのですが、そうするとfreeeやMFクラウドなど、クラウド会計サービスで計算しにくくなります。

LLPの決算書類は青色申告書なので、個人事業主用の帳票出力が必要。
ですがクラウド会計の個人事業主用は、01/01 - 12/31固定で変えられない。
法人用は決算月を変えれるけど、青色申告書がでない。(まぁ出てもそのままでは使えないけど)
...というジレンマに陥ります。
法人用の値段設定は高いので、個人事業主用で運用できればその方がいいと思うわけです。


組合員

LLPは組合員が2名以上で設立できますが、この組合員を慎重に選ぶのが大事。
その年の貢献度などにより損益分配割合を変えたくなったり、途中加入、途中脱退があったりするとまた面倒な計算が必要になります。
ちょっとしたブレを、お互い許容できるような間柄でないと、破綻しそうに思います。
もう、そうなったらLLP解散して新しく会社作るなりしたほうが良さげな....


最後に

海外のLLCはパススルー課税なのがうらやましい。早く日本もそうなって欲しいもんです。
そして、LLPだけで生活できるような目処がたったら卒業するんだ...


• • •

2016年9月22日木曜日

運動強度が見れるアプリ Zonesをリリースしました!



Zones 運動強度で効率的トレーニング
価格: 無料
iOS 10, watchOS3 以上


iOS10のDay1を目指して作成していたアプリは、Zones(ゾーン)という名前のトレーニングアプリ。無事リリース!

Flaskでは、FitPort, Standland とヘルスケアに関するアプリをリリースしてきて、とうとう本格的なワークアウトをするためのアプリまで到達です。

既にApp Storeには多くのワークアウトアプリがあります。巨大なRuntasticRunKeeperと真っ向から戦うつもりはございません。
では、なぜ作ったかというと、Apple Watchユーザーにとって最良のものを作りたかったから。

ご存知のように、Apple Watchでは心拍数を測定できる。
心拍数からは、消費カロリーだけでなく、運動強度もわかる。
そんな心拍数を活用したワークアウトができれば、もっと楽しいはず。

「モバイルファースト」という言葉が以前流行りましたが、このアプリはいうなれば「ウェアラブルファースト」なアプリ。
iPhoneしか持っていない人ももちろん使うことは出来るが、Apple Watchユーザーの方がより満足できる作りになっている。

ワークアウトは、ヘルスケアに登録があるもの全てが対象。
つまり、RunKeeperでランニングして、Zovaで体幹トレーニングしたものを、Zonesでまとめてみるというダッシュボード的な使い方も可能。

Apple Watchユーザで運動している人は楽しいはず。是非お試しを!

では、このアプリの作成の経緯を入れつつご紹介。


心拍数がみたい

私は全然運動していない人。1時間も散歩すればいっぱい動いたー!と満足してしまうレベル。運動に関する知識もほとんど無し。

そこにアプリ企画段階で、一緒にアプリを作成している @horiuni さんから「心拍数がみたい」という要望が。
その時は、心拍数をグラフとして表示するぐらいのイメージでしかなかったが、運動の時に心拍数はどんな感じに参考にできるのだろう?と調べ始めたのが、このアプリの最大のコンセプトのきっかけに。


運動強度の衝撃

調べていくと、心拍数で運動強度が分かるということを知る。
正確な運動強度を測るのは難しく、
「普通の人がランニングしたらコレぐらい」という目安があるが、その人の心肺能力に左右されるので、「自分がキツイと感じるか?」という自覚による判断方法もあったりもするよう。数字信者の私には心許ないw

心拍数はそれらの判定方法の1種だが、自分の心拍数から数値として判定できるので客観的に見るにはよい方法と思う。

運動強度を知って、まずやってみたのが、自分の散歩がどれくらいの運動強度なのか?を測ること。
散歩だから、だいたい60くらいかな?と思いつつ計算してみると、衝撃の30!
その頃の散歩はIngressで遊びながらだったので立ち止まることも多く、ほぼ、普段の生活と変らない強度になっていたらしい。

当たり前の結果なんですが.....笑えた。
「歩数も1万歩超えたし、ちゃんと運動したな」って思っていたのに、それはほとんど運動になってないよって数値で言われた訳で。人間て、なんてエコなんだ(笑)

これを知って、「運動強度」が見えるの面白い! とはじめて思ったのでした。


心拍ゾーン

運動強度を分かりやすくするため、心拍ゾーンとして、目的別に4つに区切りました。
各ゾーンに色をつけて、色でさっと判断できるようにしています。



自分の目安の心拍数を覚えている方もいらっしゃいますが、私は全く覚えていません。現在でも、完璧にこの ”色” 頼り。
運動中にゾーンが変わると、Hapticで手首をトントンして知らせる機能も便利です。

私のランニングは、オレンジの有酸素(CARDIO)ゾーンで走り、疲れたら歩く
というゆるいものだが、黄色の脂肪燃焼(FAT BURN)ゾーンになったら再び走る という決まりを作っている。
それまでは、一度歩き始めると、ぼーっと歩き続けてしまうことが多々あったが、Hapticのお知らせのおかげで、再び走り始める強制力に使えている。


体力アップの実感が欲しい

トレーニングは続けるのが大事。続けるには、だんだん体力がアップしてきたな!という実感が欲しい。
体力アップって、どうやって実感できるだろう?と出てきた案はコレ。
  1. 回復心拍数が上がった
  2. 安静時心拍数が減った
  3. 同じペースなのに楽になった(=心拍数が減った)
  4. 同じ運動強度なのにペースが上がった
回復心拍数は、上がった心拍数が回復する量が多いほど心拍能力が高いというモノ。これはこのアプリで測定できるように。



安静時心拍数は、普通の人だと65-75bpm位だが、アスリートの人は50bpm以下になったりするそうで、それをスポーツ心臓 と言うらしい。
心拍数測定ミスなどもあるし、実感を得られるほどの差が出るのは難しいかーと採用なしに。

ランニングであれば「ペースが同じなのに、以前より楽になった」「以前よりペースが早くなった」が実感できると嬉しい。
でもどんなトレーニングをしているか?によって違うので、アプリではどう見せるといいか?と議論して、辿り着いたのは、自由にカスタマイズ出来るグラフ/リスト。




設定できる項目はこんな感じ。



シンプル大好きFlaskとしてはマニアックな機能ですが、いろんな角度から情報を見ることができるので面白いと思う。
(カスタマイズは、有料オプションです)

エアロバイク事件

実は当初は、Watchでの計測ではランニング、ウォーキングをターゲットにしていたが、開発途中でメンバーが、まさかのエアロバイクを購入(笑)
最終的に、いろんなトレーニングが対象のアプリに。
エアロバイクの場合、Apple Watchでは距離を推測するのが難しい。でも、バイク自体には距離はでる。...ので、測定後に距離を手入力できる機能も追加した。

最終的に対応したのはHealthで登録出来るアクティビティタイプ全種類で、70種以上。
フィットネスジムに通っている人にも是非使って欲しいな。


運動できてる?

ちゃんと運動できたか?とか、かなり頑張ってるなー!と振り返りたい。
この、"できたか?" というのは、自分がどんな目標でやっているのかによって、それぞれなので悩みどころだった。

毎日できたか?
有酸素(CARDIO)以上の運動が週に60分以上できたか?
週に150分以上の運動できたか?
体幹を鍛える運動もできたか?

などなど。
それらを週単位で見れるように作成したのが以下のスタッツ画面。(有料オプション)



以前は、何分の運動時間だったか?ぐらいしか気にしていなかったが、運動強度が見えるようになってからは、高い強度の運動も意識するようになり、ゾーンの合計時間をよく見るようになった。

私の目標は、
週に75分以上 有酸素ゾーンの運動をする
その中には、40分以上の運動を週3〜4回いれる。
これ、なかなか厳しい...

最後に

Zonesアプリ開発で、今年も熱い夏となりました。
かなりキツかったので、忙しい開発は暫くやりたくない気分です(笑)
気づくと秋の気配でトレーニングもしやすい気候になっていますね。

Apple Watchユーザは日本ではなかなか見かけないのが寂しいですが、やっぱりWatchはアクティブトラッカーとしての使い方が一番分かりやすいと思う。
watchOS3でも、トレーニング目的の機能強化が多く、そっち方向に寄ったのかなという感触も。

「iPhoneでできることがWatchでもできる」 だけであれば、私もWatchをわざわざ着けない。Watchでないと出来ないことが魅力的であるからこそ..だと思う。

Apple Watchを手に入れる前はFitbitやUpを着けていたが、手動の同期なしで歩数や心拍数がiPhoneに入ってくるのがやっぱり便利。
大きい、バッテリー稼働時間が短いというデメリットはあるけども、逆に、ZonesのWatchアプリは大きな画面でカラー液晶であるからこその表現が出来ていると思っている。
Series2が発売されたし、もっと多くの人がApple Watchを手に入れて欲しいなー。


• • •

2016年7月30日土曜日

週の開始曜日はいつ?

開発しているアプリで、週の表示をする場合、何曜日を開始曜日にするのが一番いいのだろう...と迷う場合が出てくるので、調べてみました。

地域によって変わる開始曜日


WikiPediaによると、

週が始まる日は、日曜日とすることもあり、また月曜日からとするところもある。アメリカ製のカレンダーヘブライ語ポルトガル語アラビア語ペルシア語ベトナム語などは前者、フランス製のカレンダーなどは後者である。イスラム圏では金曜日が公休日になっていて、カレンダーも土曜日から始まるところがある。


ということです。

西暦で表すグレゴリオ暦は、もともとユダヤ暦由来で、ユダヤ暦は日曜日始まりだったため、日曜日始まりが通例だった。
だが、標準規格 ISO 8061 で、月曜日始まりと制定されてからヨーロッパ圏を中心に月曜始まりに変わっていったという経緯があるようです。


日本では、カレンダーは日曜日始まりが多いけど、手帳では月曜日始まりが結構合ったりします。
土日がお休みの人が多いので、土日がつながっている方が予定も書きやすいというのがあるのかと。
結論、開始曜日は固定にするのは難しいということですね。


週番号

週番号というのはご存知でしょうか。
年のはじめをWeek1として、週ごとに番号が振られていきます。
日本では馴染みはあまりないのですが、海外の人とやり取りしていると、「○週目の○曜日」という表現で日付指定されることもあるようです。
日本では、月単位で見ることが慣れている気がしますが、海外では週単位で見ることが多いと聞いたことがあります。

さて、これも、 ISO 8061 で規定されていて、
最初の木曜日を含む週が、その年の第1週である。
 月曜日始まりの場合、木曜日がちょうど真ん中の曜日だからですね、きっと。










今年2016年は、最初の木曜日は1月7日になります。
つまり1月1日は、2015年の最後の週になる。

ここで、週番号をさらにややこしくしているのが月曜日始まり以外での地域での週番号になります。

アメリカや日本など日曜日が週始まりの場合は、1月1日がWeek1になる。
イスラム圏では最初の金曜日を含む週がWeek1になる。

と、計算方法が違うのです。なんとまぁー!
結果、今年2016年の場合、イギリスの週番号と、アメリカ/日本の週番号にはズレが生じています。

iOSでの扱い


iOSでは日曜日1〜土曜日7 までの番号が振られます。
厳密には、カレンダーに依存するようです。
NSDateComponentsリファレンスより
Weekday units are the numbers 1 through n, where n is the number of days in the week. For example, in the Gregorian calendar, n is 7 and Sunday is represented by 1.
グレゴリオ暦を指定している場合は、週の開始は日曜日であるので、日曜日=1とする。という方針なんですね。
グレゴリオ暦、和暦、インド暦のカレンダーで見てみても、全て" 7 and Sunday is represented by 1." となっていました。
なので、週は7日前提で考えていてもまずは問題なさそうですが、週が7日でないカレンダーが導入される可能性もあるかも?ということは理解しておく必要がありそうです。

そして、曜日の表記

Calendar.current.weekdaySymbols
についても日曜始まりの配列になっています。
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]


このルールをベースに、地域毎の週の開始曜日の差は
Calendar.current.firstWeekday
Locale.current.calendar.firstWeekday
で週の開始曜日の値を別途取得出来るようになっています。

地域毎の差は、Settings > Language & Region > Region の設定に依存しており、Regionを変えると以下のようにfirstWeekdayに差が出てくるのがわかります。

日本、アメリカ1 (日曜日始まり)
イギリス2 (月曜日始まり)
イラク7 (土曜日始まり)
バングラデッシュ6 (金曜日始まり)


iOSアプリで、週の表示をする場合、
デフォルトはこのfirstWeekdayを元に決めると良さそうです。
週番号(weekOfYear)もRegionに合わせた番号が取得できます。

標準の「カレンダー」アプリでは、週番号を表示できるオプションがあります。
そして、週の開始曜日を変更出来るオプションがあります。
日本の地域設定で、週の開始曜日を日曜日以外にしてみても....週番号の値は変わらず日曜日始まりルールで表示されていました。
この場合、正確にいうと土曜日の週番号が違うことになると思うのですが、まぁよし としているのかな。

ちなみに、標準の「カレンダー」アプリの開始曜日を変更出来る設定内容はサードパーティのアプリからは参照できないようです。残念。

iOSで週の始めの日付を取得する


設定されているRegion,カレンダーなどを考慮して、週の初めの日を取得するのは次のような感じになるでしょうか


private func firstDayOfWeek(date:Date) -> Date {
let cal = Calendar.current
let firstWeekDay = cal.firstWeekday
let thisWeekDay = cal.dateComponents([.weekday], from: date).weekday!
var diff = thisWeekDay - firstWeekDay
if thisWeekDay < firstWeekDay {
diff += cal.weekdaySymbols.count
}
diff = -1 * diff
let firstDay = cal.date(byAdding: .day, value: diff, to: date)!
return firstDay
}
view raw firstDayOfWeek hosted with ❤ by GitHub

週の表示についても、各地域によって色々差があって世界は広いなー

• • •

2016年6月21日火曜日

WWDC 2016 をふりかえり


WWDC 2016 に行ってきました!

Keynoteの会場について


今年はEarlyCheckin, Keynote, BashがMoscone Centerではなく、Bill Graham Civic Auditoriumで行われました。


会場は2階席もあり、今までよりもステージが近く見えるようになった感じです。
席数も多いので、開始直前に行っても座れたと思います。
私は8時過ぎに行ったのですが、其の時間だと、もう直前にいってもさほど変らない位置だった気もー。もちろん、座れます。


1階席と2階席とで別の並び列になっていたみたいで、違う階には自由に行き来はできませんでした。
そんなことを全く知らずに並んでしまった私。もし来年も同じ場所であるならスタッフに聞いてから並ぶと良いかと思います。
日本人から見ると大きな人が多く、そういう方が前に座るとちょいカナシイので、段差がある2階席の方が私は好きかなー!

最寄り駅はCivic Centerで、ちょっと治安が悪い所。
そのせいもあり、夜中から並ぶ人は少なくなったかと。

並んでいる間は屋外なので、日差しをモロ浴びます。
かなり強い日差しなので、日焼けにご注意。

夜に開催されるBashは、Mosconeから行き/帰りのシャトルバスがでました。
治安的な意味でドキドキしていたので、このバスはとても助かりました。

発表内容について

今年はiOS, watchOSのボリュームが大きかった。
Healthにも力入れているな!とも思いました。(私の偏見が大きくアリマスガ)

iOS


iOSについては、Siri, Today Widget, Peek/Pop, Map/MessageのExtension系....と、アプリの外にある拡張機能が充実してきた印象。
言い換えると、”アプリを起動しなくても”  情報を得たり、目的の機能へショートカット出来たりする部分がとても多くなった。

今までは、アプリの構成/画面を考えてから、アプリ外のものでも使えそうなものがあれば....という方向性だったけど、iOSで出来ること全体を見渡してからアプリ構成を考えるという方向性が正当な道筋になってきたと思う。

”このアプリでは、どの機能/情報が一番大事なのか?”
というのは、今までの開発でももちろん大事だったけども、あれもこれも大事...選べない...となってしまいがち。
だが、拡張機能を考える際にはちゃんとフォーカスせざるおえない強制力があると思う。

アプリの大きさについても、「いろんな事ができる大きなアプリ」よりも「目的が明確な小さなアプリ」の方が、iOSでは合っている というのが、さらに強まった感じも受けた。

あ、あと、小さい所ですが、UICollectionViewでのPreFetchやAutoSizingの機能はとても嬉しい ♥

watchOS

watchOS3が発表されました。
アプリ起動が早くなり、Dockでアプリが選べ、シチュエーション毎にfaceを切り替えできる など、watchでの不満点を大きく解消されたものだったと思います。
歩きながら、アプリ一覧から目的のアプリを見つけてタップするのは、ほぼ無理でしたし!

開発の立場からすると、
デジタルクラウンが使えるようになったこと。
Backgroundでの処理もできるようになったこと。
が大きなニュース。

BackgroundはなかなかのBigNewsです!
Backgroundで動けないから...と諦めた機能/ アプリアイデアは多いと思うので、幅がぐーんと広がりました。

個人的に一番嬉しかったのは、HealthKit関連のバグが解消されたこと。
watchOS2では、Appを起動してBackgroundにすると、Glance/ComplicationでHealthKitの情報が取れなくなるというバグがあったため、実装を見合わせていたアプリがいくつかありました。
watchOS3では、Glanceが無くなったというサプライズ(笑)。
じゃぁcomplicationは...?と、試してみたら.... 動くようになっていた It's nice!!

complicationの更新回数も 50回/day と数が明記されました。
(watchOS2では数の明記がなく、2回/hourぐらい。それ以上だとexhaustしちゃうっぽい というdeveloper達の経験談しかなかった)

残念なのは、watch faceの作成がサードパーティに公開されなかったこと。
公開されたら、現地でちょっちょと作ってLabに持って行こうと気合入れていたのに。

HealthKitのBackgroundDelivery(データ変更があった時アプリがForegroundじゃなくてもイベントが受け取れるやつ)が、watchOS3には追加されていなかったのも残念。
それがあれば、complicationの更新も賢くなれるのにな。
(iOSには既にあります。iPhoneがLock状態だと取得できないのは多分変わってないと思われるが。)

気になる点は、watchOS2で既にリリース済みのアプリは、watchOS3用にしてしまった時どうなるのか。(未確認)
watchOS1 → watchOS2の時は、下位互換がなくて混在パッケージにする必要があったけど、今回もそうだと面倒だな....

Labチャレンジ



HealthKitラボ、XCodeラボ、SppechAPIラボに行ってきました。
英語がほとんど喋れなくても優しく聞いてくれる...カタコトの人とのやりとりが慣れている?...ので、思ったより行きやすい所でした。
UIラボには良い相談ネタが見つからず行けなかったのが心残り。

感想としては、図解最強!
コレは、こっちの意味?それともこっちの意味?というのを図解にして持って行くと、Which one? ぐらいで行けちゃいますもん。

私の大きな問題は、リスニング能力。
プレゼンテーションでははっきり発音してくれるし、スライドあるし、言う内容も想像でているしで、理解はし易いけど、面と向かってのTalkは難しい.....


聞いてみた内容を少し共有。

HelloGameKit: A skeleton app for turn based games on watchOS のサンプルアプリで、GameKitが見つからないというコンパイルエラーが出てしまう件。

シミュレーターのGameKitフレームワークの中にヘッダーファイルがなぜか無かったのが原因。
It's strange... って言っておられました(笑
はじめて見る現象だって言ってたけど、horiuniさん端末でも同じ状態だったので、みんなそうなると思うんだけどなー。
watchOSのGame系にはみんなあまり興味ないのかなw
多分、XCode betaのパッケージミス?と思うので、XCodeのアップデートで直ると想像。


SpeechAPIのLimitの詳細について

英語がそれとなくとしか聞き取れなかったので、間違いはあるかもしれないのですが!
アプリ単体として使うには十分のlimitがあると思う。
ドシドシ使うものについてはLimitが掛かる可能性がある。Appleから連絡いくかもしれないし、いかないかもしれない。guaranteeはない。
多くなりそうだったらAppleに相談してみるといい。

結論、video内容とあまり変らない返答な訳で。
しっかりとした数はまだ決めてなくて、運用してみてから考えていこうとしているのかなーと見た。

勝手に止められてしまう といえば、iAdもそうだった。
連絡もなしにiAd広告が表示出来ない状態になった時があり、問い合わせしたら、「もう見れるようになっていると思うから確認してね」と言われておしまい みたいな。
....ま、そんな感じなのかな。

外部イベント




期間中に唯一行ったのが、RelayCon
ConnectedのPodcastは聞いていたのですが流し聞き程度でして...今回、実際に見て声と人が一致できるようになりました(笑
以前、MacStoriesでFitPortのことを書いてくれた Fedelicoさんは、まだFitPortのことを覚えてくれていましたー!感動!
昨年、The Talk Showに行ってみた時同様、アジア人なのは私達だけ?という状態でしたが、部屋内が明るめだったので、少し気持ちに余裕が出来てよかったです。
が!今年のThe Talk ShowのゲストはPhil Schiller + Craig Federighi だったそうで!
すっごーいーーっ 来年は誰が追加されるんだろ?!?!


終わってみて

WWDCの旅は、一旦リセットして振り返りをする良いきっかけになりますね。
道中、中国人の青年に、「ビジネスモデルをちゃんとしないといけないのでは? 僕はそんな不安定な生活はできない」みたいなことを言われ、うーむーーーと考えさせられたりもしました。

最近のFlaskは、「最新の機能を使ったアプリをDay1にリリースする」
が、大きな動きになっていて、WWDC終了後〜秋までは案件も受けず、どっぷり開発予定です。
こうやって、さっと動ける、beta中の変更にも柔軟に対応できるというのが、IndiDeveloperの強みだと思うので。

私個人の目標としては、いい感じのUIがもっとさっと発想出来るようになりたい。
考える時間がすっごく長くて、いい所の落ち着き/妥協がなかなか出来ない私。
もうだめだーと諦め、ポイっとしてしまったことも多々.....。
慣れれば早くなるかとおもいきや、全然そんなことないのが苦しい。
そこに時間をかけていると、すごく時間を浪費してしまっている気分になっちゃうんですよね。
プログラムを書いているのが実質の作業時間という感覚が根強くあるし、近道を常に探してしまうーー。
が、近道はないのだと言い聞かせ、考える時間をつくっていこう。
それが、もっと多くのアプリを作成できることに繋がるに違いない。

コミュニケーション能力は、自分に自信がつけば得れるに違いない → 逃げたw



• • •

2016年5月2日月曜日

SceneKit: プログラムで衝突を制御

SceneKitシリーズ 第五弾。
前回の記事では、Dynamicを使って自動的に動かす方法を紹介しました。今回はKinematicを使って、動きをプログラムで制御する方法をご紹介します。

描画処理の流れ

Dynamicでは、SceneKitに全部処理をお任せできていましたが、自分で制御するには、そもそもどのように処理が進んでいくかのフローを をまず理解しておきましょう。

SceneKitでは、iPhoneデフォルトで60fps...つまり、1秒間に60frameの描画処理をしています。
その1frame毎に以下の処理が行われています。




rendererと書かれているのは、SCNSceneRendererDelegate のことで、SCNViewはこのDelegateを実装しています。

この図、おおまかにいうと以下の流れ。
  • モノを動かしてみる (renderer:updateAtTime )
  • SceneKitが衝突があるかを計算 (Scene Kit simulates physics) 
  • 衝突結果から跳ね返った位置に移動させる(renderer:didSimurlatePhysicsAtTime)
  • 描画処理を開始 (renderer:willRenderScene:atTime:)

"まず動かして、衝突をSceneKitに判断してもらう" という方向になるわけです。

ではでは、Standlandで自由に動き回るGemmy達の実装を例にご紹介していきます。



青緑色の子がGemmy達
自動でウロウロ動き回ります。
このままGemmyが歩くとぶつかるね...

1. 何と衝突させるのかを指定

前回に少し説明をした、physicsBodyをKinematicで作成します。

let shape = SCNPhysicsShape(node: bodyNode, options: nil)
let physicsBody =  SCNPhysicsBody(type: .Kinematic, shape: shape)

そして、何と衝突させるのか? の設定もします。

physicsBody.categoryBitMask = FLSPhysicsCategory.Gemmy
physicsBody.contactTestBitMask = FLSPhysicsCategory.GemmyHits

categoryBitMask が自分自身のBitMask。
contactTestBitMask が衝突する対象のBitMask。

Gemmy同士や、キャラ、看板には衝突するけど、床との衝突判定はいらない など設定ができるわけです。
幽霊キャラの場合は壁は衝突せずにすり抜けれる とか。
飛んでいるキャラは水の上にいけるけど、歩いているキャラは行けないとか。
パターンはいろいろ!

この2つのBitMaskはKinematicに限らず、Dynamic/Staticでも同様に設定します。
その他、physicsBodyにapplyForceの設定もあり、指定の向きに力を入れることもできます。
使っていないので試せていないのですが、重力以外の力、例えばボールを投げるとかそういう時に使えるものと思います。

2. 動かす

renderer:updateAtTime 

Gemmyが歩くのは、ぴょんぴょん跳ねているアニメーションに加え、updateAtTimeのタイミングで以下のようなコードで位置移動 することで実装しています。

let radians = node.rotation.z * node.rotation.w
let direction:float3 = float3(sin(radians), -cos(radians), 0.0)
let position = SCNVector3(float3(node.position) + direction * speed)
node.position = position

※Standlandではyとzは反対で扱っているのに注意

Gemmyが向いている方向へ移動する というコードです。
やっていることとしては、Nodeの位置(x, y, z)を変えているだけというシンプルなもの。

3. 衝突後の位置を判定

Gemmyを歩かせるようにしたら、衝突が発生するはず。
衝突の情報は、SCNPhysicsContactDelegate で取得します。
このDelegateは、SCNView.scene!physicsWorld.contactDelegate で設定します。


physicsWorld:didBeginContact: 

衝突した時にcallされます。
引数に情報として渡される SCNPhysicsContact が衝突の情報です。
  • nodeA , nodeB が衝突したノード
  • contactPoint が衝突した位置
  • contactNormal は衝突して跳ね返ったりした方向
  • collisionImpulse は衝突の勢い
  • penetrationDistance は衝突で重なった距離
physicBodyに設定した重量や反発係数などを元にして、SceneKitが衝突の物理計算をして結果を返してくれているわけですね。素敵です。
これらの情報から、衝突して跳ね返ったらどこの位置になるかを計算します。
Gemmyでは、contactNormal × penetrationDistanceの値を、元のpositionから動かすようにしました。

physicsWorld:didUpdateContact:

衝突情報が変更された時に呼ばれます。
なので、didBeginContactと同じように衝突の処理を組み込んでおきます。

4. 衝突の位置を設定

renderer:didSimurlatePhysicsAtTime 

衝突後の位置(position)をSCNPhysicsContactDelegateで計算しましたが、実際にNodeに設定するのは、didSimuratePhysicsAtTime で行います。


これで、処理は完成。

分かってしまうと簡単ですが、最初はなかなかスムーズには実装できませんでした。
Gemmyが壁をすり抜けてしまう...脱走事件が発生したりして!
衝突の跳ね返りの位置の設定が漏れている子がいたり、障害物がおかしくなっていたり という問題を一つ一つ見直していくことで解決していきました。

こうやってプログラムで制御することで、
「ランダムで歩いたり、向き変えたり、アニメーションいれる」
「何かにあたった後は、立ち止まって、向きを変える」
という自動制御Gemmyが出来上がっています。


Gemmy達がワヤワヤ動いている様子は是非アプリで見てみてくださいね!


作成したアプリはこちら↓


Standland - 座りすぎ解消!スタンドランド


• • •

2016年4月8日金曜日

SceneKit - 重力と当たり判定用のカラダ

SceneKitシリーズ 第四弾。
醍醐味である、物理シュミレーションの話を開始です!

SceneKitは、まぁフレームワークなので、用意された箇所に設定していけばある程度のものは簡単に作ることができます。





ボールを落としているこんな例は、以下のコードだけで実現しています。

import UIKit
import SceneKit
class ViewController: UIViewController {
@IBOutlet weak var sceneView: SCNView!
override func loadView() {
super.loadView()
let scene = SCNScene()
self.sceneView.scene = scene
sceneView.autoenablesDefaultLighting = true
setupScene(scene)
let y:Float = 8
//反発係数を変えた3つ球体を追加
scene.rootNode.addChildNode(createSphere(SCNVector3Make(-1.5, y, 0), restitution: 0.5))
scene.rootNode.addChildNode(createSphere(SCNVector3Make(0, y, 0), restitution: 1.0))
scene.rootNode.addChildNode(createSphere(SCNVector3Make(1.5, y, 0), restitution: 1.5))
//4秒後にもう一つ球体を追加
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(4 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
scene.rootNode.addChildNode(self.createSphere(SCNVector3Make(-1, y, 0), restitution: 1.0))
})
}
private func setupScene(scene:SCNScene) {
//カメラ追加
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 1, z: 15)
scene.rootNode.addChildNode(cameraNode)
//床を作成
let floor = SCNFloor()
floor.reflectivity = 0.25
let floorNode = SCNNode(geometry: floor)
let floorShape = SCNPhysicsShape(geometry: floor, options: nil)
let floorBody = SCNPhysicsBody(type: .Static, shape: floorShape)
floorNode.physicsBody = floorBody
scene.rootNode.addChildNode(floorNode)
}
//球体を作成
private func createSphere(position:SCNVector3, restitution:CGFloat) -> SCNNode {
let sphere = SCNSphere(radius: 0.5)
let node = SCNNode(geometry: sphere)
node.position = position
if let material = node.geometry?.firstMaterial {
material.diffuse.contents = UIColor.redColor()
material.specular.contents = UIColor.whiteColor()
}
let physicsShape = SCNPhysicsShape(geometry: sphere, options: nil)
let physicsBody = SCNPhysicsBody(type: .Dynamic, shape: physicsShape)
physicsBody.restitution = restitution
node.physicsBody = physicsBody
return node
}
}
床、カメラ、球体を作成してSceneViewに追加しているだけですが、これで勝手に球体が落ちて、当たって、跳ね返って...という動きになります。素敵。

この動きを実現している内容を詳しくみていきます。

重力の設定

まず、SceneKitの世界には重力がデフォルトで設定されています。

SCNSceneのphysicsWorld.gravityが重力の設定で、printしてみるとこんな値。

SCNVector3(x: 0.0, y: -9.80000019, z: 0.0)

9.8という数字、見覚えありませんか?
そう、物理で習った、重力加速度の値です。
physicsWorldには、gravity(重力)以外に、speed(スピード)、timeStep(fps) の設定があり、この世界全体の設定することができます。

PhysicsBodyの設定

物理シュミレーションの世界に参加するには、PhysicsBodyというオブジェクトの設定が必要です。いうなれば、当たり判定をするためのカラダです。


SCNNodeの中には、Geometryという見かけ上の情報と、PhysicsBodyという物理シュミレーションのための情報が別々にあるという構成です。

PhysicsBodyの種類は、以下の3つ
  • Static : 動かない、静止物
  • Dynamic : 動くもの
  • Kinematic :  動くものだけど、自動的にはうごかない

今回のサンプルコードでは、床はStatic、球体はDynamicにしています。
Kinematicの使いドコロとしては、たとえば、ユーザがPadを使ってキャラを動かすような場合です。
StandlandのGemmy(ジェミー)達も、Kinematicです。
Kinematicでの判定のお話はまた次回書きますね。

type以外に、mass(重量)、restitution(反発)、friction(摩擦)などのパラメータがあり、動きを変えることができます。

PhysicsShapeの設定

PhysicsShapeというのは、物理シュミレーションをするときの「形」の設定です。
デフォルトで用意されているSCNBox, SCNSphereなどはSCNGeometryを継承しているので、以下のように指定ができます。

let physicsShape = SCNPhysicsShape(geometry: sphere, options: nil)

3Dモデルから作成した場合は、SCNNodeを引数に以下のように指定。

let physicsShape = SCNPhysicsShape(node: modelNode, options: nil)

これらのコードは、物理的な形は、見た目の形(Geometry)から作る ということをしています。

複雑な形のモノの場合、それの物理計算のコストも高くなる。
物理計算上では、そんなに複雑でなくてシンプルな形でいいよ って場合も結構あるかと。
そんな時には、このメソッドの第二引数のoptionsを設定します。

SCNPhysicsShapeTypeKeyで設定できる以下の3つ。
  • SCNPhysicsShapeTypeBoundingBox
  • SCNPhysicsShapeTypeConvexHull
  • SCNPhysicsShapeTypeConcavePolyhedron

下に行くほど、より細かい(より遅い)設定になります。

とっても重い処理

簡単に書けるなーですが、処理としてはとても重い。
単純に考えても、「当たり判定をして、描画して」という繰り返しを1秒に60回(60fps)しているわけですから。
シミュレーターで動かすと、マシンのファンが高速回転始めてしまいます。
SceneKitは、MetalとOpenGLで動かすことはできますが、シミュレーターではMetalはうごかないのでツライのであろう...
サンプルを動かす程度ならいいですが、実際開発をする場合には実機は必須ですw

ちなみに、今回説明した内容は、わかりやすくコードで全てかきましたが、SceneKitのEditorでも設定することができます。
サンプルコードでいうと..."4秒後にもう一つ球体を追加" 以外は全部Editorで設定できます。


さて、次回は、Gemmyで使っているKinematicでのprogrammaticallyな方法をば。


作成したアプリはこちら↓


Standland - 座りすぎ解消!スタンドランド



• • •

2016年3月31日木曜日

SceneKit - 楽しいけど難しいパーティクル

SceneKitシリーズ 第三弾。今回はパーティクル編です。




Standlandでは、深々と雪が降っている雪国や、キラキラ光るGemmyなどで使ってます。
この効果はパーティクルといいます。

UIKitでもCAEmitterLayerを使用すれば同様な事ができるので、使った事がある人もいるのでは?

UIKit Particle Systems in iOS 5 Tutorial | Ray Wenderlich
UIKit上でパーティクルエフェクトを表示する - Over&Out その後
Swiftでパーティクルの描画☆*:.。.([CAEmitterLayer / CAEmitterCell] 

SceneKitのParticleと、CAEmitterLayer。設定する内容もほぼ同じで、

  • パーティクルの画像
  • Birth rate
  • Life span
  • Velocity
などを設定していきます。

パーティクルの難しさは、組み込む方法よりも、パラメータを調整して "いい感じ" にすることだと思います。
なんというか....やっぱりここにもセンスがいるし、チマチマとした調整を始めると結構時間がかかる。

CAEmitterLayerで、さらに難しくしている要因としては専用エディタがないことなのですが、SceneKit/SpriteKit ではエディタがあるんですよ!


XCodeのファイルの作成でSceneKit Particle Systemを選ぶとテンプレートがいくつか選べます。
作成したファイルを開くと、エディタの表示になります。


PreviewをみつつAttributes Inspectorでパラメータをいじることができます。
SpriteKit(2D)でも同じように作成できますが、SceneKit(3D)のほうが当然ながら設定パラメータが多いです。

エディタがあるということは....つまり.....
こういう作成が得意な人に調整をお願いすることができる
というメリットが! 

このパーティクルのファイルを、CAEmitterLayerにも使えないかと探ってみたのですが方法が見つからず.....残念。

「得意な人はいないけど、凝ったパーティクルを作りたいのだ」
という人は、ツールでカバーする方向も。

購入して試していないので、どこまで連携できるかはわからないのですが...
連携が出来なかったとしても、Particle Designer のように、いろいろサンプルがあると助かると思う。
何事も真似から学びますしw


パーティクルって "こんな感じ" と言葉に表して伝えるのは難しい。
「キラキラしたい」とか「ゴーッと燃える」とか?
そもそもの話として、どんなものがよさそうか? を、自分でイメージが明確になってなかったりもしますよね。
人にお任せで頼むとしても、「こんな感じに近いもの」というイメージを自分で探して、できるだけ伝えるべきと思う!

たとえばー「派手目のおめでとう感」といってもいろんな表現があります。

AngryBird2のスコア表示画面をみてみると....


紙吹雪、星が大きくなった時に弾き出される小さな光、背景でぐるぐる回っているSumbeamや星、明るいところの増減などなどいろいろ効果組み合わさっているのがわかります。
手馴れている感がありますよね....さすが!

紙吹雪のパーティクルを作成するという一つをとっても、
上からパラパラ降ってくるのか
下から吹き上げられるものなのか
AngryBirdみたいにパーン!と弾きだされるものなのか
などによってパラメータが全然違ってきます。

正直、Standlandはキラと光るシンプルなパーティクルだけで逃げてしまった感があります。
いろんなパターンを試してみたのですが、いい感じのものがなかなかできず... 結局、ばっさり捨てて無難なものに。

雪が降っている ようなリニアなパーティクルは簡単ですが、パーン!とはじき出されるような動きがあるものについては、パーティクルだけだとナカナカ難しい。
AngryBirdのようにパーティクル以外の効果を組み合わせて....パーティクルは補助的な役割で....考えたほうがよさそうというのがいまのところの感想です。

センスがいいものを作るのには、アニメーション同様ハードル高めではありますが、うまく使えるようになって、他のアプリにも効果的に使ってみたい。


さて、次は.... Gemmyを歩かせる(衝突判定) の話でもー。





作成したアプリはこちら↓


Standland - 座りすぎ解消!スタンドランド


• • •

2016年3月25日金曜日

SceneKit - カメラはテレビ番組のように

SceneKitシリーズ第二弾。
今回はカメラについて書いてみます。

SceneKitで作成した画面は、カメラを通して表示されます。
カメラの存在は必須で、カメラを作成していなかったらデフォルトのカメラが用意されます。


この図の内容は理解できるのですが、ここから先は専門用語がいろいろ出てきて、3D初心者の私としては最初の難関でした。

今、思うに....
SceneKitの画面ってテレビ番組のようだな と思います。
プログラムを書くあなたは、さしずめ制作プロデューサー。

「カメラさん、主人公を追って!」
「カメラさん、主人公の顔に、ズーム!」
「はい、ここで、1カメから2カメに切り替え!」

(テレビ番組の制作をしたこと無いのでこれまた想像ですが)

iPhoneはテレビ。ユーザは視聴者。
そんな現実のイメージを持ちながら見ていくとわかりやすいのかなと思います。

カメラの機能



SceneKitのカメラを通して見える世界は、カメラの位置だけでなく、角度、FOVなどの属性によって決まります。
いろいろ用語がでてきますが、できることは現実のカメラととても近い。
何かを撮すためには、カメラを持って移動して、角度を調整しますよね。
Storyboard上では、位置はPosition、角度はEulerで設定します。

ズームは、xFOV, yFOVの値で設定。
FOVとはField of Viewの略。日本語でいうと視野とかってところかな。
現実のカメラでいうとレンズとしての機能で、角度を小さくするとズームします。

NearとFarは、描画する範囲。
広大な大地に降り立った時、すっごい遠くは見えなくてもいいはず。
ゲームをやっていても、近くにきたら突然みえるようになるポリゴン、あったりしますよね。
全てを描画するとなると処理に負担がかかるので範囲の制限をかけれるわけです。
現実のカメラでいうとフォーカス? ぼやけるわけでなく、見えなくなるという差はあるけど。

「はい、ここで、1カメから2カメに切り替え!」

カメラは複数台作成ができます。
でも、画面上に表示されるものを撮しているのは1台のカメラ。
そのカメラを指定するのが、SCNViewのpointOfViewです。
画面に表示されているものは カメラ → PointOfView (POV) を経て見えています。


「カメラさん、主人公を追って!」



Standlandのオープニングではこんな感じで動きます。
これを実装するには??

はじめはカメラを動かそうとしました。
このカメラワークをカメラをもって実際やってみるイメージをしてみると....キャラクタを常に中心に表示するためには角度を変えないといけないことが分かりますか?
カメラさんは、上空でカメラを下に向けている状態からはじまり、地上に降りてくるとともにカメラを水平方向に角度を変えています。
カメラの角度は計算すれば求められるものではあるのですが、実装していくのはまぁ面倒。

そこで使えるのが、SCNLookAtConstraint という制約です。

これでカメラの位置を移動したら常に指定位置がカメラ向きになるように自動的に角度を調整してくれるようになります。とても便利!
イメージ的には、カメラとターゲット位置が紐で結ばれている...とか、ヘッドセットのカメラとかそんな感じでしょうか。
この制約は、スポットライトにも使えます。

これである程度カメラがうまく動くようになったものの、問題点が発生。
カメラ制約:Z軸と、カメラ位置:Z軸の値が違うと、カメラ位置の:Y軸が正数になるにつれて世界のZ軸が反転してしまうという....。
簡単にいうと、有る特定の位置にカメラを移動すると世界がくるっと反転してしまうという現象。

なので、Standlandではすったもんだした挙句、カメラを動かすのではなく、POVのRotationを変えること(=世界を回転すること)で実装しました。

「カメラさん、主人公の顔に、ズーム!」

実は、カメラへのSCNLookAtConstraintの制約は結果的には使用していて、利用箇所はズームです。

PinchGestureをTriggerに以下のようなコードにしています。
func pinchWithGestureRecognizer(recognizer:UIPinchGestureRecognizer) {
if lockCamera { return }
SCNTransaction.begin()
SCNTransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut))
var fov = defaultFov
var constraintFactor:CGFloat = 0
if (recognizer.state == UIGestureRecognizerState.Ended || recognizer.state == UIGestureRecognizerState.Cancelled) {
SCNTransaction.setAnimationDuration(0.5)
} else {
SCNTransaction.setAnimationDuration(0.1)
var scale:Float = Float(recognizer.scale)
if scale < 0.4 { scale = 0.4 }
scale = 1.0 + (Float(scale - 1) * 0.75)
let zoom = 1.0 / scale
fov *= Double(zoom)
constraintFactor = min(1,(CGFloat(scale) - 1) * 0.75)
}
self.pointOfView!.camera!.xFov = fov
if let constraints = self.pointOfView!.constraints {
constraints[0].influenceFactor = constraintFactor
}
SCNTransaction.commit()
}
view raw Zoom hosted with ❤ by GitHub
PinchGestureのScaleを元にズーム値を決めて pointOfView.camera.xFov に値を設定してズーム。Gestureが終わった時(指を離した時)、元のFovの値に設定して元のズームサイズに戻しています。

さて、ここで注意すべき箇所は、influenceFactor の存在。



Standlandで実現したかった動きとしては、ズーム前とズーム後で、画面の中心位置を変えること。

ズーム前はキャラクタの少し上が中心にしたかったのですが、その位置のままズームすると、キャラクタが見えなくなってしまいます....。

そこで使っているのが、influenceFactor
factorの値を1.0にすると制約あり、0.0にすると制約なしになります。

SCNLookAtConstraint の制約をPOVに設定し、influenceFactorを0に設定。
制約は、ズーム後の位置にしておきます。
let targetNode = self.scene!.rootNode.childNodeWithName(FLSNodeName.CameraTargetPoint, recursively: false)
if let target = targetNode {
let lookAtConstraint = SCNLookAtConstraint(target:target)
lookAtConstraint.influenceFactor = 0
self.pointOfView!.constraints = [lookAtConstraint]
}

そして、PinchGestureでの処理中に、ズームしていればfactorの値も1.0に近づけるように変更していく(=制約をだんだん有効にしていく)ということをしています。


カメラシリーズ、これでおしまい。
カメラはなかなかハードですよね... 次は気軽にパーティクル編を書いてみます!



作成したアプリはこちら↓


Standland - 座りすぎ解消!スタンドランド

• • •

2016年3月23日水曜日

SceneKit - 世界はSCNViewでできている

先日リリースしたStandlandでは、SceneKitを使ってみました。
SceneKitを使って実際にアプリをリリースした人はまだあんまりいないだろうなーということで、どんな感じだったかを書いてみようと思います。

SceneKitとは、iOS SDKに含まれている3Dグラフィックのフレームワークです。
モノとしてはUnityと同じアプリケーション層。

球体とかにテクスチャを貼って配置してみたり、光源を置いて影をつけてみたり、空(Skybox)を設定してみたり、重力に合わせて動かしてみたり... と最初に試してみる事としてはUnityと多分変わらないはず。

Metalを使うには iOSの新グラフィックAPI - Metal入門してみる のような処理が必要なのですが、これらを簡単に扱えるようにしたフレームワークです。

では、何でSceneKitを選択してみたか? というと、
  • iOSに特化しているから、汎用的に作られているものよりも処理が早いはず
  • ブラックボックスが少なくコードでいろいろ書ける方が、(私としては)とっつきやすい
  • 直接Metalを扱うほどシビアなものを作ろうとしているわけはない
ということ。

裏の気持ちとしては、
  • どんなものか知りたいという単純な興味
  • SceneKitで使える技が他のアプリ開発にも役立つかもしれない(Unityを学ぶよりは)
  • Appleさんにフィーチャーしてもらいたい
というのも、もちろんあります(笑)。

せっかくなのでSceneKitを使う人のためにあしあとを残してみます。
が、全体は大きすぎるので、今回はSceneKit全体像から。

世界はSCNViewでできている

SceneKitを表示するための大元のViewがSCNViewです。
SCNViewの継承元はUIView
つまり通常のアプリのUIViewの中に一部として入れることもできちゃいます。

基本構成はscnファイルで

SCNViewにあるSCNSceneが、シーンを管理しているクラス。
SCNSceneは、scnファイルを指定してインスタンスします。

scnファイルは、表示するデータが入ったバイナリファイル。
COLLADA形式の3DモデルをXCodeで変換して作成します。
COLLADAがなくても、簡単なものであればXCodeのエディタで作成できます。

実際には一つのscnファイルで全てのモデルを配置するのではなく、各部品で分けて、組み合わせていく形になるかと思うのですが、この部品もscnファイルで作成します。

scnファイルの中に他のscnファイルの参照を置くことができるし、scnの中のノードをプログラムからアクセスして他のSCNSceneに追加もできます。

実際、Standland でも
scnファイルを組み合わせて、世界全体を作成しています。



キャラクタの動きなどのアニメーションについても、scnファイルで切り出しします。
これでアニメーションを切り替えることがプログラムから可能になります。

イベントの取得はUIViewと同じ

UIGestureRecognizerを登録するとちゃんと反応します。

TouchEvent
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?)
もちゃんと渡ってきます。

Standlandでは、スワイプ操作でカメラ向きをかえたり、ダブルタップでみんなが一斉にジャンプしていますが、そのイベントの発火元はこれらになっています。

UIViewと同じように扱えるので、すでにアプリ作成を経験している人にとっては良い点ですね。

SceneKit 内のタップ判定



青いちっこいのがGemmy。
この子をタップするとGemゲットできたりジャンプしたりします。
その時必要になるのが、SCNKit上では、なにがタップされたのか?の判定です。

ここで使用するのは、SCNViewの

func hitTest(point: CGPoint, options: [String : AnyObject]?) -> [SCNHitTestResult]

というメソッド。
これでUITapGestureRecognizerで取得できたPointを使用して、SCNKit上でHitしたオブジェクトが取得できます。

HitしたのがGemmyなのか?うさぎなのか?は、ノードの名前で判断します。
そのため、scnファイルの中で設定しているノード名はちゃんとルールを用意しておきましょう。

2DのOverlayはSpriteKit

例えば、ゲーム終了後にスコアを画面上に2Dで表示したい。
という場合、overlayを設定することができます。
overlayは、SKScene .... つまり、SpritKitフレームワークのオブジェクトを利用します。
SCNSceneRenderer#overlaySKScene

...が、Standlandではoverlayは使わず、UIViewをSCNViewの上に置いちゃっています。
理由としては、Storyboard上でレイアウトをしたかったから。
そして、ボタンなどのハイライトとかUIKitの便利な機能を使いたかったから。

AppleTVにも対応する場合、overlayを使いSceneKit/SpriteKitだけで作成した方が楽かもしれません。
あと、SCNViewをインスタンス中にUIViewで表示、アニメーションとかすると、ビシっと線が入ったりして苦しそうになりますのでご注意を。


今日はこれでおしまい。
次回はカメラについて書いてみようと思います!



作成したアプリはこちら↓


Standland - 座りすぎ解消!スタンドランド
• • •

2016年3月5日土曜日

キレイなステータスバーでアプリ用のスクショを撮る方法

アプリを作成し終わった後、面倒なのはスクリーンショットの用意ですよね。
 いろんな端末サイズ × 対応言語 の数が必要で、うんざりします。

Timesheetの場合だと、
スクリーンショット5枚 × 3言語 × iPhone/iPadの6サイズ = 90 枚


90枚!!



 以前、Sketch3のSymbolを使って作っていること を書きましたが、 元となるスクリーンショットを撮る作業だけでもかなり大変。 

スクリーンショットを撮る際に、気をつけたいこととしてはステータスバーです。
普通に撮ると、



...とSoftbankというキャリアだったり、バッテリー容量が100%ではなかったりすることも。
後で画像を細工するという手もありますが、そんな手間は掛けたくない。
考えただけでも気が遠くなって気持ち悪い。


で、キレイなステータスバーにしてスクリーンショットを撮る方法をご紹介。


実機で撮る場合

1. iPhoneをMacとをUSBでつなげる
2. QuickTimePlayerのアプリを立ち上げる
3. メニューから File > New Screen Recording を選択
4. 接続先を iPhoneにする



上図で aqubi 6s とあるのが私のiPhone。

これだけで...(記録を実行せずとも)、iPhone上のステータスバーが以下のようになります。



キャリアは消え、時間は 9:41 AM。バッテリーも100%に。

USBつないでいるので、バッテリー表記が充電中の緑になっちゃうんですがこれを無しにする方法がわからない。あるのだろうか??


シミュレータで撮る場合


SimulatorStatusMagic という素敵なものがGithubにあります。
CocoaPodsで取り込むという形もできるようですが、私はサンプルプロジェクトを実行して、Apply Clean Status Bar Overrides のボタンをポチります。

そうすると、それ以降、そのシミュレータはクリーンなステータスバーになります。
(サンプルプロジェクトを終了してもその効果が残る)

戻したいときは、Restore Default Status Bar をポチり。


付録: 端末の言語を簡単に切り替える

XCodeのプロジェクトの、Shemeに言語設定があります。


XCodeからiPhone実機で実行した際にも、Schemeの言語が反映されます。
iPhoneの設定で言語切替をすると結構時間がかかるので、便利です。

付録:  タップしたところにマークを入れる

動画を作成する際、タップしたところにマークがあるとわかりやすくなります。
これを実現するものとして COSTouchVisualizer というのがありました。

いま試してみたのですが、なかなかよさ気デス。


• • •

2016年2月25日木曜日

3Dアプリ Standland をリリースするまで

とうとうリリースしました!



作成開始を決めたのが 2015年7月なので、制作期間は 約8ヶ月。
Flaskにとっては、かなり長期間の開発となりました。

どんな道のりを辿ったのかを振り返りもかねて書いてみようとおもいます。

道のり


7月20日 初めてのdaeファイルを作成して試してみる
7月22日 ポリゴン数やアニメーションの切り出しなどでハマり始める
8月17日 ゲーム要素を「合成」にしていたのを「ルーレット」に変更
9月2日 Apple Watchの検証して実装を見送り

(9月16日 FitPort ver1.5.0 リリース)
(10月4日 Coyomi ver1.1.1 リリース)
(10月7日 Coyomi ver1.1.2 リリース)
(10月30日 OnePath ver1.0.0 リリース)
(11月7日 Timesheet ver1.7.0 リリース)

11月4日 ちびキャラを登場させる案が浮上
11月17日 キャラをタップで、何を喋らせるかの具体案が出てくる
11月27日 ゲーム要素を「ルーレット」から「アチーブメント」形式にすることを決定
11月29日 Gemmyを歩きまわらせたい欲がでてくる
12月2日  Gemmyの表示数や動きについての仕様が確定
12月16日 10Dayスタッツ画面をいれることに決定
1月11日 キャラをタップした時の動作仕様が確定
1月19日 夜にランプを表示することを決定
2月24日 リリース


Github上のコミットグラフ


はじめての3Dグラフィック

Standlandは、3Dグラフィックのアプリ。 
COLLADA形式のモデリングデータを作成し、それをSceneKitで取り込みしています。
モデリングデータは @horiuni さんがBlendarを使って作成
3Dははじめての経験だったのでとても興奮しました。

時間とともに、光源を移動/色を変えて 時間毎に変わっていくようにしよう。 
キャラを眠っている、立っている状態毎のアニメーションいれよう。
 雲も動かしたい。歩かせたい。

...とやってみたいことが盛り沢山。動くって楽しい♪
「後で、自分の首を締めると思わずに...」と @horiuni さんが後でつぶやいていましたw

Flaskの得意技! 作りながら考える

とりあえず動くものを作る。
良い案がでたらまた作る。
古いものはどんどん捨てる。
です。

今回のStandlandも、それらしい最終形が見えてきたのは12月ぐらいからでした。
私達が作るような小さいアプリは、最終形がみえているなら1, 2ヶ月ぐらいでだいたい出来上がります。
どれだけ、模索の時間をおくのかー? というのが全体的な期間の差に。
アプリの開発期間の算出って難しいですよねww

2度変わったメイン機能

最初から、「多くスタンドできたら、キャラが増えていく」という発想はあったものの、どのようにキャラがGETできるのか? の方法は途中で大きく変わっていきました。

最初は「合成」するという案。
スタンドで貯めたGEMを使って合成すると、新しいキャラが増える。
キャラの数は限られているので、早々に集めてしまい楽しくないかも。
同じキャラが出てきたら、なんだか残念。という懸念がありました。

次は 「ルーレット」 の案。
ルーレットの中には新キャラの他に、GEM×10 なども入れることにより、残念度を減らそうと出てきた案。
内部の実装的にはほとんど作り上げて、実際回したりして試していました。
合成よりは懸念は少し改善したですが、全ては払拭できておらず悶々と。

この間に「ちびキャラ」の案が浮上
たまーに、小さいキャラが登場する。それをタップすると、そのキャラをゲットできるという案。今までの案よりも、出てきた時の喜びは大きい。
出なさすぎは寂しいが、出過ぎもすぐに集まってしまうという懸念は残ったままなのだが、この案が出てきた背景には メイン画面がちょっとさびしい という気持ちもあったのと思う。この時点では、Gemmyはまだ歩く予定もなかったですし....。

そして最終的には現在の「アチーブメント」の案に。
"5回連続したらボーナス" というボーナスの存在はそれまでにもあったのですが、GEMが多くもらえる という形でかんがえていました。
それを、現在の形..... 達成までの進捗が見えて、頑張ったら必ずキャラゲットできるようにしました。
"進捗がみえる" という発想の転換はとても大きくて、今までの懸念を一気に払拭してくれました!

最後の案に落ち着いたのは11月末。
途中に寄り道期間があったとはいえ、開発開始から5ヶ月目の出来事でした。

空白の2ヶ月間?

9月、10月にコミットがなくなっていますが実はこれは空白期間ではありません。
モデリングデータを作成といっても、どんなキャラがいいか? どんなものだったら作りやすいか? などの選定から、  @horiuni さんに模索しながら作業してもらっていました。
はじめての経験だから時間がかかったというのももちろんありますが、逆に私の実装側が、モデリングデータのおかげで作業量が少なくなったというのがあります。
アニメーションなどもモデリングデータ任せになるので。
Appleのサンプルコード Fox でも、コード量はホント少ないです。

その間、私はtvOSと戯れたりしておりました。

8ヶ月という期間は長いのか

メイン要素が変わっていきましたが、こういう悩む部分というのは、すっと良いアイデアが出るわけでないので、ある程度、悶々とする期間はやっぱり必要だなと思います。
が! 8ヶ月集中を持続するのはやっぱりツライなぁーという印象も。
長くても5ヶ月以内で完了できるものに絞って集中したほうがよいかな というのが今の気持ちです。

こんな感じでやっとお目見えできた、スタンドランド
無料なので、ぜひお試しくださいね!

• • •

2016年1月17日日曜日

個人事業主のマイナンバー対応

昨日、おしえて石井先生!まるっとふわっとマイナンバー講座 に参加してきました。
フリーランス目線から見た説明はとっても貴重で参考になりました!

学んだことと、今の知識をちょっとメモ。

マイナンバーのやりとり

マイナンバーを受け取って管理するのは、金銭の「支払い側」。
受け取り側は、正当な理由とともに求められたら提示する義務がある。

お仕事を受注する立場としては、提示を求められるのを待ってればよくて、とくに何か動く必要はない。
たとえば、よかれと請求書に個人番号を記載しておく なんてことはしてはダメ。
受け取った側は保管、廃棄などの義務があるので、逆に迷惑です。

個人事業主は法人番号が作れないので、個人番号を渡す必要があります。
個人番号はあまり渡したくない..ですが、しょうがないデスネ....

マイナンバーを求められる場面

マイナンバーを求められる際には、必ず正当な理由が必要。
渡す側としては、その理由が正しいか?を判断できる知識が必要。

お仕事のやり取りに関してだと、源泉徴収票、支払調書など法定調書を作るときに必要になる。

ここで注目すべきは、「報酬、料金、契約金および賞金の支払調書」
支払調書は、必要である条件があって、それに当てはまらない時には必要無い。
IT関連の個人事業主としては、

弁護士や税理士への報酬、作家や画家への原稿料や画料、講演料で、年間の合計が5万円を超える場合

が主に該当するところ。
デザイナーさんであれば業種として当てはまるので、年間合計が5万を超える場合には必要ということになる。

私のようなプログラマーだと業種として当てはまらないので、「支払元にマイナンバーを提示する必要があるのか?」 を質問してみたところ、源泉徴収額を0円として計上したりするので求められる時があるとのこと。


マイナンバーを貰う必要がある場面

お仕事を発注する場合と、家賃代関連。
家賃代が年間15万を超える場合には、不動産の使用料等の支払調書 が必要になる場合があります。
大家さんが個人/法人なのかなどなどによってなんかややこしくて....該当しそうな方は詳しく調べてみてください。

大家さんが個人だった場合、マイナンバーを貰う必要があるんですが、大家さんとしては個人情報をいろんな人に渡さないといけないのでドキドキですよね。
なので、個人事業主だから信用ならんー と、入居を拒まれるケースもありそうな予感。

お仕事を発注する際は、いつ貰えばいい?というのが悩ましいところですが、調書の提出時にあればよさそうな。
決算時に合計を計算してみて5万を超えていれば聞くとか...そんな感じになるのかな。

通知カードと個人番号カード

個人番号カードは、申請してもらえる写真付きのカード。
個人番号カードは絶対作成しないというわけではないが、作っておくと本人確認の資料がすくなくなって手間が省ける感じ。
複数の企業と取引する人は、作っておいた方が、相手先も楽になるのと思う。

注意が必要なのは、
通知カードは、表裏の両面のコピーが必要だけど、
個人番号カードは、表面のコピーのみにする。
個人番号カードの裏面のコピーはしちゃいけない。

民間企業への身分証明として個人番号カードを使うときには、個人番号カードの裏面をコピーしてはいけない... でした m(_ _)m


法人と個人事業主との差

法人は、法人番号というのがあり、公開されています。
法人番号公表サイト

お仕事を依頼する立場からすると、絶対的に相手先が法人の方が楽ですよね。
個人事業主にお願いすると、マイナンバーをもらって保管/破棄を管理しないといけない。

会社によっては、「外注先は法人のみにすること」という決めをしてもおかしくない。
個人事業主はさらに弱者になっていくなーという感じがします。

余談ですが....
私は、個人事業主 兼 LLP(有限責任事業組合) をしています。
LLPは法人税を支払わず所得税のみでよい、パス・スルー課税ができる組合団体です。
法人格はないので自動的に法人番号は振られませんでしたが、申請すれば法人番号をもらえるかも?ということなのでチャレンジしてみようと思います。
もし法人番号をもらえたら、これから仕事を受けるのは、LLP経由にしようかなーーと、ぼんやり考えています。

追記:
LLPは法人番号はもらえないそうです...残念!
• • •