HSP Syntax Highlighter 1.3


; 疑似3D道路を描画する / Drawing pseudo 3D roads
; 仮想画面解像度が変更されても遠景の見た目が同じになるようにした
;
; Up, Down key : アクセル,ブレーキ
; Left, Right key: 横移動
; Z key : 仮想画面の解像度変更
; X key : フレームレート変更
;
; 2023/09/14 by mieki256

    #include "hspmath.as"

    ; 実画面解像度
    ; 16:9
    ; #define SCR_W       640
    ; #define SCR_H       360
    #define SCR_W       1280
    #define SCR_H       720
    ; #define SCR_W       1600
    ; #define SCR_H       900
    ; #define SCR_W       1920
    ; #define SCR_H       1080
    
    ; 4:3
    ; #define SCR_W       640
    ; #define SCR_H       480
    ; #define SCR_W       800
    ; #define SCR_H       600
    ; #define SCR_W       1024
    ; #define SCR_H       768
    ; #define SCR_W       1200
    ; #define SCR_H       900
    
    ; フレームレート
    dim framerate_list, 7
    framerate_list(0) = 10
    framerate_list(1) = 15
    framerate_list(2) = 20
    framerate_list(3) = 25
    framerate_list(4) = 30
    framerate_list(5) = 50
    framerate_list(6) = 60
    
    framerate_idx = 4
    
    ; 配列初期化用マクロ
    #define set_array_n2(%1, %2, %3, %4) \
        %1(%2, 0) = %3 :\
        %1(%2, 1) = %4 :
    
    ; 仮想画面解像度
    dim vscr_size, 6, 2
    set_array_n2 vscr_size, 0,  320, 180
    set_array_n2 vscr_size, 1,  480, 270
    set_array_n2 vscr_size, 2,  512, 288
    set_array_n2 vscr_size, 3,  640, 360
    set_array_n2 vscr_size, 4,  800, 450
    set_array_n2 vscr_size, 5, 1280, 720
    ; set_array_n2 vscr_size, 6, 1920, 1080
    
    vscr_size_idx = 3

    VSCR_W_MAX = 1920
    VSCR_H_MAX = 1080

    ; セグメントの区切りを色分け. 0 or 1 or 2
    #define SEG_DELIMITER   0

    ; 同梱ファイル / bundled files
    #define SPR_FILE    "trees.png"
    ; #define BG0_FILE    "bg_2560x1440.png"
    #define BG0_FILE    "bg_2560x1440.jpg"
    
    #pack   SPR_FILE
    #pack   BG0_FILE

    #packopt name "road07"      ; exe filename
    #packopt type 0             ; generate ".exe"
    #packopt xsize SCR_W
    #packopt ysize SCR_H

    randomize 0
    
    ; get windows size
    screen 0, SCR_W, SCR_H, 0
    gsel 0
    screen_w = ginfo_winx
    screen_h = ginfo_winy
    cls 4
    
    ; init virtual screen buffer
    vscrbufid = 1
    buffer vscrbufid, VSCR_W_MAX, VSCR_H_MAX
    dispw = vscr_size(vscr_size_idx, 0)
    disph = vscr_size(vscr_size_idx, 1)

    ; init temporary image buffer
    tmpid = 4
    buffer tmpid, 4096, 4096

    ; load spite image
    sprimgid0 = 5
    buffer sprimgid0, 2048, 2048
    picload SPR_FILE
    spr0w = ginfo_winx
    spr0h = ginfo_winy
    
    ; load bg image and scaling
    gosub *init_bg
    
    ; 2次元配列初期化用マクロ
    #define set_array_n4(%1, %2, %3, %4, %5, %6) \
        %1(%2, 0) = %3 :\
        %1(%2, 1) = %4 :\
        %1(%2, 2) = %5 :\
        %1(%2, 3) = %6 :

    ; texture table
    ; set_array_n4 配列変数名, index, x, y, w, h
    dim tex_pos, 8, 4
    set_array_n4 tex_pos, 0,    0,    0,  512,  512
    set_array_n4 tex_pos, 1,  512,    0,  512,  512
    set_array_n4 tex_pos, 2, 1024,    0,  512,  512
    set_array_n4 tex_pos, 3, 1536,    0,  512,  512
    set_array_n4 tex_pos, 4,    0,  512,  512,  512
    set_array_n4 tex_pos, 5,  512,  512,  512,  512
    set_array_n4 tex_pos, 6,    0, 1024,  512,  128
    set_array_n4 tex_pos, 7, 1024,  512, 1024,  512

    ; init road y, width
    road_y = 100    ; 道路のy位置
    road_x = 300    ; 道路の幅(片側の幅)
    
    ; ビルボード(billboard) 種類定義
    #define BB_TREE     1
    #define BB_ARROWR   2
    #define BB_ARROWL   3
    #define BB_GRASS    4
    #define BB_BEAM     5
    
    ; セグメント作成用データ
    ; set_array_n4 配列変数名, index, count, curve, pitch, billboard
    ; curve と pitch は100倍した値。
    ; HSPは int と double を配列内で混在できないらしいので…
    dim segm, 25, 4
    set_array_n4 segm,  0, 25,    0,    0, 0
    set_array_n4 segm,  1, 28,    0,    0, BB_BEAM
    set_array_n4 segm,  2, 30,    0,    0, BB_GRASS
    set_array_n4 segm,  3, 20,    0,    0, BB_ARROWL
    set_array_n4 segm,  4, 80,  200,    0, BB_TREE
    set_array_n4 segm,  5, 10,    0,    0, BB_TREE
    set_array_n4 segm,  6, 80,    0,  -50, BB_GRASS
    set_array_n4 segm,  7, 20,    0,    0, BB_ARROWR
    set_array_n4 segm,  8, 10, -100,    0, BB_GRASS
    set_array_n4 segm,  9, 50, -400,    0, BB_TREE
    set_array_n4 segm, 10, 20,    0,    0, 0
    set_array_n4 segm, 11, 50,    0,  100, BB_GRASS
    set_array_n4 segm, 12, 40,    0, -100, BB_TREE
    set_array_n4 segm, 13, 60,  -50,    0, BB_TREE
    set_array_n4 segm, 14, 50,    0,    0, BB_GRASS
    set_array_n4 segm, 15, 20,    0,    0, BB_ARROWL
    set_array_n4 segm, 16, 20,  200,    0, 0
    set_array_n4 segm, 17, 30,    0,    0, BB_GRASS
    set_array_n4 segm, 18, 40,  -80,  -80, BB_TREE
    set_array_n4 segm, 19, 20,    0,   80, 0
    set_array_n4 segm, 20, 20,    0,    0, BB_GRASS
    set_array_n4 segm, 21, 40,   20,  -60, BB_TREE
    set_array_n4 segm, 22, 20,    0,   60, BB_GRASS
    set_array_n4 segm, 23, 50,    0,    0, BB_GRASS
    set_array_n4 segm, 24, 50,    0,    0, BB_TREE

    ; セグメント総数を取得
    seg_max = 0
    repeat length(segm)
        seg_max += segm(cnt, 0)
    loop
    
    ; セグメントのz幅を指定
    seg_length = 20
    seg_total_length = seg_length * seg_max

    ; 参照されるセグメントデータとして展開
    dim seg, seg_max, 6
    gosub *expand_segment
    
    ; 画面に描画するセグメントの個数
    seg_distance = 160
    
    ; セグメントの画面上の位置を記録するバッファ
    ddim seg_x0, seg_distance
    ddim seg_y0, seg_distance
    ddim seg_z0, seg_distance
    ddim seg_cx0, seg_distance

    ; セグメントに付随するビルボード情報を記録するバッファ
    dim seg_spr_kind, seg_distance
    ddim seg_spr_x, seg_distance
    ddim seg_spr_scale, seg_distance
    
    ; 画面までの距離 / distance to screen
    #define FOV     120.0
    dist = double(dispw / 2) / tan(deg2rad(FOV / 2))
    
    #define SPR_Z_LIMIT     (184.0 / 2.0)
    
    camera_z = 0.0
    
    spd = 0.0

    xd = 0.0
    yd = 0.0
    zd = double(seg_length)
    
    bg_x = 0.0
    bg_y = 0.0
    
    px = 0.0
    ppx = 0.0
    angle = 0.0
    
    fps_change = 0
    vscr_change = 0
    
    gosub *set_framerate

