Language:
more 

Category: Unity

[Unity] あなたはいつゲームを作るべきか、Unity5

「時期が悪い」というネタ(というかそう言う人達への揶揄)がある。PCなど進歩がめまぐるしく買い時が難しいものを買おうか悩んでいる人に「もうすぐ○○機能が出回る、今買うのは時期が悪い」「経済事情で数ヵ月後に値下がりが来る、今は時期が悪い」などと言って一向に買わせないというお約束ネタだ。まあ本気で言う人がいっぱいいるからこそネタになっているのだけど。

で、なぜこんな書き出しをしたかと言うと、もうすぐUnity5がくるわけだけど、現在の4.xで不満が多い部分は事前情報を見るだけでも当然色々改善されるようだ。つまり、あなたが今作っているものがUnity5の変更によって不必要になり、丸々無駄になる可能性があるのだ!まあこわい。

例えば、現在のUnityはサウンド周りはそのまま使うと色々不都合な点を抱えており一部は前に記事に書いたが、そのためにUnityに慣れている人達は自前のSoundManagerクラスみたいな汎用スクリプトを作ってBGMやSEを制御している。しかしUnity5ではサウンド回りが大きく変わる為、今までの制御の概念がそのまま使えるのか怪しい。Unity本体側が修正していて自作の制御が必要なくなる場合もあるだろうし、新たなサウンドシステムに合わせてスクリプトからして色々変わる可能性もある。(パーティクルシステムは現在のsyurikenと以前のものの2つあってスクリプトからの制御が若干違うため、少し昔Google先生で調べるとどっちも引っかかって私は大混乱した。)要するに、今からサウンド周りの制御を構築し始めたら出来上がった頃に颯爽とUnity5が現れて、あなたのそれまでの苦労にジャーマンスープレックスをかましてくる可能性があるのだ。まあこわい。

ジャーマンは痛いからいやだよね。じゃあこれを回避するためにはどうするべきか?ここで冒頭に帰結する。今はゲームを作るな、時期が悪い。

嘘です。
最善の策は「Unity5での変更点や新機能を調べて、そこを作るのは後回しにする」ことです。要するにUnity5での変更に衝突しないような製作順序の計画を立てる。苦労が無駄になる事を防ぐだけでなく、変更される点に手をつけないでおくことでバージョンアップでの最適化とかの余計な労力も最小限で済む。つまりまずUnity5ではどこが変わるのか、を知っておくことが必要です。

私が知っているだけUnity5での変更点を箇条書きにしてみましょう。

  •  サウンドシステム
  •  リアル系シェーダー
  •  ライト(グローバルイルミネーション)
  •  WebPlayer動作にプラグインが必要なくなる(WebGL)
  •  64bit化
  • Mecanimに要素追加&スクリプト制御追加
  •  マルチプレイ向けの新ネットワーク要素(UNET)
  •  NavMeshに機能追加

 今のところはっきりしてるのはこんなものかな、あと4.xの最後にUGUIがくる。そして、今挙げた部分以外のところからゲームを作りつつUnity5を待てば問題なし!みんなハッピー!

でも多岐に渡り過ぎて侵食されてない部分あんまりないね。ベースのプログラムをコツコツ作っておくかモデリングくらいしか思いつかない。まあこれがUnity5に最初から全部載るということではないだろうし、実際Unity5くるのは半年以上後とかありえるけども。64bit化もあるから最初はバグがふんだんに盛り込まれてそうだしなあ・・・。まあとりあえず、サウンド周りとか独自シェーダー作ったりとかマルチプレイのネットワークとかは後回しにしておくのがベストだと思います。

私は、寝て待機しておきます。時期が悪い。

[Unity] 他オブジェクトと連携-FindとかGetComponentとかSendMessageとか

今回はUnity始めたての人に向けて、他のオブジェクトとの連携を取る際の方法について書きます。Find系、GetComponent系、SendMessage系あたりで他オブジェクトの関数やメソッドを参照したり使いたいみたいな事です。オブジェクト指向のキモとなるところなので利便性のためいくつかの方法があり、それゆえ最初の頃に詰まる部分になってくる。自分がUnity触りはじめの頃にひとつの方法を調べたら他の方法も芋づるで出てきてどんどん調べ事が増えて困ったので、うまくまとめられれば過去の私のような方の時間短縮になるはず。

 まず系統で分けつつ方法を書き出してみる。

  1. public static
  2. Inspectorウインドゥでアサインする
  3. Find系
    • Find
    • FindWithTag(複数ならFindGameObjectsWithTag)
    • FindObjectOfType(複数ならFindObjectsOfType)
  4. SendMessage系
    • SendMessage
    • SendMessageUpwards
    • BroadcastMessage
  5. GetComponent系
    • GetComponent(複数ならGetComponents)
    • GetComponentInChildren(複数ならGetComponentsInChildren)
    • GetComponentInParent(複数ならGetComponentsInParent)

