連載「携帯アプリを作って学ぶJava文法の基礎」も今回で第7回となりましたね。前回の「Javaアプリ作成で役立つテンプレート」では、テンプレートを作成して、ケータイJava(本連載では、携帯電話/PHS/スマートフォンなどの端末をまとめて「ケータイ」と表記します)アプリの骨格を理解しました。
今回は、その骨格に肉付けしながら、フィールドの型の問題やスレッドなどのJavaの文法と仕組みについて説明します。
「実装」の前に「設計」を!
骨格に肉付け(以降、肉付けを「実装」といいます)しながら動作確認し、さらに実装して、という繰り返しは、プログラミングの最も楽しいフェイズの1つと筆者は思いますが、まず設計を行いどういうふうにアプリケーションを実装するか考えをまとめましょう。
今回の題材であるTrimisはテトリスにインスパイアされたゲームなので、ゲームのルール、つまり仕様もほぼ同じです。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
- フィールドは縦20行×横10列
- 時間とともにブロックが落ちる
- ブロックが下に積み上がる
- キーで回転や移動ができる
- ブロックが横一列にそろうと消える
- 上まで積み上がると、ゲームオーバー
テトリスと異なるところは、落下するブロックが、テトリスは4つの正方形の組み合わせ(テトラミノ)であるのに対し、Trimisは3つの正方形の組み合わせ(トリミノ)であることです。そのほかのタイトル画面やレベルアップの概念やハイスコアの保持などは、実装が簡単なため省略しています。画面設計もシンプルにしています。
ケータイのボタンでも快適に操作できるように、ハードドロップ(ブロックが一気に下に落ちる機能)だけは実装します。Trimisの規模であれば、基本設計は上記で十分です。もっと細かい仕様は作りながら決めていきます。
ソースコードをダウンロードしておこう
それでは、いよいよ実装を開始します。その前に、以下のソースコードをダウンロードして、前回ダウンロードしたそれぞれのテンプレートのソースコードと差し替えておいてください。
- DoJa:TemplateAppli.java
- MIDP:TemplateMIDlet.java
前回のテンプレートは、時計とキーを押したときのアラートが実装されていましたが、それらの実装はTrimisには邪魔なので、それすらなくしています。
フィールドを実装してみよう
フィールドは、仕様では20×10でした。ということは、2次元配列で実装できそうです。
フィールドは、ブロックのない場所を黒、落下中のブロックを赤、着地したブロックを青、外枠を灰色で表すことにします。
つまり、フィールドは黒、赤、青、灰色の4種類を表せればよいので、配列の型はbyte型(256種類)でもshort型(6万5536種類)でもよいのですが、今回はint型を使用することにします。
コラム 「Javaアプリはコードのサイズ節約に注意!」
20×10のフィールドの型にbyte型ではなくint型を使用することは、単純に考えると以下のような計算式になって、サイズが大きくなります。
- byte型の場合:1byte × 20 × 10 = 200bytes
- int型の場合:4bytes × 20 × 10 = 800bytes
配列だけを見ると、確かにint型の方がサイズは大きくなりますが、その配列を操作する処理も含めたクラスファイルのサイズとなると、逆転する傾向があります。
- byte型を用いたTrimisAppli.classのサイズ:7364bytes
- int型を用いたTrimisAppli.classのサイズ:7339bytes
これは、Java VMの仕様でint型専用の命令は充実しているものの、byte型専用の命令はない(少ない)ので、byte型からint型にして、処理して、byte型に戻す、という処理になるためです。
以下のコードについて見てみましょう(これ以降は、上級者向けのかなり難しい内容なので、読み飛ばしても構いません)。
void a() { for (int i = 0; i < 10; i++) { System.out.println(i); } } void b() { for (byte i = 0; i < 10; i++) { System.out.println(i); } }
上記のバイトコードは以下になります(Java SE 1.6環境)。
void a(); Code: 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 21 8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_1 12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 15: iinc 1, 1 18: goto 2 21: return } void b(); Code: 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 23 8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_1 12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 15: iload_1 16: iconst_1 17: iadd 18: i2b 19: istore_1 20: goto 2 23: return }
int型を使用しているa()メソッドがiincでインクリメントしているのに対し、byte型を使用しているb()メソッドは、iload_1で変数をスタックに積み、iconst_1でさらに1をスタックに積み、iaddでスタックに積んだ値を足し、i2bでバイトに変換し、istore_1でスタックの値を変数に戻す、という処理をしています。
for文のループカウンタをint型からbyte型にすると、上記のとおりバイトコードのサイズが増えているのが分かります。多くの場合、バイトコードが少ない方が処理速度は速いので、小さく速いJavaコードにするには、目先のサイズにとらわれずにint型を多用するのが有効なのです。
次ページでは、番兵を置き、スレッドについて解説し始めます。
Copyright © ITmedia, Inc. All Rights Reserved.