KLabGames Creative Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。このブログでは、KLabのクリエイターがスマートフォン向けのゲームを制作・運営していく中で培った様々な技術や挑戦とそのノウハウについて紹介していきます。

はじめに

お疲れ様です、中根です。
この記事はKLab Creative Advent Calendar 2018の25日目の記事になります。
最終日は、クリエイティブ部門の責任者である中根が普段考えていることを書いて締めたいと思います。

このAdvent Calendarがはじまったきっかけ

このAdvent Calendarは、エンジニアがやってるAdvent Calendarのクリエイティブ版をやってみたいんだけど良いか、というatsushiさんからの相談からはじまりました。
そもそもKLabのクリエイティブが発信できるブログみたいなのもないです、まだ皆に声もかけてないので人数が集まるかどうかも分かりません、それでもやるってアナウンスしても良いか、という話でした。

こういう活動には積極的に協力してくれる人が多いので、人数は集まりそうな印象でしたが、年末に向けて皆忙しい時期なので油断はできません。
ただ、万が一人数が集まらなくて企画倒れになったとしても、そういうトライをしたことがきっと次につながるはず、と思い、やってみなよ〜と言って背中を押しました。

結果、たくさんの人が協力してくれて、こうして最終日を迎えられました。
トライしてくれて本当に良かったなと思います。

最終日はこのトライすること、について書いてみたいと思います。

勝率70%のトライの重要さ

今回の発案をしたとき、atsushiさんは、人が集まらず失敗する確率はゼロではないけれど集められそうな手応えはあるからトライしてみよう、と考えて行動してくれたのだと思います。こういうトライはすごく重要だと思います。

ひとくちにトライといっても、どの程度勝算が感じられるかの程度によって以下 3 つぐらいに分類できます。

  1. 勝算が全く不明なトライ(成功確率不明)
  2. 失敗する可能性はあるけど一定勝算もあるトライ(成功確率70%前後)
  3. 勝算が見えていて失敗がほぼないトライ(成功確率90%以上)

※成功確率の数字は事前の検討でどの程度成功すると確信できるかの肌感覚が理解しやすそうなので記載してますが、数字そのものには深い意味はありません。

atsushi さんのトライはこの 3 つのうちの 2 にあたると思います。
そして、この 2 のトライ(以下、これを勝率70%のトライと呼びます)が人の成長にはとても大事だと思っています。

なぜなら、トライすることによる成功体験を積みやすく、トライする習慣づけによる成長の加速がしやすいからです。
また、失敗したとしても得るものがあるのがこのトライです。
それぞれのトライについて、失敗した時のことを考えてみます。

1のトライは失敗する要素が多すぎるトライと言えるでしょう。
そして、個人的な経験では、1のトライはだいたい失敗します(笑)。
世の中の本当に革新的なことは1のトライから産まれるので、回復不可能な失敗にならない限界ラインを見極めた上で、一定の1のトライをすることは組織の中に必要だったりするのですが、なにぶん基本的に失敗するので、トライによる成功体験、失敗体験の両方がないとつらくて続かないです。
トライの中でも上級編、という感じなので、トライ慣れしてからのほうが良いのでは、と個人的には思います。

一方、 3のトライは失敗する要素が少なすぎるトライと言えるでしょう。
そもそも失敗しないですし、周囲から見てどんなに結果が凄くても、その人からすると新規性がないので、3のトライを繰り返してもあまりその人の成長には寄与しません。

最後に残った2の勝率70%のトライですが、これはほどよく失敗する要素があるトライといえるでしょう。ちょっと背伸びすればできるトライ。
失敗する要素が限定的なため、失敗しても理由が分かることが多いです。
そのため、理由から分かる対策をすれば次の成功につながり、失敗したとしてもその人の成長に寄与します。

勝率70%のトライは人によって違う

あるトライが先程の3つの分類のどれになるかはその人の仕事の力量で変わります。
例えば、今回のAdvent Calendarのメンバ集めは、一スタッフであるatsushiさんからしたら、勝率70%のトライになるのではないかと思います。
一方、私が同じことをやれば勝率90%のトライになるでしょう。

それぞれの人のスキル・経験・ポジション等によって勝率70%のトライの内容は変わりますが、成長するために大事なのはそれぞれの人が自分にとっての勝率70%のトライを続けることだと思います。

自分にとっての勝率70%ですから、今年社会人になった人にも、仕事歴10年以上の人にもそれぞれそういうトライができる箇所があると思います。
当社のクリエイターには、ぜひそういうトライを積極的にやってほしいなと思います。

そして、時々勝率不明なトライをやって失敗してほしいです(笑)。

いつかは自分のトライ=会社のトライに

当社のクリエイターであれば、皆何かしら成長したいという思いは持っていると思います(そういうマインドのある人しか採用してないつもりです!)。

成長するにあたり、いわゆるクリエイターとしての制作スキルを身につけたいのであれば、社内外の他人や組織の持つ知識・ノウハウを吸収すれば一定水準までは成長できると思います。
また、一定水準までは、自分にとっての勝率70%のトライであっても他の人にとっては勝率90%のトライであることも多いため、他の人から教えてもらうことで成長できます。

一方で、一定水準まで成長してしまうと、他の人が考えたものを吸収することでの成長は非常に少なくなるか、望めなくなります。
ここから先の成長は、他にノウハウが無い領域ですから、自分で考えたものを実践して開拓していくしかありません。
この領域になると自分のトライ=会社のトライになります。
技術革新のスピードが速く、知識も陳腐化しやすいこの業界ですから、どんな人も遅かれ早かれこういう状況に遭遇すると思います。

