デザインデータを共有しよう

セガゲームス、第1CSスタジオ テクニカルアーティストの熊です。
いつもはデザイナーが扱うDCCツール(PhotoshopやMayaなど)のスクリプトを書いたり、プログラマーとデザイナーの仲介役などやっています。
2年ほど前までは背景デザイナーとして普通にデザイン作業をモリモリやっていたんですが、ある日突然コーディングに興味が沸いて今ではPythonやJavascriptをモリモリ書いてます。
そんな経緯なので、プログラマーさんほど書けるわけでもないけど、デザイナーでもない。中途半端な立ち位置で毎日楽しくお仕事させてもらっています。

今日は今まで作った数あるスクリプトの中から一つ、デザインデータの共有ツールをご紹介します。

デザインデータの共有ツールとは?

ここでいうデザインデータというのは、簡単に言うと椅子とか車とかのアセットと呼ばれる部品(パーツ)の事です。
こういうパーツをデザイナーの方がコツコツと作り、部屋や街のステージに配置していきます。
このパーツをデザイナーみんなで簡単に共有出来たら楽だなぁ~と思って、このツールを作りました。


そもそもなぜ必要?

大規模なプロジェクトは、外部開発会社さんも含めるとかなりの人数です。
この人数で一日に作られるデザインデータの数は相当なもので、
誰が何を作ったかを把握する事はもう、ほとんど不可能です。

そうなると、他のデザイナーが並行して制作したパーツや、すでに作ってあるパーツを探せず、同じようなものを個々で作ってしまって、無駄に時間使っちゃった。という事がよく起きるんです。
この時間のロスが多くなるとスケジュールが遅れたり、最悪、製作費の肥大化の原因になっちゃいます。

こういう無駄を防ぐ為にデザインデータの共有ツールは必須なのです。


共有ツール『K_Parts_Studio』紹介!

私が作った共有ツール『K_Parts_Studio』をご紹介します。


f:id:sgtech:20170721201800j:plain:w600


このツールを制作するに当たって、いくつかコンセプトがありました。

・パーツの登録、修正、利用が簡単で直感的あること
・検索方法が豊富であること
・起動が速いこと
・絶対に間違った登録が出来ないようにすること
・とにかくシンプルであること



まずはサクッと使い方を紹介します


1.モデルを用意

f:id:sgtech:20170721202005j:plain

まずはモデルを用意します。


2.フォルダ名、ファイル名、タグ、説明の欄に記入

f:id:sgtech:20170721201932j:plain

次に項目に必要な情報を記入します。
これで登録に必要な作業は終わりました!


3.登録!

f:id:sgtech:20170721201915j:plain

登録すると、全自動で登録作業が行われます。


4.登録されたパーツを確認

f:id:sgtech:20170721201846j:plain

ちゃんとスクリーンショットが撮られて登録されていれば成功です。


5.スクリーンショット画像をクリックしてシーンにインポート

f:id:sgtech:20170721201846j:plain

シーンにインポートします。
リファレンスとかで持ってくる事が出来ます。


6.あとは自由に配置

f:id:sgtech:20170721201829j:plain

一連の流れはこんな感じです。
登録から利用までスムーズに行う事が出来ました!



パーツの登録、修正、利用が簡単で直感的あること

特に登録の部分が非常に大事です。
デザイナーがめんどくさいと感じた瞬間に登録がされなくなります。
登録がされないと利用されなくなり、すぐにツールがゴミ箱行きになっちゃいます。

まずは登録が簡単であること。
これは絶対に外せません。

これを実現する為にさまざまな情報を自動取得させて、デザイナーが登録時に必要な作業は

・フォルダ名
・ファイル名
・タグ付け
・説明

のみとなりました。
このくらいであればそんなに負担になりませんね。

f:id:sgtech:20170721203202j:plain



検索方法が豊富であること

検索がちゃんと実装されていないと、これもまた誰も使わなくなります。
ツールを作る時、自分でめんどくさいと感じた事はみんな同じように感じています。
まずは、自分がこれ簡単で便利~♪と思えるレベルまで持っていく事が大事だと思います。
検索機能にしても、こういう検索出来ないのかなぁ?と思ったら
どんなに大変でも実装するべきです
今回は登録したすべてのキーワードでの検索とタグ検索(複数可)を実装しました。

f:id:masashi_kuma:20170718204337j:plain



起動が速いこと

起動の速さもまた大事です。
レスポンスの良いツールはストレスが少なく、利用されやすくなります。
非常に大事ですね。



絶対に間違った登録が出来ないようにすること

間違ったデータを登録しようとするとエラーになり、修正方法をメッセージで誘導します。
何が間違ったのか? 何を修正すればいいのか?
これを明示的に指示する事で大きなミスは格段に減ります。
例外に対するケアも非常に大事な要素です。



とにかくシンプルであること

こういう統合ツールは非常に機能が多くなり、煩雑になりがちです。
ですが、手軽に使ってもらうにはシンプルである事が最重要となります。
表向きシンプルでありながらも、内部は複雑な関数の塊という形が理想ですね。
ボタンの数や配置に気を遣う必要があります。
ユーザビリティを大事に。

(とは言っても、やはりボタン数は多くなりがちです・・・)

f:id:sgtech:20170721203138j:plain

モード切り替えの際など、今の状態を一目で分かるようにUIカラーを変更しています。



使う人の気持ちを考える

このツールを作った時、一番に考えたのはインターフェースの見やすさや使いやすさでした。
デザイナーの作業というのは非常に複雑で、日々さまざまなツールの機能を利用して一つの絵を完成させていきます。
単に作業しているだけでも大変なのに、そこにさらに複雑なルールや膨大な数のボタン群を見せられても、ウンザリするだけです。
テクニカルアーティストというのはデザイナーとプログラマーの気持ちを理解出来る唯一の存在です。
いかに複雑なシステムをデザイナーが扱いやすい構成に落とし込めるかが課題になります。

たくさんの機能を搭載するかではなく、どれだけシンプルな構成に出来るかを重要視する事が大事です。



コミュニケーションは大事

ここまで書くと、ずっとPCに向かって黙々とコーディングしているように思えますが、
実はけっこうな頻度で仕様についての話し合いや情報交換をしています。
丸一日ずっと仕様決めの為に色々な人と話す事もあります。
誰が何の作業で困っているのか?昨日までの仕様は変わっていないのか?というのをしっかりと把握していないと、
頼まれたツールが完成した頃にはもう、必要なくなっていた・・・。
なんて悲しい事になってしまいます。

テクニカルアーティストって、コミュニケーション能力も大事なんです♪



まとめ

というわけで、一通りツールの説明からツールを作る時に心掛けている所などを書かせていただきました。
書きながら今までの仕事を振り返っていたんですが、やはり、周りのデザイナーの皆さんの協力があったからこそ、いくつものツールが完成してきたんだと感じました。

テクニカルアーティストの仕事は多岐に渡りますが、デザイナーとプログラマーの間を行き来出来るちょっと面白い立場です。
デザインをロジカルに考えることが出来る方や、毎日新しい課題に取り組むのが好きな方はきっと、楽しめると思います。

こんな仕事してみたい!と思っていただける方がいましたら、ぜひ一緒に働きましょう!


ご興味を持たれましたら、弊社グループ採用サイトをご確認ください
採用情報|株式会社セガゲームス -【SEGA Games Co., Ltd.】

WebGLでレイトレしてゲームを作ってみた

すべての色をつなげよう WHOLE MATCH PUZZLE

操作方法

 マウスのドラッグ(タッチパネルの場合はスライド)で玉を一列横か縦にスライドさせることが出来ます。

ルール

 同じ色同士ですべての玉をつなげると一面クリアです。そのとき100点が加算されます。

20170620164101
ひとつの解答例。赤は赤同士、青は青同士、上下左右どこかでつながっていればよい。

 クリアするたびに色が増えていき最大で5色まで難易度が上がります。
 パズルの完成度(パーセント)がスコアの下2桁になります。
 2分の制限時間内で可能な限り玉を消して高得点を狙ってください。
 目指せ1000点!

※最新のfirefoxとchromeで動作を確認しています。
 また、一部のスマホでも動作を確認しています。

結果をツイート
タイトルに戻る

はじめに

 ドーモ、セガゲームスは開発技術部所属の栃木と申します。
 業務ではライブラリの制作や絵周りのことでコードを書いていることが多いです。
 今回は、タイトルの通り、WebGLでレイトレをしてゲームを作ってみました。