大別すると使うのはこの5系統かな。相手オブジェクトを探すためのものと何か実行したりするためのものがごっちゃになってるけど、組み合わせて使うことになったりして結局これらは全系統調べる羽目になると思うので一緒くたに説明していく。

あと、これら以外にもOnCollision系やOnTrigger系、Raycast系などで範囲にhitした相手を取得、という方法もあるがそっちは総じてある形状にぶつかったオブジェクトを取得するもので、ちょっと毛色が違うので今回は省く。

 

 

1.public static

変数やメソッドをpublic staticにしておくと、その世界で唯一のものになってどこからでも使えるようになる。簡単に言うとpublicによって他所のスクリプトからそこへのアクセスを許可して、staticでその世界でただ一つだよという事にしている。世界唯一と公言しているので住所を調べなくても名指しするだけでそいつだとわかって連絡できる、というイメージだろうか。具体例を挙げながら解説していく。一番使用率が高いであろうC#で話していくので、他の言語を使っている方は、まあその、頑張れ。

 この例でいけば、どこの他所のスクリプトからでも
test1.HealthPoints -= 5;
と書くだけで変数HealthPointsは95になるし、
test1.Attack();
と書くだけでAttackメソッドを実行させることができる。しかもこの方法は他の方法より軽くて最速。

えっ超簡単だしこれでいいじゃん、と思った方は罠です。この書き方はゲームの内容部分では基本的に忘れてしまって一度も使わないくらいのほうが無難です。なぜなら前述の「世界で唯一」「どこからでも簡単にアクセスできる」というメリットがそのままデメリットにもなるから。

まずその世界で唯一であるデメリット。仮にザコ敵の体力にこれを使った場合。世界で唯一の変数なので、ザコAの体力が0になるとコピーして作ったザコBの体力も0。つまりザコのグラフィックはコピーしたとしてもこの変数部分は全部一つ一つ別にしなければならない事になる。使い回しが全く効かない。こいつはボスで一体しかいないからいいんだい!と思っていたとしても、あとからボスラッシュモード作りたいなーとかなったら修正を入れることになる。

さらにどこからでも簡単にアクセスできることのデメリット。ゲームの流れを変更した場合、どこからその変数やメソッドにアクセスしていたか把握しきれなくなりバグの元になる。だってどこからでもアクセスできるから。特にチーム製作だと、別の人が手直ししようとした場合にどこからアクセスしているのかゲーム全体をチェックする羽目になるので、無計画にpublic staticにするのは非常に嫌がられます。一人での製作だとしても、数ヵ月後に見ると自分のプラグラムでも他人が書いたのを解読するようなもの(だから一人作業でもわかりやすく書こうという戒め)だから、無計画に使うとやはり未来の自分がげんなりする羽目になるかもしれない。

じゃあpublic staticいつ使うんだ、と言えばゲーム内容ではなくシステム的な部分が適していると思う。どのキャラでもどのセーブデータでも共通して持っておく値とか。簡単な例として「私」というアカウントの中に”あいつ””こいつ”という2キャラを作っているとして、あいつで10体、こいつで20体、とスライムを倒していき、分岐があったので”あいつ”のセーブデータを2つに分けて・・・となっても、全ての合計で通算100体倒したとき「スライムハンター」のトロフィーを「私」アカウントがもらう。このスライムを倒した通算数計算やスライムハンターのトロフィー付与処理をpublic staticで行えば、その世界で唯一であってどこからでもアクセスできる変数やメソッドであることが有意義であると思います。
最も良いのは、主要なゲーム内の情報を一括で管理しておく、いわゆるGameManagerみたいなクラスを作る場合。こいつがpublic staticで変数やメソッドを持っておいて使うときはそこから取ってくると決めておけば、スコアやら残機数といった世界で唯一であるべき処理を簡単に最速でやり取りできる。

ただしやはり慣れないうちはそういったゲームシステムの構築も紆余曲折したり行き当たりばったりになると思うので、むやみにpublic staticを使わず慣れてきてから有効な部分でのみ計画的に使うのをオススメしたい。public staticをまったく使わなくても大抵のゲームは問題なく作れる、と言われるくらいなので。

 

 

2.Inspectorウインドゥでアサインする

上のトピックでpublicはよそのスクリプトからそこへのアクセスを許可していると書いたが、変数をpublicにするとInspectorウインドゥ上でその変数にそったものを数値入力したりアサイン(指定)したりできる。

