@numa08 猫耳帽子の女の子

明日目が覚めたら俺達の業界が夢のような世界になっているとイイナ。

「ポップステップげっちゅー みれぃプリ!!」をするアニメーションを作ったよ MagicaVoxel + Blender + Unity

この記事は プリッカソン Advent Calendar 2018 - Adventar 12/23 の記事です。

昨日は id:saken649 さんの 「形態素解析を用いて、夢川ゆいのユメ語録を再現してみる試み」でした。

syn-station.hatenablog.com

南みれぃとぼく

みれぃちゃんが私自身にとってどういう存在だったのかは、以前ブログに書きました。

numa08.hateblo.jp

過去の自分を投影してそして過去の自分を受け入れられる存在としてみれぃも好きだって言える。俺の中のみれぃはそんな存在なんだ。

今見返してもエモさの塊だけで文章になってない、エモエモな記事ですね。ともかく、みれぃちゃんは自分自身を投影できるそんな存在です。

やっぱり造形でしょう

さて、思いの丈を表現するのなら、やっぱり造形物にしてしまうのが良いでしょう。という訳で、できあがったものがこちら。

プリパラ2期84話「ポップ・ステップ・ぷりぷりぷり!」 のみれぃちゃんを思い出しながら見てください!!

みんな大好き MagicaVoxel を使いました。アニメーションは Blender で行い、最終的に Unity に組み込みました。

ephtracy.github.io

家に「ねんどろいどこ~で 南みれぃ マジカルピエロコーデ」があるので、それを参考にモデリングを行いました。

f:id:numanuma08:20181021103429j:plain
みれぃ

ちなみに、このフィギュアは Amazon のほしいものリストに入れていたら、 [twitter:@NorsteinBekkler] が差し入れで送ってきてくれたのだけれど、ギフト扱いになっていない荷物でマーケットプレイス出品だったため

「えっ!?俺、自分でも記憶にないうちにオーダーしてた??それとも神??神様なの!?女神様!!!ジュリィありがとう!!プリチケ配達人になったんだね!!ついに俺のところにもプリチケがきたんだ!!そ、そうだ、ライブ!ライブしたい!!歌いたい!踊りたい!!おしゃれなコーデでランウェイを歩きたい!!それが今の夢。今叶えたい夢。パン!ご飯!!麺類!!ひゃっほぃ」

という感じでテンションが上りました。

ともかく、ポチポチと Voxel を組み上げてできあがったものがこちら。

f:id:numanuma08:20181223042335p:plain
みれぃ

「絵心は死んだ!もういない!!だけど、俺の心にこの胸に!!1つになって生き続ける!!」

自分の中では及第点かな?と思えるモデリングができたので満足です。

ちなみに、 MagicaVoxel の使い方は id:henteko07 さんの本が最高に詳しいです。

ボーンを組み込む

最終目標はアニメーションをすることなので、できあがったモデルデータにボーンを組み込んで動かすことができるようにします。ボーンの組み込みは Blender だけでもできると思いますが、今回は Maxiamo を使って自動生成してもらいます。

Mixamo

MagicaVoxel で obj 形式で出力を行い、出来上がった .obj, .mtl, .png を1つの zip にまとめます。

f:id:numanuma08:20181223043110p:plain
エクスポートされたファイルを zip にまとめる

zip ファイルができたら Maxiamo に Adobe アカウントでログインをして、キャラクター一覧ページにある Upload Character からさっき作った zip ファイルをアップロードします。

f:id:numanuma08:20181223043319p:plain
Upload Character を選ぶ

f:id:numanuma08:20181223043351p:plain
zip ファイルをアップロードする

f:id:numanuma08:20181223043431p:plain
アップロード成功

無事にアップロードに成功したら、次は関節の位置を決めます。丸い関節ポインターを動かして、モデルの適当な位置に配置します。

f:id:numanuma08:20181223043553p:plain
関節の配置

また、指は不要なので「Skelton LOD」から「No Fingres」 を選びます。

f:id:numanuma08:20181223043643p:plain
指の関節は不要

あとは Maxiamo が自動的にボーンを設定してくれます。ボーンの設定に失敗することが多々あって、今回は次の対応を何回か行いました。

  • 関節同士の位置を遠ざけた
  • スカートや髪の毛など、浮いているパーツと関節を遠ざけた

対応が良かったのかどうかはわかりませんが、何度かチャレンジを繰り返したらうまく行きました。

maximo で適当なアニメーションを設定して、動作確認。

ポップステップげっちゅー するプリ

アニメーションすることがわかったら、 設定をしたアニメーションは一旦削除して、fbx 形式でファイルをダウンロードします。maximo のダウンロードボタンから「Format」 を fbx、 「Pose」を T-Pose としてダウンロードします。

f:id:numanuma08:20181223044450p:plain
ダウンロードする

ダウンロードした fbx ファイルを Blender の File -> Import -> FBX と進んで、インポートします。

f:id:numanuma08:20181223044607p:plain
インポートする

ボーンを動かしてアニメーションする

インポートできたらボーンを動かすことでアニメーションを設定します。Blender を今回はじめて使いましたが「何かの値を変える」→「キーフレームに設定する」→「間が補完されてアニメーションになる」という仕組みなんですね。ボーンの操作をしやすくするため、 Armature -> Armature の設定から「Display」を Stick に変更し、X-Ray にチェックをつけました。