また、当社はまだ発展途上の会社ですから、担当分野によっては比較的社歴が浅いうち、年齢が若いうちにこの状況に遭遇する方もいると思います。
そんな時は、会社で学ぶものがなくなった、とか、自分がこのトライをしていいのだろうか?と考えるのではなく、ここから先は自分で会社の勝率70%のトライを作っていくことが会社と自分の成長につながるのだ、と考えて道を切り開く経験をして欲しいと思います。

たとえ失敗したとしても、トライを賞賛できる会社でありたいなと思います。

おわりに

社外に出すものなのに、結果として社内のクリエイターへのメッセージみたいになってしまいました。
社外の方でこの記事を最後まで読んで頂いた方には、KLabのクリエイティブは挑戦するということについてこういうスタンスでいる会社なんだ、と思ってもらえたら幸いです。

お疲れ様です、TAIGAです。

この記事はKLab Creative Advent Calendar 2018 の 24日目の記事です!

アプリ等の画面をデザイン設計している方へ

毎日デザイン設計をしていて、こう思った事ありませんか?

  • 画面をデザインする以外の資料作成系作業が多くてつらい
  • 画像の管理作業よりデザインクオリティを上げるのにもっと時間を使いたい

今回はそんな方に、設計ツール[Sketch]の機能を通して

時間をもっと効率的かつクリエイティブに使えるようにする方法をご紹介します

Sketchとは?

オランダのBohemian BVが開発する、
アプリやWebのデザインおよびUI設計のためのソフトです

2018年12月現在、MAC OSしかサポートしていません

何が得意?

「シンボル」機能によってより自由に画面を設計しデザインできます

例えば・・・以下の様な事が得意です

  • デザインパーツ素材の使い回し
  • シンボル内の情報の部分的上書き
  • 素材パーツの一元管理
  • 画面の半自動資料化

■ デザインパーツの使い回し

「シンボル機能」が非常に強力です

画面設計で使う実デザインパーツ素材は、アトラスサイズの縮小(端末への負担を下げる目的)の為、素材の1部分を伸ばして使えるようにデザインしますよね?

Sketchなら”どこを伸ばすのか”の設定を覚えたまま素材を使い、画面を設計できます

・素材をシンボルにするとパーツの”スライス設定”を記憶でき、素材とのリンクも保てる

・シンボルを組み合わせて他のシンボルを作る事もできる

■ シンボル内の情報の部分的上書き

「オーバーライド」機能によって細かい表示差分表現なら1シンボルで表現可能です

全シンボルへのリンクを保ったままなので更新が有っても、
使用箇所へ自動的に反映できます

・シンボル内の文字ラベルは全て好きな文字に上書きできる

・シンボル内の”同じサイズ”のシンボルはいつでも他のシンボルに上書きして表示できる

例えば下記画像の様な表現が1つのシンボルの使い回しで用意にできるわけです

※このイメージでは「ボタン1個」と「ボタン2個」を同じサイズのシンボルで用意しておく事で、部分的にシンボル内シンボルを”オーバーライド(切替)”しています

■ 素材パーツの一元管理

「書き出し設定」もとても優秀!

/(スラッシュ)で区切った名前をファイルにつけると、
書き出し時になんとフォルダ分けしてくれます

以下の様なルールでライブラリ内にパーツを管理すれば、必要素材を最新状態で揃えておく事ができ、さらにいつでも画面設計時に呼び出す事ができます

・例えばエンジンがUnityなら素材シンボルをAtlas/○○/なんとか.png 等で命名する

・ライブラリ(素材全部入Sketchファイル)から一気に書き出しする

■ 画面の半自動資料化

Sketchプラグイン「SpecExport」で画面指示書Htmlを一瞬で作れます!

・テキストクリックでスペースの長さ、フォント種類、サイズ等すべて解る

・画像をクリックでその画像のサイズだけでなくシンボル素材の格納場所まで案内可能

実際使ってみたら画面設計が楽になった

資料化や素材管理など単純で時間の掛かっていた手作業が効率化され、
よりクリエイティブなデザインの仕事に時間を使えるようになりました

  • リストのデザインがとても作りやすい
  • あの素材どこだっけ?これバージョン古いよね?がない
  • 開発に画面情報を伝える資料が一瞬で出来上がる

■ リストデザインがとても作りやすい

例えばリストを作るなら、「項目シンボル」を作って中身をオーバーライドするだけ!

■ あの素材どこだっけ?これバージョン古いよね?がない

画面を構成する素材を全て1つのライブラリ(素材を集めたSketchファイル)に集約!

「素材管理がとっても楽・・!」

■ 開発に画面情報を伝える資料が一瞬で出来上がる

画面を作ったらSpecExport!それだけで画面指示書の制作終了!

画面要件修正が頻繁に入る場合でも、画面をデザインし直したら資料を書き出すだけなので巻き戻りによる作業の発生コストが段違いに少なくて済みました

まとめ

ここまでSketchを通して時間をもっと効率的かつクリエイティブに使えるようにする方法をご紹介しましたが、使ってみたくなったでしょうか?

Sketchは2018年12月現在、Version53でダークモードが搭載され目にも優しくなったり オーバーライドにサムネも表示されるようになる等、より使いやすく進化を続けています

もし気になったら実際に使ってみて、
クリエイティブにより集中できる環境を作っていきましょう

KLab Creative Advent Calendar 2018 の 25日目は、中根良樹さんです。

よろしくお願いします。

はじめに

この記事は KLab Creative Advent Calendar 2018 の 23日目の記事になります。

こんにちは。KLab株式会社クリエイティブ R&DグループのYojiと申します。

2018年7月
弊社クリエイティブ部に新たにクリエイティブR&Dグループが創設されました。

image1

今回は、そんなクリエイティブR&Dグループについて紹介していきたいと思います。

クリエイティブR&Dグループって何?

R&Dって何?あまり聞き慣れない言葉だと思いますが、
R&Dは『Research & Development』の略称で、技術を調査し開発を行う
研究開発の意味合いになります。
一般的には会社の将来的な技術的競争力を高めるための組織
といったイメージではないでしょうか。