*mainloop
    #define KEY_ESC     128
    #define KEY_UP      2
    #define KEY_DOWN    8
    #define KEY_LEFT    1
    #define KEY_RIGHT   4
    #define KEY_Z       2048
    #define KEY_X       4096
    #define KEY_PRESS   (KEY_ESC+KEY_UP+KEY_DOWN+KEY_LEFT+KEY_RIGHT)
    
    stick k, KEY_PRESS

    ; ESC key to exit
    if k & KEY_ESC : goto *jobend

    ; 仮想画面サイズを変更
    if k & KEY_Z {
        if vscr_change == 0 {
            vscr_change = 1
            vscr_size_idx = (vscr_size_idx + 1) \ length(vscr_size)
            dispw = vscr_size(vscr_size_idx, 0)
            disph = vscr_size(vscr_size_idx, 1)
            dist = double(dispw / 2) / tan(deg2rad(FOV / 2))
        }
    } else {
        vscr_change = 0
    }
    
    ; フレームレート変更
    if k & KEY_X {
        if fps_change == 0 {
            fps_change = 1
            oldspdper = spd / spd_max
            framerate_idx = (framerate_idx + 1) \ length(framerate_list)
            gosub *set_framerate
            spd = spd_max * oldspdper
        }
    } else {
        fps_change = 0
    }
    
    gosub *set_framerate
    
    ; check cursor key
    if k & KEY_UP {
        spd += spda
        if spd >= spd_max : spd = spd_max
    }
    if k & KEY_DOWN {
        spd -= (spda * 3)
        if spd < 0.0 : spd = 0.0
    }
    x_spd = (spd * 0.3)
    if k & KEY_LEFT : ppx += x_spd
    if k & KEY_RIGHT : ppx -= x_spd
    px = ppx
    angle += (spd * 0.05)

    base_y = int(double(disph) * 0.5)
    h = disph - base_y

    ; 最初のセグメント番号を取得
    if camera_z = 0 {
        seg_index = 0
    } else {
        ; "\"はHSPの剰余記号。一般的なプログラミング言語なら "%" に相当
        seg_index = (int(camera_z) / seg_length) \ seg_max
    }
    
    ; カメラが居るセグメントのカーブ量とピッチ量を求める
    curve = double(seg(seg_index, 1)) * 0.01
    pitch = double(seg(seg_index, 2)) * 0.01
    
    ; カメラがセグメント内のどの位置に居るか割合を求める
    z0 = seg(seg_index, 0)
    z1 = z0 + seg_length
    ccz = camera_z \ seg_total_length
    camz = double(ccz - z0) / double(z1 - z0)

    ; セグメント内のカメラ位置に応じて事前に横方向にある程度ずらしておく
    camang = camz * curve
    xd = -camang
    yd = -camz * pitch
    zd = double(seg_length)
    
    cx = -(xd * camz)
    cy = -(yd * camz)
    cz = 0.0
    
    ; BGのスクロール量を変更
    gosub *calc_bg_pos

    ; 道路を描画するためのデータを計算
    gosub *calc_road

    ; 描画開始 / draw start
    redraw 0
    gsel vscrbufid
    
    ; 背景(遠景)を描画 / draw BG
    if SEG_DELIMITER <= 1 {
        bgid = bgimgid(vscr_size_idx)
        bw = bgw(vscr_size_idx)
        bh = bgh(vscr_size_idx)
        draw_bg vscrbufid, bg_x, bg_y, bgid, bw, bh, dispw, disph
    } else {
        gmode gmode_gdi
        color 0, 0, 0
        boxf 0, 0, dispw, disph
    }

    ; 道路を描画 / draw road
    gosub *draw_road

    ; 仮想画面を実画面に転送
    gsel 0
    gmode gmode_gdi
    if screen_w == dispw and screen_h == disph {
        ; 等倍で転送
        pos 0, 0
        gcopy vscrbufid, 0, 0, dispw, disph
    } else {
        ; 拡大縮小して転送
        xscale = double(screen_w) / double(dispw)
        yscale = double(screen_h) / double(disph)
        
        ; if xscale < yscale : scale = xscale : else : scale = yscale
        if xscale < yscale : scale = yscale : else : scale = xscale
        
        dstw = int(double(dispw) * scale)
        dsth = int(double(disph) * scale)
        dstx = (SCR_W - dstw) / 2
        dsty = (SCR_H - dsth) / 2
        pos dstx, dsty
        gzoom dstw, dsth, vscrbufid, 0, 0, dispw, disph, 0
    }