例えばこう変数の宣言を書いておくとInspectorウインドゥにexGOというGameObject用の欄ができるので、ドラッグドロップやその欄の右の丸をクリックしてGameObjectをアサインできる。それと同時に他所のスクリプトからその変数を参照できるようにもなるので、後述のGetComponentなどでこのスクリプトを指定して変数を他所から変えたりとかできる。

他所のスクリプトからその変数を参照する必要がないなら、publicの代わりに [SerializeField] とつければ単純にInspectorウインドゥに欄を表示することだけができるけど、最初の頃はpublic一辺倒でいいかも。そうすれば「Inspectorウインドゥに欄が見えてる=他所のスクリプトからいじれる変数」という簡単な視覚確認ができる。

この方法は最もわかりやすく簡単なので、アクセスするGameObjectを指定する場合などはできればこの方法を使いたい所だが、いかんせん使えない状況がある。自分や対象がゲーム途中で生成されるとか、状況により対象が変わるとかの場合だ。基本的な対象指定ではこれをまず使っておいて、使えない場合に改めて他の方法を考慮すればいいと思う。

 

 

3.Find系

上記2番目のトピックで述べたアサインで指定する方法が使えない場合は、Find系で相手を探すことを考える。オブジェクト名、タグ、型のいずかでシーン内や対象内を検索する感じになる。該当が見つからない場合はnullを返す。また、基本的にアクティブ状態(「SetActive(true)」の状態)のゲームオブジェクトでないと検索できない。

ここからの説明で階層構造を表すのに子孫や祖先という言い方を使うけど、これは文字の通り子供の子供…や親の親…といった何世代先であっても階層の下流(あるいは上流)にあれば全て範囲内ということです。ただし親の別の子供みたいな直接の上流でも下流でもないものは普通に範囲外。

あとFind系共通仕様として、複数の検索該当が存在するのにひとつを探した場合、該当のうちのどれを参照するかはシステム内部で最初に引っかかったものになるので確実性がないことに注意。例えば同じオブジェクト名のものが2つある時にFindするとどっちに対して参照するかは不確実。ためしにPlayしてみたら欲しいほうのオブジェクトを取ってくれたのでこれでいいやとか適当なことをやると、あとで何のきっかけで参照先がぶれてしまうかわからない。なので、複数が対象になり得る場合は独自のオブジェクト名をつけて確実に対象を1つにするとかの区別が必要になってくる。

また、Find系は重い処理なので、Update()内で使って毎フレーム走らせたりするのは避けて、一度Find系で相手を見つけたら変数に入れておき再利用する。ではFind系3つのタイプを見ていく。

 

Find()

ゲームオブジェクト名でシーンや対象内を検索。

この例はCubeという名前のオブジェクトを見つけて変数hogeに取っている。これで今後このCubeに何かしたい場合は
hoge.transform.position = new Vector3 (0, 0, 0);
とかhogeに対して何かすれば再び探さなくてよい。

フォルダのように階層構造になっている入れ子の子要素にあたるオブジェクトに限定する場合はtransformコンポーネントを通してアクセスすることができる。

この例の場合は相手指定をしていないので自分の子のCubeを取っている。直下の子供だけが対象で孫以下のものは無視される。同名別固体なCubeオブジェクトが他所にあっても自身の子供のCubeに限定することができるが、自身の子供にCubeが2つある場合は参照先が確定しないので注意。末尾の.gameObject の部分は、transformを介してアクセスした場合は参照する情報もtransformなので、ここでゲームオブジェクトを取るようにしている。
hoge.transform.Find(“Cube”).gameObject;
この例のようにすれば、変数hogeに取っておいたオブジェクトの子供の中からCubeを探すことになる。
あと、このtransform.Find()ではFind系の特例的に相手が非アクティブ状態(「SetActive(false)」の状態)でも見つけることができる。非アクティブにする前にFindして変数に取ってから非アクティブにできるなら必要ないけど。

さらにFind()では階層構造から探す事もできる。

この例だとそのシーンでCubeの子になっているChildというオブジェクトを探す。Box1/Box2/Cube/Child のような奥に入り込んだ構造でも見つけてくれるが、Cube/Box1/Child とかだと条件に合っていないので当然引っかからない。

transformで対象の子供からに絞って探すことももちろんできる。

この例のようにすれば対象の(この場合は相手指定をしていないので自分の)子供がCubeで孫がChildの構造になっているChildを見つけるわけだが、ひとつ注意。この場合自身がCubeで、直下にChildを持っているとしてもそのChildは引っかからない。transformが言わばそいつ自身のことで、Findでその中身から”Cube/Child”に合うものを検索していると考えればわかりやすいだろうか。

