JAVAプログラミング講座
宇宙のJAVAさん
くま先生の ノベルゲームをつくっちゃお(全4回)
第2回 読み込んだ文章を画面に表示しよう
みなさん、こんにちは。
では、さっそく前回の続きを作っていきましょう。
めずらしくまともな講義のスタートだみゃ。
何を言うのです! 私はいつでも真面目です。
さすがは、くま先生!
前回の講義の最後にJAVAコンソールにテキストを表示するプログラムを書きましたね?
覚えてますか?
確認のための表示プログラムだも〜
そうです。
ちゃんとテキストファイルが読み込まれているのがわかったので、もう必要ありません。
消してください。
| if ( s==null ) break; scene[i] = s; } for(int i=0; i<1000; i++) { if(scene[i] != null) { System.out.println(">" + scene[i] + "\n"); } } } |
さて今回は読み込んだ文章を画面に表示したいと思います。
1画面で文章を15行表示したいと思います。
画面に表示するための変数を用意します。 変数名は
screen[] としましょう。
当然文字列を入れるので、String クラスです。 グローバル変数で初期宣言します。
この screen[] 文字列が、実際に画面に表示されるわけです。
| // グローバル変数 String scene[]; String screen[]; |
次に
scene[] 同様、init() で配列の初期化をします。
画面には15行表示する事にしたので、15個の配列で初期化します。
| public void init() { // 初期処理 scene = new String[1000]; screen = new String[15]; |
さて、では実際の文章表示プログラムを書きます。
第1部の STORY1 で言ったとおり、全ての作画に関するプログラムは paint() に書きます。
まず、画面全体を灰色で塗りつぶします。
| public void paint(Graphics g) { // 作画処理 g.setColor( Color.gray ); g.fillRect( 0, 0, 480, 360 ); |
setColor
で色(灰色)を設定し、fillRect で塗りつぶされた四角を描きます。
今回のアプレットの全体サイズが 480x360 なので、これで全ゲーム画面が灰色になります。
できたみゃ。 文章の表示はどうするみゃ?
文章の作画は
drawString 命令を使います。
この命令で 変数 screen[] の中に入っている文字を、15行にわたり表示します。
for文で15回ループをさせ、上から順に描いていきます。
最初に、右と下にわずかにずらして黒色の文字を描きます。
次に白色の文字を描きます。
これで、文字に影がつけることが出来ます。
文字のサイズは 17 にします。
| public void paint(Graphics g) { // 作画処理 g.setColor( Color.gray ); g.fillRect( 0, 0, 480, 360 ); g.setFont(new Font( "Dialog", Font.BOLD, 17 )); for ( int i=0; i<15; i++ ) { if( screen[i]!=null ){ g.setColor( Color.black ); g.drawString( screen[i], 3+2, (i*22)+22+1); g.setColor( Color.white ); g.drawString( screen[i], 3, (i*22)+22); } } } // paint() |
if(
screen[i]!=null ) って、何だみゃ??
もしも
screen[] の中に null が入っている行があったら、その行は表示しないようにしているのです。
ももも? 文字の表示のY座標がおかしいも。
g.drawString( screen[i], 3, (i*22)+22);
なんで、Y座標を22ドット増やすのかも?
あ、それはですね。 画像などは表示指定が左上が基準になってますよね。
ところが
drawString 命令で作画する文字列は、文字列の左下が基準になっているんです。

こんな、感じです。 だからY座標を増やしてやらないと、1番上の行が表示されないのです。
なるほども〜 (0,0)に作画された画像は表示されるけど、(0,0)に作画された文字列は、画面からはみ出してしまって消えてしまうのか〜
でも
screen[] は宣言と初期化をしただけだから、なにも入っていないも〜
これじゃ何も表示されないも。 どうするんだも〜
まぁ、そう急がないで下さい。
もちろんこれから、screen[] に文章を入れる為の関数を作ります!
テキストファイルの内容が入った
scene[] から、画面に作画される文字列を screen[] に内容をコピーする関数を作ります。
関数名は SetText() にします。
早速作るも〜
その前にい。
テキストファイルから読み込んだ文章は、普通15行以上ありますよね?
まぁ、ノベルだから15行以上だみゃ。 最大で1000行だしみゃ。
となると、ゲームでは15行ずつしか表示出来ないので、数回に分けて表示する必要があります。
そんな訳で、「現在 scene[] の何行目までを表示し終わったか?」という事を変数に保存しておく必要があります。
その変数を now_line という名前でグローバル宣言します。
| // グローバル変数 String scene[]; String screen[]; int now_line; |
あとは
init() で初期値を入れます。 初期値は 0 とします。
| public void init() { // 初期処理 scene = new String[1000]; screen = new String[15]; now_line = 0; |
では、関数
SetText() を作っていきましょう。
引数でコピーする行番号を指定できるようにします。
つまり、 SetText(10) という命令で、scene[10] から scene[25] までの内容が、screen[0]
から screen[15] にコピーされるわけです。
SetText関数の返り値として、読み終わったテキストの次の行を返すようにします。
つまり SetText(10) の返り値は、通常は 26 になるわけです。
そして、次の文章を表示したい時は、SetText(26) とすればよいのです。

図のような感じになります。
引数で指定した番号から15行分を scene[] から screen[] にコピーするわけです。
でも、全ての文章を読み(コピー)終わった場合は、返り値はどうなるも?
いい所に気がつきましたね。 scene[]
の全ての文章を読み終わったら、返り値は 9999 としましょう。
これはこのプログラムのルールになります。
では実際に 関数SetText() をプログラミングします。
場所はどこでもいいのですが、LoadScene() 関数の下に書くことにします。
| public int SetText( int line ) { } |
では関数の中身を書いていきましょう。
最初に screen[] の全てに null を入れて初期化します。
| public int SetText( int line ) { for ( int i=0; i<15; i++ ) { screen[i] = null; } |
次に
scene[] から screen[] に文字列をコピーします。
| public int SetText( int line ) { for ( int i=0; i<15; i++ ) { screen[i] = null; } for ( int i=0; i<15; i++ ) { screen[i] = scene[line+i]; } |
しかしこれだと、scene[]
の文章が無くなっても、screen[] にコピーしてしまいます。
文章が終わったらコピーするのを止めなければなりません。
「null が出てきたら文章の最後!」という事にしてもいいのですが、
より明示的に文章の最後を指定する為に、文章の最後に必ず「END」という文字の行を入れることにしましょう!
「END」という文字列が出てきたら、scene[] の文章を全て読み終わったということなので、返り値として 9999 を返すことにします。
| public int SetText( int line ) { for ( int i=0; i<15; i++ ) { screen[i] = null; } for ( int i=0; i<15; i++ ) { if (scene[line+i].equals("END")) return 9999; screen[i] = scene[line+i]; } |
あれ?
if ( scene[line+i]=="END" ) return 9999;
じゃ、ないのかも?
うーむ、ちっと違うんですよ。 例えば
int 型 の場合は、
int a;
if ( a==27 ) return 9999;
というのはOKです。
しかし String 型は特別で、if 文の条件式で == が使えないんですよ!
そうなのかも! なぜかも?
まぁ、めんどくさい理由があるのです・・・
そのため String型(String クラス)には、文字列が同じかどうか比較するメソット(関数)を持っています。
それが equals() です。
if (scene[line+i].equals("END")) return 9999;
つまり、String型の scene[line+i] の内容が END という文字列だったら、return 9999 が実行されます。
なるほども。 そうなると読み込むテクストファイルの最後に
END という行を書かないといけないも〜
そうです。 テキストファイルに書く(文章以外の)特殊命令をこの講座では「タグ」と呼ばせていただきますが、END
は「テキストファイルの最終行を表すタグ」になります。
さて、もしも15行読み終わった時点で、そのすぐ下の行が「END」だったらどうなりますか?
一見、まだ文章が続くように考えられるが、事実上は文章は終わりだみゃ・・・
そうです。 そこで15行読み終わったあとで、次の行が
END かどうか調べます。
もし END だったら、全ての文章を読み終わったということで 9999 を返り値とします。
| public int SetText( int line ) { for ( int i=0; i<15; i++ ) { screen[i] = null; } for ( int i=0; i<15; i++ ) { if (scene[line+i].equals("END")) return 9999; screen[i] = scene[line+i]; } if (scene[line+15].equals("END")) return 9999; return line+15; } |
そして、SetText()
の最後に、return line+15 を書きます。
つまり、まだ文章が下に続く場合の返り値です。
15行コピーされたので、引数に15をたした数字が返り値となります。
これで、ゲームは動くのかも?
SetText
関数は完成しましたが、それを呼びださなくてはなりません。
init() で LoadScene("start.txt") の下に、SetText()
を呼び出すプログラムを追加します。
| public void init() { // 初期処理 scene = new String[1000]; screen = new String[15]; now_line = 0; LoadScene("start.txt"); now_line = SetText( 0 ); repaint(); // マウス処理アクションリスナー定義と登録 addMouseListener(new MouseAction()); } // init() |
ついでにその後、再作画
repaint() をします。
LoadScene() でテキストファイルを読み込んだ直後なので、SetText(0) で scene[] の1行目(番号は0)から15行分、 screen[]
に文章をコピーします。
そして、コピーした最後の行の次の行を返り値として、now_line に入れます。
これで、プログラムがスタートすると
init() の LoadScene("start.txt") で start.txt が読み込まれます。
次に、SetText(0) で読み込まれた文章の1行目(番号0)から15行目(番号14)までが、screen[] にコピーされます。
SetText(0) は返り値として、コピーされた最後の行の次の行番号(通常は 15)を返しますので、それを now_line に入れます。
で、作画 paint() で screen[] の文章が画面に表示されるというわけです。
今回使用した文章は、「爆裂健
日記BEST」より抜粋された小説です。
このコーナー用に新たに小説を書こうと思ったのですが、時間が無いので間に合わせです。
これが「start.txt」です。
ちゃんと最終行に「END」が書き込まれています。
も〜! なんだかノベルゲームらしくなってきたも!
でも、全部の文章見られないみゃ。
マウスをクリックしても、文章が進まないみゃ!
では、マウスボタンを押すと次の文章に進むようにします。
マウスボタンを押すと呼ばれる関数は mousePressed(MouseEvent e) です。
ここにボタンが押された時の処理を書きます。
| public void mousePressed(MouseEvent e) { // マウスダウンイベント if( now_line==9999 ) return; now_line = SetText( now_line ); repaint(); } // mousePressed() |
まず、もしも
now_line が 9999 だったら、これ以上文章が存在しないということなので、マウスボタンを押されても何もしません。
で、now_line が 9999でない場合は、SetText( now_line ) で次の表示文章を scene[]
から screen[] にコピーします。 そして SetText() の返り値を now_line
に入れます。
で、再作画 sapaint() です!
なるほども。 これで
now_line が 9999 になるまで(文章が読み終わるまで)、マウスボタンを押すたび次々文章が表示されるも〜
あと、今回は
start.txt しかテキストファイルを読まないので必要ないのですが、将来いろいろなテキストファイルを LoadScene()
で読むことになります。
LoadScene() が呼び出されたら now_line を 0 に初期化しないとまずいので、それを書きます。
| public void LoadScene( String filename ) { BufferedReader ds = null; String s = null; now_line = 0; |
マウスボタンを押すたびに、ちゃんと文章が進んでいくみゃ!
あれ? ネットスケープだと文字がはみ出しているも??
そうなんです。 InternetExplorer
と Netscape では、文字を表示した時の文字間が違うのです。
Netscape の方が文字間が広いので、文字がはみ出してしまうのです。
こういうものは、統一してくれないと困るのですが・・・ 現在はブラウザによって文字間が違うのが現状です。
まぁ、勘弁してください。
解決策としては、「文字列を1文字ずつ切り出し、等間隔で1文字ずつ作画いく!」という手があります。
これなら1行作画でないので、等間隔に文字が配置出来るとおもいます。
それだも! それをやるも〜
めんどっちいので、パス!
そのうち気が向いたら、やりましょう・・・
だめも〜な、講師だも〜
くま
ですから・・・
次回は、いろいろなタグを増やして、画像を表示したりフラグを処理したりしましょう!
では、また次回お会いしましょう!

はーい!
|
テキストファイルのフォーマット(タグ一覧)
|
|
|
- 第3話につづく -
|
宇宙のJAVAさん (C) BakuretuKen 2000 |