2007年8月16日木曜日

障害物のあるマップで視野を求める

フリーソフト超激辛ゲームレビュー様でHSPで作られたと思われる素晴らしいゲームが紹介されていました。Elonaというローグライクゲームです。詳しくは製作者様のサイトをご覧ください。

ローグ系はやったことも作ったこともあまり無いので処理内容の大半はよく分からないのですが、視野の計算がちょっと気になった&イメージが湧いたのでサンプルスクリプトを作成してみました。
左右非対称だったりと不完全ではありますが、充分実用に耐えうるのではないかと思われます。
#const CHIP_SIZE  16                  // マップチップの大きさ
#const WINX       320                 // ウィンドウの大きさ
#const WINY       320
#const AREA_W     WINX / CHIP_SIZE    // マップの大きさ
#const AREA_H     WINY / CHIP_SIZE
#const OBSTACLE_W 3                   // 障害物の大きさ
#const OBSTACLE_H 2
//
// 数学関数
#define ctype max%1%2 ) ( %1 * ( %1 > %2 ) + %2 * ( %1 <= %2 ) )
#define ctype round%1 ) double(strf("%%0.0f"%1))
//
// マップ描画用マクロ
#define draw_chip%1%2 ) boxf CHIP_SIZE * %1CHIP_SIZE * %2CHIP_SIZE * ( %1 + 1 ) - 2CHIP_SIZE * ( %2 + 1 ) - 2

    screen 0WINXWINY
    randomize
    sdim map, AREA_W * AREA_H
    sdim shadow, AREA_W * AREA_H  // 影を記録する変数。0なら明るい地点、それ以外は影。
    gosub *make_blocks
    onclick gosub *make_blocks
//
// メインループ
*main
    if ( light_x != mousex / CHIP_SIZE ) | ( light_y != mousey / CHIP_SIZE ) {
        renew = 1
    }
    if renew {
        renew = 0
        light_x = mousex / CHIP_SIZE
        light_y = mousey / CHIP_SIZE
        gosub *calc
        gosub *draw
    }
    wait 2
    goto *main
//
// 視野の計算
*calc
    memset shadow, 2AREA_W * AREA_H // 2...未検査
    repeat AREA_H
        y = cnt
        dy = abs( y - light_y )
        repeat AREA_W
            if peek( shadow, y * AREA_W + cnt ) != 2 : continue
            x = cnt
            dx = abs( x - light_x )
            // コメントアウトを解除すると、距離も考慮に入れるようになる
;           if ( dx*dx+dy*dy > 48 ) {
;               poke shadow, y * AREA_W + x, 1    // 1...検査済み(影)
;               continue
;           }
            m = max( dx, dy )
            repeat m
                // 内分点の公式を使い、調査地点と光源の間にあるマス全てを調べる
                _x = int(round(double( light_x * cnt + x * ( m - cnt ) ) / m))
                _y = int(round(double( light_y * cnt + y * ( m - cnt ) ) / m))
                if peek( map, _y * AREA_W + _x ) {
                    // 障害物を見つけた場合、今まで調べた地点はすべて影とする
                    repeat cnt + 1
                        _x = int(round(double( light_x * cnt + x * ( m - cnt ) ) / m))
                        _y = int(round(double( light_y * cnt + y * ( m - cnt ) ) / m))
                        poke shadow, _y * AREA_W + _x, 1    // 1...検査済み(影)
                    loop
                    break
                }
                poke shadow, _y * AREA_W + _x, 0            // 0...検査済み(明るい地点)
            loop
        loop
    loop
    return
//
// 各種描画処理
*draw
    redraw 0
    gosub *draw_map
    gosub *draw_light
    redraw 1
    return
//
// マップを描画
*draw_map
    color : boxf
    repeat AREA_H
        y = cnt
        repeat AREA_W
            if peek( map, y * AREA_W + cnt ) == 0 {
                if peek( shadow, y * AREA_W + cnt ) {
                    color 192192192
                } else {
                    color 255255255
                }
                draw_chip cnt, y
            }
        loop
    loop
    return
//
// 光源を描く
*draw_light
    color 2551280
    draw_chip light_x, light_y
    return
//
// ブロックをランダムに配置
*make_blocks
    memset map, 0AREA_W * AREA_H
    // 10個障害物を作成
    repeat 10
        poke map, rndAREA_W * AREA_H ), 1
    loop
    // 1個大きな障害物(OBSTACLE_WxOBSTACLE_H)を作成
    x = rndAREA_W + 1 - OBSTACLE_W )
    y = rndAREA_H + 1 - OBSTACLE_H )
    repeat OBSTACLE_H
        memset map, 1OBSTACLE_W, ( y + cnt ) * AREA_W + x
    loop
    renew = 1
    return

0 件のコメント: