ゲーム・アニメーション創りは面白い!

皆さん、はじめまして。

セガ・インタラクティブ 第一研究開発部 デザイン技術セクション テクニカルアーティストの鈴木です。モーションデザイナーとして、アーケードゲームのガンゲームをメインに10年ほど携わった後、テクニカルアーティストとしてモーション作業周りのサポートをしています。最近では「HOUSE OF THE DEAD ~SCARLET DAWN~」のサポートを行いました。また、採用活動業務も担当するようになりました。

ゲーム歴は長く、電子ゲームやファミコンなどに触れて、現在もゲームを楽しんでいます。

はじめに

さて、今回のブログの前置きですが、

  • 「映像アニメーション」に対応して「ゲームアニメーション」、「ゲームアニメーター」としています。
  • このブログでは、ゲームのアニメーションについて、特にゲームならではの表現方法について書いています。
  • ゲーム制作を勉強している、勉強しようとしている、興味がある方に向けた記事となっています。そのためプログラミングや数式、ツールは登場しません。

 

ゲーム制作を勉強している、またはゲーム制作に興味がある皆さんは、ゲーム制作のどの部分に興味があるでしょうか?ゲームデザイン(ゲーム企画)?キャラクターデザイン?アニメーション?表示(メニュー画面のデザインやゲーム中の表示)?背景?エフェクト?それともプログラミングでしょうか?。

 私は昔からゲームをしている中で、ゲームのアニメーションが担っている「ゲームのさわり心地」に興味があり、それをテーマに試行錯誤しています。
 今回は「ゲームアニメーションと触りごこち」に焦点を絞ってブログを書きます。ゲームアニメーションの面白さ、奥深さが伝われば幸いです。

 

ゲームアニメーションとは

「アニメーション」という言葉について、ここでは、カートゥーンや3DCGアニメーションは「映像アニメーション」、ボタンを押すことでキャラクターが動くアニメーションを「ゲームアニメーション」と分けました。

 考え出すと複雑になってしまうので、ゲームアニメーションとは、ここではシンプルに


  • ボタン操作することでリアルタイムに変化するキャラクターアニメーション。
    (ボタン入力の必要ない演出的な動きと分けて考えます)
 
とします。補足として、これらの定義以外はゲームアニメーションではないと述べているのではありません。(ここが「考え出すと複雑になってしまう」ところです)。また、会社としての見解、取り組みを述べたものではなく、私個人の見解です。この点は誤解のないようお願い致します。

ゲームアニメーター(モーションデザイナー)の仕事として求められる役割は、

  • ゲームのアニメーション素材を制作し、実装されたゲームを遊んで確認し、アニメーションを修正。時にはディレクターやプログラマーとも相談してゲームを面白くしていく

です。
 

ゲームアニメーションを創ることの面白さ

長いことガンゲームを創りづつけて、ゲームアニメーションを創る面白さには「映像アニメーションを創る面白さ」と「ゲームアニメーションを創る面白さ」があります。それぞれ分けて考えました。

映像アニメーションを創る面白さ

  • 動きの重量感、しなやかさ、キャラクターの感情を動きだけで表現できる面白さ

    「演技について」考える面白さです。自分が演技しないとしても、役者としての表現追求の奥深さ

  • 時間や言語を超えて伝わる面白さ

    自分の創ったものが良ければいつまで立っても色あせない。違う文化の人にも言葉の壁を超えて伝えることができる。普遍的な魅力。

ゲームアニメーションを創る面白さ

  • ゲームは、ボタン操作しているだけなのに、プレイヤーが重さを感じたり、爽快感を感じたりする瞬間が興味深いです。(「操作感」「さわり心地」と呼ばれるものでしょうか)

    例えば、ガンゲームであれば、ガン型コントローラーのトリガーを引いているだけなのに、弾が飛んでキャラクターに当たった感じがする。アクションゲームであれば、方向キーを押しているだけなのに、移動に加速感が感じられたり、キャラクターの疲れを感じたりする。ボタンを押しただけなのに、モノを斬っている手応えがある。といったことです。改めて考えると、「ボタンを押しただけでゲームによって感覚が変化している」と考えると不思議ですよね?
     さわり心地の、何年経っても変化することなく、文化や言葉の壁を超えて伝わる普遍的な点も魅力的です。例えば、ボタン操作でキャラクターが「重いブロックを押している」アクションをして、それを見たプレイヤーに重さが感じられたら、その感覚は何年経っても変わらないし、どの国の人が遊んでも、その感覚は得られるはずです。
     ゲームを長い期間、何度でも遊んでしまう要素の1つである「さわり心地」が追求できる点、「『ボタンを押して意図した感触が提供できているのか』といった『操作と結果』に踏み入って考える楽しさ」がこの仕事の一番興味深いところです。

  • 作成したゲームアニメーション次第でゲームが難しくなったり、簡単になったり、ゲームそのものの難易度に影響を与える

    例えば、ガンゲームのゾンビ攻撃で、「腕を振ってプレイヤーを攻撃」があるとします。腕を振るために振りかぶります。この振りかぶりのスピードが速すぎれば、プレイヤーはゾンビの攻撃に気づかず、ダメージを受けます。遅すぎれば、ゾンビは撃たれてしまい、ゲームが簡単になります。特にゾンビ攻撃については、できるだけギリギリの難易度になるよう時間をかけて調整します。自分で作成した動きでゲーム難易度に変化を与えているところが面白い点です。
     (HOUSE OF THE DEAD ~SCARLET DAWN~ のアニメーターは3~4人+協力会社さんでの開発です。少人数での開発のため、個人の裁量に任せられているところが大きいので、難易度調整も許されています。アニメーション調整の裁量についてはプロジェクトによって違います。特に家庭用ゲームについては厳密に仕様が決められているものもありますので、学生さんは希望する会社での個人の裁量について質問したほうが良いと思います。)

  • ゲームの仕組みを知ることが面白い
    仕様を「どうやって」実現するかゲームの仕組み(プログラミング)部分にもつながるパートで、ゲームの仕組みについて広く浅く理解する大変さはありますが、仕組みを知る面白さがあります。

もし、学生さんで「自分は映像アニメーターをめざすのか、ゲームアニメーターをめざすのか」を考えるとき、これらの特徴から方向を検討しても良いかもしれませんね。

 

ゲームアニメーションの難しい点

ゲームアニメーションを考える上で難しい点があります。それは「感覚的な部分のため人に伝えづらい」という点です。「伝えづらい」ということは、人から教えてもらうことが難しく、自分で体験して考えていかなければならないということになります。
 実際に製作中のゲームレビューでも、見えにくく伝えづらい操作感や動きの気持ちよさの話になりにくく、見えやすく伝えやすいアートやゲームシステム、ゲーム性に意見が集約しがちです。もちろんこれらも重要です。しかし、先程ゲームアニメーションの魅力で挙げたとおり、時間や文化を超えて伝わる部分であるにもかかわらず、議論にならないのはもったいないと思っています。

感覚的な部分をどう伝えるか課題ですが、海外で映像アニメーション制作している方の講演を聴いたとき、「アニメーションにはアニメーション12原則があり、それを共通言語としてコミュニケーションを取るとリテイク時に伝わりやすい」と紹介されていました。この講演から、表現のエッセンスを体系化して共通言語とすれば、感覚的なことも伝えられるのでは?というヒントをえました。

ちなみに「アニメーション12原則」とはアニメーションを創るための基本的な要素です。言葉で説明するよりも、映像で見るとイメージしやすいのではないでしょうか。

(音が出ます)
vimeo.com

http://the12principles.tumblr.com/post/84175638939/appeal

the12principles.tumblr.com

いかがでしょうか?基本図形が動いているだけなのに、「動きがやわらかい」「生きている感じがする」印象ではないでしょうか。12原則の要素を元にどこを修正すればよくなるのか議論したほうが修正方針が立てやすいと感じました。

 

モノが干渉した表現(当たった感じの表現)と気持ちよさ

ゲームアニメーションの感覚的な部分といっても、「モノが干渉した表現(当たった感じの表現)」「加速感の表現」「ジャンプ表現」などなど、ゲームごとに突き詰める分野が数多くあります。
 今回のブログでは「モノが干渉した表現(当たった感じの表現)」にはどのようなものがあるか考えました。

ゲームにしても、3DCGツール上にしても、現実とは違い、3D空間上では何も設定していないと、モノとモノが交差しても引っかかりもなく、すり抜けてしまいます。
 あなたがゲームを遊んでいて、当たった感じがするのは何らかの「仕掛け」をしているからです。
 当たった感じがする「仕掛け」とは何か?私がゲーム制作の上で学んだことや、ゲームを遊びながら考えたものを、アニメーション原則のような感じで「リアクション原則」としてまとめました。

リアクション原則

カメラシェイク

カメラを細かく振動させるだけの古典的な方法ですが、当たった感じ、モノの重さを伝える効果があります。

ただし、動かしすぎると3D酔いの原因になりるので、加減が難しいところです。また、カメラエフェクトのモーションブラーが入っていると、画面が汚くなってしまうことがあるので、カメラエフェクトやカメラシェイクの調整が必要です。
 ゲームでは、攻撃時やダメージを受けた、爆発、キャラクターが地面に倒れた、キャラクターが壁を蹴って飛び上がった、など使われていることに気づくと思います。

例1.「弾がカメラに当たる → 爆発して消える」

カメラシェイクが無いときは、弾が跳ね返って当たったのはわかりますが、衝撃の物足りなさがあります。
f:id:sgtech:20190325100959g:plain


弾が当たったときと、爆発したときにカメラシェイクを追加しました。衝撃がより伝わった感じがしませんか?(この揺れでもずっと見ていると疲れてしまいますね。実際にプレイして 調整の繰り返しが必要です)
f:id:sgtech:20190325100918g:plain

 

例2.「キャラクターに武器が当たる」

武器がキャラクターに当たっていますが、すり抜けています。
これが

「ゲームにしても、3DCGツール上にしても、3D空間上では何も設定していないとモノとモノが交差しても現実とは違い、引っかかりもなく、すり抜けてしまいます。」

の状態です。
これでは物足りないので、真っ先にエフェクトを付けたくなります。しかし、動きでも解決できます。エフェクトを付けたい気持ちをグッとこらえて、カメラシェイクの効果を入れます。
f:id:sgtech:20190325101122g:plain

武器の当たった感じが増していませんか?
 キャラクターに攻撃が当たったときの揺れは「ダメージを与えた気持ちよさを伝えるために揺らすのか」「ダメージを受けた時の痛さを伝えるために揺らすのか」プレイヤーにどちらの気持ちを与えたいのか考えて、揺れを調整する必要があります。
f:id:sgtech:20190325101031g:plain

例3.「2段ジャンプの踏み切り」

2段ジャンプをするキャラクターがいるとします。しかし、2段めのジャンプが分かりにくかったり、物足りなさがあります。こちらもエフェクトを付けたい気持ちをグッとこらえて、カメラシェイクを入れます。
f:id:sgtech:20190325100846g:plain

「何かに当たったときだけに使うものではない」例でもあります。空中には何もありませんが、空中で踏み切った「重さ」を与えることもできます。
 壁を蹴って飛び上がるときにカメラシェイクをしているゲームもありました。
f:id:sgtech:20190325100816g:plain


ウエイト

モノが当たった瞬間動きを止めて引っかかりを表現します。効果は強力で、ゲームではよく見かける表現です。当たった瞬間に動きが止まるので、プレイヤーに「当たった!」とわからせる効果があります。
 パズルゲームで「絵柄が揃って消える」ときの一瞬の「止め」もこちらの分類に含まれるのではないでしょうか。

この手法もカメラシェイクに注意してゲームを遊んでいると、いろいろなところで使われていることに気づきます。

例.「キャラクターに武器が当たる」

再び武器がキャラクターに当たっているシーンです。
武器がキャラクターに触れたタイミングで武器の動きを止めます。
f:id:sgtech:20190325101735g:plain

こちらのほうが「当たった感じ」が伝わるのではないでしょうか。
ゲームでは、どのタイミングで武器が当たるかわからないので、ウエイト効果はプログラミングで制御します。
f:id:sgtech:20190325101518g:plain


オブジェクトシェイク

「カメラシェイク」ではカメラを動かしましたが、当たった瞬間にモノに対して細かな振動を入れることがあります。2Dゲームではよく見られる手法です。記号的な表現ですが、少ない手間で実現できるので、ゲームでは使われることが多いです。

例.「弾がドラム缶に当たる」

弾が当たっても動かなければ、プレイヤーに背景の一部と捉えられてしまいます。

f:id:sgtech:20190325102240g:plain

現実では重いドラム缶は跳ねる事はありませんが、ゲーム世界観によっては使える手法です。弾に当たって少しでも反応すれば、背景から独立したものである。とプレイヤーに思わせることができます。
 ここではドラム缶を上下に動かしましたが、左右に動かしたほうが良いでしょうか?それとも弾が当たってずれていくのが良いでしょうか?ドラム缶の反応だけでもクリエイティブ力が試されます。

f:id:sgtech:20190325102202g:plain


ノックバック(キャラクターが押される)

格闘ゲームやアクションゲームで見られる、攻撃やガードしたキャラクターが「少し後ろにずれる動き」です。単純そうですが、実はゲーム性とも絡む、奥が深い部分ではないか、と考えています。


アニメーション

アーティストが、キャラクターが攻撃を受けてのけぞったり、よろけたり、倒れなど、キャラクターアニメーションを作成します。ゲームアニメーター(モーションデザイナー)の担当部分です。
 キャラクターアニメーションの弱点は「決まった動きしかしない」です。これをどう崩すかが課題で、いろいろな動きを混ぜたり、物理シミュレーションを混ぜたり、各社いろいろな取り組みをしています。

 

例.「キャラクターに武器が跳ね返される」(オブジェクトシェイク、ノックバック、キャラクターアニメーションの合わせ技)

武器がキャラクターに当たって跳ね返されるアニメーション素材に、青いキャラクターの細かな振動や後ろに下がる動きの組み込みをプログラマー、どれくらい振動して後ろに下げるのかの調整をゲームデザイナーやアーティストがそれぞれ担当し、各パートの役割の合わせ技で表現します。
f:id:sgtech:20190325102457g:plain


変形(形で表現する)

「リアクション」は動きでモノが当たった感じを表現しましたが、「変形」は形で表現します。モノが当たったタイミングで、急激に形を変化させることで、当たった感じを表現します。
 ゲームでは現実と同じ壊れ方や変形を再現すると、非常に手間と時間がかかるので、記号的な変形が使われることがあります。

ゲーム中では車や壁、小物が一瞬で変形したり、シューティングゲームで敵機体が壊れていくのを確認できます。

例1.「ドラム缶が壊れる」

ゲーム特有の「記号的な表現」です。
現実では弾が当たるたびに少しづつ変形しますが、ここでは一定数弾が当たると変形する仕組みになっています。徐々に変形していくのがリアルですが、ゲームでは一気に形が変形したほうが気持ち良さが増します。
変形の効果だけでは当たった感じは弱いので、他の効果も合わせて表現します。

f:id:sgtech:20190325101405g:plain


例2.「キャラクターを踏み潰す」

青がプレイヤー、赤が敵のイメージです。
敵が踏みつけられたとき、敵がペチャンコになることで、プレイヤーに「敵をやっつけた」とわからせると同時に「気持ちよさ」を提供します。
f:id:sgtech:20190325101224g:plain


リアルタイム物理シミュレーション

ゲームエンジンでは物理シミュレーションが搭載されているので、より現実的な動きを表現できるようになりました。キャラクターアニメーションの弱点「決まった動きしかしない」は克服できますが、意図する動きにするには非常に時間と手間がかかります。

ヒットエフェクト

当たった瞬間にヒットマークを出したり、当たったモノを点滅させたり、エフェクトを表示する、ゲームでは必ず見かける手法です。

爆発や攻撃が当たったとき、カメラのストロボ発光のように画面をフラッシュさせる手法もありますが、光過敏性発作 (Wikipedia)という問題があるので、使用はあまり好ましくありません。アーケードゲームでは過剰な発光をしないよう計測しながら開発しています。


SE(効果音)

「音は物質を定義する」と何かで聞いたことがあります。例えば、白い画面に■ (ただの小さい四角形) が横切る動きに、蚊やハエの羽音のSEをつければ人は■を蚊やハエと認識できます。

ゲーム効果音にもいくつか種類があると考えています。

  • 質感を伝えるSE(想像系)
    現実ではありえない音ですが、質感が伝わる音。例えば20年前の2D格闘ゲームのガード音、キャラクターが敵に触れてやられたときのSE、30年前のシューティングゲームの爆発音など、「バシッ!」や「ドカーン!」ではない音
  • 見た目、イメージ通りのSE(リアル系)
    映画の効果音のようなリアルと感じられる音。
  • 特殊
    攻撃ヒット時に和音を鳴らしたり、音楽にヒットSEを合わせたりしたもの。

 

スナップ