*draw_text
    if 1 {
        gsel 0
        font "Arial", 16, 1, 2
        color 1, 1, 1
        objcolor 255, 255, 255
        ; pos 8, 4
        ; mes "Press cursor key"
        pos (SCR_W / 2) - 80, 2
        mes "Res:" + dispw + "x" + disph + " " + int(nowframerate) + "fps", 4
        pos (SCR_W / 2) - 120, 22
        mes "bg_x, bg_y = " + bg_x + ", " + bg_y, 4
    }

    redraw 1    ; 描画終了 / draw end
    
    camera_z += spd
    
    await (1000 / framerate_list(framerate_idx))
    goto *mainloop

*set_framerate
    nowframerate = double(framerate_list(framerate_idx))
    spd_max = 960.0 / nowframerate
    spda = spd_max / (nowframerate * 3.0)
    if spd > spd_max : spd = spd_max
    if spd < 0.0 : spd = 0.0
    return

*draw_raster_road
    ; 道路を1ラスター分描画 / draw road 1 raster
    ; draw_x, draw_y : draw x, y
    ; road_w : road width
    ; kind : 0 or 1 or 2
    ; dispw : screen width

    ; draw ground
    if kind = 0 {
        ; color R, G, B
        color 114, 185, 66
    } else {
        color 22, 174, 63
    }
    ; boxf x0, y0, x1, y1
    boxf 0, draw_y, dispw, draw_y

    ; draw road base
    switch kind
    case 0
        color 133, 149, 158
        swbreak
    case 1
        color 149, 168, 179
        swbreak
    case 2
        color 255, 0, 0
        swbreak
    swend
    boxf draw_x, draw_y, draw_x + road_w, draw_y

    ; draw white line
    lw = double(road_w) * 48.0 / 2048.0     ; white line width

    switch kind
    case 0
        color 255, 255, 255
        
        lx = double(draw_x)
        boxf lx, draw_y, lx + lw, draw_y
        
        lx = lx + road_w - lw
        boxf lx, draw_y, lx + lw, draw_y
        
        lx = double(draw_x) + (double(road_w) * 0.5) - (lw * 0.5)
        boxf lx, draw_y, lx + lw, draw_y
        
        lx = double(draw_x) + double(road_w) * 0.25 - (lw * 0.5)
        boxf lx, draw_y, lx + lw, draw_y
        
        lx = double(draw_x) + double(road_w) * 0.75 - (lw * 0.5)
        boxf lx, draw_y, lx + lw, draw_y
        swbreak
    case 1
        color 228, 236, 252
        
        lx = double(draw_x) + lw
        boxf lx, draw_y, lx + lw, draw_y
        
        lx = double(draw_x) + road_w - (lw * 2)
        boxf lx, draw_y, lx + lw, draw_y
        swbreak
    swend
    
    return

