HSP Syntax Highlighter 1.3


    
; 疑似3D道路を描画する / Drawing pseudo 3D roads
; 坂道を実装してみる
;
; Up, Down key : アクセル,ブレーキ
; Left, Right key: 横移動
; Z key : 仮想画面の解像度変更
; X key : フレームレート変更
;
; 2023/09/10 by mieki256

    #include "hspmath.as"

    ; フレームレート
    dim framerate_list, 7
    framerate_list(0) = 30
    framerate_list(1) = 50
    framerate_list(2) = 60
    framerate_list(3) = 10
    framerate_list(4) = 15
    framerate_list(5) = 20
    framerate_list(6) = 25
    
    ; 仮想画面解像度
    dim vscr_size, 6, 2
    vscr_size(0, 0) = 640
    vscr_size(0, 1) = 360
    vscr_size(1, 0) = 800
    vscr_size(1, 1) = 450
    vscr_size(2, 0) = 1280
    vscr_size(2, 1) = 720
    vscr_size(3, 0) = 320
    vscr_size(3, 1) = 180
    vscr_size(4, 0) = 480
    vscr_size(4, 1) = 270
    vscr_size(5, 0) = 512
    vscr_size(5, 1) = 288
    ; vscr_size(6, 0) = 1920
    ; vscr_size(6, 1) = 1080

    VSCR_W_MAX = 1920
    VSCR_H_MAX = 1080

    ; 実画面解像度
    #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       1200
    ; #define SCR_H       900
    ; #define SCR_W       1920
    ; #define SCR_H       1080
    
    #define BG0_FILE    "bg_bg0.png"
    #define BG1_FILE    "bg_bg1.png"
    #define SPR_FILE    "trees.png"

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

    ; 同梱ファイル / bundled files
    #pack   BG0_FILE
    #pack   BG1_FILE
    #pack   SPR_FILE

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

    ; #define global ctype max(%1,%2) ((%1)*((%1)>=(%2))+(%2)*((%1)<(%2)))
    ; #define global ctype min(%1,%2) ((%1)*((%1)<=(%2))+(%2)*((%1)>(%2)))
    
    ; onkey goto *jobend
    
    randomize 0
    
    ; get windows size
    screen 0, SCR_W, SCR_H, 0
    gsel 0
    ; width SCR_W, SCR_H
    screen_w = ginfo_winx
    screen_h = ginfo_winy
    cls 4
    
    ; init virtual screen
    vscrbufid = 1
    buffer vscrbufid, VSCR_W_MAX, VSCR_H_MAX
    
    vscr_size_idx = 0
    dispw = vscr_size(vscr_size_idx, 0)
    disph = vscr_size(vscr_size_idx, 1)

    ; load bg image
    bgimgid0 = 4
    buffer bgimgid0, 1280, 720
    picload BG0_FILE
    bg0w = ginfo_winx
    bg0h = ginfo_winy
    
    bgimgid1 = 5
    buffer bgimgid1, 1280, 720
    picload BG1_FILE
    bg1w = ginfo_winx
    bg1h = ginfo_winy
    
    ; load spite image
    sprimgid0 = 6
    buffer sprimgid0, 1024, 512
    picload SPR_FILE
    spr0w = ginfo_winx
    spr0h = ginfo_winy
    
    ; init temporary image buffer
    tmpid = 7
    buffer tmpid, 4096, 4096

    ; 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
    
    ; セグメント作成用データ初期化用マクロ
    #define set_array(%1, %2, %3, %4, %5, %6) \
        %1(%2, 0) = %3 :\
        %1(%2, 1) = %4 :\
        %1(%2, 2) = %5 :\
        %1(%2, 3) = %6 :

    ; セグメント作成用データ
    ; set_array 配列変数名, index, count, curve, pitch, billboard
    ; curve と pitch は100倍した値。
    ; HSPは int と double を配列内で混在できないらしいので…
    dim segm, 25, 4
    set_array segm,  0, 25,    0,    0, 0
    set_array segm,  1, 28,    0,    0, BB_BEAM
    set_array segm,  2, 30,    0,    0, BB_GRASS
    set_array segm,  3, 20,    0,    0, BB_ARROWL
    set_array segm,  4, 80,  200,    0, BB_TREE
    set_array segm,  5, 10,    0,    0, BB_TREE
    set_array segm,  6, 80,    0,  -50, BB_GRASS
    set_array segm,  7, 20,    0,    0, BB_ARROWR
    set_array segm,  8, 10, -100,    0, BB_GRASS
    set_array segm,  9, 50, -400,    0, BB_TREE
    set_array segm, 10, 20,    0,    0, 0
    set_array segm, 11, 50,    0,  100, BB_GRASS
    set_array segm, 12, 40,    0, -100, BB_TREE
    set_array segm, 13, 60,  -50,    0, BB_TREE
    set_array segm, 14, 50,    0,    0, BB_GRASS
    set_array segm, 15, 20,    0,    0, BB_ARROWL
    set_array segm, 16, 20,  200,    0, 0
    set_array segm, 17, 30,    0,    0, BB_GRASS
    set_array segm, 18, 40,  -80,  -80, BB_TREE
    set_array segm, 19, 20,    0,   80, 0
    set_array segm, 20, 20,    0,    0, BB_GRASS
    set_array segm, 21, 40,   20,  -60, BB_TREE
    set_array segm, 22, 20,    0,   60, BB_GRASS
    set_array segm, 23, 50,    0,    0, BB_GRASS
    set_array 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
    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
    
    ; 画面に描画するセグメントの個数
    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))
    ; logmes "dist=" + dist
    ; dist = 200.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
    ; py = 0.0
    ppx = 0.0
    angle = 0.0
    
    framerate_idx = 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, 255

    ; 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
    ; px += ((double(road_x) * 0.6) * sin(deg2rad(angle)))
    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のスクロール量を変更
    bg_x += curve * spd * 0.5
    if spd > 0.0 {
        if pitch == 0.0 {
            d = spd * 0.75
            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
            }
        } else {
            d = spd * 0.5
            bg_y += pitch * d
        }
    }

    ; 道路を描画するためのデータを計算
    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

    ; 描画開始 / draw start
    redraw 0
    ; gsel 0
    gsel vscrbufid
    
    ; 背景(遠景)を描画 / draw BG
    if SEG_DELIMITER <= 1 {
        bgy = (bg1h / 2) - (disph / 2) + bg_y
        if bgy < 0 : bgy = 0
        if bgy >= (bg1h - disph) : bgy = bg1h - disph

        bgw = bg1w
        bgh = bg1h
        bgx = int(double(bg_x) * 0.6) \ bgw
        bgid = bgimgid1
        gmode gmode_gdi
        gosub *draw_bg

        bgw = bg0w
        bgh = bg0h
        bgx = int(double(bg_x) * 0.8) \ bgw
        bgid = bgimgid0
        gmode gmode_rgb0
        gosub *draw_bg
    } else {
        gmode gmode_gdi
        color 0, 0, 0
        boxf 0, 0, dispw, disph
    }
    
    gmode gmode_gdi

    ; 道路を描画 / draw road
    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 <  (dist / 4) : 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

    ; 仮想画面を実画面に転送
    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
        color 1, 1, 1
        ; pos 8, 4
        ; mes "Press cursor key"
        msg = "Res:" + dispw + "x" + disph
        msg += " " + int(nowframerate) + "fps"
        msg += "  bg_y = " + int(bg_y)
        pos (SCR_W / 2) - 100, 2
        mes msg
    }

    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