目次

  • WebGLとは
  • レイトレとは(CG初心者対象)
  • ゲームを作ってみた(ゲームを作ってみたい人対象)

WebGLとは

20170620164051 ブラウザ上で動作するOpenGLのことです。このブログを読むような方は見聞きしたことがあるのではないでしょうか。
 今回はWebGLをつかうことでレイトレをGPUで高速に動作させています。 

レイトレとは

 Ray Tracing(レイトレーシング)の略です。レイトレーシングとはレイ(光線)を投げて当たったところの色を取ることでCGをレンダリングする手法です。
 レイトレはひたすらレイを投げて当たり判定を取るだけでそれっぽい絵が出来るので、CG入門としてはおすすめな手法です。透視変換行列を必要としませんし、シャドウマップも必要もありませんし、環境マップも必要ありません。すべてレイを投げることで解決します。
 ただどういうふうにレイを投げるかで出来上がってくる絵が変わってきます。
 「CGを学びたい」けど「DirectXやOpenGLがよくわからない」、けど「Unityまでいくといじれる所が少ない(どこをいじっていいかわからない)」と感じる人は、いっそそういったグラフィクスライブラリやゲームエンジンを使用せずに、CPUでレイトレーシングを書くと幸せになれるかもしれません。
 レイトレースの第一歩はライティングされていない単色の球を1個書いてみるところからです。
 まず目とスクリーンと球を規定します。球はスクリーンに収まるように配置しましょう。スクリーンはあらかじめ適当な色でクリアーしておきましょう。
 そして、目からスクリーンのピクセルの位置に対してレイ(直線)を投げます。あとはその直線と球の当たり判定をとって、当たっていたら球の色を描きます。これをすべてのピクセルで繰り返します。レイトレースの完成です。あとはこれをいろいろと拡張していくだけでいいのです。

20170620164057
目からスクリーンにレイを飛ばしまくる。

 ね?簡単でしょ?

 以下のサンプルコードは、レイトレースをLambertのライティングまで拡張したものになります。ボタンを押すと結果が描画されます。(このサンプルコードではCPUのみでレイトレースしています。)

function raytrace() 
{
    // 各種便利関数
    function setVec3( v, x, y, z )
    {
        v[0] = x; v[1] = y; v[2] = z;
    }
    function minus( a, b )
    {
        var ret = new Array(3);
        ret[0] = a[0] - b[0];
        ret[1] = a[1] - b[1];
        ret[2] = a[2] - b[2];
        return ret;
    }
    function dot( a, b )
    {
        return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
    }
    function mad( a, m, b )
    {
        var ret = new Array(3);
        ret[0] = a[0] * m + b[0];
        ret[1] = a[1] * m + b[1];
        ret[2] = a[2] * m + b[2];
        return ret;
    }
    function length( v )
    {
        return Math.sqrt( dot(v,v) );
    }
    function normalize( v )
    {
        var len = length( v );
        v[0] /= len;
        v[1] /= len;
        v[2] /= len;
    }

    // キャンバス取得
    var elem = document.getElementById("raytracesample");
    var canvas = elem.getContext( "2d" );
    
    // クリア
    canvas.fillStyle = "rgb(64,160,255)";
    canvas.fillRect(0,0,256,256);

    // 目の場所は( 0, 0, 128 )
    var eye = new Array(3);
    setVec3( eye, 0, 0, 128 );
    
    // 球の場所は( 0, 0, -64 )、大きさは32
    var sphere = new Array(3);
    setVec3( sphere, 0, 0, -64 );
    var spheresize = 32;

    // 平行光
    var dlight = new Array(3);
    setVec3( dlight, 1, 2, 2 );
    normalize( dlight );
 
    // ピクセル数レイを飛ばす
    var ray = new Array(3);
    var screen = new Array(3);
    for( var y = 0; y < 256; ++y )
    {
        for( var x = 0; x < 256; ++x )
        {
            // スクリーンは(-31.5,-31.5,0)-(+31.5,+31.5,0)のXY平面
            setVec3( screen, x*0.25 - 31.5, 31.5 - y*0.25, 0 ); // ピクセルに対するスクリーン位置

            // 目からスクリーンへのベクトルをレイとする
            ray = minus( screen, eye ); 
            normalize( ray ); // 正規化

            // レイと球の当たり判定
            var v = minus( sphere, eye );
            var d = dot( v, ray );
            if( d > 0.0 )
            {
                var p = mad( ray, d, eye );
                v = minus( sphere, p );
                var len = length( v );
                if( len < spheresize ) 
                {
                    // 球に当たったので各種情報を求める
                    len = len / spheresize;
                    d = Math.sqrt(-len*len + 1.0*1.0) * spheresize;
                    // 衝突座標
                    p = mad( ray ,-d, p ); 
                    // 法線
                    var nrm = minus( p, sphere ); 
                    normalize( nrm );
                    
                    // ライティング
                    d = Math.max( dot( nrm, dlight ), 0 );
                    
                    // 結果を描画
                    var col = Math.floor(255*d);
                    canvas.fillStyle = "rgb("+col+","+col+","+col+")";
                    canvas.fillRect( x, y, 1, 1 ); // 点を打つ機能がないのでfillRectで代用
                }
            }
        }
    }
}


 この程度の短いコードから始められるのがレイトレのいいところです。
 ちなみに、今回のゲームのレイトレでは、球16個、無限平面5面、魚眼、直接光(LambertとGGX、および直接光への遮蔽(影))、鏡面反射、自己発光、まで拡張したものをGPUで動作するようシェーダーで実装しています。
20170620164046興味がある方は、このページを右クリックしてソースを表示でコードを確認できますので覗いてみてください。
 頂点入力から頂点シェーダー(とフラグメントシェーダーの先頭)でレイを作成し、フラグメントシェーダーで当たり判定をとりシェーディングをしています。 

ゲームを作ってみた

 今回のゲームを作っていった過程を交えてゲームの作り方を紹介していこうと思います。
 一介のプログラマーの作り方の紹介なので、こういう作り方の人もいるんだなぐらいの温度感で読んでください。
 同人ソフトやGameJamなどでのゲームの作り方の参考の一つにでもなれば幸いと考えています。

手段の選択

 なんらかのソフトウェアを作れる手段が必要です。
 それは”プログラムを書ける”でもいいですし、”Unityを使える”でもかまいませんし、”RPGツクールがさわれる”でもかまいません。
 それが出来るのがあなたである必要もありませんが、他人に無理強いはやめましょう。
 いっそ、ゲームが作りたいという理由から、これらを学ぶというのもよいと思います。ゲームを作りたいという動機は、学ぶ原動力として大きく働くと思います。

 今回は、ブログに直接プレイできる形で載せたかったため、HTML5+Javascriptを選択しました。

ルールを考える

 とにもかくにも、ルールを考えなければ何も出来ません。
 「何を作りたいのか?」「何が出来るのか?」「ジャンルは何にするのか?」
 今回は私一人で作ったため、悩むことは少なかったですが、時間や予算、自分の技術力、チームで作る場合はチームの力量を踏まえて何がどこまで作れるかをあらかじめ想像しておかなければいけません。
 たとえば、無限のお金と時間があればだれでもAAA級タイトルを目指せるわけですが、現実に無限な資源など存在しないわけです。

20170620164041今回は技術ブログに掲載するということで、技術的にレイトレースを使うということは決めていました。
 そして、レイトレースの鏡面反射を正確に表現できるという特徴を利用するために、パズルゲームのルールを考えたのですが……。 

プロトタイプ的なものを作る

 当然といえば当然なのですが、最初に考えたルールが必ずしも面白いとは限りません。
 なので、ゲーム開発初期段階でなんらかのプロトタイプを作ってゲームが面白いかどうか、面白く出来るかどうかをおおよそつかむ必要があります。
 最近ですとゼルダの伝説BotWが初期に2Dプロトタイプを作って新しいゼルダの面白さを提示したというエピソードが話題になりましたが、あそこまでしっかりしたことをするかどうかは別として、なんらかのプロトタイプ的なものを作ることは重要です。

 今回のゲームの場合も、非常に荒削りな状態でゲームのルールだけ確認できるものを最初に用意しました。
 で、最初に思いついたルールを組み込んだプロトタイプをプレイしてみたところ……面白くないことがわかりました。
 最初に考えたパズルゲームのルールはこうでした。