あと、他Find系2種にはFindWithTagならFindGameObjectsWithTag、FindObjectOfTypeならFindObjectsOfTypeという複数一括検索用のスクリプトがあるのだが、このFind()には複数を一括で探すバージョンはないので、シーン内のCubeという名前のオブジェクトを全て取りたいといった場合は自前でfor文を回したりすることになる。ただこれは、公式的にはFind()よりFindWithTag()のほうが軽いようなので、複数探すならうまくタグ分けして比較的軽いFindGameObjectsWithTag()を使えということだと思う。その点から言っても、Find()で見つけたいものは独自の名前、いわゆるユニークネームをつけておく必要がある。

 

FindWithTag() (複数ならFindGameObjectsWithTag())

タグでシーンや対象内を検索し、そのタグがついているゲームオブジェクトを取得する。公式的には重い処理のFind系3種の中ではこれが一番軽いようだ。ただ軽いからと1体にしか使わないタグをやたらめったら作るのはナンセンスなので、FindGameObjectsWithTag()で複数を一度に取るほうが使い道としては多いかな。

 この例ではEnesという配列にEnemyタグの付いたオブジェクトを全部取っている。もちろんオブジェクト郡にはタグを付けておかねばならないし、配列がよく分かっていないうちは配列も調べる必要がある。場合によっては配列でなくListが必要かも。更にこのあと大概forかforeachで配列に何か処理を行うとかすることになるので、派生の調べ事が増えがちで訳が分からなくなりやすいかもしれない。「同じタグが付いてるオブジェクト郡に一斉に何かしたい」「同じタグが付いているオブジェクト郡の中から更に特定の何かを探し出したい」というような場合に使うものだと覚えておいて、そのシステムがゲーム構築に必要な場合に徐々に調べていくほうがいいかな。色々詰め込むと本筋の話が薄くなるので、ここでは主目的のFindGameObjectsWithTag()の話だけで説明は留めます。最初の頃に使うといったら「ボムでEnemyを一斉にDestroyする」とかかな。

このくらいの処理なら配列やforの使い方があやふやでもGoogle先生の力を借りればさくっと作れるはず。

 

FindObjectOfType() (複数ならFindObjectsOfType())

型でシーンや対象内を検索。型はRegidbodyやCollider、スクリプトなどInspectorに並ぶ項目の事と思っておけばOK。コンポーネントとも言う。つまりこれは他の2種とちょっと違ってゲームオブジェクトじゃなくて持っているコンポーネントを探す。ただしこの関数は遅いFind系の中でも特に動作が遅いうえ、大抵他のもっとましな方法があるので基本的に使わない。こういう方法もあると一応覚えておく程度でいい。

 

 

以上Find系3種。何度も言うように重いので一度探したら変数に保持しておいて、使いまわすようにするのが基本。そして変数に取ったオブジェクトに対して以下残り2つのSendMessage系やGetComponent系を使って、処理をさせたりあっちの変数を参照したりといったやり取りをする感じになる。

 

 

4.SendMessage系

対象のオブジェクトにメッセージを飛ばして指定のメソッドを実行させるイメージ。普通はpublicにしないと外からメソッドを直接実行させる事はできないけど、SendMessageだとpublicにする必要なく行える。また後述のGetComponent系を使っての実行と違ってどのコンポーネントとか詳しく指定しないでよく、相手のオブジェクトがどこかにそのメソッドを持っていれば実行してくれる。やや遅めの処理だけど扱いが簡単なので、頻繁でない処理を手軽にやりたい場合に良く使う。引数が一つしか送れず、戻り値も取れないのでそもそも簡単な一方通行の伝達での使用を想定していると思う。

ちなみに引数というのは処理の伝達の際に何か値を一緒に送る場合のその値のことで、戻り値と言うのは処理後に命令を発した元のところに結果を返す事。砕いて言えば荷物を1つしか持っていけないし、行ったきり帰ってこないので送った荷物を加工して返してもらうような処理は(これだけでは)できない。要するに複雑なやり取りには向かない。あとC#,JavaScript間みたいな言語が違う間のやりとりも普通にできちゃうのも特徴だけど、これはそもそもゲーム内にベース言語が混在してる状態があまりよくないのでオマケ程度。

これも3種類あるけど3種のやることは同じで、単体宛て、入れ子の子孫宛て、入れ子の祖先宛てという送り先が違う分類になる。ではSendMessage系の3種を見ていく。

 

SendMessage()

単体のゲームオブジェクトに指定したメソッドを実行させる。例えば

この場合だと、対象指定してないので自分自身のどこかのスクリプトに

というメソッドを持っていればそれを実行する。もちろん
hoge.SendMessage(“Damage”);
とやれば変数hogeに保持しておいたオブジェクトが対象になる。

引数を持つ場合は

のようにする。この場合メソッド側も引数を受け取るようにするわけだけどこれは話が逸れるし調べればすぐわかるので割愛。

 

BroadcastMessage()