*get_easing_value
    ; イージングを考慮した値を返す
    ;
    ; easing : easing kind. 0 - 3
    ; v0 : value 0
    ; v1 : value 1
    ; per : percent. 0.0 - 1.0
    ; return ret_value
    
    if 1 {
        ; HSP のイージング関数を使って処理
        switch easing
        case 0:
            ; not easing
            ret_value = v0
            swbreak
        case 1:
            ; ease In
            setease v0, v1, ease_quad_in
            ret_value = int(geteasef(int(per * 4096), 4096))
            swbreak
        case 2:
            ; ease Out
            setease v0, v1, ease_quad_out
            ret_value = int(geteasef(int(per * 4096), 4096))
            swbreak
        case 3:
            setease v0, v1, ease_quad_inout
            ret_value = int(geteasef(int(per * 4096), 4096))
            swbreak
        swend
    } else {
        ; 自前でイージング計算
        v_diff = double(v1 - v0)
        switch easing
        case 0:
            ; not easing
            ret_value = v0
            swbreak
        case 1:
            ; ease In
            ret_value = v0 + int(v_diff * (per * per))
            swbreak
        case 2:
            ; ease Out
            ret_value = v0 + int(v_diff * (1.0 - ((1.0 - per) * (1.0 - per))))
            swbreak
        case 3:
            if per < 0.5 {
                ret_value = v0 + int(v_diff * (2.0 * per * per))
            } else {
                vv = -2.0 * per + 2.0
                ret_value = v0 + int(v_diff * (1.0 - ((vv * vv) / 2.0)))
            }
            swbreak
        swend
    }
    return