アイテム欄に装備をドラッグ・アンド・ドロップした時、「カチッ」とハマる演出がこれにあたります。office製品やグラフィックソフトでも「スナップ」という機能があり、「ピタッ」とくっついた瞬間に気持ちよさと「合った!」という感覚があると思います。

例.「離れているキャラクターを引き寄せる」

あるゲームでは「キャラクターが離れていても引きつけて投げる」技があります。ゲームとして見事な手応えが感じられます。組み合った瞬間に「ウエイト」演出が含まれています。
f:id:sgtech:20190325104706g:plain

処理落ち、スロー

格闘ゲームの最後の一撃を決めて、ゆっくり吹っ飛ぶ演出がこれにあたります。ヘッドショットが成功するとスローになる演出のゲームもあります。「ここぞ!」という時に使う事で特別な、より強い手応えを表現できます。

例1.フィニッシュブロー

キャラクターの最後の一撃をくらったやられ演出です。もう少し「やっつけた!、やられた!」という余韻を提供したいところです。
f:id:sgtech:20190325102814g:plain

 

攻撃が当たった後、スローを入れました。最後の一撃の感覚を長く残すことで、やっつけた、やられたの感覚を強調することができます。
f:id:sgtech:20190325102642g:plain
 

例2.撃破

モノに当たった感じの表現とはずれますが、気持ちよさにつながる部分だと思います。
シューティングゲームのボス機体を破壊したイメージです。激闘の末にこの終わり方をしたら、プレイヤーはどんな気持ちになるでしょうか。
f:id:sgtech:20190325102554g:plain

最後の爆発でエフェクトの動きをスローにしました。「爆発した」ことを強調することで、倒した達成感を長持ちさせる効果があります。
f:id:sgtech:20190325110007g:plain

ハプティクス(皮膚感覚フィードバック)

身近なところではコントローラーの振動があります。ゲームエンジンでもコントローラーを振動させることができます。モノに当たった瞬間にコントローラーを振動させることで、当たった感じを提供します。
 その他、引っ張られる感覚が得られたり、圧力が感じられる装置の研究が進んでいます。(セガ・インタラクティブでも新しい感覚を伝えられる装置の調査、研究をしている部署があります)

 

 

以上がゲームでよく見られる「当たった感じがする『仕掛け』」だと考えています。

これらを組み合わせてゲームにしていきます。ただし、注意点があります。

  • 「意図したことがプレイヤーに伝わっているか?」「個々の原則は十分調整されているか」を考える
    例えば、迫力を出そうと爆発エフェクトを大きくしたところ、キャラクターの動きが見えない、変形が見えない、などのことがあります。それぞれの要素をバランスよく考える事が大事です。とは言うものの、私も実装してから「しまった」と思ったり、指摘されて気づくことが多々ありますので、これは永遠の課題です。
  • タイミング
    モノに当たって、エフェクトやリアクションを「いつ開始するか」が重要です。タイミングがずれていると原則を組み合わせてもよくなりません。

  

どうすれば? ~トレーニング法~

「ゲームアニメーションの難しい点」でも述べたとおり、感覚的な部分なので、作例を見て「なるほど!そういえば!」と思う方もいれば、「当たり前のことじゃん」「そうなんだ」「そうなのかなぁ」と思う方もいらっしゃるかもしれません。重要なのは、これらの原則を頭の片隅に置きつつ「ゲームを遊んで、実感して、自分なりの仮設を立てて、実践(制作)していく」ことです。

面白いと思うゲームを遊んで、なぜさわり心地がいいか考えるうちに表現力もあがる。一石二鳥ですね(笑)。

 

 最後に

面接で「ゲームが好きです!」という学生さんは多いのですが、もう一歩「ゲームのどんなところに注目して遊んでいるのか」「どこが、なぜよかったのか」「(気になる点があれば)自分だったらこうしたい!」を伝えられると、より良いアピールになるのでは?と考えてブログを書かせていただきました。

ゲームアニメーションを希望する方が増えることを願ってやみません。

 

セガ・インタラクティブは2019年2月より大崎オフィスに引っ越しました。コンビニや社員食堂、カフェ、バーコーナーなど充実した施設があるオフィスです。気持も新たに業務に励んでいます。

 

ご興味ありましたら、下記の弊社グループ採用サイトをご確認ください。

採用情報|株式会社セガ・インタラクティブ - 【SEGA Interactive Co., Ltd.】

 

 ©SEGA

 

Tricks of Realtime VFX with Houdini詳細解説 vol.1

こんにちは、セガゲームス龍が如くスタジオの伊地知です。

 去年もHoudiniのお話をこのSEGA TECH Blogで掲載しており、これで二回目となります。

さる2018年12月2日にCEDEC+KYUSHU2018が開催されたのですが

その時私の方で講演させていただいたTricks of Realtime VFX with Houdiniというセッションがありまして

(セッションのスライドはこちらからダウンロード出来ます。)

そのスライドの後半に

「実際にすぐにゲームに出して使えるエフェクトのテクニック」=「トリック」

を3つご紹介させて頂きました。

その3つのトリックを今回のSEGA TECH Blogで詳細に解説させて頂きます。

 

  1. 加工して戻す(RestSOP) ー processAndRest01.hip
  2. 独自シミュレーション(SolverSOP)
  3. 最短経路探索(FindShortestPath)

3ついっぺんにやると果てしなく長いので全部で3回に分け、

今回は1つ目である「加工して戻す」というトリックだけを扱うことにします。

シーンファイルもご利用頂けますしHoudiniの体験版(Apprentice版)でも開けますので

実際にご自身のPC(Windows)、Mac、Linuxで確認しながらご覧になるとご理解いただきやすいでしょう。

(HoudiniはいろんなOSに対応しています!) 

 

今回は初心者の方でも安心の詳細解説となっております。

またwrangleの行数も最低限に抑えてあります。 

操作方法や基礎知識に関しては1年前の前回のHoudiniの回でも

techblog.sega.jp

御紹介させおりますのでその辺が分からない方はそちらも合わせてご覧下さい。

 


1.加工して戻す(RestSOP)

では1つ目のトリックの内容から説明させて頂きます。

Houdiniの新規シーンを開いた想定でお話をさせて頂きます。

ダウンロードして頂いた方はシーンを上から順にデータを見ていって下さい。

まず画面一番左下のボタン

f:id:sgtech:20190224233230p:plain

を押してGlobal Animation Optionsを開きます。

FPS30End256にしてSaveAsDefaultボタンを押します。

f:id:sgtech:20190224233226p:plain

ネットワークエディタのオブジェクトレベルでTabキーを押しTabメニューでgeと押すと候補が出ますのでGeometryを選び作ります。

f:id:sgtech:20190224233347p:plain

f:id:sgtech:20190224233344p:plain

f:id:sgtech:20190224233340p:plain

できたgeo1にダブルクリックやiキーなどでジオメトリレベルに潜ります。

たいていの作業はこのジオメトリレベルで行う事になります。

 

TabメニューからSphereを選択してPrimitiveTypeをPolygon

Frequencyを50にします。

f:id:sgtech:20190224233155p:plain

 

次にTabメニューからUVTextureを出してつなげます。

TextureTypeをPolarFix Boundary Seamsにチェックします。

f:id:sgtech:20190224233149p:plain

この状態でビュー上でSpace+5を押すとUVの状態が確認出来ます。

f:id:sgtech:20190224233143p:plain

UVが01の範囲内からはみ出していると都合が悪いので

TabメニューからUV Transformを出してUV Textureにつなげ

ScaleXの値に 1/$XMAX と入力します。

これはUV値のUの最大値で全体を割るという意味で

こうする事によって01の範囲内に収めます。

f:id:sgtech:20190224233137p:plain

ビュー上でSpace+1を押してPerspectiveに戻します。

TabメニューからPointWrangleを出してUV Transformの出力につなげます。

f:id:sgtech:20190224233131p:plain

ポイントアトリビュートの@rest,@Cd,@Alphaを設定します。 

VEXpressionの欄に下記のコードを記述します。

v@rest = @P;
f@Alpha = 0.0;
@Cd = {1,1,1};

@はアトリビュートという意味でジオメトリのクラス(Point,Vertex,Primitive,Detail)自体に持たせる変数です。@の前のvやfは型を指定しておりvならベクター、fならフロートを定義します。ここではRunOverがPointになっているのでPointのクラスのアトリビュートを制御するという事になります。

@restはポジション(@P)を保持しておく為のアトリビュート。

頂点α(@Alpha)は0で初期化、頂点カラー(@Cd)は白で初期化という意味になります。

TabメニューからPointWrangleを出して先程のPointWrangleにつなげます。

f:id:sgtech:20190224233244p:plain

f@Alpha = 1.0;

VEXpressionの欄にはこの様に記述します。

再度Alphaを1にするのには訳がありますがそれはあとで解説します。

TabメニューからTwist( or Bend)を出して先程のPointWrangleにつなげます。

f:id:sgtech:20190224233240p:plain

Limit Deformation to Capture Regionのチェックを外し

Twistの値を360Capture OriginのZを-1Capture Length2にします。

こうする事で球がZ軸に沿って360度ねじれます。

TabメニューからMountaionを出して先程のBendにつなげます。

f:id:sgtech:20190224233233p:plain

MountainノードのHeight1.73Element Size2.18Scale0.06,0.16,0.06、

OffsetX値を-14に設定しY値は20に設定し1フレーム目Altキーを押しながらクリック256フレーム目18と設定しAltキーを押しながらクリック

これでパラメータの欄が緑色になったのが分かります。

これはアニメーションが設定されていますという事を現しています。

ちなみにShiftを押しながらクリックでアニメーションエディタが開きます。

Max Octaves9Lacunarity2.19Roughness0.407とします。

この状態で再生ボタン(画面左下にあります)を押すか、カーソルキーの↑を

押してみて下さい。

f:id:sgtech:20190224233321g:plain

球のトゲトゲが蠢きながら下に流れていくアニメーションが確認出来ます。

確認出来たら停止ボタン(画面左下にあります)を押すか、Ctrl+↑を押して止めて下さい。

TabメニューからTransformを出して先程のMountainにつなげます。

f:id:sgtech:20190224233317p:plain

RotateX値を31Y値を5Z値を5とし少し回転させます

TabメニューからClipを出して先程のTransformにつなげます。

f:id:sgtech:20190224234332p:plain

先程と同じ要領でDistance1フレーム目-1.25256フレーム目1.36とキーを打ちます。

この状態で再生させると下半分が削れた状態が段々と上に上がっていって

トゲトゲの球が消えていくのが分かります。

TabメニューからPointWrangleを出して先程のClipにつなげます。

f:id:sgtech:20190224234328p:plain

@P = @rest;

VEXPressionの欄にこの1行を入力すると歪められていたポイントのポジションが元に戻ります。

この挙動を不思議に思う方もいらっしゃるかもしれませんがこれは

単純に@restに保持していた座標で元の位置に戻っただけなのです。

しかし消されたポイントは戻りません。

歪んだ状態で水平に切るという事は座標を戻すと水平では無く歪んだ状態で削れていくのです。

↑キーで再生させるとこの様に動作しているのが確認出来るでしょう。

f:id:sgtech:20190224234240g:plain

映像の表現ならここまでで良いですがゲームの場合

ゲーム中で表現する為に工夫が必要です。

 CEDEC+KYUSHU2018では私はテクスチャパターンアニメーションでやりましょうと言ってしまいましたが実はもっと良いαカットオフを用いた手法があります。

シェーダ側でαしきい値を用いて透明にするのですが

そのαをどう出すのかがこのトリックの鍵になります。

2つ目のトリックで紹介するSolverというノードをもうここで使います。

毎フレーム値を加工して蓄積させていく事が出来るノードで評価した結果を

キャッシュしておくことが出来ます。

TabメニューからSolverを出して上から4つ目のpointwrangle1から

左から1番目のところにつなげて左から二番目の入力にpointwrangle3をつなげます。

 

f:id:sgtech:20190224234238p:plain

ダブルクリックしてsolver1の中に潜ります。

 TabメニューからAttributeTransferを出してPrev_Frameを第一入力に

Input_2を第二入力につなげPointsにチェックを付けAlphaを指定します。

f:id:sgtech:20190224234234p:plain

AttributeTransferは第一入力に対し第二入力のアトリビュートを近いものから転写していくノードです。MayaであればTransferAttribute、Softimageをお使いだった方ならGATORを御存知だと思いますがその万能版だと思って頂ければ理解が早いかと思います。(Softimageユーザーの方ならMayaのTransferAttributeなんかと比べられるのは屈辱だと思いますがHoudiniのAttributeTransferからすればどちらも五十歩百歩です。)

SolverSOPの中でこの様なつなぎ方をすると前のフレームの結果に対し現在のフレームの第二入力のアトリビュートを転写するという意味を持ちます。

TabメニューからPointWrangleを出して第一入力にPrev_Frame、第二入力にAttributeTransferをつなげます

f:id:sgtech:20190224233439p:plain

VEXpressionの欄には 

@Alpha += @opinput1_Alpha/256;

 と記述します。

このコードと接続の意味は前のフレームの結果のアルファに対し

第二入力のアルファを256で割った数値を加算するという意味になります。

つまりこの2つのノードの意味は毎フレーム現在のアルファ値の1/256を

加算し続けるということになります。

 

uキーで1つ上の階層に戻って256フレーム目まで進めます。

すると球がこの様に見えているはずです。

f:id:sgtech:20190224233435p:plain

毎フレームアルファ値を累積して256フレーム分貯めた結果がこれです。

これをUV座標に基づいてテクスチャに書き出してやります。

GameDevelopmentToolsetのSimpleBakerを使うやり方が最も簡単でしょう。

GameDevelopmentToolsetのインストールの最も簡単な方法は左上の

GameDevelopmentToolsetタブのUpdateToolsetボタンを押すことです。

f:id:sgtech:20190224233432p:plain

ゲーム会社や映像スタジオ、CGスクールなどでプロキシ環境下であれば

この機能がうまくいかない場合があります。

その際は公式のgithubからダウンロードして手動でインストールして下さい。

GameDevelopmentToolsetがインストールされた状態になったら

TabメニューからGameDev Simple Bakerを出して下さい。

f:id:sgtech:20190224233430p:plain

ここで普通に考えればアルファにチェックを入れれば出力出来るはずなんですが

何故か真っ白になってしまうので一旦@Cdに@Alphaを移して

basecolorとして出力する事になります。

 simple bakerの1つ前にPointWrangleを足して

@Cd = @Alpha;

 記述して頂点カラーにアルファを移してからsimple baker でテクスチャを焼きます。

すると

f:id:sgtech:20190224233426p:plain

この様なテクスチャが焼き上がりますのでPhotoshop上でレベル補正かけたり

トーンカーブで補正かけたりして1~254くらいの値の範囲にしておくと

シェーダに食わせた時の見た目が良い様です。

 

モデルの方も同時に出力しておきます。

 UVを設定したuvtransform1から出すと良いのですがそのままだと

25,000頂点もありかなりメモリを食ってしまいます。

uvtransform1の下にPolyReduceを作ってつなげます。

 Percent To Keep1Vertex Attribute Seams0.4にして頂点数を252にまで

落とします。ゲーム内で綺麗に見える最低限の頂点数であれば Percent To Keepを

いろいろ試してみても良いでしょう。

f:id:sgtech:20190224233533p:plain

TabメニューからROP FBX Outputを出してつなげます。

 Output Fileを指定してSave to Diskボタンを押します。

これでモデルファイルも出力出来ました。

 ではUnity上で確認してみましょう。

普通のスタンダードシェーダでは両面に対応していないので

Create -> Shader -> Standard Surface Shader

で作ったものにちょい足ししたシェーダで表示してみましょう。

Shader "Custom/doubleSidedCutOff" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _Cutoff ( "Cutoff", Range(0, 1) ) = 0.5
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _BumpMap ( "Normal Map" , 2D ) = "bump" {}
        _BumpScale ( "Normal Scale" , Range(0,1) ) = 1.0
    }
    SubShader{
        Tags { 
            "Queue" =    "AlphaTest" 
            "RenderType"="TransparentCutout"
        }
        LOD 200
        Cull Off
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows alphatest:_Cutoff

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        half _BumpScale;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;

            fixed4 n = tex2D( _BumpMap, IN.uv_MainTex);
            o.Normal = UnpackScaleNormal(n, _BumpScale);
        }
        ENDCG
    }
    FallBack "Transparent/Cutout/VertexLit"
    
}

このシェーダをアサインしたマテリアルを用意して適当なテクスチャを貼ってあげます。

その際テクスチャのアルファに先程生成したテクスチャを入れ込んでおきます。

するとこの様に表示されます。

youtu.be

 

@restに座標を保持して戻す時に削れたpointが戻らないのは当然としても

生成されたpointがちゃんと良い感じの場所に戻ってくれるのは凄いと思います。

中で一体どういう処理が走ってるんでしょうか?気になります。 

累積アルファのカットオフはテクスチャパターンアニメーションでやるより

遥かに質も向上しメモリも削減出来る賢いやり方なので

覚えておいて損は無いでしょう。

 

