妹スレッドを作る
今日も今日とて由紀に呼び出された俺だった。
今日はいよいよ画面が動くらしい。
「はい、お兄ちゃん、ちゃんと来てくれましたね、では! いよいよ動く画面を作りますよ」
「ついに画面が動くのか、楽しみだな!」
画面が動かないとゲームっぽくないもんな、やっぱりグラフィックだよ。
「さあお兄ちゃん! そろそろどこをいじれば動くか予想がつくんじゃない?」
えーと、確か四角形の頂点と縦横幅を指定してたから……
「drawRectの括弧の中を変えればいいんじゃないかな?」
「いい考えですね、ちなみに括弧の中を数を引数と呼びます」
「じゃあ引数を変えれば……」
「変えてみますか?」
ずいとMacBookを差し出してくる由紀、onDrawの中のdrawRectの引数を変えれば……っと。
「あれ? 四角が別のところに出るだけだな……」
canvas.drawRect(300,1000,100,60,p);
四角は大きくなったけど動かないな……
「そうですね、発想はいいんですけど定数を入れても一回きりの実行時に変わるだけですね、ここは変数を使いましょう!」
変数? 確か数学で使ったなxとかyとか……代数学は苦手なんだよな……
「数字が変わっていけばいいのか?」
「そうですね、ここはy座標だけを動かしてみましょう、yは上から下に行くにつれて増えていくのに注意してください」
じゃあint y;で宣言して……drawRectの中の二番目にyを入れて、おっと4つ目の引数をy+100にしておくか……あれ?
「なあ、yをどんどん変えていけばいいのか?」
「そうですね、ヒントをあげるとfor文というものでfor(int i = 0; i < 100;i++){}で括弧内を100回実行出来ますよ。あ++は1足すことの省略記法ですよ」
ええっと例からすると……
for(int i = 0; i < 100;i++){y++;}
「これでどうだ?」
「まあやってみてください、大丈夫、壊れるようなことはありませんから」
スマホは初めから繋いであるので実行っと……
「あれ? 動かないな」
由紀はちょっと申し訳なさそうだ。
「ごめんなさい、意地悪でしたね。これをやると実行した瞬間にyが100増えてそれっきりなんですよ」
「100回だぞ! そんな一瞬で終わるの!?」
「お兄ちゃん、コンピュータを日本語に直すと「計算機」ですよ。そりゃ計算は超早いですよ」
「えぇ……じゃあどうすれば?」
「ここでマルチスレッドを使います」
「マルチスレッド? スレッドってあの掲示板とかのか?」
「いえ、今回のスレッドは並行プログラミングのことです」
なんか難しそうな言葉が出てきたぞ。
「並行? 今までのとは違うのか?」
「例えば今の例だとy座標を計算してから描画してるわけです、そこはいいですね?」
「つまり計算が終わってから描画するからyは変わらないってことか?」
どうやらyを描画前に計算しているらしい、そりゃ動かんわ。
「じゃあその並行プログラミングで解決するのか?」
「正確にはマルチスレッドはこういうのが目的じゃないんですけど使えますよ」
解決になるのはわかったが、結局マルチスレッドって何やねん?
「で『並行』ってことは二つ同時に処理するのか?」
「そうですよ、例えばカレーライスを作ろうとした時に一つしか同時に調理できなかったら冷めたご飯か冷めたカレーかができちゃうじゃないですか、カレーを煮込みながらご飯を炊くような感じです」
同時処理か……方法は想像もつかないな……
「とっても簡単なThreadクラスを使うという方法もあるんですけど、その方法はちょっと欠点もあるのでTimerTaskクラスを使います」
「タイマータスク?」
「平たく言えば別のスレッドを立ててそっちで定期実行する方法です、そんなに難しくないので安心してください」
今まででもついていくのがやっとなんだけどなぁ……と思う俺に察したように説明した。
「Javaに代表されるオブジェクト指向っていうのは『知らなくていいことは隠す』のが基本ですからわからないところはスルーしてもいいですよ。テレビを見るのにリモコンの使い方を知る必要はあっても電波の光学的なことを知る必要がないようなものです」
「使い方がわかればどうやって作ったかは問わないってことか?」
「そうですね、他の部分を変えた時にもクラスへの入力が一緒なら結果も同じっていうのが基本方針です」
知らなくていいと言われ多少気が楽になった、分かんないことだらけだもんなあ……
「ではまずclass imotoTimerTask extends TimerTask{}と書いてください」
「名前に何か意味はあるのか?」
「imotoTimerTaskは私がつけた名前なので変えてもいいです。ただextends TimerTaskはその名前にしてください、決まりですから」
由紀が作ったからイモートタイマータスクか、何というかまあ……気にしないでおこう。
「それでextends TimerTaskっていうのは何なんだ?」
「それはTimerTaskを継承するって意味です」
継承? 知らない言葉だな……
「継承というのは親クラス、ここではTimerTaskクラスですね、このクラスの内容をコピーした新しいクラスを作るってことです。設計図に例えるなら設計図をコピーして赤ペンで書き加えるようなものですね」
なるほど、おっといつも通り赤字が出たな……いつも通りにタイプしてっと……ん!?
「なあ、なんかウインドウが開いたんだが?」
「ああ、TimerTaskには継承した子クラスに書かないといけない関数があるんですよ、ここではrun()ですね。okを押せば追加されますよ」
「何を書けばいいんだ?」
「自由ですよ」
自由って……なんでも書いていいわけじゃないだろう。
「いや、こう書かないといけない、みたいなのはないのか?」
「runという名前のメソッドを作れというルールがあるだけで内容は自由に決められるんですよ、例えば『朝食を食べる』というのを決めてもそれがご飯かパンかまでは決めませんよね、それと同じです」
あ! じゃあここでyの数字を変えればいいんじゃないか!
「もしかしてこれが別スレッドで実行されるのか?」
「そうですそうです! いいじゃないですか! わかってますねえ!」
でもこれってimotoタイマータスククラスの実態を作っても実行できるのは一回じゃないかな?
「ここでyを書き換えれば別スレッドで動くのか?」
「そうなんですけど、もうちょっとトリックが必要ですね」
「それはさておき、ちょっとやっておいて欲しいことがあります」
「何だ?」
「画面の幅を取得するメソッドを作りましょう」
「えっ、画面幅は変わらないだろ?」
スマホの解像度が状況によって変わるなんてありえないだろう。
「確かに決め打ちしてもこのスマホでは動きますね、でも一応みんなが使えるアプリを目指しますよ! それに……」
「それに?」
「例えば画面幅を1000と決めて入力したとして、新しいスマホを買ったら幅が2000だったらどうしますか? 今まで書いた1000を全部書き換えますか?」
それはちょっと、いやかなりめんどくさいな……
「まあこれは簡単なのでさらりと流しましょう。private int width,height;とクラス宣言の真下に書いてください」
プライベートは確かそのクラスからしか書き換えられないんだったな、覚えてきたぞ。
「あとはgetSizeメソッドを定義しましょう、public void getSize(){}を書いてください」
「書いたぞ、あとはメソッドの内容か?」
多分さっき書いたwidthとheightを使うのだろうな。
「中身は簡単です。
-----
width = getWidth();
height = getHeight();
-----
この2行だけでいいです。
「たったこれだけなのか? だったらコンストラクタに入れてもいいんじゃないか?」
「うーん、確かにそうなんですけど、私がやった限りだとそれやると両方0になるんですよね。だからrun(){}の中括弧内の先頭にgetSize();と入れておきましょう」
「そんないい加減でいいのか?」
「いいんですよ、実装にこだわってもガチのゲームエンジンには勝てませんから、分かりやすさ重視です」
「このrunはどうやって実行するんだ? 普通にrun();と書いて呼べばいいのか?」
「下準備にimotoTimerTask tt = new imotoTimerTask();と書いてください」
「imotoTimerTaskの実態を作るってことだよな。あっ! tt.run();で呼べばいいのか?」
「いえ、今回の山場はそこです。まずはTimer t = new Timer();とコンストラクタ内に書いてください」
「タイマー? これも知らなくていいことなのか?」
由紀がちょっと考えた末に言った。
「そうですね、重要じゃないですねこのすぐ下にt.schedule(tt,33,33);と書いてくださいTimerにscheduleメソッドがあるということだけ知っておけばいいです。
ttはさっき作ったrunを持つクラスの実体、インスタンスって言います。のことですね」
「結局これは何をしてるんだ?」
「tt.run();を33ミリ秒間隔で実行するとスケジュールしています。では実行されるrun()の中身を書きましょうか」
つまり33ミリ秒ごとに実行されるんだから……こうか!
run(){y++;}
「うーん、惜しいですね。それで確かにyは変わるんですけど描画するのを忘れてます、invalidate();とy++;の下に書いてください。これは再描画をさせるメソッドです、組み込みなのでinvalidate();を実行すれば画面が更新されると覚えておけばいいです」
「じゃあこれを実行して……」
実行ボタンを押そうとすると由紀が止めた。
「まあちょっと待ってください、ついでにif文を書いておきましょう。条件分岐です」
「条件分岐? それって必要なのか? 特に分岐が必要なところはなさそうだけど」
落ち着けと言った風に由紀は言う。
「確かに無くてもいいですけど、それだと画面外に四角が出ていってそれっきりじゃないですか。簡単なので覚えておきましょう!」
「条件分岐って言うくらいだから条件があるんだよな、どんな風に書くんだ?」
「こんな風に書いてください」
-------
if(y >= height){
y = 0;
}
-------
「わかった! yが下まで落ち切ったら0に戻すんだな!」
「そうです>=は以上と言う意味で、yがheight以上なら{}の中を実行するんです!」
「よし! 書き足したし実行してもいいか?」
「どうぞ!」
実行ボタンを押し、繋がれたスマホを見る、すると……
「すげー! 動いてる動いてる!」
「ねっ! 結構感動するでしょう?」
悔しいが動くのを見たときはちょっとときめいた。
俺が書いたプログラムで動くってすごいな。
「はい、じゃあ今日はここまで。ゲームっぽくするのはまだ先ですよ!」
「おう! 任せろ! スーパーハッカーにもなれそうだぜ!」
「調子に乗らない」
俺をそうたしなめられた後、俺はMacBookを閉じた。