*set_billboard
    ; 各セグメントに記録するビルボード情報を決める
    ;
    ; billboard : ビルボード種類
    ; road_x : 道路の幅(片側の幅)
    ; return spr_kind, spr_x, spr_scale
    switch billboard
    case 0
        ; none
        spr_kind = 0
        spr_x = 0
        spr_scale = 0
        swbreak
    case BB_TREE
        ; tree
        spr_kind = rnd(4) + 1
        spr_x = rnd(400) + road_x + 100
        if (rnd(2) & 1) == 0 : spr_x *= -1
        spr_scale = 100 + rnd(100)
        swbreak
    case BB_ARROWR
    case BB_ARROWL
        ; arrow sign right to left, left to right
        if cnt \ 4 == 0 {
            ; arrow sign
            spr_kind = 5 + (billboard - BB_ARROWR)
            spr_x = road_x + 120
            if billboard == 3 : spr_x *= -1
            spr_scale = 100
        } else {
            ; grass
            spr_kind = 7
            spr_x = rnd(50) + road_x + 120
            if billboard == 3 : spr_x *= -1
            spr_scale = 100
        }
        swbreak
    case BB_GRASS
        ; grass
        spr_kind = 7
        spr_x = road_x + 200 + rnd(150)
        if (rnd(2) & 1) == 0 : spr_x *= -1
        spr_scale = 150 + rnd(50)
        swbreak
    case BB_BEAM
        ; beam
        if cnt \ 7 = 0 {
            ; beam
            spr_kind = 8
            spr_x = 0
            spr_scale = 100
        } else {
            ; none
            spr_kind = 0
            spr_x = 0
            spr_scale = 100
        }
        swbreak
    swend
    return

*draw_billboard
    ; draw billboard sprite
    ;
    ; spr_kind : kind. 0 = None, 1-4 = tree, 5-6 = sign
    ; spr_x : x position
    ; spr_scale : scale (double)
    ; z0, z1 : segment z value. segmnet[n] and segment[n+1]
    ; y0, y1 : segment y value. segmnet[n] and segment[n+1]
    ; cx0, cx1 : segment center x value. segmnet[n] and segment[n+1]
    ; dispw, disph : screen size
    ; dist : screen distance
    
    if spr_kind <= 0 : return
    
    switch spr_kind
    case 1
    case 2
    case 3
    case 4
        ; tree
        n = spr_kind - 1
        spr_w = 450.0
        spr_h = 450.0
        swbreak
    case 5
    case 6
        ; arrow sign
        n = spr_kind - 1
        spr_w = 200.0
        spr_h = 200.0
        swbreak
    case 7
        ; grass
        n = 6
        spr_w = 200.0
        spr_h = 50.0
        swbreak
    case 8
        ; beam
        n = 7
        spr_w = 1000.0
        spr_h = 500.0
        swbreak
    swend
    
    imgid = sprimgid0
    imgw = spr0w
    imgh = spr0h
    
    z = double(z0)
    y = y0
    cx = cx0
    
    scale = spr_scale * double(dist) / z
    dst_w = int(spr_w * scale)
    dst_h = int(spr_h * scale)
    
    spr_xpos = (double(spr_x) * double(dist) / z) + cx
    dst_x = int(spr_xpos - (dst_w / 2) + (dispw / 2))
    dst_y = int(y - dst_h + base_y)
    
    if SEG_DELIMITER == 0 {
        if z < SPR_Z_LIMIT : return
        ; gosub *draw_sprite_gzoom
        draw_sprite_gzoom vscrbufid, dst_x, dst_y, dst_w, dst_h, imgid, n, tmpid, dispw, disph, tex_pos
    }
    return

#module
#deffunc draw_sprite_gzoom int dstid, int dx, int dy, int dw, int dh, int srcid, int n, int tmpid, int dispw, int disph, array tex_pos
    ; スプライト相当を拡大縮小して描画
    ;
    ; dstid : 描画先バッファID
    ; dx, dy, dw, dh : 描画先左上座標、サイズ
    ; srcid : 元画像バッファID
    ; n : スプライトNo.
    ; tmpid : 作業用イメージバッファID
    ; dispw, disph : 画面サイズ
    ; tex_pos : テクスチャ座標テーブル
    
    ; テクスチャの x, y, w, h を取得
    sx = tex_pos(n, 0)
    sy = tex_pos(n, 1)
    sw = tex_pos(n, 2)
    sh = tex_pos(n, 3)
    draw_by_gzoom dstid, dx, dy, dw, dh, srcid, sx, sy, sw, sh, tmpid, dispw, disph
    return