f:id:numanuma08:20181223045140p:plain
Armature の設定

f:id:numanuma08:20181223045200p:plain
ボーンが見やすくなる

アニメーションの設定を行っていくため、 Dope Sheet -> Action Editor を開き、1フレーム目を選択します。

f:id:numanuma08:20181223045452p:plain
1フレーム目を選ぶ

3Dオブジェクトが表示されている方の画面では、 Armature を選んだ状態で左下の Edit ModePose Mode に変更ます。こうすることでボーンの編集ができるようになります。ボーンは右クリックで掴んだらそのままドラッグで移動、Rキーを押したら回転の設定、もう一度Rキーで移動に切り替え、という操作で編集が可能です。alt+Gで移動のリセット、alt+Rで回転のリセットができます。また、Aキーで全てのボーンを選ぶことができるので、この状態でalt+Gやalt+Rを使えば、編集のリセットができます。

f:id:numanuma08:20181223045931p:plain
ボーンを設定してポーズを決めよう

ポーズが決まったらIキーを押してInset Keyframe Menu -> Rotation を選び、キーフレームにポーズを追加します。

f:id:numanuma08:20181223050044p:plain
キーフレームを追加する

そして、次のフレームとして20フレーム先を選んでポーズの決定、キーフレームの追加、20フレーム先を選択・・・という手順を繰り返します。今回は20フレーム間隔でやったけれど、もうちょっと短い間隔でやってもよかったかも。アニメーションの動作確認はDope SheetTimelineに変更することで、操作コンソールっぽいのが出てきて操作ができました。

f:id:numanuma08:20181223050431p:plain
アニメーションの確認は Timeline から行う

Unity でインポート

アニメーションもできあがったしここで完成でもいいですが、もうちょっと色々なことができるように Unity に組み込むところまでやります。

テクスチャーの出力

Blender で編集をしたモデルを Unity に組み込んだところ、テクスチャーが無くなってしまったようではんぺんみたいなみれぃさんが表示されてしまいました。

f:id:numanuma08:20181223050731p:plain
「パプリカ学園校則第55301条!みれぃをはんぺんみたいに表示してはならない!!」「はんぺんみたいな委員長も素敵です」

流石に辛いので直します。テクスチャーをちゃんと画像として出力しておいて、 Unity のマテリアルで設定すれば良いようです。Blender で出力したい 3Dモデルを選び、テクスチャーっぽいマークのアイコンのタブを選び、 Image -> Sourcesave an image packed inthe .blend file to disk を押してテクスチャーの画像を保存します。

f:id:numanuma08:20181223051229p:plain
テクスチャーっぽいアイコンを選んで、 Source を見つける

f:id:numanuma08:20181223051129p:plain
テクスチャーを保存する

モデルの出力

3Dモデルとアニメーションを出力して Unity で利用できるようにします。File -> Export -> FBX を選んで fbx 形式で出力し保存します。

f:id:numanuma08:20181223051403p:plain
fbxで保存

Unity で表示する

Unity 上でテクスチャーの画像ファイルと、fbxファイルを Assets に追加します。Assets で右クリックをして Create -> Material から新しいマテリアルを作ります。新しいマテリアルを選び、Inspector の Albedo の左側の四角□にテクスチャーの画像をドラッグ・アンド・ドロップで設定します。これで、テクスチャーを表示するマテリアルが作られました。

f:id:numanuma08:20181223051732p:plain
新しいマテリアルの Albedo にテクスチャーの画像を設定する

fbx を読み込んだときに Prefab として追加された3Dモデルを選択して、 Inspector の Remapped Materials->On Demand Remap-> palettemat にさっき作ったマテリアルを設定します。

f:id:numanuma08:20181223051930p:plain
Prefab にマテリアルを設定

ここまでできたら、ちゃんとしたみれぃちゃんをシーン上に追加できます。

f:id:numanuma08:20181223052008p:plain
やっぱりみれぃはこうでなくっちゃだめプリ!

最後にアニメーションの設定です。Assets 上で右クリックをして Create -> Animator Controller を選択して、新しい Animator Controller を作ります。Animator ウィンドウ内で右クリックをして Create State -> Empty を選びます。新たにできた State を選んで Inspector 上で Motion に fbx を読み込んだときにつくられたアニメーションを設定します。

f:id:numanuma08:20181223052319p:plain

そして、この Animator Controller をシーン上にある3Dモデルの Animator コンポーネントController に設定します。

f:id:numanuma08:20181223052434p:plain
Controller に設定する

あとはシーンを再生することでみれぃちゃんが動きます!!やったぷり!!

終わりに

今回、生まれて初めて Blender を触りました。Unity でテクスチャーがつかない現象が発生していて、それを解決する方法がわからなくて困り果ててた・・・。

自分の好きなキャラクターをモデリングしたりして動かすことができるようにするのは、めっちゃ楽しいのでみんなやると良いと思う。

でじこちゃんでもなんかやろうっと!!

jcom から手紙が来ていた話

f:id:numanuma08:20180924100630j:plain

先月末、jcomからお知らせという件名で手紙が届いていた。

jcom から来た手紙を Google DriveOCR 機能で読み取った結果を修正したものが以下。

