読者です 読者をやめる 読者になる 読者になる

いものやま。

雑多な知識の寄せ集め

「BirdHead」のUIを作ってみた。(その5)

ゲーム開発 Swift BirdHead

昨日はプレイエリアの表示の実装を行った。

今日は手札の表示の設計を行っていく。
(※なお、タッチ処理の設計、実装はあとで)

手札表示の方法

手札を表示する上で考えないといけないのが、どうやって表示するか。

方法としては、

  • シンプルに横に並べて表示する
  • 手で持ったときと同じように、扇状に並べて表示する

というのが考えられる。
(自分の手番以外のときには扇状にしておいて、手番のときには横に並べて選びやすくするという方法もある)

参考として、他のカードゲームの手札表示をいくつか見てみると、以下のようになってる:

Sueca Pro(画面:縦、横に並べて表示) f:id:yamaimo0625:20151108114401p:plain:h600

Blackbird(画面:横、横に並べて表示) f:id:yamaimo0625:20151108114426p:plain:w600

Pinochle(画面:横、横に並べて表示) f:id:yamaimo0625:20151108114447p:plain:w600

Wizard(画面:縦、扇状に並べて表示) f:id:yamaimo0625:20151108114506p:plain:h600

ごいた(画面:縦、扇状に並べて表示) f:id:yamaimo0625:20151108114521p:plain:h600

モダンアート・カードゲーム(画面:横、手番以外は扇状、手番では横並び) f:id:yamaimo0625:20151108114538p:plain:w600 f:id:yamaimo0625:20151108114550p:plain:w600

実装としてはシンプルに横に並べる方が簡単。
けど、

  • 扇状に並べた方が、雰囲気がいい。
  • 扇状に並べた方が、同じ幅でもより多く表示が出来る。(*)

というメリットがあるので、出来れば扇状に並べたい。

なお、(*)はなぜかというと、次の図を見ると分かりやすく、扇状に並べた方が同じ幅でも弧の長さが長くなるから。

f:id:yamaimo0625:20151108120218p:plain

また、扇状に並べたときの方が、各手札の干渉も少なくなって、表示できる量も増える。

f:id:yamaimo0625:20151108121455p:plain

もちろん、画面を横に使う場合には、幅が広くなるので幅を気にする必要はあまりなくなり、むしろ扇状に並べたときに生じる高さの幅がネックになってくるのだけど、

  • 画面を横に使う場合、プレイするときに横向きに変えないといけない。
    • デフォルトの傾け方に好みがある。(ホームボタンを右にするか、左にするか)
    • 通知、コントロールセンター、アプリの切り替えが、横向きだと非常に使いづらい。
  • 縦幅が制限されるので、カードの縦方向の長さを短くしないといけない。

というのがあるので、出来るだけ画面は縦に使うようにデザインしたい。

ということで、Wizardやごいたと同様に、画面を縦に使い、扇状に並べる方法で表示したいと思う。

扇状表示の幾何

扇状に表示するとなると、カードの位置と角度を計算する必要が出てくる。

カードは円弧状に並ぶことになるので、図にすると以下のような感じ:

f:id:yamaimo0625:20151108124944p:plain

まず、図のように中心の角度を \thetaとしたとき、各カードのx座標は - r \mbox{sin}\thetaで表されることになる。

そして、y座標については、

  • 中心から表示されるエリア(フレーム)の一番上までの距離は、 r + \mbox{カードの半分の高さ} + \mbox{タッチ処理のためのマージン}
  • 中心からフレームの一番下までの距離は、 r \mbox{cos}(\mbox{最大の中心角}) - \mbox{カードの半分の高さ}

とすると、中心から座標系の原点までの距離は

  •  \frac{\mbox{中心からフレームの一番上までの距離} + \mbox{中心からフレームの一番下までの距離}}{2}

となり、これを使うと r \mbox{cos}\theta - \mbox{中心から原点までの距離}と計算できる。

※フレームの一番下までの距離を計算するときにカードの半分の高さを引いているけど、この大きさは任意で、カードの半分の高さを引いておけばカードがほぼ表示されるようになる(ちょっと下が切れる)ので、そうしてる。

カードの角度については、各カード間の角度を10度にするとよさそうだったので、今回はそうしている。
この場合、手札は最大10枚なので、間は最大9個となり、最大の中心角は10 * 9 / 2 = 45度となる。
(なお、角度は単位がラジアンなので、 \frac{\pi}{180}をかける必要がある)

問題は、半径 rをどうするかなんだけど、これについては(制限のある)フレームの幅をベースにして計算する。

具体的には、フレームの幅からカードの高さの2倍を引くと(これはカードの端が見切れてしまうのを防ぐため。カードの高さそのままだとちょっと見切れる可能性があるので、余裕を持って2倍を引いておく)、それが 2r \mbox{sin}(\mbox{最大の中心角})となるので、その値を 2 \mbox{sin}(\mbox{最大の中心角})で割ることで、半径 rを算出することが出来る。

まとめると、以下のとおり:

  1. 手札のフレーム幅を指定する。
  2. 半径 rを、 \frac{\mbox{フレーム幅} - \mbox{カードの高さ} \times 2}{2 \mbox{sin}(\mbox{最大の中心角})}で算出。
  3. 中心からフレームの一番上までの距離を、 r + \mbox{カードの半分の高さ} + \mbox{タッチ処理のためのマージン}で算出。
  4. 中心からフレームの一番下までの距離を、 r \mbox{cos}(\mbox{最大の中心角}) - \mbox{カードの半分の高さ}で算出。
  5. フレーム高をこの差から決定する。
  6. 中心から座標系の原点までの距離を、  \frac{\mbox{中心からフレームの一番上までの距離} + \mbox{中心からフレームの一番下までの距離}}{2}で算出。
  7. 中心角を \thetaとしたとき、カードの座標を以下のとおり計算する:
    • x座標は、 - r \mbox{sin}\theta
    • y座標は、 r \mbox{cos}\theta - \mbox{中心から原点までの距離}

今日はここまで!