#global

#module
#deffunc draw_by_gzoom int dstid, int dstx, int dsty, int dstw, int dsth, int srcid, int srcx, int srcy, int srcw, int srch, int tmpid, int dispw, int disph
    ; 画面外にはみ出る分を考量して最小面積で画像を拡大縮小描画
    ;
    ; dstid : 描画先バッファID
    ; dstx, dsty, dstw, dsth : 描画先左上座標, 描画先サイズ
    ; srcid : 元画像バッファID
    ; srcx, srcy, srcw, scrh : 元画像左上座標, 元画像サイズ
    ; tmpid : 作業用イメージバッファID
    ; dispw, disph : 画面サイズ
    
    dx = dstx
    dy = dsty
    dw = dstw
    dh = dsth
    sx = srcx
    sy = srcy
    sw = srcw
    sh = srch
    
    if dx >= dispw or (dx + dw) < 0 : return
    if dy >= disph or (dy + dh) < 0 : return
    
    if dx < 0 {
        w = (-dx) * sw / dw
        sx += w
        sw -= w
        dw += dx
        dx = 0
    }
    if dy < 0 {
        h = (-dy) * sh / dh
        sy += h
        sh -= h
        dh += dy
        dy = 0
    }
    if (dx + dw) > dispw {
        dwd = (dx + dw) - dispw
        swd = dwd * sw / dw
        sw -= swd
        dw -= dwd
    }
    if (dy + dh) > disph {
        dhd = (dy + dh) - disph
        shd = dhd * sh / dh
        sh -= shd
        dh -= dhd
    }
    
    ; gzoom は透明色を扱えないので小技が必要
    ; 一旦、仮バッファに拡大縮小描画
    gsel tmpid
    pos 0, 0
    gzoom dw, dh, srcid, sx, sy, sw, sh, 0

    ; 仮バッファから実スクリーンに gcopy でコピー
    ; gcopy なら RGB=(0,0,0) を透明色として扱える
    gsel dstid
    gmode gmode_rgb0    ; RGB=(0,0,0)を透明色として扱う
    pos dx, dy
    gcopy tmpid, 0, 0, dw, dh

    ; draw text
    if 0 {
        gmode gmode_gdi
        font "Arial", 18, 1
        color 255, 255, 255
        pos 4, 80
        mes "x, y = " + dstx + ", " + dsty
        pos 4, 100
        mes "dst x, y, w, h = " + dx + ", " + dy + " ," + dw + " ," + dh
        pos 4, 120
        mes "src x, y, w, h = " + sx + ", " + sy + " ," + sw + " ," + sh
    }

    return
#global

*expand_segment
    ; 元になるセグメントデータから参照されるセグメントデータに展開
    
    ; dim seg, seg_max, 6
    z = 0
    i = 0
    repeat length(segm)
        n0 = cnt
        n1 = (cnt + 1) \ length(segm)
        
        lp = segm(n0, 0)
        curve = segm(n0, 1)
        pitch = segm(n0, 2)
        billboard = segm(n0, 3)

        next_curve = segm(n1, 1)
        next_pitch = segm(n1, 2)
        
        ; イージング有効無効
        #define EASING_ENABLE   0
        
        if EASING_ENABLE {
            ; カーブ量やピッチ量をイージングで求める場合
            if curve == next_curve {
                curve_easing = 0
            } else {
                if curve == 0 {
                    curve_easing = 1        ; ease In
                } else {
                    if next_curve == 0 {
                        curve_easing = 2    ; ease Out
                    } else {
                        curve_easing = 3    ; ease In Out
                    }
                }
            }
            
            if pitch == next_pitch {
                pitch_easing = 0
            } else {
                if pitch == 0 {
                    pitch_easing = 1        ; ease In
                } else {
                    if next_pitch == 0 {
                        pitch_easing = 2    ; ease Out
                    } else {
                        pitch_easing = 3    ; ease In Out
                    }
                }
            }
        }
        
        repeat lp
            seg(i, 0) = z
            
            per = double(cnt) / double(lp)
            if EASING_ENABLE {
                ; カーブ量やピッチ量をイージングで求める
                v0 = curve
                v1 = next_curve
                easing = curve_easing
                gosub *get_easing_value
                seg(i, 1) = ret_value
                
                v0 = pitch
                v1 = next_pitch
                easing = pitch_easing
                gosub *get_easing_value
                seg(i, 2) = ret_value
            } else {
                ; カーブ量やピッチ量を線形補完で求める
                seg(i, 1) = curve + int(double(next_curve - curve) * per)
                seg(i, 2) = pitch + int(double(next_pitch - pitch) * per)
            }
            
            ; set billboard sprite
            gosub *set_billboard
            seg(i, 3) = spr_kind
            seg(i, 4) = spr_x
            seg(i, 5) = spr_scale
            
            z += seg_length
            i++
        loop
    loop
    return