J:COM NET サービスご利用に関するお願い 平素はJ:COM NET サービスをご利用いただき誠にありがとうございます。 このたび J:COM NET サービスのご利用方法に関してお願いがございまして、ご連絡いたしました。突然のご案内になり申し訳ございません。 お客さまのご紹約されている回線から、9月26日 と 9月27日 の2日間にわたり、大量のデータ送信(※) 確認されました。この状況が続きますと、インターネット回線の通信品質に影響が出る恐れがございますの で、ご利用状況の見直しをお願い申し上げます。 また、データバックアップの為に他社クラウドサービスをご利用の場合は、一度に大量のデータ送信を行わ ず、日にちを分散してデータ送信いただけますようお願いいたします。 引き続き、データが一定水準を超える場合は、改めて書面にて実施期間をお知らせした上で、通信速度 の制限が発生しますので、やめて下さいますようお願い申し上げます。 ご不明点がございましたら、お手数ではございますが、弊社カスタマーセンターまでご連絡くださいますよ うお願いいたします。 今後も弊社サービスをご愛顧賜りますよう、重ねてお願い申し上げます。 ※大量データ送信とは、お客さまのパソコンなどからインターネット網に送信される1日あたり 30GB(ギガ バイト)以上の上り(アップロード)データ通信と定義しています。これは一般的なご利用の約 30MB と比較 して、およそ1000 倍に該当します。 30GB 以上のデータ量の目安では、DVD に格納されるデータ量(1枚約 4.7GB)の約7枚以上、もしくは CD (1 枚約 700MW), 44枚以上、またはデンタルカメラ写真(1枚約 6MB)の約 5120 枚以上に相当します。

初動

ちょうどこの手紙が届いたタイミングで俺は東京へ出張に行っていた。そのため、初動として動いてくれたのは奥さんの方で、 jcom への問い合わせを行っていてくれた。具体的な問い合わせのログが残っていたわけではないのだが、訪ねたことは

  • データの送信が行われていた詳細な時間帯
  • 送信されていたデータのプロトコルなど、詳細な情報

の2点。しかし、電話の担当者曰く手紙に描かれていること以上のことはわからないとして、情報が得られなかった。

対応、原因解明

自分がこのことを知ったのは、東京への出張から返ってきた9/29の夜。疲れ果てて返ってきたところに、奥さんに言われたので、まあ明日(日曜日)に考えてみるかーと思って、一応PCの電源だけを落として寝た。さて、翌朝の日曜日、いったい何が発生したのかを考えてみた。

大量のデータを送信するようなプロセスを起動した記憶は無いことを考えるとウィルスの可能性もあるなー、面倒くさいなーと思ってPCの前に座った瞬間、ちょうど1週間前に北海道へ行った際に Sony のアクションカメラで撮影したデータを Google Drive でバックアプをしたことを思い出した。4k 動画で全部で2時間くらい。これだわ・・・。

対策

対策といった対策を特に取っていない。jcom の手紙には

また、データバックアップの為に他社クラウドサービスをご利用の場合は、一度に大量のデータ送信を行わ ず、日にちを分散してデータ送信いただけますようお願いいたします。

とあったので、日をまたいでのバックアップをすることが適切なんだろうな。ただ、4k 動画で撮影した動画と言えどほとんどが車載動画で、全部を見返すことはあんまり無さそうということを考えると、今後はバックアップを行うファイルを選ぶのが良さそう。

そんなことを考えた9月末でした。

#ドラえもん展 強烈な精神的汚染による自分自身都の対話。あるいは、なぜドラえもんがそこにいないのか。

f:id:numanuma08:20180918010846j:plain

作品鑑賞とはすなわち自分との対話の一環である。その作品を通して自分がどう感じたのかを自分自身に問いかけることで、自分自身の心や頭が何を考え何を感じ、そしてそれらはどういう傾向があるのかを考察する。作品は絵画や彫刻、映像だけであるとは限らない。食べ物や飲み物を対象にするときもあれば、車などの乗り物であったり他愛のない食器やそのあたりの草であったりもする。

いずれにせよ人々はそこにリソースを割かないことが往々にしてある。自分との対話とは即ち、記憶の掘り起こしであり自分を形作ってきた物の再認識であり、それらは全てが心地よいものではない。そもそも、この活動は非常に疲れる。だから、美術館に行っても気になるものや話題のものだけを鑑賞したら、それ以外についてはあんまり時間をかけない。

もしも、すべての作品が自分の興味を引くものだけで構成された展示会があったらどうなるのか。脳をフル活用して疲れ果てた人はどうなるのか。その先は何のか。

それは、自分と自分以外の境界線の喪失である。自分がどこまで自分なのかどこからが他人なのか、他の鑑賞中の客なのか、それとも作者なのか。その境が曖昧になってくる。高熱を出して朦朧とする意識の中で立っているときのような、自分が見ている景色は誰かが見ている景色のような。強力な精神汚染。

ドラえもん展はそんな展示会だった。

物心ついた頃からドラえもんというコンテンツに強く惹かれてしまうようになった自分にとって、ドラえもんのみで構成された展示会は夢のような場所であると同時に、精神的疲労を強く覚える場所だった。

一つ一つの作品に強い思い入れを抱き、作者の思いを想像し、それを通して自分の内面を見つめ直す。その一つ一つのプロセスが少しづつ心を蝕む。

流し見をすればいいのではないだろうか? いや、それはできない。なぜならドラえもんだから。