今回、KLabに創られたクリエイティブR&Dは、グラフィックスの分野に重きを置いた
研究開発グループで、高品質のグラフィック表現に向けての研究や、
より効率的なワークフローの構築、最新技術トレンドのリサーチ、
社内共通の制作支援基盤の構築・サポートやツール導入検証、マネジメント手法、
さらにはコンセプトアート創出力向上の取り組みといった、
アートワーク全般にわたる幅広い研究開発をおこなっていくグループです。

そんなR&Dグループを、なぜ創設する事になったのか
少しお話させていただければと思います。
モバイルゲーム業界は供給過多のレッドオーシャンと言われて久しく、
新たな局面を迎える過渡期の時代に入っています。
国内の各ゲーム企業から、毎月数多くの新作コンテンツがリリースされ、
さらには中国や韓国の企業からもグローバル展開される形で、
クオリティの高いコンテンツが日本のゲーム市場に次々と配信されるといった、
各社各国のコンテンツがひしめき合う状況となってきました。

数多くのゲームが溢れるストアの中から、ユーザーの皆さんの目にとまり、
「遊んでみようかな!」と選んで貰うには、面白そうとか、
このキャラクターや世界観が好きとか、選ぶ皆さんの琴線に触れるような、
ゲームの魅力という価値がとても重要な要素になってきます。
魅力が薄いありきたりな物だと、ゲームに触れてもらう事も、皆さんの目にとまり
択してもらう事さえできません。

ユーザーの皆さんに選んでもらえる存在になるためには、
KLabのゲームにしかない魅力という価値を持つゲームを創りたい。
その風景で、そのストーリーで、その音楽で、そのキャラクターで、ワクワクしたり、
ドキドキしたり泣いたり笑ったりと、心を動かすようなゲームを創りたい。
そんな感動を伝える魅力という価値を技術やアートの力で創造していく
エンジンとなるため、クリエイティブR&Dグループを創設するに至りました。

クリエイティブR&Dで追求したい事

KLabでは、漫画やアニメを原作にしたゲームからオリジナルのゲームまで、
沢山の魅力的なゲームを配信しています。
(まだ遊んでない方は、こちらアクセスして是非遊んでみて下さい!)
これらのゲームには、新しいゲームシステムがあったり、楽しい音楽だったり、
原作の世界を楽しめたり、オリジナルの世界観であったりと、
それぞれに魅力的な要素があり、ユーザーの皆さんにも
楽しんでもらえているかと思います。

ゲームは、システムやアクションといった「遊び」の要素、
原作物やオリジナル物といった「絵」や「音楽」の要素、
それぞれの要素が色々な形で組み合わされ、
それぞれのゲームの個性や魅力となって作られています。

原作の世界を楽しめるゲームもあれば、弊社KLabオリジナルタイトルで、
開発中の『禍つヴァールハイト』のように、
KLab独自のオリジナリティある世界観を表現したゲームを届けることも、
私達から提案できる一つの形ではないかと思います。

ゲームの魅力を形作る数ある要素の中から、
今回はクリエイティブR&Dだからこそのアプローチとして、
オリジナリティある世界観の価値の追求という部分に焦点をあて、
KLab独自の世界観を届けるという魅力を、より一層強化していきたいとの思いから、
コンセプトアートを創りだす事に力を注ぐ取り組みを始めました。

コンセプトアートへの取り組み

世界観を創りだす根幹となるものはコンセプトの設計ですが、
コンセプトアートはそのコンセプトを表現する手段として、
一番最初に皆さんの目に触れ世界観のイメージを決定づける核となります。
1枚1枚のコンセプトアートという断片が、パズルのようにはまっていき、
大きな一つのゲームの世界観という姿を現していきます。

魅力的な世界観を創るには、コンセプトアートの力がもっとも必要であり、
それぞれの作品の差が個性となりオリジナルの価値が創られる重要な部分でもあります。
これまで弊社内にはコンセプトアートチームやコンセプトアーティストという
明確な専門職はありませんでした。

職種としてはありませんでしたが、弊社には絵の物凄く上手いイラストレーターや
アーティストが多数在籍していますので、より魅力的なコンセプトアートを
生み出せるようにと、希望者を募りコンセプトアーティストの土壌作りの
取り組みを進める事になりました。

実際のプロジェクトでは、予算もあったりスケジュールもあったり、
社内調整もあったりと、様々な制約に左右されるため、
土壌を育てるツールとしてはなかなか機能しづらい側面がありました。
そういった制約に左右されづらく、コンセプトアーティストを育てる
土壌により適した場として、デモ制作のプロジェクトを立ち上げる事になりました。

デモ制作では、コンセプトの案出しから、実際にコンセプトアートを起こすまでを、
メンバーがそれぞれの裁量で自由に関われ、互いにアイディアを出し合ったり、
レビューをしながら、世界観を創りすすめていく場となっていて、
イラストやアニメの背景を描いてるけどコンセプトアートを描いた事が無かった人から、
ゲームのコンセプトアートを描いてきた人、3Dがメインの人まで、
キャラクター、背景の垣根なく自由に取り組み相互成長の良い機会となっています。

コンセプトアートに限らずグラフィック表現技術含めた、
魅力的な新しい世界や価値を創造するという壁は、
とてつもなく高く険しいという事を日々実感しながら試行錯誤を繰り返してます。
一歩一歩積み上げ挑戦を続けていくことが結果に繋がっていくという事を信じ、
クリエイティブR&Dの取り組みを進めていきたいと思います。

近いうちに、何かしらの形で皆さまにお届けしたいと計画していますので、
発表があった際は、あっ!この事を言っていたんだと
思い出していただければ幸いです。