対象相手の入れ子の子孫全てに指定したメソッドを実行させる。対象相手自身も実行に含む。もちろん引数もひとつなら持たせられる。何かのコピーの一群を空のオブジェクト内にまとめて入れておいて一斉に何かを起こすとかできるけど、複数一斉なぶんより重くなるので注意。

 

SendMessageUpwards()

対象相手の入れ子の祖先全てに指定したメソッドを実行させる。対象相手自身も実行に含む。祖先全部に何か一斉に処理っていう状況があまりなさそうなので他のSendMessage系より使用頻度は低いかもしれない。

 

 

以上SendMessage系3種。ちなみに相手が指定のメソッドを持っていない場合にはエラーが出てスペルミスとかがわかるようになってる。状況に応じてメソッドがあったりなかったりするだけでミスではないのでエラーがうっとうしい、みたいな場合は

のように書くとエラーチェックをしなくなる。

 

 

5.GetComponent系

コンポーネントの事はRegidbodyやCollider、スクリプトなどInspectorに並ぶ項目の事と思っておけばよいと上で述べたが、GetComponent系は名前の通りそのコンポーネントを参照する。例えば相手オブジェクトのRegidbodyを参照しとあるタイミングでUseGravityをOffにして重力から切り離すとか、スクリプトも取れるので外から変数を変更したりメソッドを実行させたりできる。SendMessageと違って複数の引数も扱えるし戻り値も受け取れる。自由度が高くそれなりに速いので、複雑なやり取りや頻繁なやり取りの場合に使う。

これも3種あって、InChildrenやらInParentと名称の末尾についているのからわかるとおり、対象のオブジェクト単体、子孫全ての中から、祖先の全ての中からの3つの分類。さらにその3つがひとつのコンポーネントだけを参照する単体用と、当てはまるコンポーネント全てを取る複数用どちらのタイプもある。

 

GetComponent<T>() (複数ならGetComponents<T>())

単体のゲームオブジェクトの持つ、指定したコンポーネントを参照する。

 この例は自身のRegidbodyを参照して変数exGCに取り、IsKinematicをオンしている。もちろんこれも
hoge.GetComponent<Rigidbody>();
とすれば変数hogeに取っておいたオブジェクトのRegidbodyを参照する。

あと、スクリプトを参照する場合の変数の型宣言がちょっとわかりづらいのでそれも書いておこう。小数点ありの数字を入れる変数の型はfloatだし、Regidbodyを入れる変数の型は当然Rigidbodyだけど、スクリプトを入れる変数の型はどうすれば?

このようにスクリプト名が型になる。

そしてここまでのトピックで話に出たが、参照先のスクリプトの変数やメソッドはpublicにしておかないと外から参照できないので注意。

こんな感じに変数exGCに取ったスクリプトの中でpublicになっていれば
exGC.HealthPoints=3;
とやって外から変数を変えたり
exGC.Attack();
とやってメソッドを実行させたりできる。GetComponentさえしちゃえば、参照したスクリプトの中だけなら一番最初の方法で出したpublic staticと似たような感じで使えるって事だね。

あと複数形のGetComponents<T>()は、単体のゲームオブジェクト内にある同じ種類のコンポーネントを全部取るわけだけど、ひとつのオブジェクトに同じ種類のコンポーネントをいくつもつけておいてどうこうっていうのはあまりやらないと思うので、一応存在を覚えておく程度でいい。

 

GetComponentInChildren<T>() (複数ならGetComponentsInChildren<T>())

 対象相手の入れ子の子孫全ての中から指定のコンポーネントを探して参照する。 対象相手自身も検索先に含む。GetConponentInChildren<T>()の方はひとつのコンポーネントを対象にするわけだけど、これも今まで同様複数の該当先があった場合はどれを参照するか不確定なので注意。単純な例では子供が3人いて3人ともRegidbodyを持ってる場合にどいつのを参照するかは不確実ってことね。

GetConponentsInChildren<T>()なら対象自身とその3人の子供全員のRegidbody全部を配列に取るような形になる。

この例ではRegidbodyを全て配列exGCに入れて、foreach文で処理を回して全員のIsKinematicをオンにしている。

 

GetComponentInParent<T>() (複数ならGetComponentsInParent<T>())

 こちらは対象相手の祖先全ての中から指定のコンポーネントを探して参照する。検索に対象相手自身も含む。子孫か祖先かの違い以外は上と変わらない。

 

 

 

以上で冒頭で述べた5系統をすべてみてきたわけだけど、総じて何かアクセスしたいオブジェクトなりコンポーネントなりを変数に保持しておいて使いまわすのが基本になる。ゲーム進行中に何かを探すというのが重めの処理で避けたいからだ。だから最初から変数に入るInspectorウインドゥ上でアサインする方法か、Start()内やAwake()内で探して変数に入れてしまうのがよい。あるいは、prefabからInstantiateで生成したときに変数に突っ込んでしまうようにすれば後々探す手間とコストを省くことができる。