しかし、何となく展示の企画者の意図も感じた。村上隆氏の大型作品「あんなこといいなできたらいいな」に始まりシンヤマザキ氏の「(Pink) Dust In The Wind ~すべては(ピンクの)もやの中に」で終わる作品配置は深く見れば見るほど、破壊され蝕まれる精神状態を表すように見えてならない。

ドラえもん展のテーマはそもそも「あなたにとってのドラえもん」だった。つまり、画家や彫刻家、クリエイターがあるいは子供のときに、あるいは大人になってから見つめ直したドラえもんを自分の形で表現したものだ。この世のすべての作品は受け取り手にとって十人十色、100人いれば101通りの受け取り方があり得る。「あなたにとっての」とは各クリエイターの心の中にあるドラえもんを、言ってみれば各クリエイターの心の中を覗き見する行為に他ならない。

私は人の心を理解することはできないと思う。したがって、人の心の中を覗き見することは自分にとっての「理解の及ばない」エリアを見ることに他ならない。自分の理解ができないものを見ることは即ち自分の精神的限界点を超えてしまうことなのだ。

ドラえもん展にはそれだけのパワーが有った。

気鋭の若手現代アーティスト達によるドラえもん展の展示は、見ているものに対する精神的汚染に他ならない。しかし、自分の心が乱される中で自分という個を保つために自分との対話が生まれる。

自分にとってのドラえもんは何だろうか・・・。それを問いかける機会ともなった。

私にとってのドラえもん

それは、SFとの出会い。

それは、漫画との出会い。

それは、アニメとの出会い。

それは、藤子・F・不二雄との出会い。

大人になってしまった今では、少年時代との出会い。

ドラえもんと言えば思い出すのは、初めて大学の単位を落としたときのこと。一年生の線形代数学の授業だった。

自分にとって「単位を落とす」とか「留年をする」はフィクションの中のことだった。

キテレツ大百科の勉三さんみたいな、あるいは「アパートの木」の五郎さんのような、どこか昭和の薫りの漂う漫画の中の出来事だと思っていた。

それが現実になったとき、大きなショックを受けた。自分の中の価値観が壊れてしまうような衝撃。自分が自分を保つため、選んだのは「ドラえもん」だった。

てんとう虫コミックス第6巻の「さようならドラえもん」。あるいは大長編ドラえもんの「のび太の宇宙開拓史」。そして、テント虫コミックス第35巻「ドラえもんに休日を」。

自分の中の「泣ける」ドラえもんの話。

これを「泣く」ことこそが、自分自身だと思っていた。落第が決定したときにこれらの作品を読んだ。でも、泣けなかった・・・。

泣くことができなかったショック。自分が自分でなくなるかのようなショックに涙を流した。

なぜここにドラえもんが居ないのか。

