2007年10月31日水曜日

どう書く?org ベータ版公開

どう書く?orgベータ版というサイトをCodeZineさま経由で見つけました。
プログラミングのお題が沢山あって、お題を探している方や新しい言語に挑戦される方にはちょうど良いサービスだと思います。

お題の流用を禁止してはいないようなので、私がどう書く?orgに投稿した記事の一部をこちらのブログにも残していきたいと思います。

2007年10月28日日曜日

角丸四角形を描画

標準命令で角丸四角形を描くモジュール。
半径が大きすぎた場合は自動で調節するおまけつき。

// 角丸四角形を描画する
#module
#define ctype max%1%2 ) ( %1 )*( %1 > %2 ) + ( %2 )*( %1 <= %2 )
#define ctype min%1%2 ) ( %1 )*( %1 < %2 ) + ( %2 )*( %1 >= %2 )
#const DEFAULT_R 20
/*
    rboxf (左上X座標), (左上Y座標), (右下X座標), (右下Y座標), (角の半径)
*/

#deffunc rboxf int _x1, int _y1, int _x2, int _y2, int _r
    x1 = min(_x1, _x2) : x2 = max(_x1, _x2)
    y1 = min(_y1, _y2) : y2 = max(_y1, _y2)
    box_width = x2 - x1 : box_height = y2 - y1

    if ( _r <= 0 ) {
        r = DEFAULT_R
    } else {
        r = _r
    }
    r = min( r, min( box_width / 2, box_height / 2 ) )

    boxf x1, y1+r, x2, y2-r
    boxf x1+r, y1, x2-r, y2
    repeat 4
        x = x1 + ( cnt \ 2 ) * ( box_width - r * 2 + 1)
        y = y1 + ( cnt / 2 ) * ( box_height - r * 2 + 1 )
        circle x, y, x + r * 2, y + r * 2
    loop
    return
#global
// サンプル
    rboxf 505030030050
    rboxf 600400400300100   // 右下→左上の順で指定してもOK
                                    // 半径が大きすぎる場合は自動で調節する

2007年10月23日火曜日

特定の文字列を説明文に含む命令・関数を抽出する

空色鉛筆の方に載せるサンプルスクリとして書いたものの、ちょっとややこしすぎたのでこちらへ。
HDLがインストールされていないと動作しませんので注意してください。
// 動作条件・SQLele1.10以降
// およびHSP Document Library BETA 0.01 以降が
// インストール済みであること

#include "sqlele.hsp"
#define DB_NAME "hdlbase.xdb"

    chdir dir_exe
    exist DB_NAME
    if ( strsize < 0 ) {
        dialog "データベースファイルが見つかりません。プログラムを終了します。"1
        end
    }

    key_word = "%ボタン%"   // 検索キーワード
                            // %は0文字以上の文字列にマッチ
                            // _は任意の1文字にマッチ
    sql_open DB_NAME

    // キーワードが説明文にマッチする命令・関数名を検索する(昇順ソート)
    sql_q "SELECT Name FROM Help WHERE Inst LIKE " + prm_text( key_word ) + " ORDER BY Name"
    count = stat

    mes "文字列'" + key_word + "'がマッチした項目一覧:"
    repeat count
        mes " - " + sql_v"Name" )
        sql_next
    loop
    mes "以上" + count + "件"

    sql_close
    stop

2007年10月9日火曜日

論理和とビットシフトで掛け算する

ビットシフトを使った掛け算。CASL2などを学んだ方なら動作原理はご存知かと思います。
前回の加算モジュールを利用しています。

関連:http://blog.livedoor.jp/dankogai/archives/50638838.html

#module
#defcfunc add int left_op, int right_op, local left, local right, local tmp
    left = left_op : right = right_op
    while ( left & right )
        tmp = ( left & right ) << 1
        left ^= right
        right = tmp
    wend
    return ( left | right )
#defcfunc multi int left_op, int right_op, local left, local right, local tmp
    left = left_op : right = right_op
    while ( right )
        if ( right & 1 ) : tmp = add( tmp, left )
        left <<= 1
        right >>= 1
    wend
    return tmp