KLabが創る、KLabにしか創れない、魅力的な世界をユーザーの皆様に
届けられる日が来る事を願ってスタッフ一同頑張っていきます。

クリエイティブR&Dコアメンバーの募集します!

少し宣伝。そんなクリエイティブR&Dグループでは現在コアメンバーを募集中です。
これまで何を経験し積み上げてきたかも確かに大事ですが、
これから何をやりたいか・実現したいかのビジョンを持ち、推進していく力を
一番大事にしています。

魅力あるゲーム体験を創りだす事を目標に、一緒に面白い仕事をしていきましょう。

特に以下の職種を積極募集中です!

R&Dグループ/3Dキャラクターアーティスト
R&Dグループ/3D背景アーティスト
R&Dグループ/テクニカルアーティスト
コンセプトアーティスト

その他の職種も絶賛募集中なので、以下のページから是非応募ください。
KLab中途採用ページ

今回はコンセプトアートへの取り組みの話しがメインとなりましたが、
次の機会ではグラフィック表現についての話しも掲載させていただければと思います。

明日のKLab Creative Advent Calendar 2018 の 23日目は、TAIGAさんです。

よろしくお願いします。

はじめに

この記事はKLab Creative Advent Calendar 2018 の 22日目の記事になります。

こんにちは、KLabで3D演出制作全般の業務を担当している、やまパンです。

3D演出といっても世の中にはたくさんの種類の演出が存在しますが、主に数秒程度のカットシーンのエフェクト制作や3Dアクションゲームのエフェクト、またゲーム中で使用する映像制作などに携わっています。

今回は数ある演出の中から、3Dアクションゲームで使用できそうなエフェクトの作例をご紹介致します。

セルルック調の3D雷エフェクトの作り方 ~素材編~

3Dアクションゲームのエフェクトといっても様々な種類の演出が存在します。

炎、水、風などの自然現象を基盤としたエフェクトや、バリア、エネルギー弾、オーラといった普段では目にすることがないような特殊な力を視覚化したものまで…ゲームの種類によって求められる演出は多種多様です。

今回はその中から、「雷」をテーマとしたエフェクトの作例を記載したいと思います。

※ちなみにUnityを使用したスマホアプリゲームをターゲットとした作例になっています。

雷のテクスチャ制作について

雷系のエフェクトはテクスチャのシルエットひとつで、クオリティや求められるゲームのテイストに合う合わないが大きく左右されてしまいます。

特にセルルックのようなアニメ調のテイストに関しては、しっかりとシルエットのメリハリを見せることが見た目の部分で重要なポイントとなります。

1.手書きで雷テクスチャのシルエット(ラフ)を描く

Photoshop、アナログ、手法は何でも良いです。

まずは手でざっくりとだいたいのシルエットを描きます。

自分の中に眠る中二心を全開にして描きましょう。

少しの恥ずかしさも抱いてはダメです。

自分がカッコイイ!!!!と思う雷のシルエットをざくざく描いていきましょう。

この段階で、ループ用のテクスチャになるように画像に調整を入れます。

・Photoshop>フィルター>その他>スクロール。

・水平方向(H)を動かし、絵の切れ目を確認。

・切れ目が自然な形になるようシルエットの形状を整える。

2.手書きしたラフを元に、AfterEffectでパスに起こしていく

上記で作成したラフをAfter Effectsに読み込み、その絵を下敷きにしてシルエットに沿ったパスを切ります。

・512×256のコンポジションを作成。

・平面を作成。

・1で描いた画像を読み込み、パスでトレースする。

この時、細い線・太い線の強弱のめりはりをしっかり付けるよう意識しながらパスを切っていきます。

そうすることで、より見た目が引き締まった雷のシルエットを作成することが可能です。

3.パスのレイヤーに各エフェクトを追加し雷の質感に近づける

AfterEffect搭載の各エフェクトを追加することで、より見た目を雷の質感に近づけていきます。

◆ブラー(方向)

・雷が流れている方向へブラーを流すことにより、テクスチャの質感にリアリティと、アニメーションを付けた際にスピード感が増す。

・画像は横方向へ雷が流れているため、横方向にブラーを流す。

◆塗り

・雷の種類によって色を変更。今回は黄色にする。

◆グロー

・グローの強さは光加減やマップ(背景)の色見に合わせて調整。

最後にテクスチャループの最終確認をして、テクスチャ作業は終了です。

雷のモデル制作について

上から下へ放たれる雷や、地面に停滞する雷などのエフェクトは基本的にモデルとパーティクルの組み合わせで作成されます。(例外もあるかもしれません…!)

今回の作例では上から下へ放たれる雷のパターンを考慮したモデルの作例を記載します。

※上記で作成したテクスチャを元に、雷のモデルを作成していきます。

メッシュの形状で雷の形を表現する

使用するメッシュは板ポリだけ。そう、たったの板ポリだけです…!

雷の元になる板ポリを作成

Mayaにて雷モデルの元になる板ポリを作成します。

・幅の分割数:12

・高さの分割数:2

板ポリのスケールを伸ばし、雷のテクスチャを適用

板ポリのスケールはX値を伸ばします。

※この時、Mayaのライティング設定を「フラットライトを使用」に設定しておくとシャドウの情報が消えるため、見た目がUnity上の見た目と同じになります。

UVを調整する

雷のディティールの密度を上げたい場合はUVのX値を伸ばし、密度を下げ絵を伸ばして見せたい場合はUVのX値を縮めます。

※テクスチャ作成の際に、ループ処理を入れているため絵の繋ぎが自然になります。

板ポリを複製してクロス型にする

板ポリをクロスさせることにより、どの角度から見ても立体的なシルエットが確認できるようになります。

・クロスの後、2枚の板ポリを結合して1メッシュにする

・結合後、頂点の重なりをマージする→メッシュの編集>マージ

