時計

一応この章通りに作ってみるとこの様なものができるという完成品スクリーンショットを…。

まずはどのような仕様にするかを決めましょう。

時計の表示を12時間or24時間にするとか、デジタルorアナログとか。

デジタル時計は意外と簡単に出来ますが、アナログは三角関数なるものを使用するので少し高度です。

…と言うことでデジタル時計を作成していきます。

まずはコチラから使用する画像をダウンロードしてください。

それでは下のスクリプトを見てください。

	buffer 2
	picload "timer.bmp"
	screen 0,220,42
	title "脱講座1章サンプル"
	color ,,100 : boxf
	gmode 2 ; 黒色透過

*main ; メインループ
	wait 1
	repeat 3
		gettime tm.cnt,cnt+4 ; 現在時刻取得
	loop
	redraw 0 ; チラつき防止
	if tm.0 > 11 {
		pos 5,5 : gcopy 2,216,0,16,32 ; 午後ならPM表示
		tm.0-=12 ; 12時間表記
	} else {
		pos 5,5 : gcopy 2,200,0,16,32 ; 午前ならAM表示
	}
	pos  30,5 : gcopy 2,tm.0/10*20,0,20,32 ; 「時」部分1。後の解説参照
	pos  55,5 : gcopy 2,tm.0\10*20,0,20,32 ; 「時」部分2。後の解説参照
	pos  95,5 : gcopy 2,tm.1/10*20,0,20,32 ; 「分」部分1。後の解説参照
	pos 120,5 : gcopy 2,tm.1\10*20,0,20,32 ; 「分」部分2。後の解説参照
	pos 160,5 : gcopy 2,tm.2/10*20,0,20,32 ; 「秒」部分1。後の解説参照
	pos 185,5 : gcopy 2,tm.2\10*20,0,20,32 ; 「秒」部分2。後の解説参照
	if tm.2\2=0 {
		pos  81,5 : gcopy 2,232,0,8,32
		pos 146,5 : gcopy 2,240,0,8,32
	} else {
		pos  81,5 : gcopy 2,240,0,8,32
		pos 146,5 : gcopy 2,232,0,8,32
	}
	redraw
	goto *main

とりあえず、動く形にしてみたものですが、無駄と思える部分が多く、ダラダラ長いです。

それぞれの命令については解説しません(脱初心者ということで)。

 

pos 30,5 : gcopy 2,tm.0/10*20,0,20,32」これと同じような6行はややこしいので説明します。

tm.0は現在時刻の「時」の部分が入ります。

で、この「時/10」は現在の時刻を10で割った整数部分を求めています。

今回は12時間表記なので「時」には0〜11の値のいずれかが入ります。

この0〜11の値を10で割った商はというと「0」か「1」にしかならないですよね?

「*20」はまだ置いといて、同じように「pos 55,5 : gcopy 2,tm.0\10*20,0,20,32」を考えると、

「時」を10で割った余り、即ち「0」〜「9」のいずれかが入ります。

これで「時」の数値を表示することが出来ます。

10時とすると、「10/10」で1、「10\10」で0、「1」「0」とそれぞれの桁が出ました。

7時なら、「7/10」で0、「7\10」で7、「0」「7」とコレも表示すべき数値が出ました。

で、「*20」なのですが、これは数値に対応した画像の位置を示しています。

例えば5時のときは「pos 30,5 : gcopy 2,0*20,0,20,32」と「pos 55,5 : gcopy 2,5*20,0,20,32」です。

画像を見るとそれぞれの位置(0と100ドットの位置)に思惑通りの数値があると思います。

if文を多用し1の時は…2の時…と12パターン(計36個)全部を書いていって100%悪いわけではありません。

	 :
	if tm.0= 0 : pos 30,5 : gcopy 2, 0,0,20,32 : pos 55,5 : gcopy 2,  0,0,20,32
	if tm.0= 1 : pos 30,5 : gcopy 2, 0,0,20,32 : pos 55,5 : gcopy 2, 20,0,20,32
	if tm.0= 2 : pos 30,5 : gcopy 2, 0,0,20,32 : pos 55,5 : gcopy 2, 40,0,20,32
	 :
	if tm.0=12 : pos 30,5 : gcopy 2,20,0,20,32 : pos 55,5 : gcopy 2,180,0,20,32
	if tm.1= 0 : pos 30,5 : gcopy 2, 0,0,20,32 : pos 55,5 : gcopy 2,  0,0,20,32
	 :

何をしているかというのは見てすぐ分かりますがif文の判定で流れに時間がかかります。

2.6だとswitchを使うとif文よりかは若干楽にはなりますが…。

 

if tm.2\2=0」についても説明しておきましょう。

tm.2には「秒」が入ります。コレを2で割った余りですので「0」か「1」しか入りません。

要するに2分の1の確率でif文、もう2分の1でelse文が実行されますね。

実行すればわかりますが「:」がチカチカ点灯している処理がココにあたります。

	if tm.0 > 11 {
		pos 5,5 : gcopy 2,216,0,16,32 ; 午後ならPM表示
		tm.0-=12 ; 12時間表記
	} else {
		pos 5,5 : gcopy 2,200,0,16,32 ; 午前ならAM表示
	}

