FreelyApps

個人によるアプリ開発の日記です。アプリの収入だけで生活できるようになるのが目標です。UnityでAndroid向けのゲームアプリを作成しています。


    アプリ『トランプ・7並べ』を公開しました。
    Android/iOS https://goo.gl/zsFces

    カテゴリ: Unity

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Asset Storeに「Gem Shader」 というものがあります。名前の通り宝石のような見た目を表現するシェーダーを提供するアセットです。デモシーンは以下のような感じです。

     
    宝石のようなモデルに専用のシェーダーがついています。デモシーンはリフレクションプローブを使用しているので、Quality Settingsでリアルタイムのリフレクションプローブを有効になっていないと、反射がされず真っ黒になってしまいました。赤く囲ったところのチェックを入れればちゃんと表示されます。
    gem-qs

    gems-noreflection

    言うまでもないかもしれませんが、きれいな表示がされる反面描画にはそれなりのコストがかかります。モバイルで使うにはドローコールが多くなりすぎる気がします。(ひとつのマテリアルごとに2ドローコール。)
    アセットに入っているモデルにStandardシェーダーを適用したものを使ったりすると、描画負荷を減らすことができます。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity Multiplayerの料金について」でPersonal版のUnityでは20CCUの制限があり、これを取り払うことはできなさそうだと書きました。一応Unityの方に質問をしてみたのですが、やはりそうだということでした。

    Unity Multiplayerを使ったゲームを作り、制限なくオンラインを行うにはPro版を買うしかありません。つまり、商用としてオンライン機能を提供するには、Personal版の場合はUnity Multiplayer以外の方法を考えるべきということになります。
    Unity MultiplayerはUnityに組み込まれているので使いやすいものです。それだけにマッチングのためのサーバーを利用したくても利用できないという現状は残念という他ありません。将来的にアセットストアで従量課金分チケットとか売ってそうな気もしますが、今のところはPhotonとかを利用するのが賢明だと思います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Invokeという便利な関数があります。これは好きなメソッドを何秒か遅れて呼ぶという機能です。ある時点で発生することは確定するが、少し遅らせて処理をしたいという時に使うと簡単に処理ができて便利です。

    「ボタンを押したときにSEが流れて、それからWebページに飛ぶ」という処理を作るときにこれを使いました。ボタンを押した瞬間でSEを再生するのですが、同じタイミングでWebページに飛ばしてしまうと制御がブラウザーの方に切り替わってしまい、SEが途切れてしまいます。
    これを防ぐため、 ボタンを押した瞬間でSEを再生し、同じタイミングでWebページに飛ばす処理をInvokeで予約しておくのです。0.5秒ほど(SEの長さによります。)遅れてWebページに飛ばすにはInvoke("GoWebpage", 0.5f);というようにします。

    すぐに処理を行わないので、1度しか実行したくない場合でも複数回実行してしまうミスが起こりやすいです。フラグによる制御でも良いし、CancelInvokeで呼ばれているものを消してからInvokeしても良いです。IsInvokingを調べて予約されていたら、Invokeしないというのでも同じことです。

    何回も処理を呼ぶInvokeRepeatingというのもあります。 間違って使うと処理の流れを複雑にしやすいですが、処理の遅延を簡単に書けるのは重宝すると思います。
    Invokeで呼んだ中でInvokeを呼ぶような構造にすると、厄介なので遅延のために1回だけ使うような単純な使い方が良いと思います。

    InvokeはMonobehaviourのメソッドで、アタッチしたゲームオブジェクトが消えると呼び出しは破棄されるようです。Invokeで重要な処理を呼ぶときは、ゲームオブジェクトが破棄されないようにしておくことになります。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    レビュー依頼をすることはGoogle Playで可能です。
    規約に違反するような可能性もあるので注意が必要なものではあります。公式の説明に違反の例があります。評価に対し報酬を与えるなどして、評価を操作することがいけないとあります。「★5の評価をしてくれたら、○○のアイテムをあげます!」とかは違反だということです。

    レビューについてやれることは、レビューのページ(アプリのあるページのこと)へのリンクをアプリ内に置くことだけです。ボタンを押したらレビューのページに飛ばすということが唯一許されています。以前は公式のページにもユーザーにレビューを求める例としてダイアログの画像があったのですが、今は消えています。したがって、将来的にはレビューをすることを頼むことも禁止されるかも知れません。

    良いレビューが増えるとランキングや新しくアプリをダウンロードしようという動機付けに一役買うでしょうから、アプリにレビューを依頼する処理を作ることは良いかもしれません。レビューしやすくなったために悪口ばかり増える可能性もあるので、単純にリンクを作るというのは駄目な気がしています。
    レビュー内容についての操作は違反なので、レビューの依頼で表示する文言は「レビューをお願いします。」というだけになります。下のような感じです。(レビュー依頼の実装は『トランプ・スピード』のものです。)
    request-review-dialog

    しかしながら開発者側に意味あるレビュー(アプリの内容に具体的に触れていて、使ったことのない人にもわかるようなもの。その上で……できれば好意的なもの。)が増えてほしいから、レビュー依頼を作るわけです。文言を変えずにそのようなことをするには、レビュー依頼のダイアログを表示するタイミングを設定するのが良いと思います。
    良いレビューをするには繰り返しアプリを遊んでもらう必要があると思います。そのため何回もリザルト画面に来ているなら、レビュー依頼をするというようにしてみました。「後で」以外のボタンを押した場合は、この後でダイアログを出すことはありません。そうすることで望んだ人以外はダイアログを何度も見ることにならずゲームの進行の邪魔をしないようにしています。

    レビューページへの遷移は、
    Google Play上のアプリのURLに飛ばすだけで可能です。レビュー依頼をするかというフラグとリザルトに何回来たかをセーブデータに持つことで何回も遊んだ人だけにダイアログを出すということが実現できます。やってみると意外と簡単な処理だと思います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    3Dではキャラクターを動かす処理を作ることが多いです。UnityでもCharacter Controllerというものが用意されています。これをゲームオブジェクトにつけてスクリプトでMoveを処理してやれば、移動ができます。

    Character Controllerは簡単に移動処理が作れるのですが、物理挙動がなされないという特徴があります。コライダーとの衝突を検知してめり込まないとかは行われるのですが、他のコライダーにぶつかってもそれに衝撃をあたえるということはデフォルトでは起こりません。OnControllerColliderHitを使うと、他のコライダーにぶつかったときにどうするかが制御できますが、どのような力を相手に与えれば適切なのかというのは物理の知識が必要です。
    リアリティを追及するには多少の知識が要りますが、入り口が緩いのがCharacter Controllerだと思います。

    Rigidbodyを制御して移動を行う方法があります。Rigidbodyを使えば物理に沿った動きを自動的に実現してくれます。移動の制御をおこなうには、力を加えるか速度を指定するかで可能です。
    力を加える場合は自然な動きになります。AddForceを使って移動方向に押してやることになります。慣性があるので動き出しと停止時にすぐに反応することはなくなります。ゲーム的にレスポンスが良くなくなるので、使いにくいことも多いでしょう。また、力の働き方によっては移動ではなく回転の方に使われてしまい、毎回挙動が変わるということも起こります。とくに問題になるのが、短い時間に力が何度も入力されることです。この場合、一気に加速されることになります。
    速度を指定する方法は自然に反しますが、ゲーム的には使いやすいです。 ある速度に強制することで瞬時の反応が可能であり、入力が連続で起こっても問題が起きにくいです。
    これはRigidbodyを使ったキャラクターの移動になります。カプセル型のオブジェクトがキャラクターです。ジャンプの処理は、地面に乗っているかを判定し上向きに速度を設定するだけで可能です。地面に乗っているかの判定は意外と難しい問題がありますが、レイキャストをキャラクターから下向きに発射しぶつかったものが何かで地面に立っているかを調べています。

    このツイートで重要なのは、TPS的な操作を実現していて物理的な動作も可能になっているということです。カメラはキャラクターの周り(キャラクターを原点とする極座標として計算)に位置するようにしています。
     

      このエントリーをはてなブックマークに追加 Clip to Evernote
    最近気付いたことなのでいつからある機能なのかわかりません。少なくとも今使っているUnity5.3.4f1 Personalには以下の機能があります。

    シーンビュー上で3D/2Dに関わらずゲームオブジェクトが重なってしまうことがあります。選択したいものの一部がクリックできれば選択状態にできますが、大きいものが手前に来るなどしているとクリックできず選択できないことがあります。

    シーンビュー上でクリックを繰り返すと、選択されるゲームオブジェクトが切り替わっていきます。クリックしたところにレイキャストして貫通したゲームオブジェクトを順番に選択しているような挙動をします。
    以下はUIでの例です。キャンバスが複数あり、UIは階層構造を持っています。クラブのQを選択したいのですが、上に重なっているUIがあるため直接は選択できません。何回かクリックすることでクラブのQが選択状態に切り替わりました。


    3Dでも同じようなことができます。2つのときはうまくいったのですが、3つ以上のゲームオブジェクトが重なっているときはうまくいくこともあれば、選択できないこともあったのでよくわかりません。
    あと位置移動のためのギズモをクリックしたときは選択切り替えは起こりません。それはゲームオブジェクト平面上で動かすためのショートカットが優先されるからです。


    重なったゲームオブジェクトをシーンビュー上で選択するには、そのゲームオブジェクトがあるところを何回かクリックしてみると良いです。これに慣れるとUnity UIを作るのが楽になります。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    第1日目
    神はプロジェクトを作った。プロジェクトには何もなかった。

    第2日目
    神はシーンを作った。シーンは世界であり、舞台であり、つまりは場所であった。

    第3日目
    神はシーンにゲームオブジェクトを作った。ゲームオブジェクトはシーンに存在するだけで何もしない存在であった。

    第4日目
    神はシーンに置いたゲームオブジェクトに世界を観察できる能力を与え、カメラとした。何も見えなかった世界はカメラを通して観察できるようになった。

    第5日目
    神はゲームオブジェクトに様々な能力を用意した。カメラ、ライト、レンダラー、……能力を与えればゲームオブジェクトは様々な役割をこなすことができた。

    第6日目
    神はスクリプトを作った。ゲームオブジェクトの能力を新たに作ることができる力である。この力をもってどんなことも成すことができた。

    第7日目
    神は完成したプロジェクトで遊ぶことにした。


    『創世記』をモチーフにUnityについて書いてみました。たまにはこういった遊びもおもしろいと思います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unityでゲームを作るとしてもやはりプログラミングが必要です。通常のゲームプログラミングとは異なる考えで作ることにもなるので、プログラミングをやったことがある人はむしろ混乱するかもしれません。
    Unityのプログラムはスクリプトと呼ばれ、 これをゲームオブジェクトというものにくっつけることで動きます。ゲームオブジェクトは様々な機能のスクリプトをくっつけることができるものです。”もの”というのはそれ自体で何かするのではなく、機能をつけることでライトになったり、カメラになったり、モデルになったりと何でもなれるけれど何でもないという置き場所のような意味です。

    何かを始めるときに覚えることが多いと嫌になります。UnityのGUI操作は覚えることがそれなりに多いので、プログラミングはとりあえず作れるようになるくらいで始めるのが良いと思います。少なければいくつでも良いですが、3つだとわかりやすいと思ったので3つの関数について説明します。

    ゲームを作るために必要な3つの関数とは、
    1. Update
    2. Start
    3. Awake 
    のことです。この名前をつけたメソッドはUnityから呼び出されて処理されるという特徴があります。これはイベント関数と呼ばれています。Updateは毎フレーム呼ばれる最も重要な関数です。ゲームの処理はここに書いていくことになるでしょう。この関数だけあればゲームを作ることもできますが、初期化は別に分けられていた方が都合が良いです。そのための処理がStartとAwakeです。 
    名前からだと微妙なところですが、Awakeが先でその後にStartが呼ばれます。通常はStartだけ使うだけで十分だと思います。AwakeとStartの微妙な違いを理解できるようになったら、他のイベントも覚えていることでしょう。

    よくAwakeとStartが呼ばれないということで問題になることがあります。大抵の場合、そのスクリプトがついているゲームオブジェクトがアクティブでないかスクリプト自身が有効でないと思います。
    それ以外で呼ばれないとすれば、ゲームオブジェクトにつけてないか名前の打ち間違えでしょう。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    開発中のスピードのアプリのUIがほぼ完成してきました。ゲーム的に重要なのは難易度ごとのAIの作成が残っていて、他にはタイトルやら細かな部分を作ることが残っているところです。
    今週くらいには予約を始めて4月中にはリリースできると良いかなという感じです。 

    トランプのアプリは今までと同じように名前をつけるので、『トランプ・スピード』 という名前にします。スピードは日本では有名なゲームなので、わかりにくいことはないと思います。シリーズものみたいなことで名前はこうすることにしました。
    スピードはトランプの中では変わったものになります。普通のゲームは順番にカードを出していくときに一人ずつ順番を守ります。 特に思考が要求されるゲームではそうです。スピードは出し方も重要ですが、どちらかと言えばスポーツよりで以下に早くカードを出せるかというゲームになります。アプリでスピードを再現するにあたり、カードの出した順番が見えることが重要だと思いました。
    当たり前のことを言うようですが、後に出したカードは表示が前に来ると、後に出したのだと知覚できます。プログラム的には表示の優先度のことです。現実でのカードは厚みを持ち、出されていくカードは重なっていきます。表示位置をずらしたり、3Dモデルを使って影を落とすことも効果的だと思いますが、最も重要なのは前面に表示されているカードがより後に重ねたものであるということです。

    2週間くらい前の画面が以下になります。
    old-input-ts-main
    画面上側のスペードの10と画面下側のハートの9が他のカードに覆いかぶさられているのがわかります。本来ならこれらのカードは移動中なので、後に出したカードのはずです。つまり、スペードの10とハートの9は前に表示されるべきです。このときはUIの優先度を制御してなかったので、このようになっていました。

    今は以下のように正しい表示になっています。優先度が動的に変わっていることを示すためgifになっています。
    input-ts-main
    クリックしたカードが最も前に表示されるようになっています。更に自分と相手のカードについても順番を考慮しています。相手と自分のカードで後に出されたものほど前に表示されます。

    どのように作っているかというと、自分と相手の場札(横に4枚並んだカード)がひとつのUIになっています。構造を見れば察しがつくとは思いますが、一応解説します。
    Cardsというのが表示のための部分です。Cardsの下に1枚のカードに対応するother0~3とmy0~3があります。otherは相手のでmyは自分の方のカードです。Unity UIではヒエラルキー上で見れる階層で下にあるものが最も上に表示されます。(同じCanvas内での話です。)カードを出すという処理をするときに対応する表示用のUIのtransformを取得し、SetAsLastSiblingで子の中で最も後ろに置くということをしてやれば表示を最優先にできます。
    ts-inputUI
    Inputがあるのは表示の優先度が変わると、それらにButtonをアタッチして押せる用にしているとまずいからです。常に固定した位置にタッチ判定を置くためにより優先されるところに入力用の透明なUIを配置しています。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity5.3からJsonUtilityという機能が追加されました。
    オブジェクトをJSONにすることとJSONからオブジェクトを作る・値を入れることができます。JSONはオブジェクトを文字列で表現する方法のことであり、単純な仕組みでエンコードされるために広く使われているフォーマットです。C#ではxmlやバイナリの形式にオブジェクトをシリアライズ(ファイルに保存できるような形式にすること)できますが、JSONはライブラリを使わないと使えません。 
    Unityで公式にサポートしてくれると使いやすくなるので、データの持ち方に1つ選択肢が増えたことになります。

    JSONの形式は文字列であるので、拡張性が高く柔軟だというのが特徴だと思います。あるオブジェクトAをJSONにして、別のオブジェクトBにデシリアライズ(オブジェクトに戻すこと)が可能です。なぜならJSONが文字列であり、データに何が含まれているのかを表しているだけだからです。

    これと異なる特性をもつのがバイナリです。データをメモリ上での表現をそのまま出力したような機械にとってわかりやすく、人間にわかりにくい表現です。
    バイナリ形式(BinaryFormatterを使った場合)は保存したオブジェクトと同じ型でデシリアライズしないとエラーになったり、バイナリにはフィールドがあるのに復元するクラスにはフィールドがなくなった場合にもエラーが起きます。これはBinaryFormatterで作ったデータになんらかの型情報が含まれていることを表しているでしょう。データがどういったクラスにより作られ、どういった要素が含まれているかがデータにあるために変更すると一致しなくなるのです。(どういった条件でデシリアライズできなくなるかまで調べていませんが、基本的にはフィールドの変更はしない方が良いと思います。)
    バイナリ形式はクラスの型を変更できないデメリットがありますが、文字列の解釈がないため一般的に変換が高速です。バイナリ形式の場合で将来拡張するなら、クラスに余分なフィールドを入れておくことが一番良い対策になります。余りに多くの使わないパラメータがあると無駄が大きいので、どれくらいの容量を取るかは判断が難しいところです。

    JSONを使う方が頻繁にアップデートが存在するアプリには合っているので、これを使っていた方が困らないことが多いでしょう。
    JsonUtilityは現在のところ型を利用する方法のみが使えるようです。JSONにシリアライズ、JSONからデシリアライズするためには変換用にクラスや構造体が必要だということです。 (intの配列とかをそのまま渡してもシリアライズできないです。)JSONに対してそのままデータを検索したり、データを追加したりするのは将来的にはあり得ると思います。(ドキュメントに「The JSON Serializer does not currently support working with ‘unstructured’ JSON」とあります。currentlyなので将来的にはサポートする気がします。)


    JsonUtility.ToJsonはそこまで注意することはないです。publicなフィールドをJSON化してくれます。NonSerialized属性をつけるとそのフィールドは無視されます。JSON化されるものが何かはインスペクタ―上で表示されているものと一致しているようです。例えば[SerializeField]がついたprivateなフィールドはJSON化されます。また逆に[System.NonSerialized]をつけるとpublicなフィールドでもJSON化されなくなります。
    ToJsonの第一引数はオブジェクトを渡します。
    [System.Serializable]
    public class Test
    {
        [SerializeField]
        private int a = 1;
        public int b = 2;
        public int[] c = { 12345 };
     
        public float f = 0.22f;
    }
    というクラスを定義して、これを作りJSONにしてみます。
    Test test = new Test();
    var json_test = JsonUtility.ToJson(test);
    というようにすると、json_testには以下の文字列が入っています。
    jsonNotPrettyPrint
    fの値が0.22に一致しないのは誤差のせいでしょう。JSONのデータはわかりやすい構造をしていて、データが正しいかのデバッグがしやすいです。

    第ニ引数にtrueを渡すと、下のように見やすく出力されます。改行とかインデントのための空白やタブは情報を持っていないので、情報としては無駄です。保存する場合やプログラム内でのやり取りにはPrettyPrintは使わないようにし、デバッグ用に使うのが良いです。
    jsonPrettyPrint


    JSONからオブジェクトを作るのは2通りあります。
    JsonUtility.FromJsonJsonUtility.FromJsonOverwriteです。
    Test obj = JsonUtility.FromJson<Test>(json);
    のようにすると、文字列jsonからTestクラスが作られます。使い方はシンプルなのですが、与える文字列jsonには注意が必要です。文字列はJsonUtility.ToJson等を使って同じクラスを表すJSONを使うのが通常でしょうが、特に制限はないです。全く異なるクラスから作られたJSONを渡したり、自分で文字列を作って渡しても問題ありません。
    そういった場合、余分なフィールドや型の異なるフィールドが出てくるでしょう。FromJsonはうまくできているようで、これらのものはすべて無視されます。FromJsonは型パラメータに渡したクラスのシリアライズできるフィールドを対象にし、JSONからフィールドの名前と型が一致したものだけ解釈しています。
    対応するものがないフィールドについては注意が必要で、このときフィールドはデフォルト値にされます。(例えばintだったら0です。)

    Unity5.3.4f1で試したところ、対応するものがなくてもデフォルト値にされてないように思います。
    [System.Serializable]
    public class Test2
    {
        public int a = 0;
        public int c = 5;
    }
    というクラスを新たに作り、
    Test test = new Test();
    var json = JsonUtility.ToJson(test);
    print("json:" + json);
     
    var from_json = JsonUtility.FromJson<Test2>(json);
    print("from_json:" + JsonUtility.ToJson(from_json));

    のように処理を書いてみました。 Test型のインスタンスをJSONにしたものを出力し、そのJSONからTest2のインスタンスを作っています。作ったTest2型のインスタンスのフィールドがどうなっているか知るため、JSONで出力しています。
    実行した結果は以下のようになりました。
    fromjson

    jsonにはcというフィールドがないので、Test2としてデシリアライズするときにcがデフォルト値0になるかと思ったのですが、そうはなりませんでした。JsonUtilityのバグかドキュメントのミスかも知れません。


    JsonUtility.FromJsonOverwriteはデシリアライズするJSONに復元しようとするクラスの対応するフィールドがない場合、そのフィールドには何もしません。
    上に述べたようにJsonUtility.FromJsonでも何故かフィールドの値が変更されてないため、現状ではインスタンスの生成の有無で使い分けることになると思います。

    このページのトップヘ