その場面で軽くて効率のいい処理にするにはどのアプローチがいいのか、というのはゲームを組み立てるときのひとつの物差しになる。投げ出してしまうよりはとりあえず動けばいいという前傾姿勢も必要だけど、自分なりに納得のいく処理アプローチを組めたときにプログラムの楽しさに気づいたりする。もちろんスマホ用みたいな軽さが重要なプラットフォームの場合は避けては通れない。そのためにも様々な手順の情報が必要。

そして数ヵ月後に見直したときに「いや、こう書けば3行ですむし…昔の私なにしとん…」みたいになるのが嗜みである。

[Unity] AssetStore アセットストアで販売してみよう-3.審査

前回まではアセット提出の方法について説明してきたけど、今回は審査を通過するためのコツというか対策というか、「こうすれば審査を通りやすいんじゃないか」という観点で話していきます。当然審査内容は公開されてないし内部事情に精通しているわけでもないので個人的見解になるけど。ただ前回言ったとおり、多くの人の情報を集めれば集計的に審査傾向が見えてくるのでその一端です。アセット提出の作業手順が知りたい人はその1その2を見てね。

まずアセットの有用性が第一ではあるが、それだけが要点ではない。同時に販売時の集客性なんかもそのまま審査へのアピールのポイントとなり得る。アセットは売り上げの3割がそのままUnity社の儲けに繋がるので、売れそうなアセットはUnity社側としてもぜひ販売したいはずだから。これは提出ガイドラインからも割と読み取れる。そこを踏まえて購入側の目を意識して「売れそう」な感じを目指すのが結果的に審査の通過に結びつくはず。あと提出ガイドラインに沿うのは大前提ね。提出前に新しいprojectにインポートしてみてバグが無いか検証、とかは必ずやっとこう。

要項の中でポイントとなるのはPackage title(アセット名)、Description(説明文)、Key images(サムネイル郡)の3点になると思うので、まずはこれらについて考察していく。

Package title

まず他のアセットとかぶらない。思いついた単語があったらアセットストアで検索してみてかぶらないか調べる。更にぱっと見である程度何に使うものか判る名前が良い。超売れっ子のNGUIやPlayMakerはもうほとんどそのまま直球な名前。英語圏以外の人でもすぐわかるような単語になっている。

あと私見ではあまり長い名前はオススメしない。小さいサムネイルで表示されている状態などでは名前が長すぎると表示しきれずに後ろのほうが途切れる。更にネットクチコミが絶大な効果を持つ今、アセットの呼称が長いと話題に上りづらいとかそういう理由。まあ逆手に取ってやたら叙情的な長い名前をつけて印象に残す作戦もアリかもしれないけど(ラノベ的作戦)。ただ基本は「端的で判りやすい」事で、これは次のDescriptionでより明確に現れる。

Description

アピールのために色々書き連ねたくなってしまうところだが、こここそ端的に判りやすく書くことを念頭に置いた方が良い。審査通過時にある程度要項内容が勝手に修正されてストアに並ぶ場合があるが、このとき大抵文章が簡潔にまとめ直される。これは私もそうだったし、アセット提出時の話を検索して調べると判る。要するに、英語に不慣れな人が読んでもこの表現はどこに掛かってるのかとかゴチャゴチャしないで、すんなり把握できるような文章にするとよいはず。

売れっ子のアセットを参考に見ると、特徴をリストとして一点づつ並べたり、一文が長い説明はせずに短く区切った文にしている。「AはBだ。CはDになる。」みたいな、中学生かよとツッコまれそうな文章でいい。英語圏以外の老若男女問わず売るという点からすればそうあるべきだし、単語さえわかればどう言い表せばいいのかとか悩まなくてすむので私たちにも都合が良い。へんに背伸びせず要点だけ短くまとめて書く。みんな幸せ。

あと審査を通ったあとでいいけど、デモや詳しい解説を置いたサイトを作ってそっちにリンクを貼っておく。公式フォーラムか自サイトの掲示板あたりにもアセット用のスレッドを立ててリンクを貼っておき、サポート体制もちゃんと作る。もちろん英語で。とは言え全て完璧に用意して審査に挑んだら落ちました、では痛手が過ぎるので、私はデモと簡単な説明だけのページを作ってそこへのリンクでとりあえず提出して、審査に通ってから本格的な説明ページを作りました。つまり別ページは最初から完全なものを用意はしなくても大丈夫。ただデモに触れられるくらいは最初からあるほうが印象良いかもしれない。

Key images