*calc_road
    ; 道路を描画するための座標値を計算して記録
    
    i = 0
    seg_z_max = 0
    repeat (seg_distance + 1)
        ; セグメントのz値、カーブ量、ピッチ量を取得
        idx = (seg_index + cnt) \ seg_max
        z0 = seg(idx, 0)
        curve = double(seg(idx, 1)) * 0.01
        pitch = double(seg(idx, 2)) * 0.01
        
        ; 最後のセグメントを超えて最初にループする場合、z値を調整する
        if z0 > seg_z_max : seg_z_max = z0
        if z0 < seg_z_max : z0 += seg_total_length
        
        cam_z = int(camera_z) \ seg_total_length
        z0 -= cam_z
        
        if z0 = 0 : z0 = 1      ; 0で除算しないように微調整
        
        ; 画面上で描画すべきy座標位置を取得
        y0 = (double(road_y) + cy) * dist / double(z0)
        
        ; 画面上で描画すべき道路の幅(片側の幅)を取得
        x0 = double(road_x) * double(dist) / double(z0)

        ; 画面上でずらすべき中心線の位置を取得
        cx0 = (double(cx) + px) * double(dist) / double(z0)

        ; 描画用に記録
        seg_z0(i) = double(z0)
        seg_y0(i) = double(y0)
        seg_x0(i) = double(x0)
        seg_cx0(i) = double(cx0)

        ; スプライト相当の情報も記録
        seg_spr_kind(i) = seg(idx, 3)
        seg_spr_x(i) = double(seg(idx, 4))
        seg_spr_scale(i) = double(seg(idx, 5)) * 0.01
        
        cx += xd
        cy += yd
        cz += zd
        
        xd += curve
        yd += pitch
        i++
    loop
    return

*draw_road
    ; 道路を描画
    
    gmode gmode_gdi
    repeat seg_distance
        i = seg_distance - cnt - 1
        i1 = seg_distance - cnt
        
        x0 = seg_x0(i)
        y0 = seg_y0(i)
        z0 = seg_z0(i)
        cx0 = seg_cx0(i)
        
        x1 = seg_x0(i1)
        y1 = seg_y0(i1)
        z1 = seg_z0(i1)
        cx1 = seg_cx0(i1)
        
        spr_kind = seg_spr_kind(i)
        spr_x = seg_spr_x(i)
        spr_scale = seg_spr_scale(i)

        if z1 < (SPR_Z_LIMIT / 2) : goto *seg_draw_cancel
        
        ; 各セグメントが作る台形を塗り潰す
        if SEG_DELIMITER <= 1 {
            ; y0 < y1 なら道路の裏面を見てる。描画をスキップ
            if y0 < y1 : goto *seg_draw_cancel
            
            h = abs(y1 - y0) + 1
            dsy = sgn(y1 - y0)
            if dsy = 0 : dsy = 1
            repeat h
                sy = y0 + (dsy * cnt)
                draw_y = sy + base_y
                
                ; 画面縦幅を超えたy座標なら描画しない
                if draw_y < 0 | draw_y > disph : continue
                
                ; 2次元的な計算で割合を求める
                if y0 == y1 {
                    p = 0.0
                } else {
                    p = double(sy - y0) / double(y1 - y0)
                }
                z = (double(z1) - double(z0)) * p + double(z0)
                x = (double(x1) - double(x0)) * p + double(x0)
                center_x = (cx1 - cx0) * p + cx0
                addv = ((int(z) + int(camera_z)) / 40) & $1
                
                ; 1ラスター分を描画 / draw 1 raster
                road_w = x * 2
                draw_x = -x + center_x + (dispw / 2)
                
                kind = addv
                if SEG_DELIMITER = 1 {
                    ; セグメントの区切りを色分けしたい場合
                    if sy = y0 : kind = 2
                }
                gmode gmode_gdi
                gosub *draw_raster_road
            loop
        } else {
            ; デバッグ用。セグメントの区切り線だけを描画
            d = (dispw / 2)
            color 255, 0, 0
            line cx0 + x0 + d, y0 + base_y, cx0 - x0 + d, y0 + base_y
            if 0 {
                line cx1 - x1 + d, y1 + base_y, cx0 - x0 + d, y0 + base_y
                line cx1 + x1 + d, y1 + base_y, cx0 + x0 + d, y0 + base_y
            }
            color 255, 255, 0
            line cx1 + d, y1 + base_y, cx0 + d, y0 + base_y
        }
        