#global
    randomize
    repeat 10
        l = rnd9 ) + 1
        r = rnd9 ) + 1
        mes strf"%1d", l ) + " * " + strf"%1d", r ) + " = " + strf"%2d"multi( l, r ) )
    loop
    stop

2007年10月8日月曜日

論理積と論理和と排他的論理和で足し算する

ビット演算で足し算を行うスクリプト。
単純で短いスクリプトですが、再帰が必要だったり排他的論理和(XOR)演算を行う必要があったりと少々高度です。やっていることは小学校で習う「筆算」と同じなのですが。

CPU内部の演算は、もしかしたらこうして行われているのかもしれませんね。ちょっと調べてみます。
#module
#defcfunc add int left_op, int right_op
    kuri = ( left_op & right_op ) << 1  // 繰り上がり
    if ( kuri == 0 ) {
        // 繰り上がりがなければ論理和を返す
        return ( left_op | right_op )
    } else {
        // 繰り上がりがある場合は、
        // 繰り上がりと排他的論理和の結果を加算する
        return add( left_op ^ right_op, kuri )
    }
#global
    randomize
    repeat 10
        l = rnd99 ) + 1
        r = rnd99 ) + 1
        mes strf"%2d", l ) + " + " + strf"%2d", r ) + " = " + strf"%3d"add( l, r ) )
    loop
    stop

再帰ではなくループを使う方法はこちら。

#module
#defcfunc add int left_op, int right_op
    left = left_op : right = right_op
    while ( left & right )
        tmp = ( left & right ) << 1
        left ^= right
        right = tmp
    wend
    return ( left | right )
#global
    randomize
    repeat 10
        l = rnd99 ) + 1
        r = rnd99 ) + 1
        mes strf"%2d", l ) + " + " + strf"%2d", r ) + " = " + strf"%3d"add( l, r ) )
    loop
    stop

2007年10月2日火曜日

俯瞰型RPGマップの描画 2

前回のスクリプトにカメラの移動とキャラクタの移動・ジャンプを追加しました。マップチップの明るさは距離に応じた変化に限定しています。
ここまでくるとRPGというよりはアクションに近いものがありますね。

カメラの位置に合わせてカーソルキーで移動する方向を変化させる処理や、ビルボードのようにキャラクタを常に手前を向ける処理など、三角関数を多用しているので、今回のスクリプトはさすがに読みづらいと思います。d3setlocal命令によって多少は読みやすくなっていますが……。興味のある方はぜひ式を解いてみてください。
// カーソルキーでキャラクタの移動
// Ctrl同時押しでカメラの移動(回転)
// Spaceでジャンプ
#include "d3m.hsp"
#include "hspmath.as"
#const CELL_SIZE 100    // d3m上のマップチップサイズ
#const IMG_SIZE   64    // bmpのマップチップサイズ
#const CAM_DIST  500
#const CHAR_SPEED 15
#const MAP_WIDTH  18
#const MAP_HEIGHT 15
    // マップチップのロード
    buffer 1 : picload dir_exe + "/sample/game/testchr.bmp"

    // 影用の画像を用意する
    color : boxf IMG_SIZE * 30IMG_SIZE * 4IMG_SIZE
    color 255,255,255 : circle IMG_SIZE * 30IMG_SIZE * 4IMG_SIZE1

    gsel 0

    // 変数の初期化
    cam_theta = M_PI * 2 / 5    // カメラの角度
    cam_phi = -M_PI / 2
    char_x  = 0.0               // キャラクタの位置
    char_y  = 0.0
    char_z  = 0.0
    char_vz = 0.0               // キャラクタの速度

    // マップ作成
    dim map, MAP_WIDTHMAP_HEIGHT
    repeat length2( map )
        y = cnt
        repeat length( map )
            map( cnt, y ) = rnd3 )
        loop
    loop
    gosub *draw

*main
    stick keys, 15 + 64
    gosub *move
    if ( is_jumping ) | ( ( keys & 15 ) > 0 ) : gosub *draw
    wait 3
    goto *main

