キー入力とFPS管理

ゲームを作る際によく出てくる処理としてキー入力やFPS調整があります。
簡単に扱えるモジュールを作りましょう。

誰得!!?
目標FPSを少数単位で設定できるようになりました。

ソース

	//--"keyFPS.hsp"
	#ifndef keyFPS
	#module keyFPS
		
	#uselib "winmm.dll"
	#cfunc keyFPStimer__ "timeGetTime"
		
	#deffunc manage_keyFPS__
		
		//FPS変更
		if fps ! prefps{
			prefps = fps
			//新たなFPSでのタイムテーブルを作成
			pn = table_num
			lpoke b
			gosa = 99999.
			sdim table, fps * FPStime
			repeat limit(fps * FPStime, 1, 65535), 1
				a = 1000. * cnt / fps
				poke table, cnt - 1, b + a
				b = 0 - a
				if gosa > a + b{
					gosa = a + b
					table_num = cnt
				}
			loop
			if pn ! table_num{
				sdim time_record, table_num
				lpoke fps_counter
				lpoke total_time
				tm = 1000. * table_num
			}
		}
		
		i = fps_counter \ table_num
		fps_counter+
		
		//FPS調整(ウェイトを行う)
		et = pt + peek(table, i) ; 今回の終了予定時刻
		at = et - keyFPStimer__() ; 余裕時間
		await at - 1 ; (余裕時間-1)だけ待って
		*@: if et > keyFPStimer__(): await: goto *@b ; 残りはawaitだけでループを回して時刻を合わせる
		
		apt = limit(stat - pt, 0, 255) ; 前回から今回までの経過時間
		pt = stat ; 今の時間を記録
		
		//FPS計測
		if fps_counter > table_num{
			total_time += apt - peek(time_record, i)
			cfps = tm / total_time
		}else{
			total_time += apt
			cfps = 1000. * fps_counter / total_time
		}
		poke time_record, i, apt
		
		//マウス位置取得
		w = ginfo(10) - ginfo(12) >> 1
		mx = ginfo(0) - ginfo(4) - w
		my = ginfo(1) - ginfo(5) - ginfo(11) + ginfo(13) + w
		
		//キー取得
		if ginfo(2) >= 0{
			repeat max
				dup key, keys(cnt)
				dup c, count(key)
				getkey j, key
				if j{
					if c > 0{
						c+
					}else{
						if flag(key){
							c-
						}else{
							c = 1
						}
					}
				}else{
					lpoke flag(key)
					if c > 0{
						c = -1
					}else{
						c-
					}
				}
			loop
		}else{
			repeat max
				dup key, keys(cnt)
				dup c, count(key)
				if c > 0{
					if type(key) = 2{
						getkey j, key
						if j{
							c+
						}else{
							c = -1
							flag(key)+
						}
					}else: if type(key) = 1{
						getkey j, key
						if j{
							c+
						}else{
							c = -1
						}
					}else: if type(key): else{
						c = -1
					}
				}else{
					if type(key) = 2{
						c-
						getkey j, key
						if j: else: flag(key)+
					}else: if type(key) = 1{
						getkey j, key
						if j{
							c = 1
						}else{
							c-
						}
					}else: if type(key): else{
						c-
					}
				}
			loop
		}
		
		return
		
	#deffunc addkey__ int p1, int p2
		if count(p1) || p1 <= 0 || p1 >= 256: return
		keys(max) = p1: max+
		type(p1) = p2
		lpoke flag(p1)
		
		if ginfo(2) >= 0 || p2 = 1{
			getkey i, p1
			count(p1) = (i * 2 - 1) << 1
		}else{
			count(p1) = -2
		}
		
		return
		
	#deffunc delkey__ int p1
		repeat max
			if keys(cnt) ! p1: continue 
			lpoke count(p1)
			max-
			keys(cnt) = keys(max)
			break
		loop
		return
		
	#defcfunc getdbl_ms__ int p1, int p2
	if ppt(p1) = pt: return peek(dbl_result, p1)
	if count(p1) = 1{
		ppt(p1) = pt
		if pt - dbl_time(p1) <= p2{
			lpoke dbl_time(p1)
			poke dbl_result, p1, 1
			return 1
		}
		dbl_time(p1) = pt
	}
	poke dbl_result, p1
	return 0
		
	#defcfunc getPhold__ int p1, int p2, int p3
		if count(p1) = 1: return 1
	#defcfunc gethold__ int p1, int p2, int p3
		if count(p1) < p2: return 0
		if p3: if (count(p1) - p2) \ p3: return 0
		return 1
		
	#deffunc init_keyFPS
		dim count, 256
		dim ppt, 256
		dim dbl_time, 256
		sdim dbl_result, 256
		FPStime = 3.0
		fps = 60.
		pt = keyFPStimer__()
		return
		
	#global
	init_keyFPS
		
	//以下インターフェース ( このモジュールで使用可能になる全ての機能です )
		
	#define global       manage_keyFPS                   manage_keyFPS__ ; キーの取得とFPSの管理を行う
		
	#define global ctype ms_to_frame(%1)                 (%1 * fps@keyFPS / 1000) ; 現在のFPSにおいての ミリ秒→フレーム数を算出
	#define global ctype frame_to_ms(%1)                 (1000 * %1 / fps@keyFPS) ; 現在のFPSにおいての フレーム数→ミリ秒を算出
		
	//FPS関連
	#define global       setfps(%1)                      fps@keyFPS = limitf(%1, 3.92157, 360); 目標FPSを設定する
	#define global ctype getfpsf                         fps@keyFPS ; 目標FPS
	#define global ctype getrealfpsf                     cfps@keyFPS ; 実現FPS
	#define global ctype getfps                          (0 +  fps@keyFPS) ; 目標FPS(整数)
	#define global ctype getrealfps                      (0 + cfps@keyFPS) ; 実現FPS(整数)
	
	#define global       setFPStime(%1 = FPStime@keyFPS) FPStime@keyFPS = limitf(%1, 0.1, 9): dim prefps@keyFPS ; FPS精度を設定
	#define global ctype getFPStime                      FPStime@keyFPS ; FPS精度
	
	#define global ctype slowdown                        (at@keyFPS <= 0) ; 処理落ちしているかどうか
	#define global ctype frametime                       apt@keyFPS ; 前回のフレームにかかった時間
		
	//キー状態取得関連
	#define global ctype _getkey(%1)                     count@keyFPS(%1) ; キーの状態取得
	//A宗向け(ms単位)
	#define global ctype getdbl_ms(%1, %2 = 0)           getdbl_ms__(%1, %2) ; Double_Click
	#define global ctype gethold_ms(%1, %2 = 0, %3 = 0)  gethold__(%1, ms_to_frame(%2), ms_to_frame(%3)) ; Hold
	#define global ctype getPhold_ms(%1, %2 = 0, %3 = 0) getPhold__(%1, ms_to_frame(%2), ms_to_frame(%3)) ; Press & hold
	//B宗向け(フレーム単位)
	#define global ctype getdbl(%1, %2 = 0)              getdbl_ms__(%1, frame_to_ms(%2)) ; Double_Click
	#define global ctype gethold(%1, %2 = 0, %3 = 0)     gethold__(%1, %2, %3) ; Hold
	#define global ctype getPhold(%1, %2 = 0, %3 = 0)    getPhold__(%1, %2, %3) ; Press & Hold
		
	#define global ctype mousex_                         mx@keyFPS ; マウスの位置x(画面外でも位置を取得)
	#define global ctype mousey_                         my@keyFPS ; マウスの位置y(画面外でも位置を取得)
		
	//監視キー関連
	#define global       addkey(%1, %2 = 2)              addkey__ %1, %2 ; 指定したキーを登録、%2 = 画面切り替え時の動作
	#define global       delkey(%1)                      delkey__ %1     ; 指定したキーを削除
	#define global       addkey_all(%1 = 2)              repeat 255, 1: addkey__ cnt, %1: loop ; 全てのキー登録
	#define global       delkey_all                      repeat 255, 1: delkey__ cnt    : loop ; 全てのキーを削除
		
	//画面切り替え時の動作設定( 2がデフォルト値になっています )
	; 0 = 非アクティブ時には検出しない
	; 1 = 非アクティブ時でも検出する
	; 2 = アクティブ→非アクティブ 一旦離すまでは検出する
	;     非アクティブ→アクティブ 一旦離すまでは検出しない
	;     アクティブ→非アクティブ→アクティブ 継続して検出されます。
	; 3 = 非アクティブ時は値が変化しない
	#define global       setkey_type(%1, %2)             type@keyFPS(%1) = %2 ; 画面切り替え時の動作設定を行う
	#define global ctype getkey_type(%1)                 type@keyFPS(%1)      ; 画面切り替え時の動作設定を取得
		
	#define global ctype keyFPStimer                     keyFPStimer__@keyFPS() ; Windows が起動してからの経過時間 ( timeGettime )
		
	#endif
	//--"keyFPS.hsp" --End Of File
	#ifndef __TEST
	
	posx = 300
	posy = 250
	c = $EEEEEE, $44FF99
		
	addkey_all ; 全てのキーを登録
	delkey 'T' ; Tキーは除外
	
	repeat
		
		manage_keyFPS ; キーの取得とFPSの管理を行う。メインループの先頭で呼び出してください。
		
		if _getkey(37) > 0: setfps getfpsf() - 1 ; FPSを上げます
		if _getkey(38) > 0: setfps getfpsf() + 0.0001 ; FPSを少し上げます
		if _getkey(39) > 0: setfps getfpsf() + 1 ; FPSを下げます
		if _getkey(40) > 0: setfps getfpsf() - 0.0001 ; FPSを少し下げます
		
		w = mousew
		if w > 0: setFPStime getFPStime() + 0.1 ; 精度を上げます
		if w < 0: setFPStime getFPStime() - 0.1 ; 精度を下げます
		
		if _getkey(1) =  1: posy += 40 ; 左クリック押した瞬間
		if _getkey(1) = -1: posy -= 40 ; 右クリック離した瞬間
		
		if getdbl_ms(2, 250): flag ^ 1 ; 右ダブルクリック(250ms以内に二回押されました)
		
		if getPhold_ms('Z',400,100): posx = limit(posx - 20, 0, 600) ; Z押した瞬間と長押し(400ms経つ時から、100ms毎に)
		if getPhold   ('X',  9,  3): posx = limit(posx + 20, 0, 600) ; X押した瞬間と長押し(9フレーム後から、3フレーム毎に)
		
		if slowdown() & cnt \ 2: continue ; 処理落ちしているときは二回に一度描画スキップ
		
		redraw 2
		color: boxf: pget 65535: pos 5, 5
		
		mes "左右、上下キーでFPS変更、ホイールで精度変更"
		mes "目標FPS = " + getfpsf()
		mes "実現FPS = " + getrealfpsf()
		mes "精度 = " + getFPStime()
		mes "処理落ち = " + slowdown()
		mes
		mes "Tキーの状態 = " + _getkey('T') ; 登録されていないキーなので常に0です 
		mes "スペースキーの状態 = " + _getkey(32) ; 押されている間は 1,2,3,4....  押されていなければ, -1,-2,-3,-4....
		mes
		mes "マウスX = " + mousex_()
		mes "マウスY = " + mousey_()
		mes
		mes "クリック → 上下"
		mes "右ダブルクリック → 色が変わるよ"
		mes "Z,Xキーを長押し → 左右"
		pos posx, posy
		color peek(c(flag),2), peek(c(flag),1), peek(c(flag))
		mes "●"
		
		redraw
		
	loop
		
	#endif