*seg_draw_cancel
        ; draw billboard sprite
        if spr_kind <= 0 : continue
        gosub *draw_billboard
    loop
    return

*init_bg
    ; load bg
    k = length(vscr_size)
    dim bgimgid, k
    dim bgw, k
    dim bgh, k
    
    i = k - 1
    bgimgid(i) = 6 + i
    buffer bgimgid(i), 2560, 1440
    picload BG0_FILE
    bgw(i) = ginfo_winx
    bgh(i) = ginfo_winy
    
    ; scaling
    repeat k - 1
        bgw(cnt) = vscr_size(cnt, 0) * 2
        bgh(cnt) = vscr_size(cnt, 1) * 2
        bgimgid(cnt) = 6 + cnt
        buffer bgimgid(cnt), bgw(cnt), bgh(cnt)
        gsel bgimgid(cnt)
        gzoom bgw(cnt), bgh(cnt), bgimgid(i), 0, 0, bgw(i), bgh(i), 1
    loop
    return

*calc_bg_pos
    ; BGスクロール位置を計算
    ; curve, pitch
    ; spd, spd_max
    ; bg_x, bg_y
    
    fper = 30.0 / nowframerate
    bg_x += curve * (spd / spd_max) * 0.1 * fper
    if spd > 0.0 {
        if pitch != 0.0 {
            bg_y += pitch * (spd / spd_max) * 0.1 * fper
        } else {
            d = (spd / spd_max) * 0.2 * fper
            if bg_y < 0.0 {
                bg_y += (d * 0.1)
                if bg_y >= 0.0 : bg_y = 0.0
            }
            if bg_y > 0.0 {
                bg_y -= (d * 0.1)
                if bg_y <= 0.0 : bg_y = 0.0
            }
        }
        
        ; bg_y += ((pitch * 2.0) - bg_y) * 0.15
        
        if bg_y < -1.0 : bg_y = -1.0
        if bg_y > 1.0 : bg_y = 1.0
    }
    return

#module
#deffunc draw_bg int dstid, double bg_x, double bg_y, int bgimgid, int bgw, int bgh, int dispw, int disph
    ; BGを描画 / draw BG
    ;
    ; dstid : 描画先バッファID
    ; bg_x, bg_y : bg position. 0.0 - 1.0
    ; bgimgid : bg images buffer ID. array.
    ; bgw, bgh : bg width, height. array.
    ; dispw, disph : screen width, height
    
    bx = bg_x
    by = bg_y
    if by < -1.0 : by = -1.0
    if by > 1.0 : by = 1.0
    
    bgid = bgimgid
    bw = bgw
    bh = bgh
    shiftx = bx
    
    sx = int(double(bh / 4) * bx) \ bw
    sy = int(double(bh / 4) * by) + (bh / 2) - (bh / 4)
    sw = dispw
    sh = disph

    gsel dstid
    gmode gmode_gdi
    
    if sx >= 0 and (sx + dispw) < bw {
        pos 0, 0
        gcopy bgid, sx, sy, dispw, disph
    } else {
        if (sx + dispw) > bw : sx = (sx - bw) \ bw
        w = -sx
        sw0 = w
        sx0 = bw - w
        pos 0, 0
        gcopy bgid, sx0, sy, sw0, sh
        
        sw1 = sw - w
        sx1 = 0
        pos sw0, 0
        gcopy bgid, sx1, sy, sw1, sh
    }
    return
#global


*jobend
    end

Last modified (2015/11/30)
v1.3 : bug fixed