4x4の玉を4面鏡に囲まれた箱の中に配置する。鏡の中も含めて同じ色を8個以上つなげたら消える。合わせ鏡で無限につながったら大きな得点を得られる。連続で消すとチェインボーナス得点を得られる。

 今回公開したものとは似ても似つかないルールです。
 脳内テストプレイでは「お、面白くなりそう。」と思っていたわけですが、実際に実装してプレイしてみたら、これがうーん、つまらない。想定どおり消えるし、得点も得られるけど、つまらない。
 いろいろと原因は考えられましたが、単純にパズルとして考えることが少なすぎるのがつまらなさの最大の原因でした。
 このルールでも解空間を広げることで面白さが出せるのではないかとも考えたのですが、レイトレースで実装することを考えるとオブジェクト数が増えることは処理負荷が増えることに直結しますし、また構想段階からできればスマホで動かしたいという思惑もあり、解空間を増やしての実験は行わずに、根本的にルールを見直すことを考えました。
 まぁ詳しくは省きますが、なんやかんやあって、ある程度面白さを感じられる今のルールに落ち着いたのです。

20170620164220このように、プロトタイプを作ることでつまらないゲームになることを事前にある程度防ぐことが出来るわけです。いうなれば、料理で言うところの”味見”みたいなものですね。
 ここで美味しくなるまで企画をちゃんと練るのが重要だと思います。 

作りこむ

 だいたいの面白さは確保できたら、作りこみフェーズに移行します。

  • グラフィック・BGM・テキストなどの必要なリソースを用意する。
  • タイトルなどのゲーム以外のシーケンスを用意する。
  • ゲームバランスを調整する。
  • エフェクトの追加など見た目を良くする。
  • 逐次、バグがあれば除去する。

 プロトタイプで出来た骨子に肉付けしていくイメージです。
 これを完成するまで続けます。
 ゲームのボリュームに比例して大変な作業となります。
 実際のプロジェクトではここからが作業の本番といえるでしょう。特にグラフィックやBGM、テキストなどのリソースを用意するのは物量を必要とするため一番大変です。
 最初に夢見がちな企画を考えてしまうと、ここであえなくオダブツとなってしまうため、実力と時間にみあった計画をしましょう。

20170620164215今回のゲームは1人で短期間で作るためにゲームの仕様を最小限に止めた為、ここではリソース作成の作業は発生しませんでした。(させませんでした。)
 せいぜい、タイトルやタイムアップ画面などの追加、ハイスコアの保存やゲームバランスの調整、ささやかな演出の追加など、コードの追加実装のみで作業は完結してます。 

完成

 ひと通りできたら完成です。
 完成したら公開することをお勧めします。身内に見せるだけでもいいですが、ネット上に公開したほうがより多くの人の目に付くので良いと思います。いろんな人にプレイしてもらえるだけでもモチベーションがあがります。
 なにより、人にプレイしてもらってこそのゲームなのです。
 同人ゲームだったらコミケに出すのもいいかもしれません。
 また、就職活動でゲーム会社を受けたときに、面接時に「こういうゲームを作りました」というアピールにも使えるでしょう。

20170620164210今作はブログにて公開と相成りました。皆さまプレイはしていただけたでしょうか?得点はいくらほど取れたでしょうか?面白い、楽しいと少しでも感じていただけたでしょうか?得点や感想などをブクマやツイートをしていただけると幸いにございます。 

最後に

 今回は技術ブログでゲームを公開するというチャレンジをしてみたわけですが、技術としては特段変わったことはしていないので、あまり書くことがなかったというのが執筆していてつらかったところです。
 あまり堅苦しいブログになってほしくない、ライトな層にも見ていただきたいという思いからゲームを公開してみようと思い立ったのですが、皆様のこころに少しでもささってくれればうれしいなと思います。

 また、セガでゲーム開発やその周りに携わりたい、もしくは興味があるという方がいらっしゃいましたら、下記の弊社グループ採用サイトをご確認ください。
 いっしょに働きましょう!

採用情報 | セガ企業情報サイト

GameJamで覚えるタスクボード(カンバン)

セガ・インタラクティブ 第三研究開発部 プログラマの石畑と申します。よろしくお願い致します。
今までアーケードゲームのタイトル開発(電脳戦機バーチャロン・シリーズ、デカスリート、パワースマッシュ3、WORLD CLUB Champion Football(WCCF)、CODE OF JOKER(COJ) )を中心に仕事をしております。
最近はマネージャとしての仕事をしつつ、社内アジャイル開発コミュニティの運営も行っております。


今回は、業務の改善に繋げるための見える化を行う手法「タスクボード」についてお話ししてみましょう。


内容は以下の6つになります。

タスクボードは、業務の見える化の代表的な手法として簡単に導入できるのですが、プロジェクトで使おうとすると、そのタスク量の多さから踏み切れないケースも多いです。
そこでGameJamでタスクボードを試しながら、実際のプロジェクト「WCCF」「CODE OF JOKER」に活用していき、その中でも改善を続けていった事例をご紹介します。

見よう見まねでプロジェクトでタスクボードを使い始めたのですが最初はうまくいかず、最初のGameJamでもうまくいかなかったものの、GameJamの回数をこなす毎に洗練され、プロジェクトへの導入、改良を進めることができるようになりました。


実際の時系列では、GameJam → プロジェクト → GameJam → プロジェクト と、交互に行っているのですが、まとまりに配慮してGame Jam、実プロジェクトという順に今回は解説していきます。


それでは1つずつみていきましょう。

タスクボード(カンバン)は、仕事を見える化するツール

皆さんは自分たちの周囲の改善をどのように進めているでしょうか?
改善をするためには、まず問題を明らかにしなければ改善する所自体が見つかりません。


では問題を明らかにする方法にはどんなものがあるでしょうか?
自分達でいろいろ考えても、なかなか何が問題なのかわからないことも多いでしょう。
問題を発見するのに役立つのは、問題だと考えている周辺を「見える化」することです。


見える化」の手法は様々ありますが、仕事の進め方で問題を感じていたならば、タスクボードを使うことで見える化ができます。


タスクボードを使うメリットには、以下のようなものがあります。

  • 残作業が一目瞭然になる
  • 優先順位通りに進められるようになる
  • 誰が何をしているのか見えるようになる
  • 作業の流れが見えるようになる


このように業務が見える化されていけば、問題がどこにあるのか発見することができ、そこから改善を始めることができます。
タスクボードの運営で気をつけることは、業務の見える化により問題は見えるようになりますが、タスクボードは問題の解決方法は示してくれません
見つかった問題を解決する方法を考えるのは、チーム自身です。問題は見える化されて共有されているので、皆で話し合いながら改善していきましょう。


それではタスクボードの使い方の紹介です。

タスクボード(カンバン)の使い方

まずタスクボードとはどんなものかを簡単に説明します。


ここで扱うタスクボードとは、実現したいものを機能そしてタスクに分解し、そのタスクがどのような状態にあるかを「見える化」するツールです。
カンバン、カンバンボード、タスクかんばん、アジャイルかんばんなど、いくつも名称があります。
トヨタ生産方式でのカンバン、システムとしてのカンバン、目的によって使い方も変わるのですが、ここでは「見える化」としてのツールとして使うことについて扱います。


使い方は単純です。
例えばあるゲームを作るとします。
使い方の流れは以下になります。


1. ゲームを作るのに必要な要素、機能をリストアップします。
2. 要素、機能を実現するために必要な作業をタスクとしてリストアップします。

3. ホワイトボードや、模造紙、イーゼルパッドなどに「ToDo」「Doing(In Progress)」「Done」の3つの列を用意します。
 この時に「Done(作業完了)」の定義をしておきます。レビューができる状態になったら、バグがあっても「Done」とする等です。

【付箋の例】
f:id:sgtech:20170522102355j:plain:w300

4. リストアップしたタスクを付箋紙に書いて、「ToDo」の列に貼り付けます。優先順位が高いタスクを上に配置しましょう
f:id:sgtech:20170522102347j:plain:w200

5. 付箋に書かれたタスクに手を付け始めたら、付箋に自分の名前を書いて「Doing」の項目に移動します。
f:id:sgtech:20170522102339j:plain:w300

6. 作業が完了したら(3.で決めた状態)、付箋を「Done」に移動します。

7. 全部「Done」になっていたら、ゲームは完成です。
f:id:sgtech:20170522102330j:plain:w300


上の4,5,7の写真は、2016年7月開催「第九回SEGA Game Jam」で使ったタスクボードです。