板ポリをエッジごとに凸凹させる

テクスチャのシルエットに加え、板ポリの形をポリポリと変えることでよりリアリティのある雷の形状に近づけます。

・エッジを上下左右に折る、サイズを拡大縮小するなど

・ゲームで使用するカメラの位置から見て、モデルのエッジの切れ目などが目立たないように形を調整していく

モデルの形によっては様々な形状の雷を作成することが可能です。

上記画像右側の雷のように、枝分かれした雷を作成したい場合、各メッシュのUVの位置をそれぞれズラすことで雷の形状のランダム性が増します。

↓下記画像の雷も、枝分かれした2本のシルエットが全く同じ形にならないよう、2本のメッシュのUV位置をずらしています。

だんだんそれっぽくなってきました。

仕上げ

天地の頂点に頂点アルファを入れることで、メッシュの切れ目を自然な形に調整します。

この処理を入れておかなければ、雷の「板ポリ感」がユーザーに伝わってしまうため、必ずこの処理は入れるようにしましょう。

おわりに

いかがでしたでしょうか!

今回は素材編ということでテクスチャ制作からモデル制作までの流れを紹介しましたが、機会があれば今回作成した素材を元に、Unityで実際にアニメーションを付ける工程の記事を書きたいと思います。

エフェクトはゲームや映像を盛り上げるためのお化粧であり、花形のお仕事です。

実際の世界には存在しないようなド派手なエフェクトを作成し、それがうまく表現された瞬間の気持ち良さを感じる時こそ、この仕事やってて良かったな~~~!!?!と実感してしまいます。

このブログを読んでくださった方が、少しでもエフェクト制作に興味を抱いて下さるととても嬉しいです。

最後まで読んでいただき、ありがとうございました!

はじめに

この記事は KLab Creative Advent Calendar 2018 の 21日目の記事になります。

こんにちは。テクニカルアーティストグループ(以下:TAG)のこかろまです。

モバイル端末は日々進化を遂げており、その成長は先日発売されたApple社のiPadProは「Microsoft社のXboxOneSに匹敵するグラフィックス性能」とまで言われるほどです。

モバイルゲームもそれに比例するように高画質・リッチ化が進んでおり、お客様に体験いただく質の向上に取り組むことがKLabのクリエイティブ部の命題ともなっています。

しかしながら、モバイル端末は世界中で生産されているので端末性能の差が激しいです。
そういった端末群がメインプラットフォームであるモバイルゲームにとって「品質を追求したうえでより多くのお客様に遊んでいただくこと」はとても重要です。

今回は私が担当した3D案件においての負荷対策の一例についてお話ししたいと思います。

負荷対策を行うまでの経緯

今回担当した案件は、15000ポリゴンほどのアニメ調のキャラクターが10人前後画面に入り乱れ、ポストエフェクトによる被写界深度やブルームなどの要素を盛り込んだ、演出重視のアクションゲーム。

すでに開発は中盤に差し掛かっており、ある程度アセットの作成指針や仕様、クオリティラインが出来上がったころの参加でした。

アクションゲームの要であるキャラクターモデルも例外ではなく、すでに30体ほどが完成した状態で開発環境に実装されていました。

前段でお話しした通り、より多くのお客様に遊んでいただくためにも「世界でより多く流通していて、性能的に十分な端末を最低動作環境とするターゲット端末」を策定する必要があったため、今回の案件では検討の結果GalaxyS7において30FPSを達成することを目標としました。

見た目の部分が一定のクオリティラインに到達してきたため、「とりあえず現状の処理負荷を計測してみよう!」という動きになりました。

意気揚々と計測したところ、ゲームパート1週目(約2分前後)は30FPSをキープできましたが、長時間プレイすることによって端末が熱を持ちはじめ、2週目から30FPSを割るようになってしまいました。

モバイルゲームにおいて、周回にストレスがたまることはゲームの離脱要因にもつながります。これはマズイと言うことで、対策を行うためのチームを結成し対策を練ることにしました。

開発の方がプロファイラーで負荷計測したところ、開発中のゲームはレンダリング・スキニング・タイムライン処理が高負荷のTOP3を占めていることが分かりました。

レンダリング処理は、シェーダー、演出、ポストエフェクトなど、複数の要因が絡み合って構築されている都合上、手を付けづらいのもあり、モデルデータがそのまま影響を与えるスキニング処理に一旦目を向けることにしました。

スキニングのコストが高い要因として、スキニングインフルエンス数の最大値
(一つの頂点に何本の骨が影響するか)
と、ジョイント(骨)数が多いことがあげられます。
改めてデータを検証したところ、以下のことがわかってきました。

  • モデルデータはすべて2インフルエンスで作成されていた
    • モバイルゲームにおいて、2インフルエンスは頂点数にもよるが現実的
    • インフルエンス数が処理負荷に影響を与えていたわけではないと判断
  • 髪の毛、衣装などのいわゆる『揺れもの』制御用ジョイント過多
  • 表情制御用のジョイント過多
    • 本数の制限を決めきれていなかった
    • クオリティラインを突破するために、動かしたい箇所を増やして行った結果、いつのまにか想定していた骨数よりも多くなってしまっていた

つまり、今回のモデルデータで負荷対策を行うとするならば、ジョイント数を削減することがスキニングコストを下げることにつながると判断しました。

その中で、揺れもののジョイント本数削減はダイレクトに品質に影響してしまうので、品質を落とさずに負荷を下げることができる箇所である表情の仕組みから着手することにしました。

やったこと

前置きが長くなりましたが、今回負荷対策として行ったことは表情をジョイントベースの
仕組みから、ブレンドシェイプベースの仕組みに構造を変更したことになります。

ここで、ジョイントベース、ブレンドシェイプベースそれぞれのメリット・デメリットについて振り返ろうと思います。

