ランダムに配置されたボタンをカーソルキーで選択する

ああいうの、どういう処理をすればできるのかな? から始まって。まだ、選択の精度は微妙だけど、一区切りついたので公開。久しぶりに有意義なコーディングが出来た気がします。

モジュール変数を始めて使いました。前から、いつかモジュール変数やろうと思っていたのでいい機会でした。

#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

インフォメーション

公開日時
2007年9月6日 午後3時5分43秒
最終更新日時
2007年9月9日 午後9時50分23秒
カテゴリ
HSP