タスクボードの作成は作業開始前にやらないといけないものではありません。
プロジェクトの中盤でも終盤でも、また一部のパートだけでも、いつでもどこでもどんな状況でも導入してください。


ゲームを作る際に、皆さんがいつもやっている作業なので難しいことはないはずなのですが、実行しようとするとなかなか難しいものです。
例えば、プリプロ作成の場合だと、(1)の段階でリストアップしきれなかったり、大規模プロジェクトでは、(2)でタスクを全てリストアップすることがあまりに多くて難しいなどです。


私も実際にいきなりプロジェクトにタスクボードを導入できたわけではありません。
タスクの粒度がバラバラだったり、更新頻度が不定期になったりしてうまく活用できていませんでした。
ワークショップや小さいプロジェクトで練習ができないものかと考えて、活用したのがGameJamです。


GameJamとは、短期間で即席チームによりゲームを制作するイベントです。
短期間ですので、結果がすぐにわかりますし、失敗してもダイジョーブ!な安全なイベントなので、実験には最適です。


以下では、私が実際にGameJamでのタスクボードを活用した事例を紹介します。

GameJamでのタスクボード運用例

初回:Global Game Jam 2012
f:id:sgtech:20170522102324j:plain
やってみたこと

  • ToDo 機能リスト作成

最初のGameJamでは、ゲームに必要な機能を付箋にリストアップしていきました。
ただし、粒度がそろわず、タスクに分解できていない項目が多く残りました。

結果としてToDoリストとして使うことはできましたが、作業が未着手なのか、完了したのか共有されておらず、運用している状態ではありませんでした。
最初のタスクボードはここからスタートでした。
振り返り
振り返りでは、タスクに分解する粒度をそろえること、進捗管理の方法が改善点としてあがりました。


2回目:福島GameJam 2012
f:id:sgtech:20170522102524j:plain
f:id:sgtech:20170522102518j:plain:w200f:id:sgtech:20170522102513j:plain:w200f:id:sgtech:20170522102507j:plain:w200
やってみたこと

  • ToDo 機能リストからタスクへ分解
  • Doneの定義(コミットしたらDone)

2回目では、実装する機能をタスクまで分解することでタスクの粒度の問題が改善し、そして完了Doneを意識して付箋をToDo→Doing→Doneと管理して、タスクボードの運用を開始することに成功しました。
ただし、忙しさのあまり終盤で進捗確認の頻度が低くなり、リリース直前に何が入っていて、何が入っていないかわからなくなり、うまく運用したとはいえなくなりました。
振り返り
振り返りでは、どうやって最後まで定期的な進捗管理として機能していくかが課題となりました。


3回目:Global Game Jam 2013
f:id:sgtech:20170522102502j:plain

【マイルストーン】
f:id:sgtech:20170522102633j:plain
やってみたこと

  • マイルストーン設定
  • マイルストーン毎にレビュー会開催

3回目では、マイルストーンを設定し、マイルストーンのタイミングでレビュー会を行い、ここで進捗確認と、次のマイルストーンまでに何を行うかを明確にするように組み合わせてタスクボードを運用することで、定期的に進捗管理のタイミングが生まれ、時間を意識したタスク管理ができるように改善されました。


ここで始めてタスクボードによる運営が成功したといえるでしょう。


4回目以降:Global Game Jam 2017
f:id:sgtech:20170522102626j:plainf:id:sgtech:20170522102619j:plain

【レビュー会】
f:id:sgtech:20170522102610j:plain

やってみたこと

  • 職種別に付箋の色分け
  • 付箋に担当者を記入
  • 付箋にいつまでに終わらせるか期限を記入

4回目以降では、タスクの分解、職種別に色分け、担当者の記入、レビュー会を意識したタスクの割り振りにより、小さな成功を積み重ね、モチベーション維持しながら毎回最後まで運用できるようになっています。


結果として、タスクボードを運用できるようになるまでに3回かかっており、これを実際のプロジェクトで試そうとすると、チャンスを与えてもらえないと何年も改善しないまま進むことになります。
GameJamは短期間でプロジェクトが終わるので、最高に安全な場であり、今回の例のようなタスクボードだけでなく他にもチーム運営、技術などの導入、改善に最適です。
ぜひお試しください。

プロジェクト(WCCF)でのタスクボード運用例

GameJamで練習したら次は、プロジェクトでの活用です。
実際にプロジェクトでの運用をどのように進めていったかの事例を以下に紹介します。

WCCF2010-2011 Ver.2.0
f:id:sgtech:20170522102604j:plain
やってみたこと

  • ネットワークパート内で、1人でテスト
  • ToDo は、タスクではなく実装する機能

プロジェクトでの最初は、ネットワークパート内でタスクの一部を管理するスモールスタートでの導入開始にしました。
ほぼ個人での活用だったため、他の人にはほとんど意識されず、改善にはつながらないタスクボードとなりました。
それでも初回のGameJamの経験があったので、運用の形にはなりました。
振り返り
振り返りとしては、多少なりとも周りを巻き込まないと、見える化の浸透のしようもないことが明確になっています。


WCCF2011-2012
f:id:sgtech:20170522103008j:plain:w200f:id:sgtech:20170522103003j:plain:w200f:id:sgtech:20170522102957j:plain:w200
f:id:sgtech:20170522102951j:plain
やってみたこと

  • エンジニア全体でタスクボード運用の対象にする
  • ToDo は、機能から分解したタスクにまで分解する
  • タスクには、工数を入力する
  • 工数を合計して、バーンダウンチャートを作成

自分が直接関わるエンジニアには、タスクボード運用に参加してもらうように協力をとりつけ、見える化の範囲を広げることができています。
2回目のGameJam経験から、最初から欲しい機能を担当者が作業可能になるところにまでタスクを分解することで、粒度が統一され、タスクボードの運用として改善しております。
この時点で付箋には「工数」が入力されています。
これによりバーンダウンチャートが導入可能になっており、進捗管理に関しては大きく改善することができました。

バーンダウンチャートとは、縦軸に残作業工数、横軸に時間を設定したグラフです。タスクがDoneになったら、その工数の数字分残作業量から減らたところにプロットしていくと、グラフの傾きからマイルストーンまでに作業が消化できるかどうかが明確にすることができます。

ただし、実装内容などの見直すポイントはわかるようになったものの、見直す際の優先順位が明確でなく、結局作業がある程度進んだ後で優先順位を変えることになってしまいました。
振り返り
振り返りとしては、優先順位の再確認の時期をどう設定するか、という改善点が残ることになりました。


WCCF2011-2012 Ver.2.0
f:id:sgtech:20170522102944j:plain:w200f:id:sgtech:20170522103111j:plain:w200f:id:sgtech:20170522103106j:plain:w200
f:id:sgtech:20170522103100j:plain
やってみたこと

  • タスクボードの運用を、プランナー、デザイナーにも広げる
  • 優先順位の確認を2ヶ月に1回実施する

3回目では、バーンダウンチャートと合わせてタスクボードを運用すること、タスクボードをプランナー、デザイナーの分も書き出すことで、プロジェクト運営として全体をコントロールできるように改善しました。
これは3回目のGameJamでの経験から、チーム全体で活用しないと、プロジェクト全体をみていることにならないと気付いたため、全体に適用しています。
優先順位も定期的に確認することで改善しています。
ただし、タスクボードの適用範囲をチーム全体に拡大したため、管理するタスク量がかなり多くなることで目が行き届かなくなり、手戻りが発生するという問題が発生しました。また工数だし時には明確になっていない要素が、作業を始めると予想以上に多く工数が足りなくなり、そのために見送った機能が出ています。
振り返り
振り返りとしては、安定して開発ができるようになったものの、お客様の満足度が向上していない点が改善したいこととしてあげられています。


WCCF2012-2013
f:id:sgtech:20170522103055j:plain
f:id:sgtech:20170522103049j:plain

【レビュー会の様子】
f:id:sgtech:20170522103146j:plain

やってみたこと

  • 工数出しに、プランナー、デザイナーに参加してもらう
  • レビュー会を月に1回実施する

4回目では、工数出しにプランナーにも参加してもらうことして、タスクもれ減少、工数の精度の改善を行っています。
定期的なレビュー会と組み合わせることで、手戻りを減らし、質を高めるように改善することができました。


安定したタスクボードの運営には4回必要としました。
それぞれタスクボードを運用することで見えた問題点について、一歩ずつ解決し続けています。


