話に出てくるアプリの紹介
DeNA吉田です。
今回ご紹介するのは、実際に下記のアプリを開発する現場で直面した課題です。
RabbitCam
Rabbit cam(ラビットカム)はアニメーションするスタンプやBGMを選び、合成して動画を作成するアプリです。
非常にかわいいスタンプをより、可愛くする細かい動きや、おしゃれに仕上げるための動画に対してのフィルタの色味など細かい調整を重ねました。
QuizNow
QuizNow(クイズナウ)は好きなトピックで友人とリアルタイムに勝負できる対戦クイズアプリです。
クイズ対戦の面白さや世界観により没頭してもらうために、スプラッシュやバトル時のマッチング時など、随所にイケてるアニメーションを組み込んでいます。
これら弊社の優秀なデザイナーさん達と連携して作り上げたのですが、どちらも開発期間が2,3ヶ月程度の中、いかにデザイナーさんが生み出してくれるすばらしいアウトプットを一緒に昇華させつつ、限られたスケジュール期間にて効率よく作業を進められるか常に意識しながら、開発を進めていきました。
これらの開発の中で使用したUI開発周りの実装のTipsに加えて、開発時における課題に対してどういうアプローチしたかをご紹介したいと思います。
アプリUI開発における課題
これらのアプリで特徴的な機能部分として
- スタンプやスプラッシュ、バトル時のアニメーション
- 動画フィルタ、エフェクト
が挙げられます。
これらは、細かいチューニングをデザイナーさんと一緒に行う必要がありますが、以下の点が課題となってきました。
デザイナーさんのイメージをどうやって忠実に実装するか
一つ目は、例えば、アプリ開発において、UI上のアニメーションは非常に多用することになりますが、それら一つ一つをデザイナーさんのイメージ通りを実現する場合、デザイナーさんが考えているイメージを、コミュニケーションしながらキャッチアップして実装すること自体難しかったりします。
アニメーション以外でもフィルタやエフェクトを作成する場合に、指定された表現を作り上げるのに、なかなか思い通りのパラメータにチューニングすることができず、当初苦労しました。
デザイナー、エンジニア間で確認のフローに時間がかかる
実装を進めて完成度を上げていく上での課題も発生します。実装を行って、デザイナーさんに確認してもらうとなると、例えば
実装完了
↓
TestFlightでアプリをあげる
↓
デザイナーさんチェック
↓
再度修正
というふうなサイクルになるかと思います。
そのアプリを配布しなくてもデザイナーさんを捕まえて確認など行ったりしますが、常に確認してほしいときに、すぐデザイナーさんの手があいているわけではないため、基本的にはこのようなフローになるかと思います。
実装がイメージに合わなかった場合、このサイクルを繰り返していきます。
その場合、たとえ一回の修正が軽微だったり、TestFlightなどのサービスのおかげでアプリの配布が簡単になっている今でも、このサイクルの繰り返しに結構時間を取られることが多々あるのではないでしょうか?
UIの開発において、デザイナーのイメージ通りの実装が行われているか、確認を行うことは非常に重要です。 UI/UX部分が重要になっている昨今のアプリ開発においては、細部の完成度がそのままアプリの質につながります。
これら実際のプロジェクト内で、これらの課題に対して以下のようなアプローチを取りました。
アウトプットイメージをデザイナーさん自身でより詳細に具体化する
結局のところ効率よく行いたいことはデザイナー、エンジニア間のコミュニケーションサイクルの改善です。以下にデザイナーのイメージがイメージした物を理解し、それを正確にインプリメントするために早い方法と考えたのが
実際にイメージしてる物をデザイナーさん自身でより詳細に目に見える形にまず作ってもらう。もしくは具体化の作業をデザイナーさん自身でなるべく行ってもらう
です。
要はデザイナー、エンジニア間でイテレーションをまわして、アウトプットを少しずつ具体化していくのではなく、
なるべく最初からデザイナーが、確認できるアウトプットを詳細に先に作ってもらってから、一気にエンジニアが実装をすすめるという進め方をとりました。
デザイナー、エンジニア間のコミュニケーション回数が少なくなればなるほど、効率は改善します。
このアプローチは、特別なことではなく、すでに皆さんもアプリで実際に作ってみる前にワイヤー、モックアップツールやサービスなどを利用しているかと思いますが、そういったアプローチは非常に有用なことは感じていることでしょう。
そのため、ごくごく当たり前のことを言っているのですが、それ以外にどのようかことを行ったか見ていきましょう。
確認用に別途専用のアプリを作る
こちら動画フィルタの開発時におこなったことです。
例えば動画フィルターの色味を細かく調整したり、スタンプのアニメーションの細かい調整を行う必要がありました。
それぞれの調整はパラメータをいじるだけなのですが、当初は
デザイナーさんで動画フィルタのイメージ画像を数パターン作成
↓
その絵を見ながら、イメージ画像通りに近づくように実装完了
↓
デザイナーさんチェック
↓
再度フィルタパラメータの数値だけを変更
↓
デザイナーさんチェック
↓
・・・
と行っていたことで、作業自体はパラメータの変更だけなのに、確認してもらって、修正しての繰り返しにあまりに時間がかかっていました。
そこで、その時間を短縮するために、別途動画フィルタのパラメータをいじって確認できる調整用のアプリを作成してデザイナーにパラメータを調整してもらう手法をとりました。
確認用アプリをデザイナーさんに渡して、イメージ通りになるまでパラメータを調整してもらい、あとでそのパラメータだけをもらい実装しました。
確認用のアプリでデザイナーさん自身がパラメーターをチューニング
↓
チューニング済みのパラメータ一式をもらってアプリに組み込み
こうすることで、チューニング時間ゼロとまではいきませんが、パラメータの変更毎にビルド、アプリ配布など繰り返していた作業が大きくなくなりました。
別のアプリを開発する実装時間が少々必要ですが、確認用のアプリなので、それほど工数はかかりません。その後の手戻りが大きく軽減だったので、その後の開発はスムーズに進みました。
このやり方がうまく行ったので、その後アニメーションスタンプを作る場合も、パラメータが調整できるアプリ上でまず作成して確認して進めていきました。
ちなみに、RabbitCamはiPad対応してませんが、デザイナーさんがわかりやすいように確認用のアプリはiPadアプリで作成したり、ちょっとした気遣いも行っていました。
具体的なイメージを動画で作ってもらう
こちらはQuizNowで行った効率化です。
QuizNowは各所に随所にアニメーションを組み込んでいます。特にスプラッシュやバトル時のアニメーションは音楽とも連動しており、非常にスタイリッシュです。
開発当初からアニメーションなどにこだわって開発を進めるということで、この点がこのアプリにおいて最重要なポイント、かつ、開発コストがもっとも高いだろうということで、どう進めるか難しいだろうなと考えていました。
そこで最初にバトル部分の開発を行ったとき、デザイナーさんと話して、まずどういったアニメーションにするか動画を作成してもらうことになりました。
先に開発をしたRabbitCamの時はアニメーションのイメージを絵で指定してもらったので、細かいイメージ確認を実際みてもらって行う必要が結構ありまして
デザイナーさんが静止画とアニメーションのイメージを口頭などで説明
↓
聞いたイメージで実装してみる。
↓
デザイナーさんチェック
↓
再度調整
と行った流れを、繰り返しました。
それがQuizNowの時は動画で作成してもらったことにより、
デザイナーさんがアニメーションの動画を作成
↓
エンジニアは動画を見てその動きを実装する。
↓
デザイナーさんチェック
デザイナーさんに逐一確認してイメージ調整することなく、動画どおりになるように実装を行えばよかったため、完成物に対するキャッチアップのコミュニケーションの回数や時間が減り、実装時間を大幅に軽減することができました。
その動画をそのまま実装に活用する
さらに、この動画自体から連続したアニメーション素材をきりだしてもらい、それをそのまま用いる実装にしました。
デザイナーさんがアニメーションの動画を作成
↓
エンジニアはその動画を元に生成されたアニメーション素材を使って実装
動画作成もすべてが無駄な工数にならず、また実装自体もその素材を用いるだけの簡単なものになりました。
この連続したアニメーション素材を使ってアニメーションさせる実装は、どちらのアプリでも多用しているのですが、これはCoreAnimationを用いることで簡単に実装が可能です。
このアプローチはデザイナーさんに工数をかけてもらうことになり、デザイナーさんの負荷は増えてしまうことになります。
しかし、アニメーションのような、言葉や静止画だけではなかなかイメージが伝わらなそうな物は、実際に作ってもらうことで、100%意図している物が伝わります。そのまま素材に活用できるため省コストの進め方でした。
続いてスプラッシュの実装に移ったのですが、さらに効率よく進めるため、まず作成してくれた動画をアニメーションで実装する前に、そのままアプリに組み込みました。
デザイナーさんがアニメーションの動画を作成
↓
エンジニアはその動画をそのままアプリに組み込み表示
↓
デザイナーさんチェック
↓
エンジニアはその間に、本実装を進める
というふうにすることで実装コスト最小で先に確認を進めつつ、その間に実装作業を進めました。
こちらは、バトル部分より、アニメーション作成時にメモリ調整など細かいチューニングが必要だったのですが、確認作業中の時間を実装につぎ込むことができたため、こちらもスムーズに開発は進みました。
これら動画を用いての確認、実装を行わない方法で実装を進めていたら、何倍も工数をかける必要があったでしょう。
その後、メインのアニメーション以外にも、細かい部分にアニメーションを組み込む場合は動画以外にもアニメーションgifで作成して動きを確認したりもしています。
動画をはじめ、動く絵があるだけで、大きくコミュニケーションコストを軽減することができました。
まさに、百聞は一見に如かずです。
実装詳細
GPUImageのフィルタ作成
iOSで画像や動画に対して、フィルタ処理を行いたい場合は、GPUImageを使うとよいでしょう。いろいろライブラリやFrameworkを試しましたが、現時点では速度面、機能面において、これが最適です。
GPUImageはFilterに対してTargetをつなげていくことで、フィルター処理を行ったり、Viewへの表示、ファイルへの書き出しが可能です。
以下サンプルコードの抜粋です。
// フィルタをかける動画ファイル
NSURL *sampleURL = [[NSBundle mainBundle] URLForResource:@"sample_iPod" withExtension:@"m4v"];
movieFile = [[GPUImageMovie alloc] initWithURL:sampleURL];
movieFile.runBenchmark = YES;
movieFile.playAtActualSpeed = NO;
// 処理したいフィルタ
GPUImagePixellateFilter * filter = [[GPUImagePixellateFilter alloc] init];
//
[movieFile addTarget:filter];
// 動画を表示させるView
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];
// 作成する動画のパス
NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
// 動画書き込みクラス
GPUImageMovieWriter *movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(640.0, 480.0)];
[filter addTarget:movieWriter];
// 音声周りの設定
movieWriter.shouldPassthroughAudio = YES;
movieFile.audioEncodingTarget = movieWriter;
[movieFile enableSynchronizedEncodingUsingMovieWriter:movieWriter];
// 処理開始
[movieWriter startRecording];
[movieFile startProcessing];
// 終了blocks
[movieWriter setCompletionBlock:^{
[filter removeTarget:movieWriter];
[movieWriter finishRecording];
}];
処理の流れは
入力元(GPUImageMovie)
↓
フィルター処理(GPUImagePixellateFilter)
↓ ↓
Viewへの表示(GPUImageView) 書き出し(GPUImageMovieWriter)
と行ったふうに、Targetをつなげていくことで、それぞれフィルタ処理と出力処理を同時に行うことができます。
ちなみにこのフィルタークラスに対して細かくパラメータを指定することができ、前述のパラメータチューニングは、このフィルターのパラメータの調整に当たります。
その他、GPUImageに付属しているサンプル集を見てみるのが便利さがわかると思います。
サンプルはこちらを見るのがおすすめです。非常に多くの画像処理があり、様々なアプリに活用が可能です。
CoreAnimationでのアニメーション作成方法
非常に細かいアニメーションを作りたい場合、画像を連続して表示させてアニメーションを作成すると実装が非常に楽です。
CoreAnimationで実現するにはCAKeyframeAnimationを用います。以下CAKeyframeAnimationを用いた処理部分の肝の箇所の抜粋です。
CALayer *layer = [CALayer layer]
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
animation.duration = 2.0f;
animation.keyTimes = @[@0.0,@0.25,@0.5,@0.75,@1.0];
NSArray *images = @[
(id)[UIImage imageNamed:@"image1"].CGImage,
(id)[UIImage imageNamed:@"image2"].CGImage,
(id)[UIImage imageNamed:@"image3"].CGImage,
(id)[UIImage imageNamed:@"image4"].CGImage,
(id)[UIImage imageNamed:@"image5"].CGImage,
];
animation.values = images;
[layer addAnimation:animation forKey:@"flash"];
CAKeyframeAnimationはタイミングを指定して,様々な処理を連続して指定することができます。
上記は
start
↓
image1表示(0.0秒経過)
↓
image2表示(0.5秒経過)
↓
image3表示(1.0秒経過)
↓
image4表示(1.5秒経過)
↓
image5表示(2.0秒経過)
↓
end
と時間に応じて刻々と変化させることで、パラパラマンガの容量でアニメーションを実現できます。
そのため表示させたいアニメーションを、画像で各フレーム毎に書き出してさえもらえれば、どんなアニメーションも実現可能と言う訳です。
QuizNowでは画像を30fpsで表示させてアニメーションを作成しています。
CAKeyframeAnimationは画像を連続させて表示させる以外にも使えます.
例えば下記はタイミングを指定して,拡大縮小を変化させることができます。
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
animation.duration=2.0f;
// 変化させたいタイミングを0.0-1.0までの値を配列で設定する
// タイミングは指定の値 × durationにしていた時間になる。
animation.keyTimes = @[@0.0,@0.25,@0.5,@0.75,@1.0];
animation.values = @[@0.0,@0.5,@1.0,@1.5,@2.0];
この場合
start
↓
サイズ0倍(0.0秒経過)
↓
サイズ0.5倍(0.5秒経過)
↓
サイズ1倍(1.0秒経過)
↓
サイズ1.5倍(1.5秒経過)
↓
サイズ2倍(2.0秒経過)
↓
end
と変化していきます。
また、これらのCoreAnimation類はCAAnimationGroupでまとめることで、複数のアニメーションを同時に行うこともできます。
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:animation1, animation2, nil];
その他、様々なアニメーションを作成することが可能です。
加えて、このCoreAnimationのすごいところは、このアニメーションLayerを、そのまま動画に重ねることができるという点です。
動画用にプラスで設定がいくらか必要ですが、AVFoundation.frameworkの AVVideoCompositionCoreAnimationToolを使えば、実現が可能です。
以下のメソッドには、パラメータの指定にCALayerを指定することができ、なおかつ、そのLayerにはアニメーションをつけることが可能です。こちらを用いて動画にスタンプを合成しています。
[AVVideoCompositionCoreAnimationTool
videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer
inLayer:layer];
AVVideoCompositionCoreAnimationTool
その他の効率化
これら、デザイナー、エンジニア間での効率改善以外にも、開発する上で効率化できそうなライブラリ、Pluginsを紹介しておきます。
アニメーションの実装もっと簡単にできないの?
こちらのライブラリは、コードをいっさい書かずに、storyboard上やxib上でパラメータを指定するだけで、アニメーションを実装できるというライブラリです。
アニメーションの実装工数を大幅に削減できるだけでなく、実装を行う必要がないので、例えばデザイナーさんでも簡単に動きを確認することができます。
UIの調整するのに何度もビルドを繰り返し修正するの面倒だな
こちらはアプリを再ビルドすることなく、コードを書き換えることができるというXcodeのプラグインです。
修正をアプリを再ビルドすることなく反映させることができるため、webの開発レベルにサクサクUI開発を進めることが可能です。特に深いところにあるUIの調整を行う場合など、その恩恵が感じられるでしょう。
利用も特に依存はなく、導入もメニューから選んで一発で済んでしまうし、パラメータ調整用のViewもあったり、至れり尽くせりです。
まとめ
個人的な開発上のこだわりとして、いかにスピーディに開発を進めるかを重きにおいています。
そのために、単に自身の開発スキルを磨くのは当然ですが、それ以外にも様々な場面で一緒に開発に取り組むメンバーの力を最大限に引き出しながら、効率よく進めるかを常に考えて進めるようにしています。
特にコミュニケーションに費やすコストは、デザイナー、エンジニア間以外にも多く発生するかと思います。
実装や設計の工夫以外にも、ちょっとしたアイデアや進め方の改善次第で、効率よく進めることができ、その分質を高める時間に実装を費やすこともできます。
皆さんもこういうことやっているよ!などありましたら教えていただけると幸いです。
- DeNAエンジニアブログでも、最新技術や開発事例について発信しています。⇒「Technology of DeNA」
CodeIQコード銀行にあなたのコードを預けてみませんか?
- CodeIQコード銀行ではあなたのコードを財産と考えます。
- お預かりいただいたコードは、CodeIQコード銀行がしっかり評価し、フィードバックいたします。
- 当コード銀行にお預けいただいたコードは、企業がみてスカウトをかける可能性があります。
- 転職したい方や将来転職することを考えている方で、今の自分のスキルレベルを知りたい方はぜひ挑戦してみてください。
- 企業からスカウトがきたら困る人は挑戦しないでください。
興味を持った方はこちらからチャレンジを!
前職ではオレンジ色のSNSの開発に従事。株式会社ディー・エヌ・エーへ転職後、アプリ開発に携わる。現在、クイズアプリ「QuizNow」の開発を担当。サーバーサイドからiOS/Androidまで何でも作ってます。
Twitter:@mixiappwchr