画像はぱっと見でのアピールになるので、一番集客に関係するポイントかもしれない。第2回でも言ったが見た目が重要なアセットなのか、スクリプト的なアセットなのかで難易度は大きく変わる。モデルやエフェクトなら見た目こそが最大の商品価値なのでそのまま見た目を前面に出せば良い。購入側が一番知りたいのもそこだしね。ただスクリプトなどの機能性を売るものだとどうデザインしたものか困るかもしれない。

私は審査1度目落ちて2度目で通ったのだけど、そのとき一番変えたのはこのKey images。他の文章の部分はより簡潔にまとめ直したくらいで大きな方向性は変えなかった。提出ガイドラインにも「ストアで大きく取り上げられたかったらKey Imagesのプロ的なデザインを心がけてね」みたいな事が書いてある。要するにサムネイルのデザイン性も見ているということ。アセットストアのトップページから適当に見回ってみても、素人落書き風のサムネイルはほぼ見当たらず、皆ある程度のデザイン性を持っている。

更には、近年の日本風の彩度明度が高く可愛さで訴えかけるような萌え的デザインも今の時点ではほとんど見つからない(ギャルゲ製作ツールやトゥーン系モデルくらいかな)。逆に多いのは暗く彩度低めのリアル指向の見た目で、目立たせたいものにライトが当たったり発光してるとかそんな感じ。洋ゲーによくあるデザインだね。これが要するに前回言ったアメリカ的ニュアンス。私たちが日本人向けセンスで、ゲームで言ったらカービ○的な可愛くパステルで星キラリーン(誇張)な楽しげなデザインをしたとしても、通常のアセットではプラス評価になりづらいかもしれない。王道で行くなら海外FPSゲーム的なデザイン方向を目指すのが無難という事になる。

ここで審査落ちしたときの私のサムネイルの一部を見てもらうと

asset11
こんな感じでした。審査通った現在のデザインはストアで見てみて下さい。一応言うとゲーム内にキーコンフィグを追加するアセットね。(ぱっと見でわからない場合はデザイン的に失敗なので判って欲しいところ)イラスト技術的な上手下手はおいておいて、上の落ちた方のサムネイルはおもちゃ的というか手軽さ感を狙ったのだけど、審査通ったストアのほうは配置などはほぼ同じままでアメリカ的ニュアンスを心がけて描き直している。アセット自体にはほとんど変更無しで一番手を加えたのがこのKey imagesで再提出し、テンプレ返答落ちだったのが審査通過になったのだからデザインの方向性も重要である感じがするよね。私的には受かったほうもあまり好みではないけど。デザインセンスのある方の方向性を決める際のとっかかりにでもなればと思います。ただ通過しやすくなる、という感じであってアセットさえ相当有用なら多分どんなデザインでも通過すると思う。

あともちろんこれは現状であって時間とともに変わりうる。アセットストア初期の頃のものなら割と適当なサムネイルのものもあったりするし、これから日本人のアセットストア利用者が増えて萌えやアニメ的デザインでも売れるとなればそういう方向でも通りやすくなるだろう。Unity Technologies Japanもユニティちゃんとかそっち方向の導入を推していたりするので、将来的には日本感覚のままの私たちに親しみやすいデザインで普通に勝負できるようになるかもしれない。なのでそのためにも日本人のアセットストア利用者もっと増えろーと思っている。

 

以上が悩む人が多いであろう3項目。

あと上記3点以外に値段もやや迷うかもしれないが、提出ガイドラインに基本的な値段のラインは書かれているし、似たような他のアセットから大きく外れた値段はつけてもあまりメリットがなさそう。値段以前にまずアセットストアで注目されることが相当に難しく、ちょっと他より安くした程度では売り上げは伸びないし、なんなら無料で有用なアセットでも評価の☆がほとんど付いていないとかけっこうある。

売り上げを伸ばすという事ではUnity社に気に入られてしょっちゅうセールでトップに並べられるのが一番大きいと思う。NGUIはたしかセール期間中に最大売り上げを記録していたはず。元の値付けが安いとセールの恩恵が薄くて購買意欲に繋がらないしUnity社の儲けも少ないとか考えると薄利多売を目指すのが必ずしも良い結果になるとは限らない。まあ私は他より安い値段を付けちゃってるけど、これは最初自分で使うためにキーコンフィグのアセットを探してたら、無料~5$のものはxbox系パッドしか使えないとかダメダメで、全てのパッドを取得できるものは20~40$と高く、「なんかいいのがない!もう自分で作る!」となったついでに、同じような人に安価で供給できればと思ったからこうなってる。こういう変な紆余曲折がなくシビアに儲けを狙うならば安さで勝負はあまり効果がないように思うので、Unity社に3割払うビジネスパートナーだくらいの気持ちで自信を持って値をつけたらいいと思う。

 