時系列では、GameJam → プロジェクト → GameJam → プロジェクト と、交互に行っていくことで、タスクボード運用改善のスピードを早くすることにつなげています。
これはWCCFが半年単位で新しいバージョンをリリースしていたので、改善のチャンスが多かったことはタイミング良く導入できたことに繋がったと考えています。

他のプロジェクトでのタスクボードの運用について

WCCFの例が完成形ではありません。プロジェクトが変われば、運用方法もプロジェクト毎に変わります。
例えばCOJでは、今までよりもさらに短期リリースが必要とされたため以下の運用に変更しました。

  • 工数出しにプランナー、グラフィックデザイナーも参加して、よりタスク量、工数の精度を上げる
  • 優先順位の変更の指標にするために、機能毎にToDoを分けて、機能単位の合計工数を書いておく(オレンジと緑)

f:id:sgtech:20170522103143j:plainf:id:sgtech:20170522103137j:plain:w300

これにより、無駄なタスクを減らし、継続的に短期リリースができるような体制を構築しております。

他の例では、以下のような運用もあります。

  • 割り込みタスクを赤い付箋で追加する
  • バグ修正もタスクにして管理する
  • Doneの前に、「レビュー」を入れる

付箋については、Redmineから直接プリンタを使って付箋を作ったりしたこともあります。


自分達のプロジェクトに合わせて、自由に変えられるのがタスクボードの強みです。
ですので、最初はアナログな手書きで始めると、何度も簡単にやり直せるのでおすすめです。

まとめ

タスクボードをGameJamで運用して、プロジェクトに適用することで以下のことがわかりました。

  • タスクボードの使い方は簡単だが、チームに合わせて何度も変更することで安定運用になります。
  • GameJamで試してからプロジェクトに適用すると、失敗のリスクを減らすことができます。
  • タスクボードによる見える化のテスト中でも、問題点は次々に明るみになるので、見える化の効果は高い。

  ただし、タスクボードによって問題点は見えても、問題そのものの解決方法はチーム自身で皆で協力して考えて改善してください。


今回はタスクボードによる業務の見える化について話してみました。
タスクボードの使い方は難しくありませんが、実際の運用には多くの問題が発生してきます。
なぜなら問題点が見えるようになることが、タスクボードを使う目的だからです。


最初からうまくいくはずはありませんし、これが最終形でもありません。
完璧を目指さず、シンプルにはじめ、必要になったら後からいくらでも変えていきましょう。


さあ明日からタスクボード、始めてみましょう!


タスクボードを最初から最後まで一通り試してみたい、という方には短期間でゲーム開発ができて、失敗してもダイジョーブ!な環境、「GameJam」でタスクボードを試してみませんか?
セガグループでは、失敗してもダイジョーブ!な環境、「SEGA Game Jam」を開催しております。
次回、記念すべき「第十回 SEGA Game Jam」は2017年7月15日~16日開催です!


私達、第三研究開発部では、このような取り組みにも積極的な方と一緒に働きたいと考えています。
もしご興味を持たれましたら、弊社グループ採用サイトをご確認ください。

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


アーケードで稼働中の「WORLD CLUB Champion Football」もよろしくお願い致します!
www.wccf.jp

そうだ、勉強会を開こう

セガゲームス、開発技術部の竹原です。
普段はビルド & QA エンジニア(自称)として開発部署の環境の効率化のお手伝いを主にさせて頂いています。
最近は VR とキャベツダイエットにハマっています。
前回、前々回と良い意味で濃ゆい技術の話が続きましたので、私からは技術寄りの話から少し離れた形で SDC というグループ内カンファレンスの紹介をさせて頂こうと思います。
技術的な話はほとんど出てきませんので、箸休めのキャベツをつまむノリで気楽に楽しんでいってくださいね。

はじめに

今回のブログの主題はグループ内カンファレンスイベントを開催するメリットに関してとなります。
また、更にそこからもう一歩踏み込んで、グループ内カンファレンスの運営を通して自分(QA エンジニア)が得られたものについてもお話しできればと思っています。
「グループ内カンファレンスなんてやる意味あるの?」
と疑問に思っている方から、
「グループ内カンファレンス最高!もっとやれ!」
と思っている方まで、グループ内カンファレンスの価値を見直して頂くきっかけになれば幸いです。

ちなみに、今回の記事内では社内の呼び方に則ってグループ内カンファレンスという表現をしていますが、この辺りは会社によって変わると思いますので、適宜「社内勉強会」等に読み替えて頂ければと思います*1

SDC とは?

本題へ入る前に、まずは SDC とは何ぞや、という説明をさせて頂きます。
SDC とは「SEGA Developers Conference」の略称で、セガグループ内のコンテンツに関わる全ての人を対象に、開発、運営等多くのグループ内技術情報をセガサミーグループ内で共有するために開催されているグループ内カンファレンスイベントです。
社内勉強会のでっかい版、もしくはプチ CEDEC みたいなものをイメージして貰えれば大体 OK です。

開催時期は毎年 3 月後半で、現在までの開催数は 2016, 2017 年の計 2 回とまだまだ若いイベントです。
基本的には CEDEC ような一般に行われるカンファレンスイベントと同様の形態なのですが、一番の特徴としてはグループ内の開催ならではの講演内容を重視している事が挙げられます。
ここで言うグループ内の開催ならではとは「赤裸々な失敗事例」や「内製ツールやサービスの紹介」、「未公開の技術デモの展示」等々、グループ外では公開できないような内容のものを指しています。
これは、

  • 折角開催するならグループ内カンファレンスでないと聞く事が出来ない内容を共有した方が有意義である
  • グループ外に出せるような情報であれば外で講演を行って業界全体に情報共有を行って欲しい

という SDC 運営の意向を反映した方針です。

さて、これだけでは SDC の実態のイメージが付かないと思いますので、ここからはもう少し具体的に SDC の内容や規模感について紹介させて頂こうと思います。

講演の種類

講演の種類は以下のように分かれています。

  • 通常セッション 50 分
  • ショートセッション 25 分
  • ラウンドテーブル 110 分
  • 展示 110 分

ラウンドテーブルは 2016 年の開催時は 50 分での開催だったのですが、「話し足りない!もっと時間が欲しい!!」という要望を多く頂いたので、 2017 年では 110 分に変更してがっつり話し合って貰いました。
ただ、今度は逆に「長過ぎて参加し辛い(聞きたいセッションと被ってしまう)」という意見も頂いたので、この辺のバランシングの難しさを痛感しています。
その他では 2017 年のセッションの中に「通常セッション枠を使ってライトニングトーク 5 連発!」というような面白い試みもありました。

セッション数

セッション総数は

  • 2016 年総数 : 28
    • 通常 : 10
    • ショート : 6
    • ラウンドテーブル : 9
    • 展示 : 1
  • 2017 年総数 : 26
    • 通常 : 11
    • ショート : 4
    • ライトニングトーク : 1
    • ラウンドテーブル : 8
    • 展示 : 2

とった感じで大体 20 台後半くらいとなります。
これを朝 10 時から夕方 18 時までみっちりと詰め込んで開催しています。
セッションの採択率は大体 50% 前後とかなり激戦で、期末の忙しい時期の開催にも関わらず積極的な応募を頂いています*2
こうしたグループ内の社員のモチベーションの高さは同じ会社で働いている身としては嬉しい限りです。

参加人数
  • 2016 年
    • のべ : 1184 人
    • ユニーク : 340 人
  • 2017 年
    • のべ : 1281 人*3
    • ユニーク : 401 人

と数多くの方に参加頂いています。
開発以外の方が試しに開発の話を聞きに来てみたり、自社ビル内での開催なので業務の合間に 1 セッションだけ聞いて仕事に戻る人が居たりと一般のカンファレンスと違いフレキシブルな参加も行えるのはグループ内カンファレンスならではの良い所だと思います
また、参加者の職業別比率は以下の通りです。

  • プログラマー : 55%
  • デザイナー : 17%
  • プランナー : 7%
  • その他(TA 等) : 21%

割合的にはプログラマの参加が多めなので、他の職種の方にももっと参加頂けるように工夫をしていきたい所です。

参加会社

セガサミーグループ内の各社から参加頂いています。

  • アトラス
  • サミー
  • サミーネットワークス
  • セガインタラクティブ
  • セガゲームス
  • セガサミークリエイション
  • セガサミーホールディングス
  • セガトイズ
  • セガホールディングス
  • セガライブクリエイション
  • ダーツライブ
  • マーザ・アニメーションプラネット