さて今回はここまでです、いかがでしたでしょうか?
ゲームに出力する部分は様々な手法を考慮し工夫する必要があります。

現状ビルボードが主流のゲームエフェクトですがこんなトリックを用いる事で思いがけない表現をプレイヤーの方々にお見せし感動体験を演出する事も可能になるのです。

ゲームエンジン側の知識もフル動員すればもっと凄いものが表現出来るでしょう。

勉強する事は山程ありますし考えなければいけない事もいっぱいあります。

ですがやりたい表現が達成出来た時の喜びもまたひとしおです。

次回のHoudiniの記事もお楽しみに。 

 

この記事に興味を持って頂けた方は弊社で私達と一緒に働いてみませんか?

弊社ではHoudiniに興味を持って取り組めるような人を募集しています!
我こそはという方、興味のある方は以下のリンクを是非クリックしてみてください。

 
sega-games.co.jp

 

©SEGA

 

Are you readyyy to Deep Learning!?

 セガゲームス第4事業部第4開発1部TA(テクニカルアーティスト)セクション*1 宮下です。2019年が始まって早1カ月がたとうとしてますが、あなたにとって2018年はどんな年でしたか?(少々時期外れな質問なのですが…)

Readyyy!

 私は2019年2月1日にリリースされたスマホタイトル「Readyyy!」に携わっており、とても忙しい1年でした。このタイトルは、プレイヤーが新人プロデューサー兼寮長として、男子高校生アイドル18人を育成するスマホゲームで、ゲームエンジンUnity*2を使って開発しています。

f:id:sgtech:20190120235301p:plain

Readyyy! キービジュアル

 TAとして、3Dによるチビメンや「Live2D*3」の立ち絵をはじめとする「Readyyy!」の技術的なグラフィック表現の根幹を担っています。特に立ち絵については、ライティングという結構面白いことしているんですよ。

 昼間、夕方、逆光などの環境によるライティングを実現するための仕組みの設計と、それに関連するシェーダーやコンポーネント*4を作りました。そのデータを「Photoshop」や「Live2D」から出力するためのツール整備もしています。

f:id:sgtech:20190120235043j:plain

昼間順光の表現

f:id:sgtech:20190120235101j:plain

夕焼け逆光の表現

 ご興味あれば、ぜひ遊んでみてください!

ready.sega.jp

CEDEC+KYUSHU2018

 実はこのセガテックブログのおかげで、昨年12月に「CEDEC+KYUSHU2018」での講演という、貴重な体験をさせていただきました。おかげというのは、「CEDEC+KYUSHU2018」の関係者の方が、2017年に私の書いた記事、

techblog.sega.jp

をご覧になって、講演を依頼してくださったのです。

 今回は、そのとき講演した5つの自動化効率化のお話*5の中から、ディープラーニングをテーマにしたものにプラスアルファしてお届けします。

 「CEDEC+KYUSHU2018」での講演をお聞きになった方も復習を兼ねてご覧いただければと思います。説明も、より丁寧になっていますので!

ディープラーニングで自動的にサムネイルを作れないか!?

 前置きが大変長くなりました。それでは始めましょう!

 なお、ディープラーニングを勉強するのにゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」という本を使っています!難しいですがとてもいい本です。

アニメ顔検出

 2016年ごろ、とあるデザイナーさんからこのような相談を受けました。

「サムネイルを自動的に作れませんか?」

f:id:sgtech:20190120234116j:plain
f:id:sgtech:20190120234114j:plain
f:id:sgtech:20190120234112j:plain
f:id:sgtech:20190120234110j:plain
f:id:sgtech:20190120234107j:plain

 サムネイルとは縮小した画像のことですが、このように顔など特徴的な箇所をクローズアップしてトリミングするケースもあります。ちなみに彼らは、「Readyyy!」の「SP!CA」というユニットのアイドルたちです。

 そのデザイナーさんが所属しているプロジェクトのプラットフォームはスマホでした。スマホタイトルは運営を長く続けていくため、新規要素をどんどん追加していきます。その要素を一覧で表示させたりするために、サムネイルが必要となるのです。

 また、ご存知とは思いますがゲーム開発ではアニメ顔が使われることが多く、そのプロジェクトでもアニメ顔が採用されていました。

 これを自動的に作成…何か良い方法はないものか…。検索すると…出てきました!「OpenCV」を使ったものです。

ultraist.hatenablog.com

 多くの人がこれを使ってアニメ顔検出しているようですね。勉強も兼ねて「Photoshop」のプラグインという形で実装してみました。一から実装すると大変なので、サンプルのプラグインに間借りする感じで作りました。
 余談ですが、「Photoshop」プラグインのAPIって独特というか、なんとも難しいですよね…。下のgif動画は、プラグインを動作させたときのものです。

f:id:sgtech:20190120234252g:plain

Photoshopプラグイン

 どうですか!?素晴らしい性能ですね!ただ、おしいのが誤検出(下の半透明で赤く塗り潰した部分)です。

f:id:sgtech:20190120234339p:plain

 このOpenCV(lbpcascade_animeface)を使った手法は検出精度の調整ができるのですが、精度を上げすぎると、顔ではない部分も検出してしまい、精度を下げると、顔を検出しづらくなってしまうという現象が発生します。そのちょうどよいバランスを見つければいいのかもしれませんが、その設定で絶対大丈夫と言い切る自信がありません。残念ながら、これでは自動生成するツールとして、信頼性が足りませんよね…。

顔なのか?顔ではないのか?

 しばらくして、1つのアイデアが閃きました。OpenCV(lbpcascade_animeface)が検出した画像の、「顔」「顔ではない」をディープラーニングで学習させて、判断させるようにすればいいのではないか…と。ここではディープラーニングの代表的な手法、畳み込みニューラルネットワーク(Convolutional Neural Network、以下CNNと呼びます)を用います。本来なら切り出した顔などの特徴量から誰なのかを判定できる素晴らしい手法なのですが、今回は贅沢(?)に、「顔」「顔ではない」の2種類の判定のためだけに使います。

 後で知ったのですが、この物体検出後にCNNで分類する手法は「R-CNN」という立派な名前がついていました。

Keras

 ディープラーニングのフレームワークには、簡単で分かりやすいという評判のKeras*6を使いました。KerasはPythonによるフレームワークで、TensorFlowやTheanoなどのディープラーニングライブラリをバックエンドとして選択できます。今回バックエンドには、CPU版TensorFlowを使いました。

 KerasやCNNについては、こちらの記事を参考にしています。

qiita.com

教師データ

 ディープラーニングを始めるにあたって、まず教師データというものを大量に用意します。ここでは「顔」と「顔ではない」画像ファイル群ですね。実は、この教師データを用意するところに1つのハードルがあります。個人で用意する場合「大量に」という点が大変だと思うのですが、セガでは複数のタイトルを開発していますので、他のプロジェクトから画像を提供してもらいました。これに先ほどのOpenCV(lbpcascade_animeface)を使ったアニメ顔検出をさせて、「顔」と「顔ではない」画像群を、比較的簡単に用意できました。

f:id:sgtech:20190120234423j:plain

顔 700枚

f:id:sgtech:20190120234420j:plain

顔ではない 1000枚

 これで準備が整いました。100回学習させてみると、7時間ほどかかりました。

 学習させた成果のことをモデルと呼びます。モデルは基本的に学習回数が多いほど、性能が良くなります。ただし、特定の教師データにだけ過度に対応した状態、過学習(overfitting)になってしまうことがあります。未知のデータに対応できない状態ですね。

 下の図は今回学習させたモデルの性能を表しています。

f:id:sgtech:20190120234336p:plain

 「acc」は正答率、「loss」は性能の悪さを示す指標で、教師データに対してどれだけ一致していないかを表しています。横軸は学習回数で、多いほど性能が良くなる傾向を示しているのがお分りいただけると思います。

 また、実は教師データの全てを学習に使っているわけではなく、教師データの一部は未知のデータに対応できるかどうかのテストに使っており、そのテスト結果が、val_acc」val_loss」です。

 緑のグラフval_loss」は、学習を繰り返すたびに上昇していく傾向にありますが、これは過学習状態であることを示しています。なので100回ではなく、30回ぐらいで止めておいたほうが良いモデルと言えそうですが、今回はこのまま100回学習したモデルで話を進めます。

 では、このモデルを切り出した画像に適用してみましょう。

f:id:sgtech:20190120234332j:plain

学習の成果は!?

 「Readyyy!」のキービジュアルでの判定の成果はこのようになりました。7番だけ99%顔と判断して失敗してしまいましたが、この手法は有効そうです。

デザイナーが使えるようPhotoshopで動作させてみる

 では、この顔検出+顔判定のシステムを、「Photoshop」から使えるようにしてみましょう。システムはPythonで動作しているので、「Falcon*7」というフレームワークを使って、WebAPIとしてアクセスするようにしてみます。手順としては、

  1. Photoshop上でドキュメントを一時的にファイル(JPEG等)保存する。
  2. Photoshop上でそのファイルを読み込んで、WebAPIを使ってアップロードし、応答を待つ。
  3. 画像を受け取ったWebサーバー側で、アニメ顔を検出する。
  4. 引き続きWebサーバー側で、「顔」か「顔ではない」かを判定し、「顔」の矩形の座標を返す。
  5. Photoshop上でその結果を受け取り、選択範囲として反映する。

となります。

 今回は「Photoshop」上での動作には、プラグインではなくスクリプト(JavaScript)を使います。青い文字が「Photoshop」スクリプトでの処理赤い文字がWebサーバーでの処理となります。

 「Photoshop」スクリプトでバイナリデータをアップロードする方法は、ここに良いコードがありますので、参考にしてみてください。

forums.adobe.com

 「Falcon」を使ったWebAPIも簡単に実装できます。今回はこちらを参考しました。

qiita.com

f:id:sgtech:20190120234223g:plain

WebAPI+Photoshopスクリプトによる実装サンプル

 対象の画像内に複数人いる場合は最初に見つかった人の顔の矩形を返します。上の動画では、左から2番めの茶色の髪の比呂君が選択されてますね。

 これで、「Photoshop」スクリプトを使って、画像から自動的にサムネイルを作りだせるようになりました。

  ここまでが、「CEDEC+KYUSHU2018」でディープラーニングについて講演した内容です。

ツールと連携させる(プラスアルファ)

 ここから先が今回追加する内容で、「Readyyy!」で試みた★1と★2フォトの自動生成をご紹介します。人間並みの高い精度でアニメ顔を矩形選択できるのであれば、いろいろ応用できそうですよね。

 ちなみにフォトというのは、「Readyyy!」内で手に入るアイドルの写真のことです。★5フォトは、このような豪華なフォトです。

f:id:sgtech:20190120235131j:plain

★5フォト

 ★1と★2のフォトは、アイドル画像と汎用的な背景画像を組み合わせたシンプルなフォトです。

f:id:sgtech:20190120235139j:plain

★1フォト

 なお、連携させるツールのフローは以下のようなもので、全て「Photoshop」上での操作です。

  1. アイドル画像を読み込み、あとでデザイナーが位置調整しやすいように、スマートオブジェクト化しておく。
  2. アイドル画像の顔の部分を選択範囲として囲む。
  3. 背景画像を読み込む。
  4. その背景画像にあらかじめ指定してある基準位置と、先ほどのアイドル画像の選択範囲の大きさが一致するように調整して、アイドル画像をコピー&ペーストする。
  5. 完成した画像を書き出す。

 2番(赤い文字の部分)にアニメ顔検出判定システムを組み込んで、自動化を試みます。では実行してみますね。動画の左側が「Photoshop」、右側が出力フォルダの様子です。また20倍速で再生しており、本来はもっとゆっくりとした動作となります。 

f:id:sgtech:20190121133210g:plain

ツールへの組み込み

 よし!どんどん生成されている!結構いい感じです!

f:id:sgtech:20190120235152j:plain