いくつかある最終回を除けばドラえもんが居なくなった瞬間は漫画の中では描かれない。大学生になったのび太がタイムマシンを利用していたシーンもあれば、大人になったのび太ドラえもんとの再会を懐かしむシーンも有る(45年後・・・

ドラえもんは最も近くて最も遠い存在だったのかもしれない。近くにいるけれど見つけられない。灯台下暗し。「心をゆらして」探せば見つかるのかもしれない。

今では、そんなこともあったと思える。

結局、自分を立ち直らせたのはしばらくしてから発売された劇場版ドラえもんのDVD BOX だったり、藤子・F・不二雄大全集だった。

自分が自分であること、それはドラえもんを読んで「すこし」笑っていたり「すこし」泣いていたりすること。

そんな、在りし日の思い出を揺り起こすことだった。

ドラえもん展を見に行ってほぼ1年。今では、全国を巡回している。大阪に来る日もあるだろう。

1年前とはもしかしたら「すこし」違う、でもやっぱり一緒な自分を見つめ直すこの展覧会をまた見に行こうと思う。

そのアプリ、本当に MVI が必要?

久々に技術っぽいポエムを。

Qiita で Android の MVI アーキテクチャについての記事を書いている。

qiita.com

思ったよりも大作になってきて、ちょっとどこから編集を追記すれば良いのかなという気持ちになっていたりするのだけれど、ぼちぼち作っていきたい(また書籍化したい・・・?)。

さて、 この Qiita の記事を書くきっかけはあるプロジェクトの中で MVI アーキテクチャを採用しませんか?ということになったため。練習レベルで MVI アーキテクチャiOS の Reactor Kit を使ったコードを書いたことはあるけれど、ちゃんとプロダクトで使ったこともないし、人に説明ができる感じでもないなという状態だったため、今一度自分のスキルセットを確認しようと思い筆を執った次第。

で、コードを書いていて思ったのだけれど全部が全部 MVI っぽい感じのコードにはならない。まあ、そんなのは当たり前で、アーキテクチャを決める会なんかのときにも「いやあ、全部が全部 MVI っぽい書き方にすると大変っすよー」みたいなことは言うのだけれど、具体的に「全部が全部」じゃない部分ってどれよ?ってのを今ならある程度言語化できそうなので、忘備録的な意味合いでちょっとまとめておく。

プロジェクトが終わった時に考え方が変わっているかどうかが見もの。ちゃんとプロジェクトが終わりますようにという思いも込めて 🙏

静的な View

そもそも MVI を導入する目的は「複雑になった画面の状態を抽象化し、コードで表現できるようにしてデバッグやテストをしやすくする」ことだと思う。その目的にそぐわないのが「静的な View」だ。一般的なアプリだと

  • ヘルプ、使い方画面
  • WebView だけの画面

とか。「WebViewだって、View の中身のページが変わるじゃん!」みたいな考えもできるけれど、中身のコンテンツ自体は WebView が状態を管理してくれることが多いのでそっちに任せておくのが良さそう。静的な View しか存在しない画面は MVI っぽく作る必要がないし、動的に変化をする画面でも 静的な部分については State で管理をする必要はない。

画面遷移

画面遷移も Fragment なり Activity なりを管理してくれるのは os なので、アプリの方では気にしない。ボタンを押したりしたら普通に startActivity や add/replaseFragment で画面を開けばいい。個人的には Espresso でテストがしやすいので EventBus 的に Fragment → Activity で通信を行う仕組みを作っていると、EventBus をモック化することで Fragment の UI テストがしやすくなるので好み。ただしこれは MVI とは関係がない。

Runtime Permission

これもなー、画面遷移に近いのかなーって思っている。ボタンを押して現在位置を取得する、みたいな実装の場合

  1. ボタンを押す
  2. リスナーで Runtime Permission のチェック
  3. Permission の状態に応じて ViewModel に対して Intent を飛ばす

のように、View 側で Permission の管理をしちゃうのが良さそう。Runtime Permission 関連の API が Context に依存していてさらに画面上にダイアログなんかも出ちゃうので、このダイアログを State で管理をするとかやりたくないよね?って感じ。

ダイアログ、 Toast、 SnackBar

これに関してはまだよくわからない。 State に isShowDialog: Bool なフラグをもたせておいて、 True なら表示をすればいいのだけれど、じゃあいつ False になるんだろうか?Toast や SnackBar も同じ。これらの View が「閉じた」というイベントをトリガーにしてフラグを倒すのかな。SnackBar は簡単にできそうだけれどダイアログと Toast は大変そう。 Droidkaigi でも Toast に関しては「表示してからn秒後にフラグを倒す Intent を発行する」とか言ってて、やべえなって思った。ダイアログに関してはダイアログが閉じられるケースを全部網羅するの?まじで?って気持ちもある。

画面遷移に近いようなきもするけれど、こういう UI を出したいタイミングって非同期処理なりビジネスロジックの終了をトリガーに行うことが多いイメージ。なので「ボタンを押したらすぐ表示」というわけにはいかないよね。

大変そう。頑張ってフラグで管理をするのかな。

俺にとってのみれぃ、つまりプリパラそしてでじこちゃんときどきハートキャッチプリキュア

みれぃは自分自身だった。

アニメ「プリパラ」を始めて見たのが確か2018年の5月くらい。 Netflix で配信が配信されていて、目が止まったのが最初。

序盤のらぁらのがんばりに涙を流しながら見ていたけど、あるとき俺が見ていたアイドルはラァラではなかったことに気がついた。

みれぃだ。

日常生活では生徒会長や風紀委員として、そしてプリパラでは語尾が「ぷり」のポップなアイドルとして、彼女は自分を表現していた。彼女は全部を計算して、計算どおりに練習をして頂点を目指そうとする彼女の姿は天才ではない自分と重なっていた。

第2シーズンに入ってプリパラがセレパラになってしまった世界。彼女は努力をしてトップを目指そうとしていた。しかし、しかしだ。彼女の努力は天才たちによって打ち砕かれてしまう。そふぃのライブを見て勝利をすることに諦めてしまう。さらに彼女に追い打ちをかけたのが、紫京院への対決の宣言。自分の行動に対して過剰に責任をおってしまう。

自分の責任で、あるいはそう思い込んだときにどん底に落ちるこれほど苦しいことはない。それが、俺にはわかる。

自分は、電気通信大学という名前となんとなくソフトウェアや情報工学が好きだったので大学を選んだ。この場所なら自分が最高に輝くことができる、最高に才能を発揮することができる。そう信じて大学に進んだ。

でも、現実は違った。大学1年生のときに線形代数学の単位を落としてしまったときに最初の涙を流した。そのあと、3年生のときに実験の単位が足りなくて4年生に進学できなかった。その理由は簡単で、3年生の実験の授業が理解できなかったから。根拠のない自信で自分はソフトウェアやプログラミングに強いと思っていた。でも、現実は違ったんだ。学問の奥は深くて難しくて自分には理解ができなかった。

それが辛かった。自分にはできると思っていた自信が破壊されてしまった。

みれぃもそうなんだ。成績が優秀でしっかりとした性格。自分の中で全部を解決できる。そして、何より自分はプリパラが大好き、自分の中で満足のできる努力をしてきた。それが彼女の根拠だった。それが紫京院を前に、そふぃを前に打ち砕かれて涙を流す。

完全に俺とかぶっていた。根拠として信じていたものが打ち砕かれたとき人はだめになってしまう。彼女もそうだった。俺が大学3年生のときも、実験の内容が理解できなくて引きこもり状態になってしまった。あの時の俺とみれぃが完全に被ってしまう。そして、その理由が自分自信にあることが嫌だった。だから動けなくなってしまった。動きたくなってしまった。暗い部屋でゲームをするだけの存在になってしまった。

そんな彼女をライブのステージに戻したのはがぁるるだった。がぁるるのライブはたとえ途中でこけても続けた。人の誰よりも努力を続けて立ち上がったがぁるるのライブはみれぃの涙になった。それは自分の経験にもある。同期の人達に厳しいことを言われたり、教授に頭を下げてレポートを遅れて提出したり。その中で人に頼ることを覚えたり。みれぃもそうなんだよな。自分ひとりで立ち続けていたから人に頼ったり、だれかの力で立っていることに気がつけなかった。シーズン1の最初の方ではらぁらに対してきつい言葉を言ってしまうことがよくあった。でも、それは自分が自分ひとりの力である程度のところまでこれてしまったと勘違いをしていたから。

「自分がこれくらいできるのだから、お前もこれくらいできるだろう」と思ってしまう衝動は自分にも経験がある。彼女もそうなんだ。だから「自分がこれくらいできた」という自信が破壊されたときに立ち上がれなくなってしまう。

そんな彼女が再度立ち上がったのが地下パラだった。地下パラでの成功体験の積み重ねが彼女の力になっていった。俺もそうだ。C言語ポインターとかの概念がわからなくて詰んでいたときに、Androidが登場した。Androidのアプリを開発するためにjavaを覚えて、参照渡しの概念がわったとき、かつてのC言語のレポートくらいなら書けるようになっていた。必要なのは成功体験でもあった。

でも、それだけじゃない。彼女をステージに立たせたのは彼女とそして彼女を支えるそらみスマイルやドレッシングパフェやだった。彼女のソロライブ、そしてエアリーチェンジを実現した曲「ぷりっとぱ〜ふぇくと」はそらみスマイルの「トライアングル・スター」のアレンジ。彼女を立たせていたのは、そらみスマイルだったんだ。


プリパラ 84話 『 ぷりっとぱ~ふぇくと 』 みれぃ

「むいてもむいても」と一皮むける彼女。「唇をキスの形スタンバイ」と彼女が計算の末に作り出した最高の言葉「ぷり」。みれぃは自分の力と仲間の力で最高潮に達した。

少しずつの成功体験、仲間の協力、それが彼女の力になった、自分の力になった。

彼女はすべてを受けいれて次のステージへ進んだ。彼女はチェンジをしたんだ。

チェンジ、そう、ハートキャッチプリキュアのテーマだ。

ミラージュプリキュアの試練の中でブロッサムはチェンジする前の自分を受け入れる。かつての嫌いな自分も自分自信だったことを受け入れる。今の俺ならそれができる。ステージに再度立ったみれぃもそれができる。完璧じゃない自分を受け入れて今の自分を見つめてより高みを目指すことができる。

みれぃを応援したのがそらみスマイルやドレッシングパフェだったように、俺を応援してくれたのが id:henteko07 だったり @sseze だったり id:ytRino だったりする。Android道という勉強会をやっていたときに助けられた。

そして、無条件に応援をしてくれたのがでじこちゃんだった。健気に頑張る彼女を見て自分もそうしようと思うことができた。でじこちゃんのおかげであのときがあったし、今がある。

numa08.hateblo.jp

今なら最高に可愛いのはでじこちゃんだって迷いなく言える。でも、過去の自分を投影してそして過去の自分を受け入れられる存在としてみれぃも好きだって言える。俺の中のみれぃはそんな存在なんだ。

Elastic Leadership を読んだ

エラスティックリーダーシップ ―自己組織化チームの育て方

エラスティックリーダーシップ ―自己組織化チームの育て方

2018年の Droidkaigi の中でこの本が紹介されていたので、会社の経費でポチってもらった。回し読みをしてようやく順番が回ってきた。

numa08.hateblo.jp

感想

チームが今ある状態を「サバイバルフェーズ」「学習フェーズ」「自己組織化フェーズ」に分割し、リーダーはチームを自己組織化フェーズへと導くことが仕事であるとしているのが著者の主張だと感じた。これに関して自分はたしかにそのとおりだと感じた。思えば、自分は小さな組織でリーダー的なポジションになることが多かった。例えば高校の部活動、例えば大学のサークル、例えばバイトetc...

どんなチームであっても思うのは、メンバーに勝手にやってほしいと思うことだった。今にして思えば、それは自己組織化フェーズへと進化したチームのことだったのだろう。そしてこれは、大体のリーダーが考えていることだと思う。よく言う「経営者的思考を身に着けてください」と経営者が言うアレだと思う。

チームのメンバーはチームリーダーが自己組織化されたチームを目指していても、メンバー自体はそこまでの給料をもらっていない事を考えるとモチベーションは普通の状態では持ち合わせない。チームリーダーの仕事で難しい部分は、自己組織化されたチームの状態がどういうもので、なぜそれを目指すのかをチームメンバーで共有するところかもしれないと感じた。

自分たちのチームはどんなチームだろうか

この本を読んで、自分たちのチームがどんなチームなのかを振り返ってみたいと思う。実は、今のチーム(コベリン)は会社全体をチームとして見たときにまだサバイバルフェーズのままなのかなと感じている。と言うのも、案件に関するノウハウの共有もできていない部分も多く(バス因子)、スケジュールがカツカツと言うわけではないが、組織で体系的に学習ができているわけでもない。

例えば、デザインであれば今のところ社長がほぼ1人でこなしている。これはバス因子だろう。こういったノウハウやスキルを他のメンバーがみんな学習し始めることで初めて学習フェーズへの移行ができていると言えるのではないだろうか。バス因子のリストアップをするだけでも良いかもしれない。学習フェーズでは生産性が落ちるだろうが、それは仕方のないことなんだろう。

小手先のタスクリストという意味であれば、あとはコミットメント言語をしっかりと使っていくことだろうか。本を読んで感心したのは「バグを明日までに取り除きます」という発言は、約束できる言葉では無いと批判していたこと。ソフトウェアのエンジニアならだいたい今日考えられると思うけれど、バグは大体の場合自分が思っているよりも広範囲に影響を与える物だったり、修正自体は簡単だけれど調査がめちゃくちゃ大変なものが多い。つまり、こう言った場合に利用するコミットメント言語は「少なくとも、1日に5時間はバグの調査を行います。そのために、他のタスクを止められるように調整します」と言うことだ。確かに、そのとおりだなと思った。リーダーがサポートする部分は、コミットメント言語を得ること、そしてそれを実現するためにサポートすることなのだろう。

こうやってこの本を読んで自分のチームを見直すと色々と不十分な部分が目立つ。もちろん、ある側面ではできている部分もあるだろうが、ある側面ではできていない部分もあるのだろう。学習フェーズとサバイバルフェーズの中間くらいにはいるかもしれないが、自己組織化フェーズには至っていないと言ったところだろうか。

DJI Tello のビデオストリーム iOS でデコードする

できあがったものがこちら

github.com

4月の終わりにDJIから発売されたトイドローン、Tello。発売と同時に購入したけど、前評判通りに高い安定性を発揮してくる上、風がなければ屋外でも遊べるので、今までとは違うアングルからの写真撮影が楽しめて面白い。

そんな Tello は今のところスマホWifiで接続をして操作することしかできない。この値段帯のトイドローンならだいたいプロポが付いて来るんだけど、そういうのを削って本体のコストにしたってことなのかな?

あと、一応UDPで通信をするAPIもあるのだけれど、純正アプリと同じことはできない。純正アプリではカメラの画像をリアルタイムにプレビューできるし、前後左右上下の操作以外にもフリップや360度回転、手のひら着地なんかのコマンドを実行できる。一方で公開されているAPIは前後左右上下の操作を行うことしかできない。

そういった部分に不満を覚えつつ、夜な夜なパケットキャプチャしたデータを眺めていたら、すでに同じようなことをしていた人が海外にはいたらしい。go言語で書かれたiot用ライブラリのgobotにtelloの隠しインターフェースが追加されて公開されていた。

Hello, Tello - Hacking Drones With Go

日本版が出る前に公開されたエントリーなので、最初にこのブログをみつければパケットを開くこともなかっただろうな。

このエントリーのサンプルコードを実行するだけで、 mplayer を利用して動画のプレビューをPCから行うことができる。さらに、動画の仕様に関しては次のように書かれていた。

The streaming video is also sent to the ground station on a different UDP port. Unfortunately the Tello does not just use one of the existing standards for streaming video such as RTSP. Instead, the raw video packets are sent via UDP and need to be reassembled and decoded before they can be viewed.

Since the size of a single video frame is larger than the size of a single UDP packet, the Tello breaks apart each frame, and sends the packets with header of 2 bytes to indicate how they need to be reassembled.

Position Usage 0 Sequence number 1 Sub-sequence number 2-n Video data The video data itself is just H264 encoded YUV420p. Using this information, it is possible to decode the video using standard tools such as ffmpeg, once you remove the header bytes.

H264でエンコードされたYUV420pのピクセルデータだけど、いわゆる動画配信用のプロトコルには則っていなくて、生のバイナリをUDPで送っているので適当にデコードする必要があるよ!!って感じ。

H264でエンコードされた動画だったら、ffmpeg + lib264 とかでデコードできそうだなと思ったので、iOS 上でやってみることにした。

コード解説

ffmpeg や libx264 のビルドはすでにビルド用のスクリプトを作ってくれた人がいたので、それを利用した。

github.com

github.com

実際に動画のフレームをデコードする実装の抜粋が以下。

gistb93addb5b78d3a6417df2e7d77412655

コードの全体はこちら

Turkey/ImageDecoder.mm at 430f060486b902ee0f9178b4dc20bdc7de2242e8 · numa08/Turkey · GitHub

ffmpeg にバイナリを与える

利用しているのは ffmpeg , libx264 、そしてさっきの gobot を iOS 用にラップしたライブラリ。gobot は tello が送ってくる動画のバイナリをコールバックで渡してくるので、バイナリを処理する必要がある。アプリ側はバイナリを得ることはできるのだけれど、どうやらffmpegのインターフェースを利用する場合、直接バイナリを渡すには AVIOContext インスタンスを作って、コールバックを利用してバイナリを返す必要があるのだけれど、ちょっと考えることが多くて面倒くさい。

そこで、 NSPipe というAPIを利用することにした。

NSPipe - Foundation | Apple Developer Documentation

Linuxpipe(8)をラップしたAPIで、 pipe の作成、そして書き込みと読み込み用の file descriptor を作ってくれる。また、linux は pipe を作ると pipe:<file descriptor> というファイル名でアクセスをすることができるようになる(これの仕様ってどこに書かれてるんだ?)。この仕組みを利用することにした。

init[NSPipe pipe] を使って NSPipeインスタンスを生成する。 Tello と通信を行い動画のバイナリを取得する onNewFrame: では [self.fifoPipe.fileHandleForWriting writeData:packet]; を行うことで動画のフレームを書き込んでいる。 ffmpeg を利用する captureInFFMpeg では

int fileDescriptor = self.fifoPipe.fileHandleForReading.fileDescriptor;
const char* file = [NSString stringWithFormat:@"pipe:%d", fileDescriptor].UTF8String;
// 中略
ret = avformat_open_input(&format_context, file, input_format, &format_dictionary);

として pipe:<file descriptor> というパスのファイルをパラメータにして ffmpeg に与えている。こうすることでファイルの入力なら簡単にできる ffmpeg にファイルに見せかけたバイナリを渡す仕組みを実現することができた。

正しいバイナリを作る

Tello から取得したバイナリを ffmpeg に与えることができたのであとは ffmpegAPIを使ってデコーダーを実装していく。これはそんなに面白くないので割愛。

しかし、実は Tello から送られてくるバイナリは厳密には動画の生のバイナリではない。実は1つのフレームのバイナリを8個の送信に分割して送ってくる。今回、アプリを作り始めるまで知らなかったのだけれど udp は送信容量に上限がある。

UDPパケットサイズと転送レートの関係:プログラマー社長のブログ:オルタナティブ・ブログ

Ethernetフレームは最大サイズが1518バイトですので、そこから、Ethernetヘッダ(14バイト)とFCS(4バイト)を除いた、1500バイトが送出できる最大サイズになります。IPv4UDPを送出するには、IPv4ヘッダ(20バイト以上)とUDPヘッダ(8バイト)を除き、1472バイト以下がデータサイズになります。

今まで心を無にしてHTTPを触ってきたので、こんな仕様があることを知らなかった。動画のバイナリは 1472 バイトを余裕で超えるので、分割されるというわけ。どうやら8個のパケットに分割されているようなので、8という数字を決め打ちにしてしまって実装を進めることにした。

動画のフレームを受け取るonNewFrame: で、8個分の NSDayaNSArray を作り、マージして書き込みを行う。

[self.packetArray addObject:frame];
if (self.packetArray.count == 8) {
  NSMutableData *packet = [NSMutableData data];
  for (NSData *d in self.packetArray) {
    [packet appendData:d];
  }
[self.fifoPipe.fileHandleForWriting writeData:packet];
self.packetArray = [NSMutableArray array];
}

先頭に正しい動画のフレームを挿入する

ここまで実装をしてどうにか iPhone の画面上に Tello の動画をプレビューすることができた。

そのしばらく後

コードを全く変えていないのに h264 がデコードに失敗するようになった。なんで??なんで??

どうやら、先頭のフレームに含まれているヘッダー情報を正しく読み込むことができなくて失敗しているように思えたので、じゃあ正しい情報を与えてやればいけるのでは・・・?と思い実装。

PCで取得をした Tello の動画のフレームの先頭をプロジェクトに加えて、Telloが動画フレームを返すより前に ffmpeg に渡してやることにした。コードではopenのあたり。

// 正しいフレーム情報をもった動画ファイルを読み込んで、
// avformat_find_stream_info を成功させる
dispatch_async(writeFifoQueue, ^{
        NSString *p = [[NSBundle mainBundle] pathForResource:@"movie" ofType:@"mov"];
        FILE *i_file = fopen(p.UTF8String, "r");
        if (i_file == NULL) {
            NSLog(@"failed fopen");
            return;
        }
        int buffer_size = 10 * 1024;
        char buff[buffer_size];
        size_t size;
        while (true) {
            size = fread(buff, buffer_size, 1, i_file);
            if (size == 0) {
                break;
            }
            write(self.fifoPipe.fileHandleForWriting.fileDescriptor, buff, buffer_size);
        }
});

ちょっとパワーの有る方法だったけどこれで確実に成功するようになった。めでたい!!ちゃんとgcdを利用していたので、変なブロック処理やwaitを挟む必要が無いのも偉い。

感想

iOSシステムコールって読んで良いんだ・・・?

最近、LinuxシステムコールAPIに関する本を読んで勉強し直したところだったのでちゃんと役立てられて嬉しい。

numa08.hateblo.jp

C言語最高!!これで行きていける!!!みたいなことを考えることもなく、それでもswiftとかkotlinとかレイヤーの高いところで生きていきたいのだけれど、とは言え触ることができるっていうのは良いことだと思う。

あと、go言語で書かれたライブラリを利用できたのも良かった。個人的にgo言語は好きな言語で、CLIのツールを作ってgithubのスターを稼いだこともあったけど、しばらくご無沙汰だったため、久しぶりに利用できたことが素直に嬉しい。はじめ、gobotのtello関連のモジュールだけをswiftに移植しようかと考えていたけど、流石に面倒だったのでやめた。gomobile を利用することでiOSAndroid向けのアプリやライブラリをビルドすることができることは知っていたけど試したことはなかったので、良い挑戦だった。と言っても、gobotが依存しているライブラリも少ないためか、特に何もしなくてもそのまま素直にビルドをすることができた。時代はgo言語って感じ。

今後

この手順で最終的に UIImage インスタンスを取得することができたので、例えば Vision framework なんかを利用して自動操縦の自撮りドローンなんかを実装できそうだなーって考えている。