セガサミーグループは関連会社の数が非常に多く、そうした中でシナジーを高めて開発効率を高めていくにはこういったカンファレンスは必要不可欠だと感じています。

セッションの一例の紹介

続いて SDC で講演された各資料を公開します!
一般のカンファレンスでは見られないあんな話やこんな話が盛り沢山!
……と言いたい所だったのですが、前述した通り SDC の講演はグループ内の開催ならではのものがほとんどなので、ここでお見せできるものがほとんどありません。
セッションではありませんが、「Git 運用を考える」というラウンドテーブルの概要であれば公開しても大丈夫そうですので、これを紹介してみようと思います。
セッションやその他のラウンドテーブルにも興味がある方は是非セガゲームスに入社して SDC に参加してください。
お待ちしております!!

さてさて、「Git 運用を考える」は以前当ブログで「モダンな OpenGL で頂点モーフ」という記事を書いた山田と私でモデレーターを務めているラウンドテーブルです。
社内での Git の利用増加を踏まえて 2016,2017 と連続で開催しており、自分で言うのもおこがましいですが、社内の Git 事情や状況、運用方法を情報交換する場として非常に有益なものとなっています。

具体的にどんな議題が話されていたか話題をピックアップしてみますと、

  • 2016 年
    • git の利用状況
    • git への移行
    • クライアントソフトについて
    • サーバソフトについて
    • Git LFS について
    • トラブル事例
    • 企画、デザイナーに使ってもらうには
  • 2017 年
    • フローは git-flow? github-flow?
    • どのくらいルールを守れているか
    • Master ブランチへの追従にリベースを使うかマージを使うか
    • コンフリクトの扱い
    • 非プログラマ向けの布教について
    • Git からの卒業を考えたことがあるか
    • SVN からの移行
    • サブツリー、サブモジュール
    • MS GVFS

という感じで、 2016 年はまだ探り探り使い始めたくらいだったのが、 2017 年に掛けて本格的に利用が始まり、突っ込んだ議論を多くできるようになったことが分かります。
毎年同じテーマでラウンドテーブルを開催する事により、グループ全体の状況をお互いに把握しながら情報交換できるのはグループ内カンファレンスならではの強みと言えると思います。
こうした情報交換の熱が高まり、意見交換の頻度を高めた方が良い状況になってきたら、月 1 くらいの定例開催の勉強会等にスムーズに移行できると尚良いですよね。
実際 SDC がきっかけで開催されるようになった「自動化交流会」や「アジャイル開発コミュニティ」というような勉強会も存在しています。
次はこうした SDC 開催による効果についてお話ししようと思います。

グループ内カンファレンスの効果

グループ内カンファレンスを開催するメリットは盛り沢山です。
SDC を通じて実感している点をご紹介したいと思います。

学習の場になる

カンファレンスの効果と言えばこれをイメージする方が多いと思います。
受講者は講演を聞く事により新しい知見を得て、講演者は情報をまとめる事により復習の機会を得る事ができます。
グループ内での開催では一般のそれに比べ、講演内容に対する「うちはこうしてるよ」というようなフィードバックが包み隠さず積極的に得られる傾向が高く、より大きな効能があると感じています。

情報共有の場になる

こちらも「カンファレンスと言えばこれ!」という効果ですよね。
講演者の話を聞くだけでなく、講演者やその他の参加者と情報交換を行う目的としても、カンファレンスは非常に貴重な場となります。
更にグループ内カンファレンスの場合、

  • プロジェクト内容に深く関連する話を気兼ねく出来る
  • 特定のツールに特化したラウンドテーブルを開催できる

等々、より一層突っ込んだ形で情報交換を行う事ができるのが魅力です。

人と人を繋げる場となる

組織の規模の大小に関わらず、隣のチームや部署が何をやっているか、どういう技術を持っているかを知る機会は意外に少ないのではないでしょうか。
そんな中で、グループ内カンファレンスは隣の部署やチーム、会社が何をやっているか知り、担当者の顔を覚える事ができるのはとても貴重な機会となります。

  • 〇〇で困ったがそういえばグループ内カンファレンスで△△さんが関連したことを話していたな……ちょっと聞いてみよう
  • カンファレンスで紹介されていたあのツール便利そうだったのでうちにも使わせてくれないかな

等々、グループ内での業務協力の切っ掛けやシナジーの創造に一役買う事ができると思います。
前項目で挙げた「自動化交流会」や「アジャイル開発コミュニティ」もこうした繋がりから生まれたものです。

情報を発信していく土壌ができる

グループ内カンファレンスのようなものを継続して実施していく事により、情報共有を行う事の価値や効果が組織内に浸透していきます。
SDC 発の勉強会は元々こうした情報共有を得意とする人たちが作ったものですが、こうした活動の認知度が徐々に上がっていく事により「じゃあ自分もやってみようかな」と人が増えていき、最終的に組織全体として情報共有を積極的に行っていく雰囲気が生まれてきています。

組織全体のモチベーションが上がる

講演者、受講者の方々から SDC に参加した事によりモチベーションが上がったという感想を頂いています。
自分を省みてもそうなのですが、エンジニアにとってこうしたイベントはとても良い刺激になりますよね。
ましてそれが同じグループ内でやっている事例や技術の紹介であれば、尚更ではないかと思います。

グループ内カンファレンスの効果まとめ

上記のようなメリットは直接見えてこないものが多く、運営や講演の工数にどうしても目が行ってしまいますが、掛かる工数以上の効果があると感じています。
とは言え、数字に見えてないからこそ最低限取れる部分の KPI はきっちり計測して、有用性を示して行く事も大事だと思います。
例えば SDC ではアンケートの集計を行い、その効果を測定しています。
※アンケート集計の一例
f:id:sgtech:20170419104149p:plain

グループ内カンファレンスの運営を通じて得たもの

グループ内カンファレンスに上記のような多くのメリットがある事は色々な所で語られていると思います。
しかし、その運営の仕事からも得られるものが数多くある事はあまり知られていないように感じています。
これはとても勿体ない事だと思いますので、自分が SDC の運営を通じて得られたものを紹介してみます*4

社内の情報ハブとしての役割を強化できる

カンファレンスの運営を担当するには、その役割上一通り講演概要を把握する必要があります*5
また、講演を行うような情報を持った人に名前や顔を覚えて貰う事もできます。
このように情報の在り処やキーパーソンを把握する事で、何かあった際に

  • その情報はだれだれさんが持っていますよ
  • いついつのカンファレンスで発表されてましたよ

というような形で、社内の情報ハブとして強く機能する事ができるようになります。

カンファレンスと通じて組織全体の品質・開発効率を向上させる事ができる

グループ内カンファレンスは個々の開発者の能力の底上げをするにはもってこいの場で、最終的にはプロダクトの品質や開発効率の向上に繋がるイベントと言えます。
QA エンジニアの使命の一つはプロダクトの品質や開発効率の向上にあると思います。
つまり、グループ内カンファレンスは QA エンジニアにとっては普段の業務の延長線上にある、効果の高い仕事内容と言えるのではないかと思います。
これは運営の中で得たもの、というよりはカンファレンス自体の効果と被った内容ではあるのですが、グループ内カンファレンスを開催する事が QA の仕事に繋がる、という気付きを得る事ができたので、あえて運営を通じて得たものとして紹介させて頂きました。

開発者が必要としている情報を見つける事ができる

運営の仕事の特性上、人気のあるセッションの傾向やアンケートのコメント等の情報を知る事ができます*6
これにより、今グループ内で必要とされているもの、足りないものは何かを見つける良いヒントを得る事ができます。

自分が情報を発信する際の参考になる

QA エンジニアは資料を作成して費用対効果を示す等、ステークホルダーに情報をうまく伝えなくてはならない機会が数多くある職種です。

  • 参加者へのカンファレンス情報の公開
  • スタッフ用の説明資料の作成
  • 講演者の発表資料の確認

等のような仕事を通じて、情報の発信方法や伝え方について多くの気付きを得る事ができました。

一般のカンファレンスや勉強会へ参加する意義を見直すきっかけになる

運営の仕事をこなしていく中で、限られたコストの中でどうすればこのカンファレンスの効果が最大になるかという問題には常に頭を悩ませられました。
こうした経験を通じて、翻って自分がカンファレンスや勉強会への参加する場合はどうしたら最大限の効果を得られるだろうか、とイベントに参加する姿勢を見直す切っ掛けとなりました。