1つ目にに説明した方法でココを短くまとめられそうですね。

tm.0が12以上だとgcopyで216、違うなら200ドットから取り出せばいいわけですから、

gcopy 2,tm.0/12*16+200,0,16,32」これでもいけますねぇ・・・。

ということで下記の様に2行にまとめることができます。

	pos 5,5 : gcopy 2,tm.0/12*16+200,0,16,32
	if tm.0>11 :tm.0-=12

わかってると思いますが、先にtm.0-=12としてはダメですよぅ?

 

先ほど説明した「:」印の点滅部分ですが、

	if tm.2\2=0 {
		pos  81,5 : gcopy 2,232,0,8,32
		pos 146,5 : gcopy 2,240,0,8,32
	} else {
		pos  81,5 : gcopy 2,240,0,8,32
		pos 146,5 : gcopy 2,232,0,8,32
	}

ここも下記のようにまとめることができます。

	pos 81,5 : gcopy 2,tm.2\2*8+232,0,8,3
	pos 146,5 : gcopy 2,1-(tm.2\2)*8+232,0,8,322

「1-」というのは「0」か「1」しかない時に1を引くと反転できるのです。

「0」は1-0=1に、「1」は1-1=0と言うように、フラグには結構使えます。

 

この他の問題として、ウィンドウにマウスカーソルを近づけるとチカチカっと点滅しますね?

これは毎回毎回通るたびにウィンドウを書き換えているから起こるのです。

wait 1」だから1秒間に(約)100回書き換えていることになりますが、

書き換える必要があるのは1秒に1回ですよね?とても無駄な処理をしています。

だからと言って「wait 100」とすると、1秒ごとにはカウントしてくれますが、

タスクトレイ上の時計の時間と合っていないでしょう(秒まで表示している場合)。

そこで「wait 1」だが無駄な書き換え処理を防止する為のトラップを仕掛けるのです。

どんなのかといいますと「先ほど計ったときと同じ『秒』なら書き換えない」というトラップです。

古い「秒」情報を入れておく変数としてtm.3というのを使いましょう。

	if tm.2!=tm.3 ; 秒が先ほど計ったときと違ったら書き換え実行
	tm.3=tm.2 ; 新しい秒を入れておく

このようにしてしまえばよいのです。試すとカーソルのチカチカがなくなりましたね。

 

最後の問題として、mainのループが長い(mainラベルからgoto *mainまでが長い)ですよね。

こういうのをスパゲティ化と言うのですが、非常に見にくい(醜い)です・・・。

これを処理ごと(時間取得)や(画面描画)とかを処理単位ごとにgosubでくくってやるのです。

上記で書いたのをきれいにまとめる(最適化)と下記の様になります。

	buffer 2
	picload "timer.bmp"
	screen 0,220,42
	title "脱講座1章サンプル"
	color ,,100 : boxf
	gmode 2

*main ; メイン
	gosub *time
	gosub *draw
	wait 1
	goto *main ; ここまでがメインループ

*time ; 時刻取得処理
	repeat 3
		gettime tm.cnt,cnt+4
	loop
	return

*draw ; 描画処理
	if tm.2=tm.3 : return ; 先ほどの「秒」と同じなら描画処理終了
	tm.3=tm.2
	redraw 0
	pos 5,5 : gcopy 2,tm.0/12*16+200,0,16,32
	if tm.0 > 12 : tm.0-=12
	pos  30,5 : gcopy 2,tm.0/10*20,0,20,32
	pos  55,5 : gcopy 2,tm.0\10*20,0,20,32
	pos  95,5 : gcopy 2,tm.1/10*20,0,20,32
	pos 120,5 : gcopy 2,tm.1\10*20,0,20,32
	pos 160,5 : gcopy 2,tm.2/10*20,0,20,32
	pos 185,5 : gcopy 2,tm.2\10*20,0,20,32
	pos  81,5 : gcopy 2,tm.2\2*8+232,0,8,32
	pos 146,5 : gcopy 2,1-(tm.2\2)*8+232,0,8,32
	redraw
	return

さぁこれで時計の土台が完成しましたね!

こんな風にして「タイマー機能」なり「表示文字・背景の変更」等を付け足していき作成しましょう。

(一応ストップウォッチ機能の例)

	title ""
	width 200,50
	objsize 100,25
	button "start",start
	pos 100,0 : button"stop",finish
	stop

*start
	ms+
	if ms >= 100 : ms=0 : ss+
	if ss >=  60 : ss=0 : mi+
	if mi >=  60 : mi=0 : hh+
	redraw 0
	str mi,2 : str ss,2 : str ms,2
	color 255,255,255 : boxf : color
	pos 60,30 : mes ""+hh+":"+mi+":"+ss+"."+ms
	int mi : int ss : int ms
	redraw
	await 1
	goto *start

*finish
	stop

上記を追加してみたりしてみてください。

コピペすればいけるのだけど一応元のスクリプトほしい方はコチラから。

それでわっ。