ジョイントベース … ジョイント(骨)を頂点に紐付け、動かすことで表情制御

【メリット】

  • 表情制御するデータ容量はジョイントを制御するアニメーションデータのみなので、ジョイントが共通であればモデルデータが増えても一定使いまわすことが可能
【デメリット】
  • 骨を動かして表情を制御するので、現状の骨でクオリティが足らない場合骨を追加する必要がある
  • 骨数・端末スペックにもよるが現状のモバイル端末では比較的重い処理
ブレンドシェイプベース … 表情のメッシュを用意しブレンドすることで表情を制御

【メリット】

  • 頂点を増減させなければ自由に形状を調整することができるため、クオリティをアップさせやすい
  • 基本、頂点変形を行うのみなので、ジョイントベースに比べると比較的軽い

【デメリット】

  • あらかじめMaya等のDCCツールでブレンドしたい表情をセットアップしておく必要があり、ブレンドシェイプしたいメッシュごとに表情分の容量が増える
  • 作成したブレンドシェイプ以上の形状にすることはできない
  • 表情アニメーションデータの共通化が基本不可能
    (※頂点IDや頂点のトポロジーを揃えることで共通化は可能だが今回データでは不可能だった)

どちらの手法も一長一短あります。

モデラーの方が本番モデルをブレンドシェイプ化出来るように手動で対応してくださったので、こちらを使ってジョイントベースのデータと処理負荷を比較してみたところ、すべてのテスト端末において
ブレンドシェイプの方がジョイントベースのデータより高速であることが判明しました。
処理負荷対策の構造として是非とも採択したいところです。 
しかしながら、一般的なブレンドシェイプのように頭部モデル全体に対して表情分のブレンドシェイプを設定してしまう場合、同一キャラクターの別メッシュ(例えば、生え際を変更したい等、頂点の増減に影響が出る変更の場合)の際にもブレンドシェイプを用意する必要があり、かつ表情が一つでも増えた場合、すべての頭部モデルに対して追加表情分のブレンドシェイプをマージしなければいけません。

ジョイントベースであれば、ジョイントのアニメーションデータを作成するだけで
表情を追加することができ、容量には大きく影響がありません。

また、アセットとしても頭部モデルデータに手を加える必要もありません。

あらかじめ表情ごとに.meshを作成、ランタイム時に表情ごとの.meshを取得してベースメッシュに対してブレンドシェイプを追加する方法もありますが、ランタイム負荷はロード時間の増大につながるので避ける必要がありました。

とはいえモバイルゲームにおいて過去配信したアセットに表情を追加する等、手を加えることはテストケースの増大、なによりお客様へのデータダウンロードの負担にもつながるので、こちらもなるべく避けたいところです。

どちらもいいとこどりをした手法ができないだろうか…

改めて、頭部のモデルについて振り返ってみます。

上のGIF動画はいらすとやさんからお借りした、
『いろいろな表情の猫のイラスト「ひらめいた顔・驚いた顔・焦った顔・悩んだ顔」』を
連番にしたものです。

あることに気づきませんか?

上記動画の頭部でアニメーションする範囲は「目の周囲・口の箇所」のみで、
それ以外の部位は動いていないのです。これは、アニメ調である3Dモデルも同様のことが言えそうです。
※リアリスティック系は筋肉の動きを再現するように表情を作ることもあるので3Dモデルが一概にはそうとは言い切れません。

つまり、

  • 目、口など、あらかじめジョイントベースで作った表情から動く頂点を計算
  • 「動く部分のメッシュ」を切り出して、表情分ブレンドシェイプ化
  • それ以外の頂点を「動かないメッシュ」とする
  • ゲーム再生時に目、口などの「動く部分のメッシュ」と「動かないメッシュ」を合体させる

とすることで、ブレンドシェイプメッシュを最小限に抑えることができます!

しかも、動かないメッシュ部分は差し替えることができるので、例えば別の髪型を設定する際は生え際の部分のメッシュの流れを変更したい!となっても問題なく対応できます。

さらには動かない部分のメッシュはスキニングしなくて済むため、スキニングコストをさらに削ることもできます。

完全に勝ちました。

実装について簡単にご説明します。

実装

まず、ジョイントベースで作成された表情データを取得します。私はイテレータを多用する都合上PythonAPI 2.0ではなく、Python API 1.0とmaya.cmds、UIにqtを併用しています。

あらかじめ、デフォルトとなる表情の頂点IDと各頂点座標を取得します。

cmds.pointPositionでも取得することは可能ですが、数千、数万ポリゴンを超えてくると永遠と処理に時間がかかってしまうので、基本的にPython APIで取得します。

表情名 フレーム(60FPS)
通常 000
010
030
050
070
090
110
微笑み+目閉じ 130
微笑み+目閉じ笑い 150

今回表情データは上記のリストのように、それぞれの表情を一つのmaデータの指定フレームごとに作成されていたので、あらかじめ辞書として用意指定表情分のフレームの頂点IDと頂点座標を取得します。

import maya.cmds as cmds
import maya.OpenMaya as om


class ReducedBlendShape(object):

def __init__(self):
   self.eye_expression_mesh_name = 'eye_facial'
   self.mouth_expression_mesh_name = 'mouth_facial'
   self.eye_blend_shape_mesh_name = 'Eye_Around'
   self.mouth_blend_shape_mesh_name = 'Mouth'
   self.eye_blend_shape_attr_name = 'EyeBlendShape'
   self.mouth_blend_shape_attr_name = 'MouthBlendShape'