グループ内カンファレンスの運営を通じて得たものまとめ

グループ内カンファレンスの運営の仕事は(規模にもよりますが)エンジニアとしては精神的に辛い事務的な作業も多くあります。
反面、通常の業務をしていては中々得る事ができない上記のような貴重な経験を積む事ができます*7
カンファレンスイベントの運営、と聞くと抵抗感を持つ方も多いと思いますが、運営の仕事の大変さ以上に得られるものは多いので皆さんも是非グループ内カンファレンスを開催してみてください。

全体まとめ

SDC を通じ、グループ内カンファレンスの概要と効果、運営を担当する事により得られるものについて紹介させて頂きましたが如何でしたでしょうか。
こうした活動に興味がなかった方には、少しでもグループ内カンファレンスに興味を持って頂き、自分の所でも開催してみようかな……、と思って頂ければ嬉しく思います。
逆に既にグループ内カンファレンスの運営のような活動をしている方は、グループ内カンファレンスをやる意義を考え直す機会となれば幸いです。

最後になりますが、セガグループでは SDC 以外にも月例の勉強会や社内ゲームジャムの開催等、様々な取り組みを行っています。過去の SDC の講演資料や録画映像も見放題です!
こうした活動に興味と意欲がある方は是非セガグループで一緒に働きましょう!!


採用情報|株式会社セガゲームス -【SEGA Games Co., Ltd.】

採用情報|株式会社セガ・インタラクティブ



それでは次回の投稿をお楽しみに。

*1:グループのスケールメリットを活かした内容も若干ありますが、基本的にはどの規模の会社にも共通して言える事をお話しするつもりです

*2:セッション以外の応募については大体用意している枠と同じくらいの数となります

*3:2017 年の SDC では一部のグループ会社オフィス上へ、セッションのライブストリーミング配信を実施しました

*4:QA エンジニア目線の話が多めではありますが、一般のエンジニアの方にもあてはまる話も結構あるのではないかと思います

*5:審査や資料確認等

*6:集計作業が必要なのです!

*7:私も今では運営をやって良かったと強く感じています

Vulkanでシェーダリフレクション(Shader Reflection)を取得してみる

みなさん初めまして。工藤@セガゲームス開発技術部です。

社内ライブラリを開発する仕事を長年しています。これまでゲーム機のSDKやDirectX, OpenGLなどのグラフィックスAPIを使い、グラフィックスライブラリを作成してきました。最近のAPIとしてはDirectX12, Metal等がありますが、昨年にはさらにVulkanがリリースされました。VulkanはKhronosグループが策定しているマルチプラットフォーム向けグラフィックスAPIです。


情報が乏しく複雑怪奇なこのVulkanには苦戦させられています。Vulkanがリリースされてから一年が経ち、昨年秋には赤本(Vulkan Programming Guide)が発売されたり、GDC2017(Game Developers Conference 2017)での発表があるなど、やっと情報が増えてきました。みなさんはいかがでしょうか。


最近はゲームエンジンを使う機会が増え、ローレベル(低階層)のグラフィックスAPIを直接使う人の数も減っていると思います。ここでVulkanの情報を発信しても役立つ人がどれだけいるのかわからないような状況ですが、今回のブログはこのVulkanでのシェーダリフレクションの使い方について取り上げたいと思います。
※「シェーダリフレクション」とはシェーダの中にある変数の情報を取得することです。


Vulkanでシェーダを使うには一般的にシェーダ言語にGLSLを使用しSPIR-Vへ変換して使用します。SPIR-VはVulkanで導入されたシェーダの中間言語です。HLSLからSPIR-Vへの変換なども今後は対応していくようです。


GLSLの使い方はOpenGLで使用していた場合とほとんど同じですがVulkan用にキーワードが追加されています。Vulkan用に追加されたキーワードにはシェーダへユニフォームバッファやサンプラなどのリソースをバインドするために必要なsetとbindingがあります。この値は上位ライブラリを実装するときに必要になりますが、この値を取得する関数はVulkan SDKには用意されていません。

前置きが長くなりましたが、今回はVulkanで追加されたsetとbindingの値をシェーダからと取得したいと思います。


目次

VulkanでのGLSLの例を見てみよう

まずVulkanでリフレクションに触れる前に簡単なシェーダの例を見てみましょう。

#version 450 core
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout(set = 0, binding = 0) uniform buf {
        mat4 MVP;
        vec4 position[12*3];
        vec4 attr[12*3];
} ubuf;
void main() 
{
    // Do nothing!
}

このシェーダではuniform bufにset = 0とbinding = 0が設定されています。この値がVulkan SDKからユニフォームバッファへリソースをバインドするときに必要になります。set、bindingがない場合はset、bindingとも0として扱われます。Vulkan SDKへのdescriptorsetとbindingへの設定は話が長くなりますので今回は説明しません。uniform bufにはmat4のMVP変数、vec4のpositionの36個の配列、vec4のattrの36個の配列がメンバーにいます。

GLSLをロードしSPIR-Vへ変換する

ゲーム開発中で絵作りがなかなか決まらない場合、何度もシェーダを書き換える必要が出てきます。ゲーム中でGLSLをSPIR-Vへ変換できるとゲームを一旦終了することなしにシェーダを書き換えた時点でシェーダを切り替えることが可能になります。開発終盤になりシェーダが確定したら事前コンパイルしたSPIR-Vを直接ロードして使用します。GLSLをglslangを使用してSPIR-Vへ変換します。glslangはValkan SDKに入っています。

bool ShaderReflection::GLSLtoSPV(const vk::ShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv)
{
    glslang::InitializeProcess();
    glslang::TProgram &program = *new glslang::TProgram;
    const char *shaderStrings[1];
    TBuiltInResource Resources;
    init_resources(Resources);

    EShLanguage stage = FindLanguage(shader_type);
    glslang::TShader *shader = new glslang::TShader(stage);

    shaderStrings[0] = pshader;
    shader->setStrings(shaderStrings, 1);

    EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
    if (!shader->parse(&Resources, SHADER_VERSION, false, messages)) {
        delete &program;
        delete shader;
        return false;
    }

    program.addShader(shader);

    if (!program.link(messages)) {
        delete &program;
        delete shader;
        return false;
    }

    glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
    glslang::FinalizeProcess();

    delete &program;
    delete shader;
    return true;
}

glslangにリフレクションがあるけど使えないの?

先ほどGLSLからSPIR-Vへ変換にglslangを使いました。glslangを見ていますとglslang\glslang\MachineIndependentにreflection.hとreflection.cppがあります。結論から言えば変数名や型、サイズなどは取得できます。ですがVulkanで追加された今回の目的であるsetやbindingの値を取得することができません。ソース提供されているので改造すれば取得できるようになるかもしれませんし、今後対応されるかもしれません。

SPIRV-Crossを使用してリフレクションを取得する

glslangを使用してsetとbindingが取得できないので次の一手を探しました。SPIR-Vからsetとbinding値をとることができないものかとSPIRV-Cross-masterを入手してソースコードを眺めていたところ、spirv_cross.hppにspirv_cross::Compilerクラスを発見。get_nameやget_typeなどの関数の他にメンバー変数にはsetやbindingもあったのでSPIRV-Crossを使用してみます。ユニフォームバッファのメンバーも取得したいのでメンバー数とメンバータイプを取得する関数を派生クラスDemoCompilerクラスを作成しました。

class DemoCompiler :public spirv_cross::Compiler
{
public:
    DemoCompiler(std::vector<uint32_t>& ir) :Compiler(ir) {}
    virtual ~DemoCompiler() {};

    size_t get_member_count(uint32_t id) const
    {
        const spirv_cross::Meta &m = meta.at(id);
        return m.members.size();
    }

    spirv_cross::SPIRType get_member_type(const spirv_cross::SPIRType &struct_type, uint32_t index) const
    {
        return get<spirv_cross::SPIRType>(struct_type.member_types[index]);
    }
};


取得した情報を入れる構造体を2つ定義します。UniformInfo構造体とBufferInfo構造体です。SPIR-Vから取得した情報をこれらの構造体へ入れていきます。

typedef struct
{
    spirv_cross::SPIRType::BaseType baseType;
    std::string     name;     // ユニフォーム名
    size_t          bytesize; // バイトサイズ
    int             arraysize;// 配列数
    int             offset;   // バッファ先頭からのオフセット
}  UniformInfo;

