AndroidでSurfaceViewを使ったゲームアプリを作るときの設計

標準

世間的にはわざわざJavaをメインにせず、Kotlinを使ったり、Swiftを使ったりというのが主流なようです。なので、現段階ではこの記事はあまり役に立たないと言っても過言ではないと思います。が、それはそれとしても自分がハマって面倒くさくなったので、同じ轍を踏まないようにという備忘録がてら書いていきます。

作りたかったのは、こういう感じの画面構成のゲームアプリでした。

図1

左右ボタンがあって、ゲーム画面をアレコレ操作すると、スコアが増えたり減ったりというシンプルな構成です。別にAndroidでなくてもいいんですが、PCゲームは主流ではない、HTMLは使いたくない、iPhoneアプリを作るにはMacが必要……という理由からAndroidアプリを選びました。

上記画面で、どういうviewの構成にしようかと考えたところ、シンプルに『スコアはTextView』『ゲーム画面はSurfaceView』『ボタンはButton』という構成で考えてました。

図2

こんな感じですね。基本的にはSurfaceView内に色んな処理を突っ込んで、ButtonをクリックしたときはSurfaceView内のボタン処理メソッドをコールする、という流れです。SurfaceView内に別スレッドを立てて、ゲーム中はそのスレッドを無限ループさせるわけですね。

Threadを継承したオリジナルスレッドを作成し、surfaceCreatedでスレッドの無限ループを開始する、という流れは割とありがちな設計だと思います。というか、ネットでググったものを適当につまんだので、同様の流れにしてる人も多いと思います。

が、これだとあまりよくない設計のようだと気付いてちょっと心が折れているのがこれを書いてる最中です。良い画面設計は、おそらく以下のような感じです。

図3

画面全部をSurfaceView1枚で覆います。そしてSurfaceView内でスコア表示やゲーム画面表示、ボタンなどを配置していきます。ボタンはボタン画像を表示しておいて、タッチイベントを検知→ボタン画像と同じ座標だったらボタン処理を実行、という設計。

AndroidにはいろんなViewがあるんだからそれぞれで使い分ければいいんじゃないかという気持ちはわかります。が、SurfaceView1枚の方が良さそうという理由は『ほかのViewとの連携が楽ちん』というのが一番の理由です。

面倒くさいなと思ったのが『SurfaceViewのスレッドからスコアを更新する方法』でした。そもそもSurfaceViewから他のViewをどうやって操作するの?というところでだいぶ悩み、結果としてコンストラクタでtextViewを登録するやり方を検討しました。

ただし、その方法だとTextViewを更新するときに怒られます(buildエラーにはならない)。具体的には『Only the original thread that created a view hierarchy can touch its views.』というエラー。つまり、SurfaceView内で作ったスレッドで直接更新しようとすると怒られるわけです。

つまり、他のViewからSurfaceViewを更新する場合には問題ないのですが、SurfaceViewから他のViewを更新するときに不具合が生じてしまうわけです。対処法としてはHandlerを経由してActivityにTextViewを更新させる処理を追加する必要があります。

……という理由から、いちいち更新処理を実装するのが面倒なのでSurfaceView一枚にした方がいいよ、という話でした。ほかの入門書籍だとどうなってるんですかねぇ?

コメントを残す

メールアドレスが公開されることはありません。