@staticmethod
def get_selection_mesh_node():
  
   selection_list = om.MSelectionList()
   m_node_fn = om.MFnDagNode()
   m_dag_path = om.MDagPath()
   m_object = om.MObject()

   om.MGlobal.getActiveSelectionList(selection_list)

   try:
       selection_list.getDagPath(0, m_dag_path, m_object)
       iter = om.MItSelectionList(selection_list)

       while not iter.isDone():
           iter.getDagPath(m_dag_path, m_object)
           if m_dag_path.hasFn(om.MFn.kMesh):
               m_node_fn.setObject(m_dag_path)
               return m_dag_path
           iter.next()

   except ValueError:
       pass

# 頂点IDと頂点座標を取得(頂点座標は5桁で丸める)
@staticmethod
def get_vertex_information(dag_path):
   '''
   :return : dict
   '''

   m_dag_path = dag_path
   m_object = om.MObject()

   mesh_fn = om.MFnMesh(m_dag_path)
   vtx_iter = om.MItMeshVertex(m_dag_path, m_object)

   vtx_dict = {}

   while not vtx_iter.isDone():

       m_point = vtx_iter.position(om.MSpace.kWorld)

       vtx_id = vtx_iter.index()
       vtx_position = [round(m_point.x, 5), round(m_point.y, 5), round(m_point.z, 5)]

       vtx_dict[vtx_id] = vtx_position
       vtx_iter.next()

   return vtx_dict

上記で取得した表情を元にあらかじめ取得しておいたデフォルト表情の頂点IDと

各頂点座標を比較し、動く頂点を{頂点ID : 頂点座標の辞書}として追加します。

取得した頂点IDを元にメッシュを分割すると、各パーツごとに分離されたメッシュができます。

@staticmethod
def split_mesh_from_vertex_id(dag_path, vertex_ids):
   '''
   :return : string[]
   '''
   # validate
   if not vertex_ids and not dag_path:
       return

   dag_path_name = dag_path.partialPathName()

   cmds.select(clear=True)

   for vertex_id in vertex_ids:
       vertex_attr = dag_path_name + '.vtx[%d]' % vertex_id
       cmds.select(vertex_attr, add=True)

   face_list = cmds.polyListComponentConversion(fromVertex=True, toFace=True)
   cmds.select(face_list, replace=True)
   cmds.polyChipOff(ch=True, kft=True, dup=False, off=0)

   dag_path.extendToShapeDirectlyBelow(0)
   split_meshes = cmds.polySeparate(dag_path_name)

   return split_meshes

しかし、ウェイト付けの方法にもよりますが、これでは左目部分、右目部分、口部分、
動かない部分など、それぞれのアイランドごとで分離されてしまいます。

できる限りまとめた方がブレンドシェイプターゲットの最小限化による容量削減、
ゲームエンジン上でのマテリアルコール数の節約に繋がります。

今回のデータはすべてのキャラクターの原点が同一だったので、どのキャラクターも分離されたメッシュ全体の頂点の平均座標を取得することで左目付近、右目付近、口付近、それ以外の動かないメッシュとして部位の特定を行うことができました。

部位の特定後、目付近、口付近、動かないメッシュとしてメッシュ結合、名前を整理します。

次に、整理された分離メッシュでのそれぞれの表情のブレンドシェイプメッシュを作成します。

先ほど取得した表情ごとの動く頂点IDと頂点座標辞書を元に新たに1からメッシュを作成してもいいのですが、ここではメッシュ分離した際のスキニングヒストリが残っているBADノウハウと言いますか、Mayaの仕組みを利用しました。

あえてジョイントでスキニングされた状態のヒストリを残してあげることで、頂点やメッシュが分離されているにもかかわらずそのままジョイントベースのアニメーションを流し込むことで再生することができるので、これを利用します。

cmds.file等でジョイントベースのフェイシャルアニメーションを読み込み、該当の表情データのフレームにcurrentTimeで変更した後、ブレンドシェイプ化したいメッシュをduplicateします。

@staticmethod
def get_duplicate_from_time_mesh(dag_path, time, rename):

   # validate
   if not dag_path:
       return

   dag_path_name = dag_path.partialPathName()

   cmds.currentTime(time, edit=True)
   mesh = cmds.duplicate(dag_path_name)
   cmds.rename(mesh, rename)

   return mesh

これを表情分繰り返すことで、分離した状態での頂点IDでのブレンドシェイプターゲットメッシュを簡単に作成することができます!

もちろん、最後には不必要ヒストリを削除する必要があるので、表情作成後にヒストリを削除します。今回はスキニングウェイト等のデフォーマーも不必要なので特に考慮することなく、

cmds.delete(dag_nodes, ch=True)

を呼んであげればまるごとヒストリが削除されます。

表情作成後にブレンドシェイプターゲットを設定します。

ブレンドシェイプアトリビュート名がそのままUnityのanimationclipのpropertyNameとなるので、きちんと明示してあげます。

@staticmethod
def set_blend_shape_attrbute(dag_path, expression_mesh_name, blend_shape_attr_name):

   # validate
   if not dag_path:
      return

   dag_path_name = dag_path.partialPathName()

   expression_meshes_naming = expression_mesh_name + '*'

   cmds.select(clear=True)
   cmds.select(expression_meshes_naming, add=True)
   cmds.select(dag_path_name, add=True)
   target_blend_shape_mesh = cmds.ls(sl=True, transforms=True)
   cmds.blendShape(target_blend_shape_mesh, name=blend_shape_attr_name)

   return

切り出された箇所の頂点の法線はそのままだとばらばらとなってしまっているので、シェーディングによっては継ぎ目がバレてしまったり、法線を使用しているアウトラインシェーダー等に影響が出てしまいます。

そこで、メッシュとメッシュの頂点位置が近似の箇所を取得し、法線を合わせます。