typedef struct
{
    spirv_cross::SPIRType::BaseType baseType;   // Struct, Image, SampledImage,Samplerなど
    std::string     name;         // バッファ名
    size_t          bytesize;     // バッファバイトサイズ
    int             arraysize;    // 配列数 ない場合は0
    int             offset;       // バッファ先頭からのオフセット
    int             descriptorSet;// descriptorSetID
    int             binding;      // bindingID
    std::vector<UniformInfo> uniformInfos;
} BufferInfo;

spirv_cross::Compilerクラスに入った情報はspirv_cross::Resourceのvectorで定義されている各変数へ入ります。getReflection関数を作成しDemoCompilerから情報を収集します。初めにユニフォームバッファ情報やサンプラ情報をBufferInfo構造体へ収集し、ユニフォームバッファの場合メンバー変数の情報もUniformInfo構造体を使用して収集します。今回のブログの目的であるsetとbindingの値はcomp.get_decorationの第2引数をspv::DecorationDescriptorSetとspv::DecorationBindingにすることで取得することができます。各値の取得方法についてはspirv_cross.cppにソースコードがあるので参考にしてください。

void ShaderReflection::getReflection(const std::vector<spirv_cross::Resource> &resources, const DemoCompiler &comp, std::vector<BufferInfo> &bufferinfos)
{
    using namespace spirv_cross;

    for ( const auto& resource : resources)
    {
        const SPIRType spirv_type = comp.get_type(resource.type_id);

        BufferInfo binfo;
        binfo.baseType      = spirv_type.basetype;
        binfo.name          = resource.name.c_str();
        binfo.offset        = comp.get_decoration(resource.id, spv::DecorationOffset);
        binfo.arraysize     = spirv_type.array.empty() ? 0 : spirv_type.array[0];
        binfo.bytesize      = spirv_type.basetype == SPIRType::Struct ? comp.get_declared_struct_size(spirv_type) : 0;
        binfo.descriptorSet = comp.get_decoration(resource.id, spv::DecorationDescriptorSet);
        binfo.binding       = comp.get_decoration(resource.id, spv::DecorationBinding);

        size_t num_value = comp.get_member_count(resource.base_type_id);
        for (uint32_t index = 0; index < num_value; ++index)
        {
            const SPIRType &member_type = comp.get_member_type(spirv_type, index);

            UniformInfo uinfo;
            uinfo.baseType  = member_type.basetype;
            uinfo.name      = comp.get_member_name(resource.base_type_id, index).c_str();
            uinfo.bytesize  = comp.get_declared_struct_member_size(spirv_type, index);
            uinfo.offset    = comp.get_member_decoration(resource.base_type_id, index, spv::DecorationOffset);
            uinfo.arraysize = member_type.array.empty() ? 0 : member_type.array[0];
            binfo.uniformInfos.push_back(uinfo);
        }
        bufferinfos.push_back(binfo);
    }
}

次に作成した関数を使用してSPIR-Vから情報を収集します。DemoCompilerを作成しresourcesを取得しuniform_buffers、sampled_images、separate_images、separate_samplersからリフレクションを収集します。

void ShaderReflection::getSPVtoReflection( const void *pBinSPV, size_t BinSPVBytes)
{
    using namespace spirv_cross;
    std::vector<uint32_t> spirv_binary;
    spirv_binary.resize(align4(BinSPVBytes) / sizeof(uint32_t));
    memcpy(spirv_binary.data(), pBinSPV, BinSPVBytes);
    DemoCompiler comp(spirv_binary);
    ShaderResources resources = comp.get_shader_resources();
    //uniform_buffersから情報取得
    getReflection(resources.uniform_buffers, comp, m_bufferuniform_info);
    //sampled_imagesから情報取得
    getReflection(resources.sampled_images, comp, m_sampleruniform_info);
    //separate_imagesから情報取得
    getReflection(resources.separate_images, comp, m_sampleruniform_info);
    //separate_samplersから情報取得
    getReflection(resources.separate_samplers, comp, m_sampleruniform_info);
}

収集した情報を標準出力へ表示するサンプル関数です。

void ShaderReflection::printReflection(const std::vector<BufferInfo> &bufferInfos )
{
    const char *BaseTypeNmae[] =
    {
        "Unknown",  "Void", "Boolean",  "Char", "Int",  "UInt", "Int64","UInt64",
        "AtomicCounter","Float","Double","Struct","Image","SampledImage","Sampler"  
    };

    for ( const auto& binfo : bufferInfos)
    {
        std::cout << "name         :" << binfo.name << std::endl;
        std::cout << "baseType     :" << BaseTypeNmae[binfo.baseType] << std::endl;
        std::cout << "size         :" << binfo.bytesize << " Byte" << std::endl;
        std::cout << "arraysize    :" << binfo.arraysize << std::endl;
        std::cout << "offset       :" << binfo.offset << std::endl;
        std::cout << "descriptorSet:" << binfo.descriptorSet << std::endl;
        std::cout << "binding      :" << binfo.binding << std::endl;

        if (!binfo.uniformInfos.empty())
        {
            std::cout << "uniformInfo" << std::endl;
            for ( const auto& uinfo : binfo.uniformInfos)
            {
                std::cout << "    name         :" << uinfo.name << std::endl;
                std::cout << "    baseType     :" << BaseTypeNmae[uinfo.baseType] << std::endl;
                std::cout << "    size         :" << uinfo.bytesize << " Byte" << std::endl;
                std::cout << "    arraysize    :" << uinfo.arraysize << std::endl;
                std::cout << "    offset       :" << uinfo.offset << std::endl << std::endl;
            }
        }
        std::cout << std::endl;
    }
}

実験

test.fragからリフレクションを取得し出力してみます。テスト用のシェーダですのでシェーダ内容には特に意味はありません。

#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout(std140, set = 0, binding = 0) uniform buf {
        mat4    MVP;
        vec4    position[12*3];
        vec4    attr[12*3];
        float   f1_val;
        vec2    f2_val;
        vec3    f3_val;
        bool    bool_val;
} ubuf;

layout (set = 0, binding = 1) uniform sampler2D samp2d;
layout (set = 1, binding = 0) uniform texture2D tex2d;
layout (set = 1, binding = 5) uniform sampler samp;

layout (location = 0) in vec4 texcoord;
layout (location = 0) out vec4 uFragColor;

void main() {
   uFragColor = texture(samp2d, texcoord.xy);
}

サンプルを実行結果は以下のとおりになります。

---------------------------------------------
filename     :shader/test.frag
---------------------------------------------
name         :buf
baseType     :Struct
size         :1248 Byte
arraysize    :0
offset       :0
descriptorSet:0
binding      :0
uniformInfo
    name         :MVP
    baseType     :Float
    size         :64 Byte
    arraysize    :0
    offset       :0

    name         :position
    baseType     :Float
    size         :576 Byte
    arraysize    :36
    offset       :64

    name         :attr
    baseType     :Float
    size         :576 Byte
    arraysize    :36
    offset       :640

    name         :f1_val
    baseType     :Float
    size         :4 Byte
    arraysize    :0
    offset       :1216

    name         :f2_val
    baseType     :Float
    size         :8 Byte
    arraysize    :0
    offset       :1224

    name         :f3_val
    baseType     :Float
    size         :12 Byte
    arraysize    :0
    offset       :1232

    name         :bool_val
    baseType     :UInt
    size         :4 Byte
    arraysize    :0
    offset       :1244


name         :samp2d
baseType     :SampledImage
size         :0 Byte
arraysize    :0
offset       :0
descriptorSet:0
binding      :1

name         :tex2d
baseType     :Image
size         :0 Byte
arraysize    :0
offset       :0
descriptorSet:1
binding      :0

name         :samp
baseType     :Sampler
size         :0 Byte
arraysize    :0
offset       :0
descriptorSet:1
binding      :5

push any key

まとめ

Vulkanでのシェーダリフレクションを取得をやってみましたがいかがだったでしょうか。今回はspirv_cross::Compilerクラスを使うことでシェーダの必要なデータを簡単に取得することができました。すぐに導入することができますのでVulkanを使って上位ライブラリを作る方の助けになれば幸いです。

このような取り組みにも積極的な方と一緒に働きたいと考えています。もしご興味を持たれましたら、弊社グループ採用サイトをご確認ください。
採用情報 | セガグループ


それでは次回の更新をお楽しみに。

Powered by はてなブログ