*draw_bg
    ; BGを1枚描画 / draw BG. 1 layer
    ; bgid : bg image buffer id
    ; bgx, bgy : bg position
    ; bgw, bgh : bg image width, height
    ; dispw, disph : screen width, height
    if bgx >= 0 & bgx < (bgw - dispw) {
        pos 0,0
        gcopy bgid, bgx, bgy, dispw, disph
    } else {
        if bgx < 0 {
            w = -bgx
            pos 0, 0
            gcopy bgid, bgw - w, bgy, w, disph
            pos w, 0
            gcopy bgid, 0, bgy, dispw - w, disph
        } else {
            w = bgw - bgx
            pos 0, 0
            gcopy bgid, bgx, bgy, w, disph
            pos (bgw - bgx), 0
            gcopy bgid, 0, bgy, dispw - w, disph
        }
    }
    return

*get_easing_value
    ; イージングを考慮した値を返す
    ; easing : 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
    
    imgid = sprimgid0
    imgw = spr0w
    imgh = spr0h
    
    switch spr_kind
    case 1
    case 2
    case 3
    case 4
        ; tree
        n = spr_kind - 1
        z = double(z1)
        
        spr_h = 450.0
        scale = spr_scale * spr_h * double(dist) / z
        dst_w = int(scale)
        dst_h = int(scale)
        
        spr_xpos = (double(spr_x) * double(dist) / z) + cx1
        dst_x = int(spr_xpos - (dst_w / 2) + (dispw / 2))
        dst_y = int(y0 - dst_h + base_y)
        swbreak
    case 5
    case 6
        ; arrow sign
        n = spr_kind - 1
        z = double(z1)
        
        spr_h = 150.0
        scale = spr_scale * spr_h * double(dist) / z
        dst_w = int(scale)
        dst_h = int(scale)
        
        spr_xpos = (double(spr_x) * double(dist) / z) + cx1
        dst_x = int(spr_xpos - (dst_w / 2) + (dispw / 2))
        dst_y = int(y0 - dst_h + base_y)
        swbreak
    case 7
        ; grass
        n = 6
        z = double(z1)
        
        spr_h = 200.0
        scale = spr_scale * spr_h * double(dist) / z
        dst_w = int(scale)
        dst_h = int(scale)
        
        spr_xpos = (double(spr_x) * double(dist) / z) + cx1
        dst_x = int(spr_xpos - (dst_w / 2) + (dispw / 2))
        dst_y = int(y0 - dst_h + base_y)
        swbreak
    case 8
        ; beam
        n = 7
        z = double(z1)
        
        spr_h = 1080.0
        scale = spr_scale * spr_h * double(dist) / z
        dst_w = int(scale)
        dst_h = int(scale)
        
        spr_xpos = (double(spr_x) * double(dist) / z) + cx1
        dst_x = int(spr_xpos - (dst_w / 2) + (dispw / 2))
        dst_y = int(y0 - dst_h + base_y)
        swbreak
    swend
    
    if SEG_DELIMITER == 0 {
        if z < (dist / 2) : return
        ; gosub *draw_sprite_gzoom
        draw_sprite_gzoom dst_x, dst_y, dst_w, dst_h, imgid, imgw, imgh, n, tmpid, dispw, disph, vscrbufid
    }
    return