もうひとつ、よくひっかかりそうな注意点。アセット内にちゃんとReadMeを入れること。これは提出ガイドラインに”含める必要がある”と明記されている、要するに必須事項なので無いと審査落とされます。更に言うと、他の説明ページなどを見なくてもReadMeを読めば基本的な使用法はわかるくらいには説明を入れておく必要があるみたい。私は審査通った際の提出時にはReadMeも推敲しなおして端的且つなるべくわかりやすく直して、日本語ReadMeも付けときました。英訳が大変なので「なんかもう大体でいいや」ってなりがちなところだけど、ReadMeも審査にちゃんと関係してるというのは覚えとこう。

 

そして、これらを考慮して提出要項が全てまとまったら、Asset Store Toolsの右下のSubmitボタンを押せば最後の提出確認ウインドゥが開く。これは初回と2回目以降ではちょっと項目が異なる。

asset12
左が初回で右が2回目以降。初回ならチェック項目が了承できればチェックしてOkボタンを押せばパッケージングしてアセットの送信が始まる。2回目以降は見てのとおり選ぶ項目が追加される。これはパッケージ内容に変化は無く売り文句などを書き換えただけの場合は上を、パッケージ内容の更新がある場合は下を選ぶ。下を選んだ場合はバージョンを入力する項目が更に現れて、以前のナンバリングより大きい数字にしないと提出できない。バージョンのナンバリングの型は1.00でも1.0.0でも割とどういう型でもいいみたい。

ちょっと困ると思うのが真ん中の大きい空欄で、これは審査員になんかあればどうぞって感じ。私は初回の落ちたときは未記入で、2回目は色々変えて審査傾向をつかもうと思ってたのもあって「一回落ちたんで文章練り直してバグ取りしてサムネイルも変えてみた。ReadMeも書き直して、自分が日本人なんで日本語ドキュメントも追加しといた。これでどうよ?」みたいなことを書いて送った。アセットのどこを変えたって書くのは、もしあちらの記憶に残っていれば落ちたポイントが修正されているのか把握しやすいだろうからいいように思う。初提出を想定して解説したので前回の説明で軽く流したけど、提出要項のところにバージョン変更点について書く欄あるしね。審査員ももちろん人間なので、空欄よりは判りやすいほうがいいだろうし。失礼さえなければ。あと日本人だとそれとなく入れることで英語が多少ヘタでも許容して修正してくれるかなという算段も込めた。文章が直される事があるってのは事前に調べて知ってたしね。

ただ、「英語ヘタです」みたいなド直球はあまりオススメしたくない。英語圏以外の提出者皆が皆これ書いて送ってた場合なんかウザそうだから。というか私が審査側でこれを何十回も見てたら見慣れてだんだんスルーする感じになりそうな気がするので。できればスマートに話の流れでそれとなく判るのがいいんじゃないかな。「日本人を主なターゲットに見てるんだがストアで英語以外使えないの?」とか「日本語サポート対応アセットを特集してくれ!」とかね。意見を述べつつ相手に悟らせる大人の手腕を見せたいところ。

 

そしてOkボタンを押してちゃんと提出されると、Submission successful というダイアログが出て提出作業は完了となる。審査結果はアカウントのメール宛に3日~長いと2週間くらいで返答が来る。3営業日とかを真に受けて数日で返事が来ないからといってあせらないように。審査中の状態はAsset Store Tools の Publisher Adminstration の項目から開くwebページで確認できる。ちゃんと提出されていれば、webページのPendingタブのStatusの欄が”Pending Review”になっている。

ここで気をつけたいのが、提出後にAsset Store Tools の Package Manager を不必要に開かないこと。Previewボタンなどを押して記入項目がセーブし直されると、Pending(審査中)状態がDraft(草稿)状態になり提出したものが取り消しになる。再び提出しても審査待ちの列の最後尾に並び直しになるはずなので、Package Manager自体を開かず余計なミスキャンセルを防いだほうがいい。

逆に、審査待ち中に致命的バグなどに気づいて修正したい場合、審査結果を待たずに再提出することができる。この場合は審査員の手を煩わせないためにもまずバグに気づいた時点でPreviewなどしていったん提出を取り消そう。その後、バグをきちんと修正して改めて提出するべし。

 

というわけで、3回に分けてアセットストアの審査への提出について書いてきたけど、何かつかめたことがあったならこれ幸い。あなたが無事アセットを提出できて、審査を通る事を祈ってます。そしてできれば合否に関わらずあなたの提出時のことをブログでもツイートでもいいので公開してください。それが更に他の人や私の情報源になっていくので。英語が壁になるUnity市場に、日本の情報の輪で楔を打ち込もう!