ああいうの、どういう処理をすればできるのかな? から始まって。まだ、選択の精度は微妙だけど、一区切りついたので公開。久しぶりに有意義なコーディングが出来た気がします。
モジュール変数を始めて使いました。前から、いつかモジュール変数やろうと思っていたのでいい機会でした。
#include "hspmath.as"
#const global TRUE 1
#const global FALSE 0
#module mdl_button x, y
#const BUTTON_RADIUS 20
#enum CURSORKEY_LEFT = 0
#enum CURSORKEY_UP
#enum CURSORKEY_RIGHT
#enum CURSORKEY_DOWN
#enum MAX_CURSORKEY
#const CURSORKEY_KEYCODE_START 37
#const NOT_EXIST_BUTTON_NUMBER -1
//
// モジュール初期化
#modinit int _x, int _y
x = _x
y = _y
return
//
// 一つのボタンを描画する
#modfunc draw_button int number, int is_selected_this
if is_selected_this : color $CC, $CC, $00 : else : color $4A, $6C, $76
circle x - BUTTON_RADIUS, y - BUTTON_RADIUS, x + BUTTON_RADIUS, y + BUTTON_RADIUS
color 255, 255, 255
font msgothic, 15
pos -32000 : mes number // テキストの描画サイズを取得
pos x - ginfo_mesx / 2, y - ginfo_mesy / 2 : mes number
return
//
// 座標を取得
#modfunc get_x
return x
#modfunc get_y
return y
//
// ランダムにボタンを配置
#deffunc random_set array buttons, int num, \
local up_cnt, local mx, local my, local dx, local dy, local IsOverlap
repeat num
up_cnt = cnt
// 重ならない位置を探す
repeat 50 // 無限ループが怖いので上限を設定
mx = rnd( ginfo_winx - BUTTON_RADIUS * 2 ) + BUTTON_RADIUS
my = rnd( ginfo_winy - BUTTON_RADIUS * 2 ) + BUTTON_RADIUS
IsOverlap = FALSE
repeat up_cnt
get_x buttons.cnt
dx = abs( stat - mx )
get_y buttons.cnt
dy = abs( stat - my )
if sqrt( dx * dx + dy * dy ) < ( 2 * BUTTON_RADIUS ) {
IsOverlap = TRUE
break
}
loop
if IsOverlap == FALSE {
break
}
loop
newmod buttons, mdl_button, mx, my
loop
return
//
// 「選択中のボタンから移動先のボタンへの角度」と「カーソルキーの示す角度」の差を計算( 1.0 ~ -1.0 )
#defcfunc calc_angle_dif var selected_button, var target_button, int cursorkey_number, \
local cursorkey_angle, \
local sb_x, local sb_y, local tb_x, local tb_y, \
local dif_angle
cursorkey_angle = ( M_PI@ / 2 ) * ( 2 - cursorkey_number ) // カーソルキーの示す角度
// ボタンの中央座標を取得
get_x selected_button : sb_x = stat : get_y selected_button : sb_y = stat
get_x target_button : tb_x = stat : get_y target_button : tb_y = stat
// 角度の差を計算
dif_angle = atan( sb_y - tb_y, tb_x - sb_x ) - cursorkey_angle
if dif_angle < -M_PI@ : dif_angle += M_PI@ * 2
if dif_angle > M_PI@ : dif_angle -= M_PI@ * 2
//dif_angle = 1.0 - absf( dif_angle ) * 2 / M_PI@
return dif_angle
//
// 選択中のボタンから移動先のボタンまでの距離を計算
#defcfunc calc_dist var selected_button, var target_button, \
local sb_x, local sb_y, local tb_x, local tb_y, \
local dx, local dy
// ボタンの中央座標を取得
get_x selected_button : sb_x = stat : get_y selected_button : sb_y = stat
get_x target_button : tb_x = stat : get_y target_button : tb_y = stat
dx = absf( tb_x - sb_x )
dy = absf( tb_y - sb_y )
return sqrt( dx * dx + dy * dy )
//
// 移動先としてのふさわしさの得点を計算する
//*/
#defcfunc calc_new_select_points array buttons, int selected, int cursorkey_number, int target, \
local len, local dist, local angle, local points
len = length( buttons )
dist = calc_dist( buttons.selected, buttons.target ) // 距離
if absf( dist ) < 0.001 : return 0
angle = absf( calc_angle_dif( buttons.selected, buttons.target, cursorkey_number ) ) // 角度の差
points = ( 1 - angle / ( M_PI@ / 4 ) ) * pow@( 1000 - dist, 2 )
return points
/*/
// 番号順のパターン
#defcfunc calc_new_select_points array buttons, int selected, int cursorkey_number, int target, \
local len, local points
len = length( buttons )
points = 0
if target == ( ( selected + 1 ) \ len ) {
points = 100
} else : if target == ( ( selected - 1 + len ) \ len ) {
points = -100
}
if ( cursorkey_number == CURSORKEY_LEFT ) || ( cursorkey_number == CURSORKEY_UP ) {
points *= -1
}
return points
//*/
//
// 各ボタンが選択し各カーソルキーを押したときの移動先ボタンのテーブルを生成する
#deffunc make_table array buttons, \
local len, local cnt_buttons, local cnt_keys, local cnt_other_buttons, \
local point, local max_point, local button_of_max_point, local new_select
len = length( buttons )
dim new_select_table, MAX_CURSORKEY, len
repeat len
cnt_buttons = cnt
repeat MAX_CURSORKEY
cnt_keys = cnt
// 他のボタン全てからどれが一番移動先としてふさわしいかの得点をそれぞれ計算し、一番高いものを採用
max_point = INT_MIN@ // 最小値でリセット
repeat len
cnt_other_buttons = cnt
if cnt_buttons == cnt_other_buttons : continue
point = calc_new_select_points( buttons, cnt_buttons, cnt_keys, cnt_other_buttons )
// 最高記録を更新したか(もしくは最初の記録か)
if point > max_point {
max_point = point
button_of_max_point = cnt_other_buttons
}
loop
dup new_select, new_select_table( cnt_keys, cnt_buttons )
if max_point <= 0 {
// ただし最高が0点以下だったら移動先ボタンはなしに
new_select = NOT_EXIST_BUTTON_NUMBER
} else {
new_select = button_of_max_point
}
loop
loop
return
//
// 新しく選択するボタンを取得
#defcfunc get_new_select int selected, int _keycode, \
local cursorkey_number, local new_select
cursorkey_number = _keycode - CURSORKEY_KEYCODE_START
if ( cursorkey_number < 0 ) || ( cursorkey_number > ( MAX_CURSORKEY - 1 ) ) {
return selected
}
new_select = new_select_table( cursorkey_number, selected )
if new_select == NOT_EXIST_BUTTON_NUMBER : return selected
return new_select
#global
// ランダムにボタンを配置
randomize
random_set buttons, 16
selected = 0 // デフォルトで選択されているのは0番のボタン
make_table buttons // 移動先ボタンのテーブルを生成
onkey gosub *l_onkey
gosub *draw
stop
//
// 描画
*draw
redraw 0
color 128, 128, 128 : boxf
foreach buttons
draw_button buttons.cnt, cnt, ( cnt == selected )
loop
redraw 1
return
//
// キーが押されたとき
*l_onkey
selected = get_new_select( selected, wParam )
gosub *draw
return