#module

; *draw_sprite_gzoom
#deffunc draw_sprite_gzoom int dst_x, int dst_y, int dst_w, int dst_h, int imgid, int imgw, int imgh, int n, int tmpid, int dispw, int disph, int dstid
    ; スプライト相当を拡大縮小して描画
    ;
    ; dst_x, dst_y : 描画先の左上座標
    ; dst_w, dst_h : 描画先のサイズ
    ; imgid : 元画像バッファID
    ; imgw, imgh : 元画像サイズ
    ; n : スプライトNo.
    ; tmpid : 作業用イメージバッファID
    ; dispw, disph : 画面サイズ
    ; dstid : 描画先バッファID
    
    ; テクスチャのソース座標を算出
    src_w = (imgw / 4)
    src_h = (imgh / 2)
    src_x = (n & 3) * src_w
    src_y = ((n >> 2) & 1) * src_h
    
    if dst_x >= dispw or (dst_x + dst_w) < 0 : return
    if dst_y >= disph or (dst_y + dst_h) < 0 : return
    
    dx = dst_x
    dy = dst_y
    dw = dst_w
    dh = dst_h
    sx = src_x
    sy = src_y
    sw = src_w
    sh = src_h
    
    if dx < 0 {
        ; assert
        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, imgid, 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

    return
#global

*jobend
    end
      

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