FreelyApps

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


    ブログを
    https://freelyapps.net/
    に移転する予定です。
    リンク切れがある記事はこちらに移動した可能性もあります。

    カテゴリ:Unity > UI

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

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

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


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


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

      このエントリーをはてなブックマークに追加 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
    UIの位置を調整する場合はスクリーン座標を基準に考えるのが最も良いです。スクリーン座標とは表示するディスプレイ上でのピクセル単位の座標です。
    Unityでは左下が(0,0)、右上が(幅,高さ)となるように座標を定めています。カメラのところにちょろっと説明が載っています。
    こういったゲームビューなら右上の座標は画面のサイズになります。
    gameview

    なぜスクリーン座標で考えると良いのかというと、画面上でどこに表示されているかを直接表しているためです。画面上の位置が表示にそのまま関係するため最もわかりやすいのです。

    3DとUIを連携したい場合
    わかりやすい例で言うと3D空間上にいる物体の近くにUIを常に出したいといったことがあるでしょう。これはキャラクターにHPのバーを表示し続けるといったことが該当します。
    キャラクターが3Dだとすると、キャラクターの位置は3D空間上での位置を表しています。(モデルの中心位置に一致することでしょう。)x,y,zの3成分があるベクトルで表されますが、この数字はワールド座標であるはずです。空間上の位置から画面上のどこ(スクリーン座標)に表示されるのかを求める必要があるわけです。これはキャラクターを移しているカメラから変換行列をかけてもらい求めます。Unityではこの行列計算は、カメラコンポーネントを取得してWorldToScreenPointを位置に適用するだけなので簡単です。
    キャラクターのスクリーン座標がわかったら、UIの位置を補正するなどしてUIのスクリーン座標を決めます。頭上にHPを出したければ、キャラクターのスクリーン座標に適当な量yを増やせばいいです。このスクリーン座標をUIの座標にして、それをUIにセットすれば目的が達成されます。RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform rect, Vector2 screenPoint, Camera cam, out Vector2 localPoint)を使えば、あるRectTransform内でのローカル座標を求めることができます。rectは設定したいUIの親になることがほとんどになるでしょう。camはCanvasに使っているカメラを渡します。オーバーレイモードならnullにします。localPointには結果を取得するための変数を渡します。これで得られたlocalPointを設定したいUIのRectTransform.localPositionに設定すると完成です。

    UI同士を連携したい場合
    UIのCanvasが異なるなど単純には位置を求められない場合もスクリーン座標を介することで位置を連携することができます。
    あるUIの位置がスクリーン座標でどこになるかが分かれば良いことになります。これはRectTransformUtility.WorldToScreenPoint(Camera cam, Vector3 worldPoint)を使うと可能です。なぜかこれについてはドキュメントに載っていないようです。使い方は引数からわかるようにUIを表示しているカメラとRectTransform.position(ワールド座標)を渡します。
    スクリーン座標からUIの座標を求めることは既に述べたので、これでどちらにも変換ができます。


    UIのもつ座標→スクリーン座標とその逆であるスクリーン座標→UIのもつ座標の変換方法があれば自由に位置を調整できるということになります。
    • UIのもつ座標→スクリーン座標はRectTransformUtility.WorldToScreenPoint
    • スクリーン座標→UIのもつ座標はRectTransformUtility.ScreenPointToLocalPointInRectangle
    を使うことで可能です。
    注意点はピボットの位置がRectTransformの位置である(localPositionとかはピボットの位置を表しているということ)ということです。変換を行うときに微妙に位置がずれるときはピボットの位置がセンターだったり、トップだったりと統一がされていない可能性があります。

    意外とありそうなミスとしてはアクティブでないUIの座標を取得してしまうことです。このときの座標は正しくないようなので、起こりやすい間違いです。 これは消していたいUIの座標を取りたいという時に困るのですが、どうしようもないようです。

    以上のような変換はスクリプトで制御したい場合に必要になることが多いでしょう。Canvasの異なるUI同士の座標をそろえたりする機能が今のところなさそうなので、こういった方法が要ると思っています。
    もっと簡単な方法があると良いのですが…… 

    • カテゴリ:
      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity UIにはOutlineというコンポーネントがあります。
    実は影をつけるエフェクトもそうなのですが、単にコピーを下に描いているだけのものです。コピーの数は上下左右で4つです。そのため描画する文字は5倍になります。
    outline
    OutlineコンポーネントのEffect Distanceを大きな値にすると4つのコピーがあることがわかります。

    outline-edge
    「あ」の右下のはらいの部分で縁がいびつにになっているのがわかります。大きな文字だと意外と気になることもあります。
    4つコピーを配置することでも十分なこともあるのですが、もっと多くのコピーが存在している方が文字の縁が綺麗になります。ある文字から等距離にコピーを配置していけばうまくいきます。つまりコピーはオリジナルの文字列から一定距離の円周状に配置します。
    Unite2015 uGUIの拡張と応用」を参考に拡張したOutlineコンポーネントを作ってみました。拡張したOutlineコンポーネントCircleOutlineのソースコードはこの記事の一番下に置いておきます。(Unity 5.2.3では動いていますが、Unity 5.3以降でBaseMeshEffectの使い方が変わっている可能性があります。実際5.2で何回か変わっていて、そのたびに少しずつ変えています。)

    CircleOutlineで縁をつけたものが以下のようになります。10個のコピーを使った例が下の文字になります。縁が丸みを帯びて滑らかになっているのがわかります。
    circleoutline

    これはアウトラインが単なるコピーによってできていることを示す動画です。
    かなり離れたところに出るように設定してあるので、増えていくと円形の模様になっています。
    ol
     
    コピーしているという性質上オリジナルのアルファを下げていくと、コピーが重なっているところが目立ってきてしまいます。不透明にして使うとコピーがオリジナルに隠れて最も見やすいです。
    アウトラインをつけられるのはUIの要素のText以外にもImageも可能だったりします。

    ソースコードを以下に載せておきます。 

    このページのトップヘ