顔の大きさが違う…

 ただ極端な例なのですが、このあたりを比較してみるとアイドルの顔の大きさがそろっていません。このあたりの微調整は、デザイナーさんにがんばってもらいました…。さらなる自動化を目指すには、工夫が必要そうです。

 今回は検出にOpenCV(lbpcascade_animeface)を使いましたが、検出自体をDeepLearningで行う手法(Faster R-CNNもあるので、次はこちらも試したいと思っています。

まとめ

  • アニメ顔検出は、OpenCV(lbpcascade_animeface)が使える
  • 誤検出には、ディープラーニング(CNN)で対応
  • 検出されたアニメ顔のトリミングに課題あり

最後に…

 ここまで読んでくださり、ありがとうございました。課題も見つかり今回は道半ばという結果になってしましたが、いかがだったでしょうか?今後も継続的に改良を積み重ねて、またここでお伝えできればなぁと思っています。

 TAセクションではこのようにデザイナーの制作に役立つ環境を提供できるよう力を注いでおります。Unityの登場でTAの活躍できるフィールドは格段に広がり、シェーダーやポストエフェクトなど面白い表現を生み出せないかと日々格闘中です。

 そんな中でお仕事したい方は、ぜひ下記の弊社グループ採用サイトをご確認ください。いっしょに働いてみませんか?

sega-games.co.jp

 そして、「Readyyy!」、よろしくお願いします!

 

*1:1月に組織改編がありまして部署名が変わりました。

*2:https://unity3d.com/jp

*3:ここではLive2D Cubismのこと。2Dイラストに擬似3D的な滑らかなアニメーションを追加することのできるソフト。Spineという海外の競合ソフトがある。また、最近はVTuberにも良く使われている。https://www.live2d.com/ja/products/cubism3

*4:Unityにおいてオブジェクトの振る舞いを記述するためのスクリプトのこと。

*5:https://cedil.cesa.or.jp/cedil_sessions/view/1972

*6:https://keras.io/ja/

*7:https://falconframework.org/

龍が如くにおけるキャラクター制作ワークフロー

初めまして。
セガゲームス 第1CSスタジオの有賀千陽です。
キャラクターデザインの業務に10年以上携わったのち、現在は新設されたデザインサポートチームでデザイナーの作業を支援するツールを制作しています。

 

「龍が如くスタジオ」で制作されているタイトルにはたくさんの実在の人物が登場します。
今回のSEGA TECH Blogでは「龍が如くスタジオ」キャラクター班流のリアルなキャラクターを作成するフローを皆さまにご紹介させていただきます。

【目次】

 

まずは「龍が如く」シリーズのキャラクターの特徴から説明しましょう。

f:id:sgtech:20190313135651j:plain

  • プラットフォームはPS4などハイエンド機である
  • 開発期間が短い※おおよそ一年
  • 芸能人とタイアップし、ご本人がゲーム中でキャラクターを演じている
  • 沢山のユニークなキャラクターが登場する

などが挙げられます。

 

そのリアルなキャラクターたちを短い期間にどれくらいのクオリティで、どれくらいの顔の数を作らないといけなかったのか?

クオリティはもちろん写真のようなクオリティです。

f:id:sgtech:20190313135644j:plain

タイアップキャストになると誰が見てもそっくりに作らなくてはなりません。

 

次に数の話ですが…

f:id:sgtech:20181224005946j:plain

1プロジェクトで使用したキャラ(男性のみ)

『龍が如く6 命の詩。』の男性NPC*1の顔だけで上記の画像ぐらいです。

 

これに加えて女性NPC・重要キャラクター・キャバ嬢のタイアップキャラなどを含めると、

  • 男性の顔:約200種 
  • 女性の顔:約70種 

あいかわらずたくさん作ってますね…。まとめると、

短時間でリアルなキャラを沢山作らないといけない!

リアルなキャラを大量に作成するに当たり活躍してくれたのが、これから説明するフォトグラメトリーを活用した制作フローでした。

f:id:sgtech:20181224005310p:plain

 

まずは、フォトグラメトリーの説明と、フォトグラメトリーによる3Dの自動生成、Maya等のDCCツールに持っていくまでのフローの説明をします。


フォトグラメトリーとは?
------------------------------------------------------------------------------------
3次元の物体を複数の観測点から撮影して得た2次元画像から、
視差情報を解析して寸法・形状を求める写真測量のこと。

小難しくてよくわかりませんね…

要は撮った写真から自動で3Dデータを生成してくれる技法のことです。
この文言だけで、かなり簡単に作れるように感じられるのではないでしょうか?

 

フォトグラメトリーを本格的に導入したのは『龍が如く6 命の詩。』の開発中でした。

ではそれ以前はどうしていたのか?


「龍が如くスタジオ」で行っていた3Dスキャンの歴史を簡単に説明します。

「龍が如く」シリーズにおける3Dスキャンの歴史

  1. リアリティの追及
  2. 制作スピードの向上

この2つの要求を満たすために、「龍が如く」シリーズでは2007年PS3対応が始まった『龍が如く 見参!』の制作から3Dスキャンによるワークフローが導入されました。

PS2世代までは写真を見てモデリングを行っていました。いわゆる目コピーです。つまり制作クオリティはデザイナーのスキル頼りだったのです。

PS3世代になってゲーム機のスペック向上に伴い、リアリティのあるゲームモデルの必要性が高まり、さらに『龍が如く 見参!』から実在の俳優を登場させることにもなり、リアリティとクオリティの追及がさらに重要視されるようなりました。

光学式スキャナー

2007年の『龍が如く 見参!』以降『龍が如く3』『龍が如く4 伝説を継ぐもの』『龍が如く OF THE END』まで社外のスタジオで役者さんを撮影し、メッシュデータもそのスタジオで生成されたものを使用していました。
3Dスキャンを行うようになりキャラクターのモデリング時間は40%削減でき、クオリティも上がりました。

f:id:sgtech:20181224005306p:plain

外部スタジオを利用していたころのスキャンメッシュ

そのスタジオでは当時としては最新式だった光学式(ヘリウムネオンレーザー)の3Dスキャナーを使用していましたが、スキャンメッシュの精度はごらんの通り、なんとなく誰だか認識できる程度で細かいディテールは撮れていません。

 投影式の3Dスキャナー

以下の画像は『龍が如く OF THE END』の途中から導入が始まった社内撮影スタジオでスキャンされた3Dモデルです。

f:id:sgtech:20181224005302p:plain

投影式3Dスキャナを利用してキャプチャされた3Dモデル

 

この頃から社内に撮影専用スタジオを設け、テクスチャはカメラ1台(手持ち)での撮影からスタートし、後にカメラ5台(+三脚・ストロボ)でシャッターを同期させて撮影する方法に移行。より精度の高いテクスチャを撮影できるようになりました。

3DモデルのキャプチャにはWhite-Light (走査型白色)方式*2の3Dスキャナーを導入しました。プロジェクターを用い撮影対象に画像を投影して専用ソフトウェアで解析して3Dメッシュを生成するという方法です。

社内に専用スタジオを設け新しい3Dキャプチャのシステムを導入したことにより、メッシュの精度が以前より向上し時間面でも費用面でも社外スタジオの利用時に比べて大幅なコストカットを達成しました。

 ただ、暗室で5秒程度息を止めて動かないでいる必要があるため、まばたきや体が動いてしまうだけでメッシュの精度が低下するなど正確な3Dモデルのキャプチャは非常に難しく、撮影されている人の負担も大きかったのです。

さらに、社外スタジオのスキャンデータも社内スタジオのスキャンデータも、どちらもテクスチャの生成ができなかった為、3Dメッシュを生成した後撮影した画像をメッシュにベイクしてテクスチャを自分たちで作成する必要がありました。

f:id:sgtech:20181224005259p:plain


その頃のテクスチャ作成方法は、DCCツール内部に画角が同一となるカメラを作成し、位置を合わせてカメラプロジェクションUVを作成しテクスチャベイクしていたのですが、この作業だけで数時間かかっていました。

f:id:sgtech:20181224005255p:plain

また、社外スタジオのスキャンデータも社内スタジオのスキャンデータもそのままではノーマルマップベイクのソースにできるほど精細にキャプチャできるわけではなく、Zbrushでのスカルプトによるディテールの追加作業は必須でした。

 

PhotoScanの導入

そんななか、フォトグラメトリーの技術を使い3Dメッシュを生成するPhotoScanというソフトウェアがプロジェクトに導入されました。

 3Dデータ生成用の写真撮影の様子

f:id:sgtech:20181224005401p:plain

上の写真は2015年から2018年現在まで使用している社内フォトスタジオです。

事業所移転前の羽田にあった旧スタジオの様子なので手作り感あふれる仕上がりですが、ここで沢山の芸能人の方々をスキャンしてきた見た目にそぐわずできるヤツなんです・・・が、なんと!現在大崎の新オフィス内に「龍が如くスタジオ」キャラクター班こだわりの新スタジオを構築中なのです!
今年度中に完成予定ですのでお楽しみに。

話は戻って、カメラのスペックは以下のようになっています。

  • 台数 : 30台
  • 機種 : CanonX7i
  • レンズ : 標準ズームレンズ
  • 焦点距離 : 55mm

基本設計や機材の調達は社外の専門家に委託し、その後の運用、撮影のオペレーション、メンテナンスなどはキャラクター班が行っています。

 画像の現像

X-Rite社のColorCheckerPassportと一緒に撮影した画像からカメラプロファイルを作成し全ての現像に使用します。

f:id:sgtech:20181224005357p:plain

 

秘訣は 「龍が如くスタジオ」独自の「ドラゴンエンジン」

「龍が如く」シリーズは常に部内開発のゲームエンジン上で制作されてきました。そうすることでレスポンスが良く柔軟なゲーム開発が可能になり、デザイナーの時には無茶な要求にも短時間で実装することができるのです。
『龍が如く6 命の詩。』の開発時にアップデートされた内製ゲームエンジン「ドラゴンエンジン」は、それまでのゲームエンジンよりシームレスでリアルな表現が可能になりました。

部内でゲームエンジンを開発できるような高い技術力を持ったプログラマがたくさんいるのも、「龍が如くスタジオ」がクオリティの高いゲームを素早く作れる要素の一つとなっています。

キャラクター制作ワークフロー

前置きがだいぶ長くなってしまいましたが、本題のキャラクターワークフローの解説に移ります。
今回は「龍が如く」シリーズ関連のプロモーションでおなじみ島野さんの顔制作に沿ってお伝えします。

3Dメッシュ生成

3Dメッシュの生成にはAGISoft社のPhotoScanというソフトウェアを使用しています。

f:id:sgtech:20181224005951j:plain

PhotoScanの操作画面

このソフトウェアの導入により3Dメッシュの生成と同時にテクスチャも生成できるようになったことから、テクスチャをMaya上でベイクしていた数時間の作業が削減されテクスチャ作成時間が大幅に短縮できました。


この一連のフローにより、デザイナーのスキルに依存していたクオリティの向上と制作時間が短縮し安定して高水準なクオリティがアウトプットできるようになったのです。

メッシュ生成 フロー

では実際に3Dメッシュを生成する過程を紹介しましょう。

  1. 始めに現像した撮影画像をツールに読み込みカメラ位置を計測
  2. ポイントクラウド(点群データ)を生成
  3. 3Dメッシュを生成
  4. 最後にテクスチャベイクで終了

PhotoScnanは各画像のカメラの位置や画角を画像解析により取得し、ソフトウェア上で撮影環境を再現しますが、ソースにしている写真の点数が少ないと各画像の位置関係が予測できずカメラの位置情報が取得できなくなります。

現在弊社スタジオは30台のカメラで構成されており、横の耳から後ろの首回りまで360度カバーできるようになりました。

f:id:sgtech:20181224005825j:plain

高密度クラウド

上記の画像はポイントクラウド(点群)です。

この状態では点群と認識できないくらい高密度なので寄ってみます。

f:id:sgtech:20181224005822j:plain
更に寄ってみます

f:id:sgtech:20181224005818j:plain

 小さい点が見えるのがわかりますか…?

 

このポイントクラウドを元にメッシュとテクスチャを生成します。

f:id:sgtech:20181224005948j:plain

PhotoScanで生成したポリゴンメッシュ



上の画像は生成したメッシュデータです。1000万ポリゴンで生成しています。

 

f:id:sgtech:20181224005815j:plain

テクスチャ表示とUV

上の画像はテクスチャです。
PhotoScanが生成したポリゴンメッシュとUVに写真がベイクされます。

これらを任意の形式でエクスポートしてR3DS社のWrapXというツールでPhotoScanからエクスポートした3Dメッシュにベースメッシュをシュリンクラップします。
この工程の詳細は後ほど解説しますね。

 

顔モデル作成フロー

PhotoScanやWrapXで得たソースを元に実際にどのようにゲームのデータに落としこむのでしょうか。

まずはスキャンメッシュとゲームメッシュを合わせるという作業になります。

f:id:sgtech:20181224005353p:plain

これはPhotoScanで生成したメッシュです。

f:id:sgtech:20181224005350p:plain

これがWrapXで生成したメッシュです。

f:id:sgtech:20181224005346p:plain

実際に合わせてみるとこうなります。

わかりにくいですが目の周りのエッジフローが少し歪んでいますね。

ゲームメッシュのエッジのラインがきちんとスキャンメッシュの特徴に沿うように修正していきます。

目の周りはまつ毛や二重や粘膜の部分など細かい造形が多くPhotoScanでは再現しきれません。口の周りも唇の内側など写真には写らない部分はメッシュが生成されないので、その二か所はどうしても手作業で修正する必要があります。

f:id:sgtech:20181224005536p:plain

f:id:sgtech:20181224005533p:plain

f:id:sgtech:20181224005529p:plain

これらの作業は非常に重要で、メッシュの法線情報を正しく作ることによりクオリティを上げるという目的もありますが、「龍が如く」シリーズではフェイシャルモーションが複雑に動くためエッジフローが実際の筋肉の流れに沿っていないと後々イベントムービーシーンで思わぬ破たんを生みだす原因となってしまいます。

この作業に関してはWrapXの導入でかなり効率化されました。

テクスチャ作成

次はテクスチャ作成なのですが、その前に簡単に描画周りを説明しておきます。

わかりやすいのでまずはPVをご覧ください。

youtu.be

「龍が如く」ではいわゆる主流ではない、f0値を扱ったPBRを採用しています。

この理由は、「龍が如く」というプロジェクトがゴリゴリのフォトリアル指向ではないことと、ゲームに妖怪や怪物*3など現実にいないキャラクターが登場する可能性を加味し、ある程度振り切った表現ができる今の形に収まりました。

 

テクスチャについて

次は「ドラゴンエンジン」で使われているテクスチャについてです。

f:id:sgtech:20181224005954j:plain

顔一つの表情に沢山の種類のテクスチャが使われています

上記の画像にあるような8枚*4のテクスチャをキャラクター班で作成して使用しています。
DiffuseMap、NormalMap、ShinnessMap、AmbientOccrusionMapは、おそらく大体のプロジェクトでお馴染みだと思いますので説明は割愛します。

アンビエントオクリュージョンマップ

アンビエントオクリュージョンマップをわざわざ1枚のマップで持っているのは、キャラ単体では画面を占める割合は低くスクリーンスペースのアンビエントオクリュージョンが充分に乗らないので個別でベイクしキャラごとに用意しているからです。

トランスミッタンスマップ

肌透過用のトランスミッタンスマップは、肌の薄い部分を表現するために使っています。逆光を当てられた時の耳や、指などに効果的です。

F(0°)マップ

黒い画像ですが、F(0°)マップと呼んでいます。
F(0°)とは屈折率の比から求まる、0度のフレネル反射率という意味らしいです。
屈折率0度、法線方向から見たときの、鏡面反射率、スペキュラーの総量です。
mayaにあったmental_rayのマテリアルでは、IOR(屈折率)と書かれていることからリフレクションの強さにも影響します。値が高いほど正面から見ても環境が映りこみます。

キャラクター班としては素材ごとに担当プログラマーに用意してもらった値で作業するという認識です。

テクスチャ作成フロー

次にテクスチャの作成フローです。

龍のテクスチャワークフローは、タンジェントノーマル、AO、シャイニネスの順番に一連の流れで制作しています。
ディフューズは状況によって前後いたしますが、スキャンメッシュと生成された画像をソースに、ゲームメッシュでベイクすればほぼ終了です。

タンジェントノーマルマップ

まずは16bit高解像度テクスチャを利用してディスプレイスメントマップを作成します。

そのディスプレイスメントマップをZbrush上でスキャンメッシュにアサインしてゲームメッシュにベイクするのが目的です。

PhotoScanにより自動で生成されたテクスチャがコレです。

f:id:sgtech:20181224005905j:plain

PhotoScanで生成したテクスチャ

ご覧のとおりバラバラです。

 

ほくろやシミなどの余計な凹凸情報になりえるものは先にフラットにしておきます。

f:id:sgtech:20181224010044j:plain

 

前処理が済んだらPhotoshop上でハイパス等を使いディスプレイスメントマップを作成します。


次にZbrush上での作業にうつります。

f:id:sgtech:20181224005812j:plain

右側がZbrush上で、スキャンメッシュにディスプレイスメントマップを適用した状態です。

f:id:sgtech:20181224005526p:plain

ディスプレイスをかける前よりかなり細かい凹凸が表現できているのがわかりますでしょうか。

先ほど調整したゲームメッシュをインポートし、レイヤー分けしてからスキャンメッシュにディスプレイスメントを適応します。その凹凸情報を、projectALLを使って転写します。

スキャンメッシュにはエラーも多いので、この作業の前に事前にモーフターゲットを登録しておきprojectALLで投影したくない部分をマスクしてするなどして都合の悪い部分をフィルタリングしています。

その後、キャラクターによってはさらにスカルプトしてディティールアップを図ります。

後はSubdivisionの一番上と一番下をエクスポートし、XnormalやSubstance等のテクスチャベイカーを使ってタンジェントノーマルとAOを生成します。
人によってはディフューズもこの段階でスキャンメッシュからベイクします。

生成したテクスチャが…

f:id:sgtech:20181224005902j:plain

これですね。ちゃんとバラバラだったUVが繋がっています。

スキャンメッシュからベイクによって得たデータとなります。

ディフューズマップはベイクしたままというわけにはいかないのでさらに手を入れて調整します。

テクスチャの撮影時に陰影を消すようにはしているものの、どうしても取りきれない陰影が入ってしまいますので手作業で余計な陰影を除去します。

f:id:sgtech:20181224010047j:plain

少しわかりづらいかもしれませんが、除去前と除去後で陰影の差が少し緩和されています。
消しすぎると単調に見えるのでさじ加減に多少経験が必要な作業です。


シャイニネスマップ

シャイニネスマップは、ノーマルマップをキャビティマップに変換するアクションを使い作成したマップをオーバレイで5枚重ね共通のトーンカーブを適用した後、光って欲しいところ、欲しくないところを既定の値で塗り完成です。

すべてのキャラクターはUVが共通なので、実際はキャビティレイヤーの差し替えと個人で違う位置になる唇周りと眉毛周りの塗りの調整のみ行っています。

Substanceでも同様に上の一連の作業が組んであります。

トランスミッタンスマップ

つづいて肌透過用のトランスミッタンスマップの作成も紹介しましょう。

f:id:sgtech:20181224005522p:plain

顔モデルにスムースをかけた後に、白黒反転をしてアンビエントオクリュージョンをベイクしたものです。
このフロー自体が正しいものなのかは正直わかりませんが、コストと効率を鑑みて、最も効果的なフローでした。

処理軽減のため全てのキャラが同じテクスチャを使っています。

以上ですべてのテクスチャ作成が完了です。

 

仕上げにテクスチャの中間データはDDSを採用しているのでDDSで書き出します。

その際のインターフェイスが煩雑で多くの設定ができてしまうので、デザイナーが迷わないようにツールを用意しました。

リサイズのアルゴリズムとDDS保存形式を任意で選ぶことができます。

f:id:sgtech:20181224005737p:plain

f:id:sgtech:20181224005943j:plain

元々はガンマ補正、レベル補正の設定や、ラップモード設定、ミップマップ設定など細かく分かれていたのですが、それらの仕様が確定されてからは、混乱を避けるため全て非表示にして触れないようにしてあります。

細かいことですが、これによりかなりミスも少なくなりかなり効率的に作業できるようになりました。

その後いろいろ微調整*5して、完成したモデルですが…

f:id:sgtech:20181224005912j:plain

f:id:sgtech:20181224005909j:plain

全身

どうでしょう、島野さんに見えますか?

「龍が如くスタジオ」流のフローでこのクオリティの顔モデルが今では1日もあれば誰でも制作できるようになりました!

 

架空のキャラクター

ついでに架空キャラクターのフローについてもすこし触れてみましょう。

基本的に弊社の社員に協力してもらい、リアルなキャラクターを作成する場合のフローでほぼそのまま再現します。

その後、キャラクターの設定に合わせて修正します。

f:id:sgtech:20181224010040j:plain

このキャラクターは眉毛の角度や目つきの修正ぐらいしか行っていません。

設定によっては元の風貌からは別人になるケースもあり、特に重要度の高いキャラになるとコンセプト画で用意した顔に近づけるために原形をとどめなくなります。

その場合は、ほとんど肌のディフューズ以外は、使用しないのでデザイナーの腕の見せ所でしょうか。

 

次は顔モデリング以外の話を簡単にご紹介します。

 

顔のしわ

各キャラクターには顔の表情が変わったときに表出する皺を表現するための皺マップが用意されています。

下の画像はとあるキャラの顔の皺マップです。

f:id:sgtech:20181224005734p:plain

用意するマップはノーマルマップのみです。

皺用のディフューズやシャイニネス等は用意しておらず、頂点カラーの塗分けによってノーマルマップの表出を制御しています。

f:id:sgtech:20181224010212j:plain

皺表出用の頂点カラー

イベントムービー班が設定した表情のパターンに合わせて表出するようになっており、

デザイナーの作業負担軽減の為全キャラクター設定はほとんど同じです。


キャラ作成をサポートするツール群

ここまでモデルを作成するフローを説明してきましたが、ここからはそのフローの作業を軽減するツールについてお話したいと思います。

まずウェイトのセットアップツールです。DCCツールをSoftimage*6からMayaに切り替えた経緯上、Softimageと同じ感覚でMayaでのウェイト編集を出来るようにする必要がありました。

そこでまずはデザインサポートチームでウェイトのエディタを作成することになりました。

顔のポリゴンメッシュはWrapXによって生成されたものを使っていれば全て同じUV・同じ頂点番号なので、ウェイトセットアップは頂点番号かUVのポジションでウェイトをアサインします。

これで殆どの場合うまくいくのですが、目の形によって目を閉じた時に破綻したり眉毛の位置によっては眉毛が付いてこない、あるいは瞬きしたときに眉毛もついてくる等のバグが発生します。

Maya用のウェイトエディターを使って修正していきます。

f:id:sgtech:20181224005731p:plain

ウェイトのスムースや、ジョイントのロック、ウェイトを保持したままのエッジ切り直し、スキンモデルのデュプリケート、スキンモデルのコンバインなど沢山の便利なツールが用意されています。
このエディタが用意されてからはコンポーネントエディターの数倍速くウェイトの修正ができるようになりました。

 
WrapX

上記でチラホラ名前の挙がっていたツール、WrapXを紹介します。

WrapXとは大抵はDCCツール上で行うシュリンクラップを、高精度で行うスタンドアロンのアプリケーションです。
今は多機能な新バージョンもリリースしている古いツールですが、スクリプトで操作できるのが決め手でこのツールを長らく使用しています。

「龍が如く」プロジェクトでは、PhotoScanで取得した高精細なポリゴンモデルを、リトポロジも兼ねて一定のトポロジのメッシュに落とし込む目的で使用しています。

Pythonでスクリプトを書かないとカスタマイズができないのでデザイナーからしてみれば使いにくいツールですが、このツールを導入しあえてデザイナーが触れないようにしてリトポロジやポリゴンの削減をWrapXで吸収してしまえば、こだわりがちなデザイナーの工数も増えづらいのではないかという狙いもあります。

f:id:sgtech:20181224005858j:plain

WrapXでシュリンクラップを行います

左がベースメッシュで約5000頂点あります。
ベースメッシュとは、「龍が如く」プロジェクトにおいて素体として最初に作られるモデルで、ほぼすべてのキャラクターモデルはこのベースモデルを基準に作られています。
右がスキャンメッシュです。

まず、既存のPhotoScanで生成した顔のメッシュやテクスチャ、トポロジを共通にするためのベースモデルを読み込み、最初は三点、両目頭と人中にポイントを指定して大まかに頭部の位置とスケールを合わせます。

次に、詳細にシュリンクラップを行うため、位置合わせのためのポイントをPhotoScanのメッシュとベースモデル両方に指定していきます。

計算を除外するポリゴンも指定する事ができるので、ソースのメッシュが大きく欠けている場合も、ベースのモデルを大きく変形させる事なくシュリンクラップを行う事ができます。

設定が完了したら計算を行います。

その過程をGifにしてみました。

f:id:sgtech:20181224010051g:plain

シュリンクラップの様子(約40倍速)

最後にスキャンメッシュをシュリンクラップしたベースメッシュと、それに位置とスケールを合わせたスキャンメッシュをエクスポートして完了です。 

f:id:sgtech:20181224010206j:plain

WrapXをMayaから操作するツール

最初はWrapXのみでシュリンクラップの作業を行っていましたが、現在では手軽にシュリンクラップや編集が行えるようにMayaからコントロールできるツールを用意してあります。

こちらのツールでは、WrapXの鬼門であったフローの途中で指定していたポイントの編集を行う機能も追加しました。

顔のハイメッシュは、このWrapXのフローを採用することでまだ技術的に未熟なデザイナーでも比較的ハイスピード&ハイクオリティに作成できるようになり、トポロジを共通化することで面倒なウェイト関連の作業もストレスなく行う事ができるようになりました。

 LODの作成

ゲーム制作の辛いところはここからLODを作成しないといけないところですが、LOD作成もツール側である程度自動化してあります。 

現在既成のLOD作成ツールは「龍が如く」プロジェクトでは使用せず、クオリティ重視で手作業によりLODを作成しています。

ですが顔のLODは先ほどのトポロジの共通化によりボタン一つでクオリティを維持したまま生成できるツールが使えるのでLOD制作の時間はほとんどかかっていません。

そのほかのツール

「龍が如く」プロジェクトではほかにもリアルなキャラモデルを短時間で作り上げる為に、いろいろなフローを取り入れています。

しかし、「リアルにする為のフローを取り入れる=工数の増大につながる」ことが多いため、フローの採用とその際に増える工数の圧縮の為のツールの制作はセットで行うようにしています。

たとえばMarvelousDesignerで作成したデータのリトポロジ用のツールなどがあります。

 

f:id:sgtech:20181224005728p:plain

f:id:sgtech:20181224005725p:plain

MarverousDesignerは衣服などの3Dデータの作成や、シミュレーションを行えるアプリケーションです。
リアリティのある衣服の3Dモデルを作成するには最適なアプリケーションでしたがリトポロジが大きな課題でした。
この問題もデザインサポートチームでリトポロジーツールを作成し、さらにゲームエンジンにすぐに組み込めるようウェイトやマテリアルの設定、各種テクスチャの焼きつけなども簡易的に行えるようにしました。

他には

  • 今まで作成した大量の顔データをブレンドして新しい顔を作るツール
  • 写真を撮影するだけでPhotoScanにより全自動でスキャンメッシュが生成されるツール
  • そのPhotoScanで生成したスキャンメッシュを短時間でゲーム用のデータに落とし込むツール

など沢山あります。

Mayaはスクリプトを覚えてツールを作れるようになるとあちこち自動化・高速化ができてとても楽しいツールです。
私はデザイナーとしてセガに入社しましたが、今ではPythonでMayaのツールを作るのにすっかりハマっています。
このブログを読んでいるデザイナーなのにツールを作って自動化するのが大好きな方!クオリティの高い作品作りに懸命に取り組む弊社デザイナーたちの作業効率化に向けた環境づくりに、共に取り組んでいただけませんか?
もちろん、そうではない方も「龍が如くスタジオ」であんな芸能人やこんな有名人の方々と一緒にお仕事してみませんか?
「龍が如くスタジオ」は一緒にゲームを作ってくれる仲間を募集中です!

sega-games.co.jp

それでは皆さま、よいお年をお迎えください!


©SEGA

 

*1:「龍が如く」プロジェクトの方言で簡易的なフェイシャルアニメーションが適用されるスペックのキャラクター

*2:プロジェクターから発する光のパターン画像をデジタルカメラで撮影するという技術

*3:河童や小野ミチオなど

*4:シェーダーによって前後します

*5:企業秘密★

*6:つい最近まで「龍が如く」シリーズのキャラクター班はSoftimageで開発していました

20年オヤジのUnreal Engine 4 TIPS

皆さん、はじめまして!
セガ・インタラクティブ技術統括室の林田です。

1997年入社、20年越えのおっさんプログラマです。入社以来、おおむねアーケードゲームに携わってます。

いくつか私の代表作を紹介しておきましょう。Crazy Taxiシリーズ、頭文字D Arcade Stageシリーズ、WCCFシリーズなどなど。古いタイトルもありますので、あまりご存知ないかもしれませんね。ご興味があればWebやYoutubeなどで検索してみてください。その他、変わり種としては、初代ダーツライブのCPU戦、Answer×Answer Pocket (Objective-Cネイティブ!)、あとは古い時代の部内ライブラリなども担当したことがあります。

主な担当分野は、キャラクタアニメーション周り、あとは「火消し」と呼ばれる厄介な事件を解決する人、最近ではアンリアルエンジン4全般を担当しています。

今回の記事は、セガテックブログ初(!)の「アンリアルエンジン4(以下UE4)」についてです。初のUE4記事ゆえ力が入ってしまい、とても長くなってしまいました。お時間のある時にゆっくり読んでいただければと思います。

この記事は、UE4の(ちょっとマニアックな)TIPSを中心に書かせていただいています。なお、ちょっと紙面を余分に頂いて、我々がUE4タイトルをリリースする前にやっていた実験などの紹介などを、前半に書かせていただいています。「前置きはいいから、本題を!」という方は、途中を飛ばして「マクロは悪?」のあたりから読んでくださいませ。

もくじ

セガ製UE4タイトルは、お近くのゲームセンターで好評稼働中です。

見かけた際には、ぜひプレイしてみてくださいね!

セガUE4タイトル第1弾「SEGA World Drivers Championship」


『SEGA World Drivers Championship 2019』アドバタイズムービー

開発チームより

UnrealEngineの機能を存分に使って、SUPER GTの実車両や実在コース(実在コースは期間限定)を再現しています。
ぜひ、体感してみてください。

セガUE4タイトル第2弾「HOUSE OF THE DEAD ~SCARLET DAWN~」


HOUSE OF THE DEAD ~SCARLET DAWN~ JAEPO 2018 MOVIE

開発チームより

13年ぶりにアーケードゲームへ帰ってきました。
ゲームのグラフィックやプログラムは全てUE4エンジンで完結しています。
またアーケードゲームらしく、筐体の音や振動が非常に凝った演出となっていますので、お近くのゲームセンターで見かけたら、ぜひプレイしてみてください。

言い出しっぺが語るセガ×UE4

何を隠そう、私がセガのアーケードゲームにUE4を持ち込もうと騒ぎ始めた張本人、いわゆる「言い出しっぺ」です。そしてご多分に漏れず「言い出しっぺの法則」が発動しまして、ここ数年は社内UE4担当大臣として活動しています。

なんで自社ライブラリを持っている会社がゲームエンジンを?等々、会社の政治的・経済的・人事的な事情は脇に置いておいて(とは言え、おおむね多くの大手ゲームメーカーさんと同じ事情だと思います)、ここは「テック(技術)」ブログですから、UE4を使ってモノを作る話をしましょう。

タイトルを作る前は、こんな実験をしてました。

UE4を甘噛みしてみる実験(UE4.3のころ)

f:id:sgtech:20181125225944p:plain f:id:sgtech:20181125225942p:plain

四輪駆動のおもちゃが壁のあるコースを走るアレです。あのおもちゃと同様、前進しかできず、コリジョンで(壁に沿って)曲がるように作られています。原理的には非常に簡単ですので、興味のある方はぜひ真似をしてみてください。

作り方ヒント

  • f:id:yoshimasa_hayashida:20181019113423p:plain こういうポリゴンを使って、スプラインメッシュでコースを作っています。コリジョンは見た目よりもかなり高い壁を作りましょう。でないとクルマが簡単に吹き飛んでコースアウトします。
  • 車はアクセルボタンに応じた前進しかしません。あとは運を天に任せて、うまく曲がってくれるのを祈る感じです。場合によっては吹き飛んでしまいますが、それはそれで楽しいかと思います。何ならブレーキ操作をできるようにして、コーナーではうまく減速するゲームにしても面白いでしょう。
  • コースの各所にトリガーボリュームなどを置いて、「時間内に通過するとパワーアップ」などのギミックを入れても面白いでしょう。

 格好いい映像を作ってみる実験(UE4.5のころ)

上記の「四輪駆動のおもちゃ」はプログラマ主導でやっており、アーティストにはコース作成だけお願いしていましたが、今度の実験はアーティスト主導で行いました。この実験ではSubstance Designer/Painterも試しています。 

f:id:sgtech:20181125230654p:plain f:id:sgtech:20181125225931p:plain

さすが本職の人が作ると違いますね。HOUSE OF THE DEADを意識するというテーマを悪用して(?)、作ったメンバーの趣味が存分に盛り込まれています(笑)。

UE4の特徴である「物理ベースレンダリング」を活かそうと、光と影を意識した映像の実験となります。従来のセガのアーケードゲームとは多少趣が異なり、明暗のコントラストを強めに出しています。いわゆる「洋ゲー」に寄せた、といったところでしょうか。

一通りゲームを作ってみる実験(UE4.9のころ)

ここまでの実験である程度UE4が分かってきましたので、今度は一連のゲームの流れを作る実験をしました。何らかのタイトルプロジェクトを発足させる前に、踏める地雷はあらかじめ踏んでおこう、がテーマです。

パーティクルエフェクト担当のアーティストや、ユーザーインターフェース担当のアーティストも参加して、チェインクロニクルをアーケード向けにアレンジしたらどうなるか?を試しに作ってみたものです。

f:id:sgtech:20181125225927p:plain f:id:sgtech:20181125230016p:plain

本家チェインクロニクルの倍(10人!)のプレイヤーキャラクタ、倍どころじゃない数の敵、パーティクルエフェクトが派手に飛び交い、カメラもグルングルン回る。これを80型の大型タッチパネルモニタで両手を振り回して遊ぶ。アーケードゲーム化するんだったら、派手に!大味に!半ばパニックになっちゃうくらい忙しい!そんな風にしちゃいました。これは作るのも楽しかったですね。

この実験成果の報告会では、100名を超える社員が会議室に集まってしまい、大半は立ち見、エアコンが追いつかず会議室はどんどん暑くなるという事態に。おかげさまで社内へのUE4の認知度は高まり、UE4採用プロジェクトとしてSWDCとHODを発足させることが出来ました。

ご注意いただきたいのですが、本家チェインクロニクルがUE4に移植される予定も、アーケード版をつくる予定も全くありません。あくまで実験の題材として使用しただけですので、「次のチェインクロニクルはUE4だ!」なんて、間違った情報をうっかり拡散してしまわないようにお願いしますね!

さて、今回の記事は…

20年越えのオッサンプログラマが、その経験をBlueprintに活かすとどうなるか、そういう話をできればいいなと思っています。

UE4の紹介記事などもWebを検索すればたくさん見つかるようになりました。申し訳ありませんが、この記事には初心者向けの説明はあまりありません。それよりはもうちょっと深い部分、私視点でのUE4の「コーディング(code-ing)」ならぬ「ノーディング(node-ing)」事例などを紹介したいと思っています。

初心者の皆さんには、有名な書籍をご紹介。

初心者お断り!なんてバッサリ斬り捨ててしまうのも心が痛いので、書籍を数点ご紹介しますね。本を見つつ、Webで検索しつつ、おそらく最初は苦労されるかとは思いますが、それを乗り越えて楽しくなってきたら、ぜひこの記事に戻ってきてくださいませ!

では、書いてある通りにやれば、何か動くものが出来上がる、おそらくはUE4ユーザーの大半がお世話になっている良書から。

Unreal Engine 4で極めるゲーム開発:サンプルデータと動画で学ぶUE4ゲーム制作プロジェクト

Unreal Engine 4で極めるゲーム開発:サンプルデータと動画で学ぶUE4ゲーム制作プロジェクト

 

 同じく、「書いてある通りにやれば何かできる」もう一冊。

作れる!学べる!Unreal Engine 4 ゲーム開発入門

作れる!学べる!Unreal Engine 4 ゲーム開発入門

 

 こちらは残念ながら、おそらくUnityで使われてただろうキャラクタモデルが流用されており、Unlit(ライティングなし)前提ですので、UE4の美しいレンダリングには触れられていません。それでもキャラクタをアニメーションさせて動かす等々、おそらく多くの皆さんがやりたいと思っているだろう要素は、ちゃんと揃っていますのでご安心を。

もっとお手軽に試してみたい方には、書籍ではありませんが、こちらの動画はいかがでしょうか。


Unreal Engine 4 勉強会 『40分でBlock崩しを作る』(1)

これは「Unreal Engine 4で極める…」の著者、湊さんによる勉強会での公演の動画です。私も一番最初にこの動画をマネをすることで、UE4へのとっかかりを得ました。動画では画面の細かい文字が見えませんが、Blueprintのスクショを貼ってくれた方がいらっしゃいますので、こちら(↓)のページと合わせて見ると良いでしょう。

www.jonki.net

なお、この動画は慣れた方がサクサク組んでいらっしゃいますが、初心者にはテンポが速すぎて追いつけません(当時の私もそうでした)。一時停止や巻き戻しを繰り返して追いかける感じになりますので、決して、40分では完成しません。悪しからずご了承ください。2~3日かけてゆっくりやるくらいの心づもりで。

UE4はアップデートの周期がとても短く(この記事の執筆時点でUE4.20.3。記事のチェックをしている間にUE4.21が出てしまいました)、気を抜くとあっという間に情報が古くなってしまいます。上記3つの書籍・動画は古いバージョンのものですので、お手持ちのUE4バージョンとは違い、もしかしたらメニューの位置やコマンドの名前などが変わってしまっているかもしれません。新旧のバージョン間の機能の違いを、Web検索などを駆使してなんとか見つけ出すのもUE4の勉強の一環だと思って、頑張ってみてください。

Web上では、UE4は難しい…という声をチラホラ見かけます。

確かにそうかもしれません。私も最初の2か月くらいは、UE4が嫌いでした。
それを乗り越えて、ざっくりでもUE4が分かってきたころから、逆にサクサクものが作れていく感覚を覚え始めます。今では「テキストエディタを開いてプログラミング言語を入力する」こと自体に抵抗を覚えるくらいのUE4信者になってしまいました。特にBlueprintとMaterialは手放せません。C++/C#の時代は終わったなんて言っちゃっています。

皆さん、最初は我慢です!(これ、プロからの助言としてはどうかと思いますが…)

UE4に限らず、初めて使うツールって、体に馴染むまでは難しい顔してやるもんでしょ? ましてや、UE4はAAAタイトルすら作れちゃうくらいの豪華ツールです。大量の機能や設定の中から、今自分が欲しい機能を見出せるようになるには時間をかけて慣れるしかありません。

今回つかう題材は、UE4ぷちコン応募作品です。

株式会社ヒストリアさん主催の「UE4ぷちコン」はご存知でしょうか。毎回テーマが出され、それに沿ったゲームや映像作品をUE4で作って応募する、ちょっとしたゲームコンテストです。「UE4の勉強の成果」を発表する、勉強会の延長線上にあるもので、誰でも参加できます。皆さんも参加してみてはいかがでしょうか。

一方で、意外と本気勢も多く(私もその一人ですね)、「ガチコン」と言われてしまうこともあるようです。ヒストリアの佐々木社長はこの言葉を嫌がっているようですが、幸か不幸か、初心者作品からプロ作品まで非常に幅広いレベル帯の作品が集まるコンテストになっており、自分の現在のレベルと、次に目指すべきレベルが見えやすい、とても良いイベントです。

それはともかく、今回の記事は、私が第9回ぷちコンに応募したゲームを題材にお話しようと思っています。

第9回ぷちコン応募作品「Gravity One」


【UE4ぷちコン#9応募作品】GRAVITY ONE

テーマは「ループ」。なので、丸い地球上(マップがループしている)でのシューティングゲームを作ってみました。ありがたいことに、「カナマイ賞」を受賞することができ、アンリアルフェスWEST2018でプレイアブル展示していただきました。

社内でも、毎日昼休みにスコアアタックをしてくれるドハマリプレイヤーが出現。最終的にノーミスクリアを達成! 記念にご本人の強い要望に応えて、このゲームのexeを進呈しました。

第10回ぷちコン応募作品「MAGITYPE」

つい先日行われました、第10回ぷちコンにも応募させていただいています。


【UE4ぷちコン#10応募作品】MAGITYPE

テーマは「ぷち」。ぷちコンの「ぷち」です。
安易に思いつく「プチプチ」(梱包材のアレです)は断固避ける!と意気込んだものの、制作期間の6割くらいの間、ずっと迷走してしまい、結局は未完成のままの応募になってしまいました。テーマ「ぷち」の回収方法として私が選んだのは、過去10回の「ぷち」コンのテーマを全部使う!明らかに盛りすぎですね。間に合うわけもありません。

審査結果発表会では軽くご紹介いただきましたが(動画2:15:00くらいから)、もちろん落選です。プロとして恥ずかしい限りです。

そして、いまだに鋭意作成中…(どうなることやら)

この「MAGITYPE」からも、ご紹介したい事例がいくつもあるのですが、長くなってしまいそうなので、また次の機会に。例えば「MAGITYPE」が完成した後などに記事にできればと思います。

(困ったことに、2018年内にすら完成させられる自信はありませんが…)

プロなのに応募してずるい!

いやいや! 応募要項には「日本在住のUE4ユーザーなら誰でもOK!」と書いてあります。

…いえ。確かにズルいとは思っています。ですので、自分なりにハンデを設けて作っています。

  • 「絵も音もプログラムも、全素材を自分一人で作る!」

面白そうでしょ? やること多くて死ねますけど。

21年もゲームプログラマをやっていますと、アーティストやサウンドデザイナーとのやり取りなどで、それなりにツールの使い方や用語などは覚えるものです。なので、箱を作ってテクスチャを貼れる程度には使い方を知っています。まあ、その程度の技量で全素材づくりに挑むのですから、十分ハンデかと。

おかげさまで、2回のぷちコン参加で、SDキャラクタ作成・テクスチャ作成・アニメーション作成なども自分でできるようになりました(クオリティはともかく)。ほら、ちゃんと勉強になっているから、ズルくない!

参考までに、プログラムは組めるけど絵素材や音素材がなくて困っている皆さんや、インディーズを目指している皆さんに、私が使っているツールをご紹介しておきますね。(ちょっと世間の流行りからはズレていますけど…)

MODO indie(月々1500円ほど)

3Dモデリングツールの中では新しく、古い時代のしがらみなどが少ない分かりやすいツールです。MODO JAPAN GROUPのページにはチュートリアル動画やTIPS動画がたくさんあり、学習のしやすさもオススメしたい理由の一つです。

store.steampowered.com

CorelDRAW Graphics Suite(買い切り6万円ほど)

私は学生のころから論文などの図の作成に使っていました。未だにアップデートを繰り返して使い続けています。いわゆるフォトショップやイラストレーターに相当するツールがセットになっています。特に CorelDRAW(イラストレーター相当)は、私や多くのアーティストでない方々のように、フリーハンドで思い通りに線が描けない人にはありがたいツールです。筆で描くのではなく、方眼紙に定規やコンパスで図を描くイメージですね。私はベジエで描いてから、ビットマップに変換する流れでテクスチャを作成しています。

www.coreldraw.com

Logic Pro(買い切り2.5万円ほど)

いわゆるDTMソフト、音楽編集ソフトです。私の使っているのは古いバージョンの方、Logic Pro 9です。当時は5.5万円ほどしていたのが、今では1.7万円ですね!驚愕です。なお、私が音楽の世界に足を踏み入れたのは、ボカロ(なぜかミクさんではなくルカさん)の影響です。ありがちですね。

www.apple.com

C-Deck(540円×3種)

トランプ大のカードにコード進行が書かれた作曲ツールです。Aメロ、Bメロ、サビカードをランダムに選ぶだけでコード進行が完成です!私はこれがないと作曲できません。古いツールのなので、今ではAmazonなどでも欠品してますね…。再販してほしいものです。

blog.shimamura.co.jp

マクロは悪? いえ、善です。(Blueprintに限る)

前置きが長くなってしまいました。
さて、ここからは具体的なUE4プログラミングやTIPSの話を書いていきましょう。

以下、Blueprintのスクリーンショットが多く登場します。紙面の都合上、どうしても画像が小さくなってしまいますが、クリックすると大きく表示されますので、その機能をご活用ください。

また、ブラウザを複数立ち上げて、片方では文章、片方では画像の拡大を表示する、などの小技を使えば、より見やすくなるでしょう。

定数はどうしてますか?

最初に固定値を決めたっきり、以後は絶対変更したくない変数、つまり定数のことですが、皆さんはBlueprintではどのようにしていますか? 残念ながら、Blueprintには定数という概念がありません。

多くの方が、例えば変数名を大文字とアンダースコア(_)の組み合わせにしてみたり、

f:id:sgtech:20181125230013p:plain

接頭辞や接尾辞を付けるなど、命名規則を工夫して対処しているかと思います。

f:id:sgtech:20181125230011p:plain

C++ならconst修飾子がありましたが、

const int MaxPositionHistory = 256;

Blueprintではそうはいきません。

ここでマクロの出番です。
特にC言語の時代を知っている人は、マクロは危険という認識から、UE4でも避けてしまっているのではないでしょうか。

#define MAX_POSITION_HISTORY (256)

こんな風に書いちゃった日には、確実に先輩にドヤされます。なぜなら、C/C++言語のマクロとは、その数値が整数型なのか実数型なのか、ましてやそれが数値であるかどうかすら、コンパイラは全く気にせず、単なる文字列の置き換えで対応してしまうからです。

ところが、ご存知の通り、UE4のマクロはきっちり型を意識してくれます。ですので、

f:id:sgtech:20181125230008p:plain

こういうマクロを作ると、これは間違いなくInteger型の256となってくれます。
そうすると、

f:id:sgtech:20181125230005p:plain

ミソは、詳細(Details)パネルの「Compact Node Title」の欄にノード名を書くことです。

f:id:sgtech:20181125230053p:plain

これを書くことで、「いかにも定数です」みたいな見た目になってくれます。

f:id:sgtech:20181125230050p:plain

ゲーム用定数マクロライブラリを用意する。

上記の例はアクタの中で作っていましたが、ゲーム全体で使う定数はどうしましょうか。答えは簡単、マクロライブラリを作れば解決します。私はUE4で何か作る際には必ず「Game Constants Macro Library」といった名前のマクロライブラリを作り、ゲーム全体で使う種々の定数を入れています。

これで、このマクロライブラリを編集するだけで、ゲーム中の様々な状態を変更することができるようになりますね。例えば、バランス調整用の定数マクロライブラリなどがあっても良いかもしれません。(ゲームエンジンの時代に定数でバランス調整って、時代遅れな気もしますが…)

なお、定数を変更したらコンパイルしなくてはなりません。ここはご注意ください。

ノードグラフをより見やすく、便利にするための定数マクロ

それでは、ゲーム専用に限らず、Blueprintそのものの見通しをよりよくするために、こういうマクロを作ってみてはいかがでしょうか?

f:id:sgtech:20181125230047p:plain

作り方は先ほどの Max Position History と同様です。Boolean型のFalseを返すマクロを作り、Compact Node Title に「False」と書くだけ。

f:id:sgtech:20181125230630p:plain

同様の方法でTrue マクロも用意しておきましょう。

これらは、作成者の意図を明示するだけでなく、ノードグラフをサクサク組むための助けにもなってくれます。「Compact Node Title」に書かれた文字列は、右クリックで出てくる検索ウィンドウに引っかかるようになってくれますので、例えば「True」や「False」と入力するだけで、今作った Trueマクロや Falseマクロがサクっと引っぱり出せるようになります。

f:id:sgtech:20181125230043p:plain

これらもマクロライブラリを作って、便利そうなものはどんどん入れておきましょう。参考までに、私のマクロライブラリに入っている便利定数をご紹介します。UE4/Blueprintライフが向上すること請け合いです。

f:id:sgtech:20181125230701p:plain

よく使う整数として「Integer 0」や「Integer 1」。このほかにも「Integer -1」などを用意してます。これを使うと、Make Literal Int を検索して、(あの小さい枠に)0を入力して、の手間がまるっと解消されます。

f:id:sgtech:20181125230037p:plain

同じく「Float 0, 1, -1」。ちなみに上記は軸との内積の正負で、回転角度のプラスマイナスを決めるような、比較的よくありそうなノードグラフですね。

f:id:sgtech:20181125230144p:plain

あの巨大な「Make Transform」ノードが非常にコンパクトになる「Transform Identity」ノード。ちなみにIdentityとは「単位行列」の意味。高校数学でしたっけ?内容は、Location=(0,0,0), Rotation=(0,0,0), Scale=(1,1,1) です。これも右クリックのノード検索メニューで「Identity」と入力するだけで出てきます。

下側のSpawn Emitter at Locationに繋がっているのは、意外と面倒な(0, 0, 0)とか(1, 1, 1)を一発で書くためのものです。

マクロ内ローカル変数を活用する。

 下のGIFアニメはぷちコン応募作品「Gravity One」に登場するザコ敵です。「プレドニゾローン」という、とても覚えられない名前がついてます。さて、このプレドニゾローン、キノコ状の「フタ(Flap)」が一定時間ごとに上がったり下がったりします。これをBlueprintで書いてみるとどうなるでしょうか。

f:id:sgtech:20181125230128g:plain

普通のコーディング例、もとい「ノーディング」例

Event BeginPlayとEvent Tick

f:id:sgtech:20181125230124p:plain

  • Event BeginPlay: 使っているメンバ変数に初期値をセットしています。
  • Event Tick: 後述のTickEvent UpdateFlap(カスタムイベント)を呼び出しています。ザコ敵とは言え、弾を撃つなど、もっとたくさんの処理がぶら下がるはずですが、上の図はあくまでサンプルだととらえてください。

TickEvent UpdateFlap

f:id:sgtech:20181125230120p:plain

  • Event Tickから呼び出され、「フタ(Flap)」の回転や上下動を行うカスタムイベントです。
  • カスタムイベントTickEvent Open/Close Flap を呼び出しています。このカスタムイベントは、フタの高さ(Float FlapHeight)を決めています。
  • FlapYaw変数の値を算出しています。現在のYaw角度+DeltaSeconds × RotationSpeedで、次の角度を求めています。
  • Set Relative Location And Rotation で、フタモデルSM_Flap (Static Mesh Component)の相対位置と相対角度をセットしています。

TickEvent Open/Close Flap

f:id:sgtech:20181125230116p:plain

  • TickEvent UpdateFlapから呼び出され、フタ(Flap)の上下動を行います。
  • Sequence - Then 0側:
    ・経過時間(Float ElapsedSeconds)を算出しています。
    ・経過時間÷開け閉めの秒数(Open/Close Duration)で開け閉めの割合(Alpha)を求ています。
    ・フタの高さを求めるマクロ(Make Flap Height Macro)(後述)に、開け閉めの割合(Alpha)と現在の開け閉め状態(Boolean CloseFlag)を渡しています。
    ・算出された新しい高さを、Float変数 FlapHeightに格納します。
  • Sequence - Then 1側: 経過時間が、開け閉めの秒数(Open/Close Duration)+待ち秒数(Wait Duration)を越えたら、経過時間をゼロクリアし、開け閉め状態のフラグ(CloseFlag)を反転します。
  • Open/Close Duration、Wait Duration、Elapsed Seconds の使い方がちょっとだけトリッキーなので注意してください。
    ・Elapsed Secondsの変化する範囲は 0~(Open/CloseDuration+WaitDuration)ですが、Make Flap Height Macro に渡す Alphaの値は WaitDurationを考慮に入れていないため、1を超えてしまいます。
    ・Make Flap Height Macro 内で Alphaの値を0~1にクランプしています。これで、Wait Duration秒数だけ開いたまま/閉じたまましばらく待つ、という状態を作ってます。

Make Flap Height Macro

f:id:sgtech:20181125230226p:plain

  • 入力された開け閉め率(Float Alpha)を0~1の範囲にクランプしています。
  • 入力された開け閉め状態のフラグ(Boolean Close)に応じて、OpenHeightからCloseHeightまでのをEaseするのか、CloseHeightからOpenHeightまでをEaseするのかを切り替えています。
  • Easeで求められた高さをNewHeightとして出力します。

多少トリッキーな書き方をしているのは認めますが、考え方は至って普通です。

  • 経過時間を計算する。
  • その経過時間から、フタの高さを求める。
  • 一定時間経過したら、開け/閉めを切り替える。

さて、ここで話題に挙げたいのは、この「フタの開け閉め」で使われている4つのメンバ変数です。

f:id:sgtech:20181125230223p:plain

  • Boolean CloseFlag: このフラグがTrueの時は閉め、Falseの時は開け、の動作を行う。状態切り替えのためのフラグ。
  • Float ElapsedSeconds: フタを開け始めてから/あるいは閉め始めてからの経過時間(秒)。
  • Float FlapHeight: フタの高さ(Z値)
  • Float FlapYaw: フタの回転角度(Yaw値)

 

さて、この、至って普通のノーディング、20年オヤジは何が気に食わないのでしょうか。

これらの変数は、単にフタを開け閉めするだけのための用意されました。しかし、フタの開け閉めなんて、このActorにとっては影響度/重要度はかなり低いです。私としては、重要度の低い変数(や関数)は、できる限り重要度の高いものとは分けて置いておきたいのです。本音を言うと、メンバ変数にすらしたくない。さもないと、クラスに機能が増えていくにつれ、メンバ変数の欄はどんどん肥大化し、重要度の高い変数と低い変数が同レベルでゴチャっと並んでしまうのです。

実はこの問題、マクロ内ローカル変数を使うことで解消できます。

マクロ内ローカル変数で重要度の低いメンバ変数を減らした例

(Event BeginPlayやEvent Tickは、おおむね変更はなく、想像がつくと思いますので割愛します)

TickEvent UpdateFlap (Modified)

f:id:sgtech:20181125230220p:plain

まずは簡単にできるところから。

フタの回転角度として使っていたメンバ変数FlapYawをなくしてしまいました。その代わりにUpdate Yaw Macroというマクロを作りました。これがミソです。

Update Yaw Macroの中身はこうです。

f:id:sgtech:20181125230217p:plain

Local Floatノードが「マクロ内ローカル変数」です。

  • 普通に「+」ノードなどに挿して、Floatとして使えます。
  • いわゆるSETノードに相当するののが「Assign」ノードです。
  • 上の図では「マクロ内ローカル変数に、YawAdderを足して、マクロ内ローカル変数にAssign(セット)する」「マクロ内ローカル変数をNewYawとして出力する」が行われています。

マクロ内ローカル変数の制限などについて。

  • 変数名がつけられませんので、複数のマクロ内ローカル変数を使うときは混乱しがちです。
  • 使う場合は、Local Floatから必ずラインを伸ばす必要があります。上の図のように、ひたすらLocal Floatノードから、相手のノードに直接つなげる必要があります。したがって、マクロ内ローカル変数をたくさん使うと、非常にたくさんのラインが飛び交うスパゲッティコードになりがちです。

このマクロ内ローカル変数、「Event Graph内に限り、常に値は保持される」という特徴があります。例えば、入力イベントに1つカウントアップするマクロを繋げると、ボタンを押すたびに1, 2, 3... と一つずつ増えてくれます。このアクタが破棄されるまで、この値がクリアされることはありません。(もちろん、自分でクリアした場合は除きます)

f:id:sgtech:20181125230212p:plain

ボタンを押すたび1増やす例

f:id:sgtech:20181125230325p:plain

Count Upマクロの実装

ただし、イベントグラフ内ではなく、関数内でマクロ内ローカル変数を使った場合、関数呼び出しのたびに値がクリアされますので注意してください。例えば、関数内で DoOnce マクロを使って、思ったように動かなかった経験のある方はたくさんいらっしゃると思います。これもマクロ内ローカル変数のこの性質が原因です。(どちらかというと、イベントグラフが特殊で、関数が普通の挙動をしているのですが…それはさておき)

興味のある方は、UE4標準の種々のマクロの実装を見てみてください。ダブルクリックするだけで見られます。皆さんが良く使うであろう ForLoop や ForEachLoop もマクロ内ローカル変数を使って実装されていますよ。

TickEvent Open/Close Flap (Modified)

さて、次は TickEvent Open/Close Flap関数がどう変わったか見てみましょう。

f:id:sgtech:20181125230322p:plain

こちらは比較的トリッキーです。戸惑われた方もいらっしゃるかもしれません。マクロが2か所あるのは良いとして、恐るべきことに(?)1つのグラフにカスタムイベントが3つ繋がっています。

それでは個々に見ていきましょう。まずは、Elapsed Seconds Macroから。経過時間を格納していたメンバ変数Elapsed Secondsをこのマクロで置き換えています。

f:id:sgtech:20181125230318p:plain

ここまでに紹介してきたマクロとほぼ同じような形ですが、InputsノードにあるResetピンで経過時間をゼロクリアできるようにしてあります。カスタムイベントTickEvent Open/Close Flap (Modified)のグラフをもう一度見てみてください。このResetピンには、カスタムイベント Reset ElapsedSeconds が繋がっており、さらにそのReset ElapsedSecondsイベントは、Sequence - Then 1の、時間経過後のOpen/Close切り替え時に呼び出されています。ここで、ElapsedTimeにゼロをセットするのと同等の処理を行っているわけです。

次に Make Flap Height Macro (Modified) マクロについて。これは「普通版」の同名マクロを変更して、メンバ変数CloseFlagを置き換えました。

f:id:sgtech:20181125230314p:plain

変更前のMake Flap Height Macroと見比べていただくと分かると思いますが、

  • CloseFlagの代わりにマクロ内ローカル変数を用意。
  • Toggle Open/Close ピンを新設して、CloseFlagの役割をするBoolean型のマクロ内ローカル変数のTrue/Falseを切り替え。
  • Toggle Open/Closeピンには、カスタムイベントToggle Open/Closeを繋げ、時間経過でのOpen/Close切り替えのタイミングでこのカスタムイベントを呼び出し。

という仕組みになっています。

いかがでしょうか。これでActorクラスにとって重要度の低い3つの変数 CloseFlag、ElapsedSeconds、FlapYawがメンバ変数から消えて、まさに必要な部分だけに登場する適材適所なグラフになりました。
(その代償として、初心者お断りなトリッキーなグラフになっていますが…)

次はさらにマニアックな技です。ご注意を!

さて、重要度の低い4つの変数のうち3つを「適材適所」な位置に移すことが出来ました。こうなると、最後の1つFlapHeightもメンバ変数から外してしまいたくなります。

あらかじめ言っておきます。ここから先はマニアックな使い方になります(ここまでも十分マニアックかもしれませんが)。ですので、他人がノードグラフを見ると、頭の上にクエスチョンマークを浮かべてしまうかもしれません。あるいは先輩に「やりすぎだ!可読性を上げろ!」と怒られてしまうかもしれません。

f:id:sgtech:20181125230310p:plain

  • TickEvent UpdateFlap (Maniac)
    ・TickEvent UpdateFlapのマニアック版です。
    ・ここまでに登場した同名イベントと同様、算出された高さと回転角度をフタ(Flap)モデルにセットしています。
  • TickEvent Open/Close Flap (Maniac)
    ・TickEvent Open/Close Flapのマニアック版です。
    ・ここまでに登場した同名イベントと同様、経過時間を計算し、そこからフタ(Flap)の高さを算出しています。
  • Flap LotationマクロとFlap Rotationマクロ
    ・Set Relative Location And Rotation 関数のNew Location引数とNew Rotation引数をバラすのが面倒になったので、マクロを作って見た目をシンプルにしています。
    ・Flap Location マクロ
     f:id:sgtech:20181125230429p:plain
    ・Flap Rotationマクロ
     f:id:sgtech:20181125230426p:plain

さて、このマニアック版でのミソは、カスタムイベントSet Flap Height (Maniac)と、Set Flap Height Macroです。

Set Flap Height Macro

f:id:sgtech:20181125230422p:plain

  • Inputsノードに指定されたマクロの引数In Flap Heightを、Float型のマクロ内ローカル変数に格納しています。
  • Outputsノードから、その格納された値をOut Flap Heightとして出力しています。

一見、全く何の意味もないプログラムのようにも思えますが、このマクロは「Event Graph内に限り、マクロ内ローカル変数の値は保持され続ける」特性を利用して、変数のGETとSETの両方の機能を持たせたものです。

  • SET機能: カスタムイベントSet Flap Height (Maniac)が呼び出されると、その引数の値をマクロ内ローカル変数にSETする。
    ・つまり、カスタムイベント、TickEvent Open/Close Flap (Maniac)でフタ(Flap)の高さが決まった際に、FlapHeight変数にその高さをSETする代わりにカスタムイベントSet Flap Height (Maniac)を呼び出します。
    ・SET(Assign)された値は、再びSET(Assign)されるまで、変更されることはありません。
  • GET機能: Out Flap Height を通じて、マクロ内ローカル変数に格納された値をGETする。
    ・マクロ内ローカル変数に格納された値は、Event Graph内であれば、いつでもGETすることができます。

このマクロを用いて、TickEvent UpdateFlap (Maniac)や TickEvent Open/Close Flap (Maniac)イベントは、以下のメカニズムで動いています:

  • TickEvent UpdateFlap (Maniac)で、Set Relative Location And Rotationが呼び出される際、Set Flap Height Macroの出力Out Flap Height経由で、マクロ内ローカル変数に格納された値(現時点のフタ(Flap)の高さ)をGETしています。
  • TickEvent Open/Close Flap (Maniac)で、Make Flap Height Macro (Maniac)で算出されたフタ(Flap)の高さを、Set Flap Height (Maniac)イベントを使って、マクロ内ローカル変数にSETしています。

さすがに無理矢理やった感は否めませんが、Actorのメンバ変数からはフタ(Flap)の上下動に関する4つの変数をメンバ変数欄から消すことができ、Event Graph内の必要なところでのみGETあるいはSETされる「適材適所」な形に整理することができました。

マクロ内ローカル変数を活用する際の注意点

  • デバッグが難しい: ウォッチができないので、Print Stringなどのログ出力を使う必要があります。
  • スパゲッティになりがち: マクロ内変数が増えるたびに、おびただしい数のラインが増えてしまい、可読性が非常に下がります。

簡潔に言うと、気を付けないとバグの温床になりやすい、ということです。そういう意味では、UE4やプログラミングにある程度慣れた人以外は、多用しない方が良いかもしれません。まずは、小さなサンプルを作って試し、正しく動くことを確認出来たら本使用に移すなど、守備的な方法で運用するように注意してください。特にチームで作業している場合は、チーム全員でマクロ内ローカル変数をどう使うかの意思統一してから使うべきでしょう。

弾幕で分かるBlueprintの限界

さて、次はBlueprintの最適化とプロファイリングの話をしましょう。

ぷちコン応募作品「Gravity One」は弾幕シューティングを目指しました。その意気込みの割には密度が薄かったのは反省点ですが… やはり弾が多いと処理落ちしてしまうのです。

Blueprintは重いと知りつつも、Blueprint信者としてC++に逃げることは許されません。結局、弾一個一個をアクタで作ってしまいました。結果、本当に重くなってしまい、ボス戦などでは60FPSをキープできていません。というか、40FPSくらいです。ホント、プロとしてお恥ずかしい限りです。

f:id:sgtech:20181125230408p:plain

この弾幕の最適化、今までずっと放置していたのですが、今回のブログのネタとしても面白そうだと思ったのでやってみました。Blueprint信者としての信念を曲げてのC++化もやっています。ここではそのあたりのお話をさせていただこうと思います。

当初から重くなるのは覚悟してました。

もともと弾幕を出すつもりでしたので、作り始めた当初から重くなるのは想定していました。ですので、弾のActorはできる限りシンプルに組んだつもりです。

まずは、Tickの仕事を最小限にするように気を付けました。

f:id:sgtech:20181125230359p:plain

  • Sequence - Then 0: 移動計算は地球表面に沿って直進するだけ(Ballisitic Object.Update)。その結果をSet Actor Location And Rotationに渡している。
  • Sequence - Then 1: Dynamic Material Instance を使って、時間に応じて色を変化させる。
  • Sequence - Then 2: 一定時間で自滅させる。

コンポーネントの構成も非常にシンプルです。

f:id:sgtech:20181125230512p:plain

  • Sphere Collision: ルートコンポーネントとして球コリジョンを使っています。
  • Bullet: その子コンポーネントとして、Material Billboard Component を使っています。つまりメッシュですらありません。

コリジョンプロファイルも限定的にしました。

f:id:sgtech:20181125230509p:plain

  • プレイヤーオブジェクトに対してのみ「Overlap」
  • その他のオブジェクトに対してはすべて「Ignore」
  • 「Block」は一切使っていません。

セッションフロントエンドでCPU負荷をチェックしてみる。

セッションフロントエンド、使ったことのない方は、これを機にぜひ使ってみてください。使い方は、UE4公式にあります。ちゃんとわかりやすく書かれているのでご安心を。

では、ボス戦をDevelopmentパッケージで調べてみた結果を見てみましょう。

f:id:sgtech:20181125230505p:plain

画像をクリックすると拡大して見られますが、それでも見づらいと思いますので要点をまとめます。

  • 敵の弾Actor (BP_EnemyBullet01)がダントツで重い。14.404 ms。案の定、弾幕がボトルネックになっていた。

そのうち顕著なのは、

  • Set Actor Location And Rotation 7.167 ms/コール数 814.3
  • BP_BallisticObject.Update 2.582 ms/コール数 814.3

となっています。

ちなみに60FPSですと、1フレームは16.667 msです。
つまり、弾幕だけでCPUリソースの14.404 / 16.667(9割弱!)使っていることになります。

最初からシンプルになるように組んだことが功を奏しています。なぜなら、ボトルネックのトップとしてUE4の標準関数(Set Actor Location And Rotation)が挙がっており、自作の関数たちは次点以下になっているからです。自作の関数BP_BallisticObject.Updateが次点ですので、ここはもちろん考察しておくべきですね。

まずは、自作関数を見てみましょうか。

では、ボトルネック次点となった自作関数BP_BallisiticObject.Updateから。

f:id:sgtech:20181125230459p:plain

正直なところ、この関数もとてもシンプルで、これを最適化しろと言われると困ってしまいます。やっていることは、1フレーム前の「弾を地球中心から見たベクトル」を、今回の移動量分だけ地球の中心で回転しているだけです。

この関数については、最適化方針が見出せなかったので見送ります。これ以上軽くするには「とんち」(試行錯誤の労力、あるいは天才的な閃き)が必要そうです。後述しますが、結局は、こんなシンプルな関数でも大量に呼び出されるとボトルネックとなってしまうということですね。なんせ、ボス戦では700~1000個の弾が動いているわけですから。

では、Set Actor Location And Rotation をどうにかしましょうか。

念のため言っておきますが、UE4標準関数のSet Actor Location and RotationのC++コードにテコを入れるという話ではありませんよ。ここで問題視したいのは、コール回数の多さです。ざっくり調べてみたところ700~1000回の呼び出しがありました。ですので、今回の最適化の方針は

  • Set Actor Location And Rotation を呼ばなくていい時は呼ばない

ということにしてみます。

それはそうと、Set Actor Location And Rotation をはじめ、アクタのトランスフォームをセットする関数(以下、総称して Set Actor Transform系と呼びます)が重いということを知らない人が意外と多いようです。

f:id:sgtech:20181125230455p:plain

せっかくセッションフロントエンドを開いていますので、Set Actor Location And Rotation のやっていることをざっくり確認してみましょう。

f:id:sgtech:20181125230559p:plain

見づらいでしょうから、要約します。なお、エンジンのC++コードを開いて詳しく調べたわけではなく、関数名から想像した話でしかありませんので、ご注意を。

GeomSweepMultiple 2.480 ms/コール数 814.3

関数名から、コリジョンのSweep形状を作っていると想像できます。Set Actor Transform系の「Sweep」パラメータが関連するのでしょう。

f:id:sgtech:20181125230556p:plain

ということは、Sweepしない方が軽いのでしょうが、さすがにSweep処理なしでシューティングゲームを作るのは至難の業ですね。

UpdateComponentToWorld 2.037 ms/コール数 814.3

関数名から、そのアクタに付随するコンポーネントのトランスフォームをワールド系に変換していると想像できます。つまりは、コンポーネントが多ければ多いほど重くなるのでしょう。

Set Actor Location And Rotationのその他の処理

  • Component PostUpdateNavData: NavMesh系の処理だと想像できます。NavMeshが不要なら切りましょう。
  • UpdateOverlaps Time, MoveComponent FastOverlap: どちらもコリジョンのOverlap系の処理だと想像できます。

これらをまとめると、下記のようになるでしょうか。

  • Sweepが不要なら、積極的にOFFにする。
  • コンポーネントの数を必要最小限にする。
  • NavMeshが不要なら、積極的にOFFにする。

また、Set Actor Transform系が重いということから、UE4の基本として:

  • そもそも、Set Actor Transform系の呼び出しは、Tick1回の流れで1回だけにとどめるべき。

についても留意するようにしましょう。

カリング処理を作ってみる。

f:id:sgtech:20181125230551p:plain

上の画像は、地球を非表示にした状態でのボス戦です。見ての通り、実は地球の裏側でも弾幕が動いていたのです。どうしようもなく無駄ですね。このような見えないところにあるオブジェクトの処理を省くことを、カリング処理といいます。

カリング処理については、Gravity Oneを実装していた当初から、気にしつつも放置してきた部分です。と言いますか、プロとしてカリング処理をしていないこと自体お恥ずかしい。それくらい基本中の基本です。では、当時の私はなぜカリング処理を実装しなかったのか。もちろん1番の理由は、やることが多すぎてそれどころじゃなかったからですが、実際のところ他にも躊躇する理由があります。

  • 重いと言われているBlueprintでカリング処理を組んだ場合、逆にそのカリング処理が重くてボトルネックになってしまう可能性がある。

Blueprintでは関数コールは重い処理の一つです(関数名で検索してから呼び出す仕組みとの話です)。ノード1個1個が関数に相当しますから、ノードの数が増えると比例して重くなる、ということです。したがって、カリング処理もできる限りシンプルに組む必要がありますが、カリング処理の多くは算術演算が比較的多くなってしまいがちです。ご存知の通りBlueprintでは「+」や「-」すら一つのノードになっています。これも、もちろん関数呼び出しです。つまり算術演算は関数コールが増えやすいものの一つです(なお、Math Expressionを使って軽減することはできます)。この算術演算周りの負荷が高くなってしまった場合、せっかく作った最適化処理(カリング処理)は逆効果となってしまいます。ここを懸念していました。

Blueprintでの最適化処理は、(今の私のUE4技能レベルですと)「とりあえず作ってみて、効果を確認する」の試行錯誤を繰り返すことが必要だと思います。労力がかかる上に、うまく行くかどうかが不明。困ったものです。そういう意味では、Blueprintでの最適化に悩まずに、とっととC++へ移行した方が、費用対効果は高いかもしれません。

今、このブログの執筆時点では、時間に追われているわけではありませんし、面白そうだし!ということで、やってみました。

内積1発で済ますカリング処理。

上記に書いた通り、Blueprintで組んだカリング処理が重くなっては意味がありません。ある意味、C++で組まれたUE4標準関数Set Actor Location And Rotationに勝てるような、できる限りシンプルなカリング処理を選ぶ必要があります。そこで今回は「内積1発でざっくり判定」する方法を選びました。

もともと、プレイヤー機や敵の弾をはじめ、すべてのアクタのUpベクトルが地球表面の法線ベクトル(球の中心から球面を垂直に突き抜けるベクトル)に一致するように作ってあります。このUpベクトル同士の内積を取れば、プレイヤー機と敵の弾の「球面に対する」位置関係が分かるはずです。内積の値が1に近ければ、球面上でプレイヤー機に近い位置にいて、-1に近ければ、球面上では反対側にいるはずです。

結果、弾のアクタのTickはこうなりました。

f:id:sgtech:20181125230547p:plain



  • クラスメンバ変数Location (OptTest)とRotation (OptTest)を用意。修正前ではGet Actor Locationから取得し、Set Actor Location And Rotation でセットしていたところを、いったんこの変数に保存している。これは、Set Actor Location And Rotation を呼ばなくても、Tickのたびに更新される位置と回転を保持しておくため。
  • 修正前ではSet Actor Location And Rotationを呼び出していた場所で、カスタムイベント「TickEvent Cull If Outsight」を呼び出している。このカスタムイベントについては後述する。
  • その他の個所については、変更なし。

カスタムイベント TickEvent Cull If Outsight

f:id:sgtech:20181125230625p:plain

  • TickEvent Cull If Outsight: プレイヤー機と敵の弾のUpベクトル同士の内積の大小を判定しているところ。Math Expressionの 0.7071は cos(45度)の値。つまりプレイヤー機のUpベクトルとの角度差が45度以内のものを可視と判定している。
  • SubEvent Cull If Outsight > Sequence - Then 0: 可視圏内/可視圏外へ切り替わった時の処理。SweepのOFFと、コリジョンのON/OFF、アクタの表示/非表示を切り替えている。可視圏内に入った直後はSweepをOFFにしておかないと、過去の可視圏内だった位置から可視圏内に戻った現在位置まで、長いSweepが作られてしまい、意図せぬコリジョンが発生してしまう。
  • SubEvent Cull If Outsight > Sequence - Then 1: 可視であれば Set Actor Location And Rotation を呼び出した後、SweepフラグをTrueにしている。

さて、「内積1発で済ます」と言った割には大きなノードグラフになってしまいました。これがプログラミングの怖いところですね。果たしてこの修正でパフォーマンスは改善したのでしょうか。

セッションフロントエンド再び。

f:id:sgtech:20181125230544p:plain

  • 敵の弾Actorの負荷は 2.991 ms改善(14.404 msから11.413 msへ)
  • Set Actor Location And Rotation のコール数が半減(814.3から429.0へ)。今回作ったカリング処理で、半数の弾の Set Actor Location And Rotation が呼ばれなくなったため。結果、負荷が半減(7.167 msから3.566 msへ)。
  • BP_BallisticObject.Update は現状維持(変更を加えていないため)

おおよそ3 msの軽減という、良い結果が得られました。Blueprintでも負荷の低いカリング処理が組めたようです。3 msというと、単純計算で弾があと234.5個出せます。これはかなり良い結果と言えます。成果が得られずに色々な手法を試行錯誤する覚悟をしていましたが、一回で結果が出てホッとしています。

現実のゲーム開発では0.5 msも稼げれば相当頑張ったと言えます。各プログラマがおのおの自分の担当分野で頑張って少しずつ稼ぎ、合計で3 ms稼ぐ、といったところです。そういう意味では、今回の3 msという成果は、逆に怒られる領域かもしれませんね。「こんな基本的なところを今までなぜ放置してた!」と。(はい。申し訳ありません…)

C++化も試してみる。

さて、せっかくですからC++化も試してみましょう。C++化と言っても「Nativize」の方です。ところで「Nativize」とは日本人には非常に読みにくい綴りです。Web辞書で調べてみたところ「ネイティバイズ」と発音するようです。このNativize機能はUE4による「自動C++化」です。非常にありがたい機能が実装されたものです。

プロジェクトセッティングを見てみると、Nativizeには3つのモードがあります。Project Settings >Project > PackagingにあるBlueprint > Blueprint Nativization Method の項目があります。

f:id:sgtech:20181125230639p:plain

  • Disabled: Nativizeなし。デフォルト。
  • Inclusive: プロジェクトのBlueprintをまるっと一式Nativizeする。
  • Exclusive: 指定されたBlueprintのみNativizeする。(ただし関連するBlueprintも一緒にNativizeされる)

残念ながら、Nativize機能はうまく行く場合とうまく行かない場合があるようです。本シューティングゲームGravity Oneも、Inclusive(まるっと全部)のNativizeはエラーが出てしまい、うまく行きませんでした。今後のアップデートに期待です。

一方、今回の「敵の弾」に対してのみNativizeするように指定した、Exclusiveモードではうまく行きました。今回はこちらについてお話します。

ExclusiveなNativizeのしかた

まずは、NativizeしたいBlueprintを開き、上のボタンバーから「Class Settings」をクリックします。そうすると詳細パネルにクラス設定一覧が出ますので、その中の「Packaging > Nativize」にチェックマークを入れます。

f:id:sgtech:20181125230636p:plain

これをNativizeしたいすべてのBlueprintに対して行います。なお、チェックマークを入れたBlueprintだけでなく、それと参照関係にある他の(チェックマークを入れていない)BlueprintもNativizeの対象となるようです。それがいわゆるReference Viewerでみられる参照関係と同じなのか、他に法則があるのか、などは調べていないので分かりません。悪しからずご了承を。

次に、プロジェクトセッティングを開き、Project > Packagingから、Blueprints > Blueprint Nativization Methodを「Exclusive」にします。(意外とこの設定を忘れがち)

f:id:sgtech:20181125230634p:plain

あとは、パッケージングするだけです。ちゃんとNativizedされたかどうかを確認するには、Output Logで「NativizedAssets.cpp」を検索すると良いでしょう。また、NativizedAssets.cppの付近には、NativizeされたBlueprint名に妙な数字と.cppがつけられて並んでいますので、何がC++化されたかを知ることもできます。

今回は、カリング処理なし/ありの両方についてNativizeしてみました。それらのプロファイリング結果を見てみましょう。

Nativizeされた弾幕はどれくらい軽いか?

結果を以下に並べてみます。()内の数字は、最適化前の状態のNativizeなし/カリングなしの状態に対して、何 ms高速化できたかを表しています。

  • Nativeなし/カリングなし 14.404 ms(0.000 ms)
  • Nativeなし/カリングあり 11.413 ms(2.991 ms高速化!)
  • Nativeあり/カリングなし 7.618 ms(6.786 ms高速化!)
  • Nativeあり/カリングあり 7.266 ms(7.138 ms高速化!)

見ての通り、小躍りしたくなるような結果を得られました。これを見ると手動でC++を組むのがばかばかしくなってきますね! 問題はNativizeが失敗することが多々あることですが、今後のUE4アップデートに期待することにしましょう。

弾幕で分かるBlueprintの限界

ここまでの話をまとめます。文字ばっかりになりますが、ご容赦くださいね。

Blueprintをできるだけ軽く組むために。

Blueprintは非常にサクサク組めて、イテレーションサイクル(試行錯誤の繰り返し)も速いので、UE4でのゲーム開発では積極的に取り入れるべきものだと思います。一方で、Blueprintは所詮スクリプトに過ぎないという一面もありますので、CPUパフォーマンスに問題が出やすいのも事実です。大事なことは、Blueprint/Nativize/手動C++の境界をどこに置くかで、これを見極めることができると、サクサク作れて高パフォーマンス、という夢のような環境を手に入れることも不可能ではないでしょう。

その見極めの基準の一例として、Blueprintで注意するべき項目を並べてみようと思います。これはあくまで私個人の見解で、セガのUE4エンジニアの総意ではありませんので、その辺はご注意ください。

  • アクタの数やTickの数を必要最低限に絞る: この数は純粋にCPU負荷に影響します。今回の弾幕のようにアクタ数が多いと、明確に分かりやすく処理落ちし始めます。まずは、アクタの数を必要最低限に絞ることです。また、Set Actor Tick Enabledなどを使って、不要なTickは切ってしまいましょう。
  • Set Actor Transform系はTick1回につき1回まで: 前述したとおり、一見トランスフォームをセットするだけのように見えて、その中ではコリジョンのSweep処理や、コンポーネントの座標変換など多くの処理が行われています。位置や回転などのトランスフォーム値は変数で計算しておき、確定した段階でSet Actor Transform系を呼び出すようにしましょう。また、不要なSweepは行わないこと、コンポーネントの数を絞ることも大事です。
  • ループの数を減らす: 今回の記事では取り上げていませんが、Blueprintのループ処理も重い処理の一つです。試しに、256回回るForLoopを2重にして(つまり256x256=65536回ループ)実行してみてください。劇的に重いことが分かるかと思います。これは、パッケージ化すると改善はされますが、劇的に軽くなるわけではありません。また、ループはエディタでの実行とパッケージでの実行との速度差が大きく、開発中にCPU負荷を見積もるのが難しいので、ループを多用している場合は、パッケージでの動作チェックを頻繁にすべきでしょう。
  • できる限りノードの数を減らす: Blueprintの関数呼び出し、イベント呼び出しは、比較的重い処理のようです。できる限りシンプルなノードグラフを組んで、関数呼び出しそのものを減らす工夫をしましょう。また、UE4標準で用意されている関数は、たいていは内部がC++化されています。似た処理を複数のBlueprintノードで自作するよりは、UE4標準関数を積極的に使いましょう。また、UE4アップデートのたびに便利関数がこっそり増えていたりします。UE4リリースノートや右クリックの検索ウィンドウなどを活用して、新しいノードや機能をマメにチェックしましょう。

何を基準にNativize/手動C++化に踏み切るか。

個人的には、上記4項目がそこそこ出来ているならば、最適化処理をBlueprintで組んで試行錯誤するより、Nativizeを試すべきだと思っています。そういう意味では、上記4項目がBlueprintの限界点だと言えます。

今回例に挙げたカリング処理は幸いにも成果を得られましたが、一方で、Blueprintで最適化を模索するのは、Blueprintそのものの重さが災いして、うまく行かない可能性も高いと思っています。その試行錯誤にかけた手間暇に対して、得られる効果が見合ったものかどうか、いわゆるコストパフォーマンスは、私のUE4技能レベルではまだまだ謎の領域にあります。

ですので、Blueprintが一定のレベルまで組めているなら、最適化の模索より先にNativizeを試すほうが建設的かと思います。前述したとおり、得られる効果は絶大です。Nativizeでエラーが出てしまうようなら、そこで初めて、Blueprintでの最適化を模索するか、手動でC++を組むかを悩むべきでしょう。

ここで、Blueprintでの最適化と、手動でのC++化が、同レベルで天秤に掛かっていることを不思議に思う方もいらっしゃるでしょう。一見、手動C++化で決まりだと思えます。しかし、私が手動によるC++化を避けている理由は、関連するゲームプログラム周りの設計を再構成する必要がでてしまいやすい点にあります。例えば、ゲームバランスに直結するようなBlueprintを安易にC++化してしまうと、以後ゲームバランスを変更するたびに Visual Studio でのビルドが必要になり、作業スピードが落ちてしまいます。また、アーティストやレベルデザイナーが頻繁に触っているBlueprintをC++化するわけにもいきません。そのためには、基底クラスをC++で作って、そのBlueprintの親にする等、何を表(Blueprint)に出して、何を裏(C++)に隠すかを設計しなおさなくてはならないのです。一方のNativizeはパッケージングにのみ影響する機能ですから、その辺の心配はいりません。

手動C++化については、もっと気楽な方法もあります。Blueprintでゲーム全体を組むと決めてしまい、C++はそのためのツールを提供するためのものと割り切ってしまうことです。例えば、基本機能を充実させた、そのゲーム専用の基底クラスをC++で作っておいて、Blueprint側はその基底クラスを派生して使うようにする、他には便利関数や便利クラスなどの小さいが高速なモジュールをC++で用意しておくなどです。

まあ、これらはBlueprint信者の妄信かもしれません。参考にしていただきつつも、皆さんは皆さん流のBlueprint/C++使い分け術を見出していただければと思います。

おわりに

 夢中になって書いてしまった結果、非常に長くなってしまいました。ここまで読んでくださった皆さん、本当にありがとうございます! 楽しんでいただけましたでしょうか?

とは言え、これでも書こうと思っていたトピックの4分の1だったりします。現時点でもあと6つくらいトピックがありますし、今後もUE4を使う限りどんどん増えていくでしょう。また、社内の他のUE4エンジニアたちも、あれこれたくさんトピックを持っているようです。今回紹介できなかったトピックも、また機会を作って紹介できればと思っています。

ですので、今回の記事をお楽しみいただけた方、ぜひご反響をくださいませ! 今後もUE4のトピックをどんどん増やしていきたいと思います。

インディーズのススメ

ゲームエンジンの時代が到来して久しいですね。それに伴い、そのゲームエンジンで活かしてもらおうと、3Dモデリング環境や、画像・映像制作環境など、ゲームの素材を作る環境もどんどん安価に・身近になってきています。冒頭でもいくつかツールを紹介しましたが、今やゲーム制作環境の大部分が一般に降りてきています。何ならフリーのツールだけでも全部作れてしまいます。やる気さえあれば、誰でもゲームを作れる時代が到来しました。

今回題材に使いましたぷちコン応募作品「Gravity One」も、未完ながら「MAGITYPE」も、概ね全部の素材を私一人で作っています(もちろん、ものすごく大変でしたけど)。一昔前なら、チームを組まなくては作れない規模のゲームも、いまや個人や少人数グループの頑張りで作れてしまいます。

明らかに時代は変わってきています。ゲームエンジンが台頭する前は「特定の分野一つに強い職人」を集め、チームを組んで、リーダーが全体を統括しながら作る時代でした。それゆえに、企業が強い分野でもありました。一方、昨今では、高度な/専門的な知識はあらかじめゲームエンジンに内包されており、ゲーム開発者は「その使い方と効能」を理解してさえいれば良くなりました。その結果、ゲーム開発者は、特定の分野一つを専門にして深く理解/応用する労力から解放され、複数の分野に渡って広く知る余裕が出来てきました。

今後は、「複数の分野を知っていて、それらを上手く組み合わせてコンテンツを作れる人」同士がチームを組んで、その相乗効果でより面白いものを作る時代になるのでしょう。ゲーム開発者はより「面白さ」に専念できるようになりました。

皆さんも、小規模で良いので、「一つのパッケージを自分一人で作り上げる」を試してみてはいかがでしょうか。他人の評価を得ることは何にもまして勉強になりますし、その成果を何らかの形で発表すると、なお良いでしょう。ゲーム制作の全部の流れを何となくでも知っておく(体に通しておく)と、非常に視野が広がり、例えば、プログラマがアーティストの作業を知ると、以後、お互いの作業領域に重なりができることで、お互いにカバーしあえる体制が出来上がります。これは非常に良い信頼関係となります。

ゲームエンジンの台頭によって、ゲーム開発者の価値が変わりつつあります。今はまだ「UE4を上手く使える」「Unityを上手く使える」という技能面だけで、それなりのステータスを得られますが、将来は、ゲームエンジンを上手に使えることよりも、「どんなテイストのゲームが作れるか」「どんな触り心地のゲームが作れるか」といった「クリエイティブ力」が、今まで以上に、ゲーム開発者の価値となる時代が来るでしょう。例えば、いわゆる「絵師さん」。そのテイストや画風が人々に受け入れられ、その絵師さんの価値となるように、あなたの作ったゲームの「テイスト」や「触り心地」が人々に受け入れられ、あなたの価値となる、そういう時代になるのでしょう。

セガ・インタラクティブではUE4エンジニアを募集しています。

長いうえに説教臭い話でしたね。すみません。私も年を取ったもんです。

それはさておき。

我ら「セガ・インタラクティブ」は、アーケードゲームをはじめ、スマホ、テーマパーク、ショッピングモール、はたまたeスポーツなどなど、様々なマーケットにエンタメを投入する、セガの中でも一風変わったオモシロカンパニーであります。弊社では、UE4エンジニアがまだまだ足りません。まさに、この長い記事を最後まで読んでくださったイケてるUE4遣い(とその候補)の皆さん! 弊社にご興味を持っていただけると嬉しいです。

採用情報などはこちらに記載されています。是非ご確認ください。

採用情報|株式会社セガ・インタラクティブ - 【SEGA Interactive Co., Ltd.】

 

また、UE4インターンも「画策」しています。「開催」や「予定」と書かず「画策」と書いたのは、まだいつ開催できるかが不明だからですが、弊社のUE4エンジニアと、イケてるUE4遣い(とその候補)の皆さんとで、一緒にゲーム開発を体験できれば!と思っています。開催された折にはぜひご参加くださいませ。

弊社では、もちろんUnityタイトルも出しています。スマホに限らず、ゲームセンターにもUnityタイトルを出していますので、イケてるUnity遣い(とその候補)の皆さんも是非、セガ・インタラクティブへ! そして、おそらく最も万能な、イケてるC++遣い(とその候補)の皆さんも是非、セガ・インタラクティブへ!

私たちと一緒にオモシロ体験を作っていきませんか?

 

©SEGA

Powered by はてなブログ