*move
    if ( keys >> 4 & 1 ) & ( is_jumping == 0 ) {
        char_vz = 20.0  // ジャンプ開始
        is_jumping = 1  // ジャンプ中フラグをON
    }
    char_vz -= 2.0      // 重力による加速
    char_z += char_vz   // Z方向の移動
    if char_z < 0.0 {
        // 着地時の演算
        char_z = 0.0
        char_vz = 0.0
        is_jumping = 0
    }
    if keys & 64 {
        // ctrl同時押し…カメラの移動
        cam_theta = limitf( cam_theta + 0.05 * ((keys >> 3 & 1) - (keys >> 1 & 1)), 0.01, M_PI / 2 - 0.01 )
        cam_phi += 0.05 * ((keys >> 2 & 1) - (keys & 1))
    } else {
        // ctrlを押さない…キャラクタの移動
        keys_x = (keys >> 2 & 1) - (keys & 1)
        keys_y = (keys >> 3 & 1) - (keys >> 1 & 1)
        char_x = limitf( char_x - ( sin( cam_phi ) * keys_x - cos( cam_phi ) * keys_y ) * CHAR_SPEED, -MAP_WIDTH/2*CELL_SIZE, (MAP_WIDTH-1)/2*CELL_SIZE)
        char_y = limitf( char_y + ( cos( cam_phi ) * keys_x + sin( cam_phi ) * keys_y ) * CHAR_SPEED, -MAP_HEIGHT/2*CELL_SIZE, (MAP_HEIGHT-1)/2*CELL_SIZE)
    }
    return

*draw
    // カメラの設定
    d3setcam cos(cam_phi)*sin(cam_theta)*CAM_DIST + char_x, sin(cam_phi)*sin(cam_theta)*CAM_DIST + char_y, cos(cam_theta)*CAM_DIST, char_x, char_y, 0

    redraw 0
    color : boxf
    gz = 0000 // d3texture用の配列
    repeat MAP_WIDTH, -MAP_WIDTH/2 : x = cnt
        repeat MAP_HEIGHT, -MAP_HEIGHT/2 : y = cnt
            dx = ( x * CELL_SIZE - char_x ) / CELL_SIZE
            dy = ( y * CELL_SIZE - char_y ) / CELL_SIZE
            gmode GMODE_ALPHA, , , 255.0 / pow( dx * dx + dy * dy + 10.25 )
            // d3texture用の配列を準備
            gx( 0 ) = x * CELL_SIZE - CELL_SIZE / 2, x * CELL_SIZE + CELL_SIZE / 2
            gx( 2 ) = gx( 1 ), gx( 0 )
            gy( 0 ) = y * CELL_SIZE + CELL_SIZE / 2 : gy( 1 ) = gy( 0 )
            gy( 2 ) = y * CELL_SIZE - CELL_SIZE / 2 : gy( 3 ) = gy( 2 )
            d3texture gx, gy, gz, 1IMG_SIZE * map( x + MAP_WIDTH/2, y + MAP_HEIGHT/2 ), 0IMG_SIZEIMG_SIZE
        loop
    loop

    d3setlocal char_x, char_y, 0sin(cam_phi),cos(cam_phi),0,-cos(cam_phi),sin(cam_phi),0,0,0,1
    // 影を描画
    gmode GMODE_SUB,,,32
    gx( 0 ) = -CELL_SIZE / 2CELL_SIZE / 2
    gx( 2 ) = gx( 1 ), gx( 0 )
    gy( 0 ) =  CELL_SIZE / 2 : gy( 1 ) = gy( 0 )
    gy( 2 ) = -CELL_SIZE / 2 : gy( 3 ) = gy( 2 )
    gz( 0 ) = 0000
    d3texture gx, gy, gz, 1IMG_SIZE * 30IMG_SIZEIMG_SIZE
    // キャラクターを描画
    gmode GMODE_RGB0
    gy( 0 ) = 0000
    gz( 0 ) = CELL_SIZE + char_z : gz( 1 ) = gz( 0 )
    gz( 2 ) = int( char_z ) : gz( 3 ) = gz( 2 )
    d3texture gx, gy, gz, 1IMG_SIZE * 1IMG_SIZEIMG_SIZEIMG_SIZE
    redraw 1
    return