class Normal(object):

   def __init__(self):
       self.tolerance = 0.05

   def tolerance(self):
       return self.tolerance

   @tolerance.setter
   def tolerance(self, value):
       self.tolerance = value
       return

   @staticmethod
   def get_vector(x_point, y_point):

       delta_x = x_point[0] - y_point[0]
       delta_y = x_point[1] - y_point[1]
       delta_z = x_point[2] - y_point[2]

       vector = math.sqrt(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z)
       return vector

   def copy_normal(self, object_a, object_b):

       # validate
       if not object_a and not object_b:
           return

       object_a_vertex = cmds.polyListComponentConversion(object_a, tv=1)
       object_b_vertex = cmds.polyListComponentConversion(object_b, tv=1)
       object_a_vertex_expand = cmds.filterExpand(object_a_vertex, sm=31)
       object_b_vertex_expand = cmds.filterExpand(object_b_vertex, sm=31)

       for object_a_vertex in range(0, len(object_a_vertex_expand)):
           object_a_vertex_position = cmds.pointPosition(object_a_vertex_expand[object_a_vertex], w=1)
           for object_b_vertex in range(0, len(object_b_vertex_expand)):
               object_b_vertex_position = cmds.pointPosition(object_b_vertex_expand[object_b_vertex], w=1)
               compare_vector = self.get_vector(object_a_vertex_position, object_b_vertex_position)
               if compare_vector > self.tolerance:
                   continue

               normal_value = cmds.polyNormalPerVertex(object_b_vertex_expand[object_b_vertex], q=1, xyz=1)
               cmds.polyNormalPerVertex(object_a_vertex_expand[object_a_vertex], x=normal_value[0], y=normal_value[1], z=normal_value[2])

       return


完成です!

動くメッシュと動かないメッシュを自動的に切り分けることができました。

手でやると時間のかかってしまいコストパフォーマンスに優れない要件であっても、スクリプトで実装することで現実的に実装可能になるのがスクリプトのよいところだと思います。

結果

ブレンドシェイプベース変更前と変更後を再度プロファイリングしたところ、驚きの結果が出ました!

スキニングコストは 中央の赤の箇所ですが、3.521549043 -> 2.208194279 !!

なんと6FPSもの余裕ができ、ゲームプレイを重ねても30FPSで安定するようになりました。

本案件はUnity2018.2.0なのですが、近日リリース予定のUnity2018.3.0ではComputeShaderによるブレンドシェイプのGPU Skinning対応なども盛り込まれており、アップデート後の端末次第でさらなる高速化が期待できそうです。

まとめ

実は、今回実装したデータは表情から動く箇所を抽出した最小限の頂点をブレンドシェイプ対象としていましたが、実装を変更しルートジョイント以外のスキニングウェイト値が入っている頂点を切りだし対象メッシュとするように変更しました。

理由としてはアプリ運営時に表情が増える想定をした際に、現状の表情群では動いていない頂点、ただしスキニングは設定済みの頂点を動かしたくなる可能性が出てくる懸念があり、そうなってしまった時にデータ更新でしか対応できないためです。

なぜ今回上記のような表情ごとの差分での手法を紹介したかと言いますと、上の方が実装が大変だったから

余談ですが、弊社内ではこの動く部分のみブレンドシェイプ化をしたデータを
「バットマン型ブレンドシェイプ」と呼ばれている時期がありました。

切り出された形状がバットマンのマスクのようだったからです。

このページを見てくださった方の何かお役に立てましたら幸いです。

明日のKLab Creative Advent Calendar 2018 の 22日目は、しおりさんです。

よろしくお願いします。

© Unity Technologies Japan/UCL

はじめに

この記事は KLab Creative Advent Calendar 2018 の 20日目の記事です。

はじめまして!
だーやまです。

前職ではDTPの制作進行管理をしており、
1年半ほど前にKLabに入社いたしました。
現在、主に3Dの制作進行管理をしています。

今回のお話は……Unityバージョンアップについてです!

所属する案件でUnityのバージョンアップが実施されることになりました。
単純に「はいっ!アップデート完了!」となる訳もなく、
特にエフェクトは、品質管理者(以後品管)が全キャラ確認する必要がありました。

品管の確認作業を出来るだけスムーズにするために
どんなフローを組んだかを今回はお伝えできたらと思いますので、
暫しお付き合いください!

Unityをバージョンアップする理由

Unityのバージョンアップをすると起きる問題

確認作業の効率化

ただ、これを実現するには開発さんに各種スクリプトを作ってもらわないといけないのと、 早急にSHOTGUNの確認ページを構成する必要がありました…

無理を承知で開発さんのところにお伺いすると…


次に、無理を承知でSHOTGUN担当 ノベさんのところにお伺いすると…

開発さん・SHOTGUN担当ノベさんの絶大なご協力のもと、
1~3のスクリプトと、確認用のSHOTGUNページが無事完成しました!
しかも短納期!
SHOTGUNの具体的なページ構成は、カレンダー3日目ノベさんの記事でご確認下さい。

確認作業の進捗

まとめ

まず、
今回キャプチャ&動画化のスクリプトを作成頂いたfo-taさん、
SHOTGUNへの自動アップロードスクリプトを作成頂いた姫野さん、
SHOTGUNのページ構成を作成いただいたノベさん、
この場をお借りしてお礼申し上げますm(_ _)m

今回のUnityバージョンアップのチェックフロー整備を通して、
できるだけ品管の方が

「確認する」という行為を楽に・スムーズにできるようにするにはどうしたらよいか

ということを改めて考える事案になりました。

「確認する」という作業を効率化することによって、
品管・デザイナーに、「調整する・ブラッシュアップする・新規作成する」
といった時間を多く作ってあげたいと思っています。

そのためには、無駄な作業をとことん省き、
生産性の高いフローづくりが必要だと改めて感じました。

今回の記事が、どなたかの作業効率のヒントになれば幸いです。

以上、お読みいただきありがとうございました!

KLab Creative Advent Calendar 2018 の 21日目は、こかろまさんです!