俯瞰型RPGマップの描画

RPG関連のスクリプトを書こうと思い立ち、作成。普通のマップじゃつまらないので3Dにしてみました。
drawmodeが1の時の明るさの計算は、CodeZine: 3Dモデルを表示するJavaアプレットの作成を参考としています。
障害物のあるマップで視野を求めるで使った視野計算を合わせれば、よりそれっぽくなりそうです。

少々単純すぎるうえに用途が限られるので、開発wikiには投げずにここへ置いときます。
#include "d3m.hsp"
#const CELL_SIZE 100    // d3m上のマップチップサイズ
#const IMG_SIZE   64    // bmpのマップチップサイズ
#const CAM_X       0    // カメラ位置(45°手前から俯瞰)
#const CAM_Y    -400
#const CAM_Z     400
#define ctype pow(%1,%2expf(logf(%1)*(%2))    // x^y

    // マップチップのロード
    buffer 1 : picload dir_exe + "/sample/game/testchr.bmp"

    // カメラの設定
    gsel 0
    d3setcam CAM_XCAM_YCAM_Z000

    // 変数の初期化
    gz = 0000     // d3texture用の配列
    ch = CAM_Z          // CH = カメラの高さ

    // マップ作成
    dim map, 2020
    repeat length2( map )
        y = cnt
        repeat length( map )
            map( cnt, y ) = rnd3 )
        loop
    loop
    title "クリックで描画モード変更"
    onclick gosub *change_drawmode
    gosub *draw
    stop

*draw
    redraw 0
    color : boxf
    for x, -1010
        for y, -1010
            // マップチップの明るさを計算
            switch drawmode
                case 0  // 均等描画(どのマップチップも同じ明るさで描画)
                    c = 255
                    swbreak
                case 1  // 光の反射を考慮(45°手前から俯瞰するとして最適化済み)
                    // カメラの位置を点C、カメラの位置からXY平面におろした垂線の足を点H、
                    // マップチップの中心を点B、マップチップの中心からY軸におろした垂線の足を点A、
                    // XY平面上の原点を点Oとする。
                    // このとき、マップの中心が最も明るいという条件(すなわち光が45°奥から入射するという条件)を課すと、
                    // マップの明るさは cos( ∠COH - ∠CAH ) * cos( ∠BCA ) で決定される。
                    ah = y * CELL_SIZE - CAM_Y                  // AH
                    ab = x * CELL_SIZE                          // AB
                    cb = sqrt( ab * ab + ah * ah + ch * ch )    // CB
                    c = intdouble( ah + ch ) / ( sqrt2.0 ) * cb ) * 255 )   // = cos( ∠COH - ∠CAH ) * cos( ∠BCA ) * 255
                    swbreak
                case 2  // 距離に応じて変化(遠ければ遠いほど暗くなる)
                    c = int255.0 / pow( x * x + y * y + 10.2 ) )            // 距離に反比例させると暗すぎたので0.2乗で調整
                    swbreak
            swend
            gmode GMODE_ALPHA, , , c

            // d3texture用の配列を準備
            gx( 0 ) = x * CELL_SIZE - CELL_SIZE / 2, x * CELL_SIZE + CELL_SIZE / 2
            gx( 2 ) = gx( 1 ), gx( 0 )
            gy( 0 ) = y * CELL_SIZE + CELL_SIZE / 2 : gy( 1 ) = gy( 0 )
            gy( 2 ) = y * CELL_SIZE - CELL_SIZE / 2 : gy( 3 ) = gy( 2 )
            d3texture gx, gy, gz, 1IMG_SIZE * map( x + 10, y + 10 ), 0IMG_SIZEIMG_SIZE
        next
    next
    redraw 1
    return

*change_drawmode
    drawmode = ( drawmode + 1 ) \ 3
    switch drawmode
        case 0 : title "均等描画"         : swbreak
        case 1 : title "光の反射を考慮"   : swbreak
        case 2 : title "距離に応じて変化" : swbreak
    swend
    gosub *draw
    return